第一个问题 Error Message
Not enough memory. Please load a smaller dataset or use larger heap size.
原因:
This error message appears when a user attempts to load or process a dataset that WEKA can not handle with the used heap size.
解决方案:
The Java heap size can be specified with the -Xmx option.
Let the classpath that contains a JAR file (weka.jar) be:
C:/Matlab/weka-3-5-7/
E.g., to use 256MB as heap size, the command line looks like this:
C:/Matlab/weka-3-5-7>java -Xmx256m -jar weka.jar
第二个问题 weka的StringToWordVector类解析
weka的StringToWordVector类可以将给定的文档格式的内容转换为vms模型的内容,而后者是文本分类必须的模块。按照weka要求,生成arff格式的文本:
- @relation D__java_weka_data
- @attribute text string
- @attribute class {test1,test2,test3}
- @data
- 'here we go go go go to do ',test1
- 'Mostly, I expect we are interested in indexing XPath queries',test1
- 'so what do you think you can do anything?',test2
- 'Sparse ARFF files are very similar to ARFF files',test3
按照StringToWordVector类的命令格式,设定options:
- String[] options = { "-C", "-T", "-i", "data//train.arff", "-o","data//res_train.arff", "-c", "last"};
生成结果如下:
- @relation 'D_java_weka_data-weka.filters.unsupervised.attribute.StringToWordVector-R1-W1000-prune-rate-1.0-C-T-N0-stemmerweka.core.stemmers.NullStemmer-M1-tokenizerweka.core.tokenizers.WordTokenizer -delimiters \" \\r\\n\\t.,;:\\\'\\\"()?!\"'
- @attribute class {test1,test2,test3}
- @attribute I numeric
- @attribute Mostly numeric
- @attribute XPath numeric
- @attribute are numeric
- @attribute do numeric
- @attribute expect numeric
- @attribute go numeric
- @attribute here numeric
- @attribute in numeric
- @attribute indexing numeric
- @attribute interested numeric
- @attribute queries numeric
- @attribute to numeric
- @attribute we numeric
- @attribute anything numeric
- @attribute can numeric
- @attribute so numeric
- @attribute think numeric
- @attribute what numeric
- @attribute you numeric
- @attribute ARFF numeric
- @attribute Sparse numeric
- @attribute files numeric
- @attribute similar numeric
- @attribute very numeric
- @data
- {5 0.693147,7 1.609438,8 0.693147,13 0.693147,14 0.693147}
- {1 0.693147,2 0.693147,3 0.693147,4 0.693147,6 0.693147,9 0.693147,10 0.693147,11 0.693147,12 0.693147,14 0.693147}
- {0 test2,5 1.098612,15 0.693147,16 0.693147,17 0.693147,18 0.693147,19 0.693147,20 1.098612}
- {0 test3,4 0.693147,13 0.693147,21 1.098612,22 0.693147,23 1.098612,24 0.693147,25 0.693147}
属性评估方法:
CfsSubsetEval:根据属性子集中每一个特征的预测能力以及它们之间的关联性进行评估。 ChiSquaredAttributeEval:根据与分类有关的每一个属性的卡方值进行评估。 ClassifierSubsetEval:根据训练集或测试集之外的数据评估属性子集。
ConsistencySubsetEval:根据利用属性子集进行分类时得到的分类值的一致性进行评价。 CostSensitiveAttributeEval:根据使其基础子集评估开销敏感性,变化选择子集评估方法。 CostSentitiveSubsetEval:方法同上。
FilteresAttributeEval:运行在任意过滤器之后的数据上的任意属性评估。 FilteredSubsetEval:方法同上。
GainRatioAttributeEval:根据与分类有关的每一个属性的增益比进行评估。 InfoGainAttributeEval:根据与分类有关的每一个属性的信息增益进行评估。
LatenSemanticAnalysis:根据数据的潜在的语义分析和转换进行评估,与随机搜索结合。 OneRAttributeEval:根据OneR分类器评估属性。
PrincipalComponents:根据数据的主要成分分析和转换进行评估。
ReliefFAttributeEval:通过反复测试一个实例和其同类或不同类中最近的实例上的属性值进行评估。
SignificanceAttributeEval:计算双向功能的概率意义评估属性值。
SymmetricalUncertAtrributeEval:根据与分类有关的每一个属性的对称不稳定性进行评估。 SymmetricalUncertAtrributeSetEval:根据与其他属性集有关的每一个属性的对称不稳定性进行评估。
WrapperSubsetEval:使用一种学习模式对属性集进行评估。
注:Filter类型的评估方法关注每一个属性的预测能力(最优特征子集由预测能力最强的那些单个属性组成),其一般借鉴统计、信息论的思想,根据数据集内在特性来进行评估; Wrapper类型的评估方法是用后续的学习算法嵌入到特性选择方程中,通过测试特征子集在此算法上的预测性能来决定其优劣。
搜索算法:
BestFirst:可回溯的贪婪搜索扩张,最好优先原则。 ExhaustiveSearch:穷举搜索,从空集出发。
FCBFSearch:基于相关性分析的特征选择方法。相关性匹配搜索。 GeneticSearch:Goldberg(1989)提出的简单遗传算法。 GreedyStepwise:向前或向后的单步搜索。 LinearForwardSelection:线性向前搜索。
RaceSearch:比较特征子集的交叉验证错误情况。 RandomSearch:随机搜索。 Ranker:对属性值排序。
RankSearch:选择一个评估器对属性进行排序。 ScatterSearchV1:离散搜索。
SubsetSizeForwardSelection:按照特征子集大小向前线性搜索,这是线性搜索的扩展。 TabuSearch:禁忌搜索。
第四个问题
Weka中文文本分类示例
第零步,准备你需要的工具,weka.jar,lucene-core.jar,IKAnalyzer.jar,把它们加到工程中。分词包你喜欢用什么自己选,不必非用IKAnalyzer.jar。
第一步,你要有中文的数据集,如果你已经有了任务,自不必说。如果没有,那一定要选一个公认的最好,我以前是用搜狗的文本分类数据集,后来发现搜狗的数据好像也不怎么被人承认。看网上说,北京大学建立的人民日报语料库、清华大学建立的现代汉语语料库这两个数据集似乎比较正式点,但人民日报这个数据集我感觉实在不怎么样,并且它毕竟是人民日报呀,能不选就不选。现在汉语语料库找了两下没找到。谭松波先生的数据集要一个声明,懒得写。感觉最方便的还是复旦的一个数据集:http://www.nlp.org.cn/docs/20030623/25/tc-corpus-train.rar。这个数据集我感觉不好的一点是它不是从同一个源上找的。
比如在Art类别中,它的文档是下面这种(有删除):
【 文献号 】1-2340
【原文出处】中国图书评论
【原刊地名】沈阳
【原刊期号】199510
【原刊页号】61-62
【分 类 号】Z1
【分 类 名】出版工作、图书评介
【 作 者 】杨小民
【复印期号】199602
【 标 题 】图书评论应当重视对书籍装帧艺术的评价
【 正 文 】
图书评论是近代报刊业兴起后,在世界各国得到长足发展的一种新型评论体裁。而不论是书评理论还是书评实践都有一个不小的疏漏,即忽
视了图书的形式因素。因为图书是内容与形式的综合体,忽视了“图书形式”这一重要方面,会导致在图书评论活动中忽视对图书的出版形式这
一重要方面的品评论述,而这对于出版物的达到基本要求:“形神俱佳”(“形”指书装艺术,“神”指内容叙述)或最高要求“尽善尽美”(
“尽善”指内容而言,“尽美”指形式而言)无疑是有缺憾的。
// Koala++省略
(本文责任编辑 韩忠良)*
而在Philosophy类别中有下面的这种:
发信人: warmoon (宁静致远), 信区: philosophy
标 题: 哲学的用处
发信站: 日月光华站 (Thu Apr 22 08:10:28 1999), WWW-POST@134.68.57.63
哲学的用处 (季羡林)
我曾在很多文章中说到过自己的一个偏见:我最害怕哲学和哲学家,有一千个哲学家
,就有一千种哲学,有的哲学家竟沦为修辞学家。我怀疑,这样的哲学究竟有什么用
处。
// Koala++省略
--
我的灵魂象被裹在蚕茧里的蛹,在黑暗中痛苦与挣扎,几乎被茧壳窒息。但
窒息我的茧壳却给予我充足的养料,让我的灵魂逐渐成长,壮大,使它有一天终
于有力量挣脱躯壳,自由飞舞在人世间
Who said night is always cold?
--
是的,如果借助这些特殊的信息,比如我发现有”日月光华站”(假设它是一个词),那它就是属于哲学类别的,但这明显是不对的嘛。如果要用这个数据集,我认为应该自己写程序去把无关的信息去掉,比如这些头信息,还有尾部的本文责任编辑,个人签名。
第二步,数据集要准备成weka能处理的结构,这很好做到,你把数据集压缩了就行了,因为它要求的格式是,一个类别的文件放一个文件夹下(你可以参考我weka[48])。但是还有一个问题,你的机器往往没那么多内存去处理这个数据集,那么你可以选几个类别出来,在每个类别中放几十个文档来做就可以了。
第三步,分词,在wvtool里你可以继承它的分词类,使用自己的逻辑,weka也是可以的。但是最方便的还是直接分词。
我的做法很简单,把源文件夹下的文件全部分好词,再保存到另一个文件中,下面是我的实现代码(呵呵,见笑了,其实我不懂java):
package preprocess;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.TermAttribute;
import org.wltea.analyzer.lucene.IKAnalyzer;
public class Segmenter {
private String sourceDir;
private String targetDir;
Segmenter( String source, String target ) {
sourceDir = source;
targetDir = target;
}
public void segment() {
segmentDir( sourceDir, targetDir );
}
public void segmentDir( String source, String target ) {
File[] file = (new File( source )).listFiles();
for (int i = 0; i < file.length; i++) {
if (file[i].isFile()) {
segmentFile( file[i].getAbsolutePath(), target +
File.separator + file[i].getName() );
}
if (file[i].isDirectory()) {
String _sourceDir = source + File.separator +
file[i].getName();
String _targetDir = target + File.separator +
file[i].getName();
(new File(_targetDir)).mkdirs();
segmentDir( _sourceDir, _targetDir );
}
}
}
public void segmentFile( String sourceFile, String targetFile ) {
try {
FileReader fr = new FileReader( sourceFile );
BufferedReader br = new BufferedReader(fr);
FileWriter fw = new FileWriter( targetFile );
BufferedWriter bw = new BufferedWriter(fw );
Analyzer analyzer = new IKAnalyzer();
TokenStream tokenStream = analyzer.tokenStream("", br );
TermAttribute termAtt = (TermAttribute) tokenStream
.getAttribute(TermAttribute.class);
while (tokenStream.incrementToken()) {
bw.write( termAtt.term() );
bw.write(' ');
}
bw.close();
fw.close();
} catch( IOException e ) {
e.printStackTrace();
}
}
public static void main( String[] args ) throws Exception {
Segmenter segmenter = new Segmenter( "train", "train_segmented" );
segmenter.segment();
}
}
第四步,把TextDirectoryLoader复制一份到自己的包中,weka本身的实现在字符集方面似乎有点问题,但我又没兴趣去理解(总是有更值得去了解的事嘛),以下做法仅供参考:把下面几行代码注释掉:
/*int c;
while ((c = is.read()) != -1) {
txtStr.append((char) c);
}*/
在它下面添加几行代码:
FileReader fr = new FileReader( txt );
BufferedReader br = new BufferedReader(fr);
String line;
while( (line = br.readLine()) != null ) {
txtStr.append( line + "\n" );
}
第五步,使用weka wiki中的例子将数据集转换成arff格式:
package preprocess;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.Random;
import weka.classifiers.Evaluation;
import weka.classifiers.bayes.NaiveBayes;
import weka.core.Instances;
import weka.core.stemmers.NullStemmer;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.StringToWordVector;
public class TextCategorizationTest {
public static void main(String[] args) throws Exception {
String filename = "D:\\workspace\\text_mining\\test";
// convert the directory into a dataset
TextDirectoryLoader loader = new TextDirectoryLoader();
loader.setDirectory(new File( filename ));
Instances dataRaw = loader.getDataSet();
//System.out.println("\n\nImported data:\n\n" + dataRaw);
{
FileWriter fw = new FileWriter( "dataRaw.arff" );
BufferedWriter bw = new BufferedWriter(fw );
bw.write( dataRaw.toString() );
bw.close();
fw.close();
}
StringToWordVector filter = new StringToWordVector();
filter.setStemmer( new NullStemmer() );
filter.setInputFormat(dataRaw);
Instances dataFiltered = Filter.useFilter(dataRaw, filter);
{
FileWriter fw = new FileWriter( "dataFiltered.arff" );
BufferedWriter bw = new BufferedWriter(fw );
bw.write( dataFiltered.toString() );
bw.close();
fw.close();
}
//System.out.println("\n\nFiltered data:\n\n" + dataFiltered);
dataFiltered.setClassIndex( 0 );
// train J48 and output model
NaiveBayes classifier = new NaiveBayes();
/*classifier.buildClassifier(dataFiltered);
System.out.println("\n\nClassifier model:\n\n" + classifier);*/
Evaluation eval = new Evaluation(dataFiltered);
eval.crossValidateModel(classifier, dataFiltered, 3, new Random(1));
System.out.println(eval.toClassDetailsString());
System.out.println(eval.toSummaryString());
System.out.println(eval.toMatrixString());
}
}
唯一要提醒的是TextDirectoryLoader这个类当然是要用第四步修改的类,不然出来的全是乱码。你可以把dawData和dataFilted打印出来看一下。
最后我想解释一下产生的arff文件,它的类别在第一列,别搞错了。有的weka使用者可能以前没有见过压缩格式的arff,我举一个压缩格式的例子:{1 1,6 1,7 1,12 1,13 1},它表示第2个字段值为1,第7个字段值为1,第8个字段值为1,第13个字段值为1,第14个字段值为1。如果你用文本编辑器打开最后产生的arff文件,你可能会糊涂,怎么搞的,第一个类别没有?其实是第一个类别它的离散值就是0,所以不显示。别激动,呵呵。