|||
根据 屈伟博客 编写。 原文 http://quweiprotoss.blog.163.com/blog/static/40882883201103051150347/
第零步,准备你需要的工具,weka.jar,lucene-core.jar,IKAnalyzer.jar,把它们加到工程中。分词包你喜欢用什么自己选,不必非用IKAnalyzer.jar。
第一步,你要有中文的数据集,如果你已经有了任务,自不必说。如果没有,那一定要选一个公认的最好,我以前是用搜狗的文本分类数据集,后来发现搜狗的数据好像也不怎么被人承认。看网上说,北京大学建立的人民日报语料库、清华大学建立的现代汉语语料库这两个数据集似乎比较正式点,但人民日报这个数据集我感觉实在不怎么样,并且它毕竟是人民日报呀,能不选就不选。现在汉语语料库找了两下没找到。谭松波先生的数据集要一个声明,懒得写。感觉最方便的还是复旦的一个数据集:http://www.nlp.org.cn/docs/20030623/25/tc-corpus-train.rar。这个数据集我感觉不好的一点是它不是从同一个源上找的。
第二步,数据集要准备成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;
publicclass Segmenter {
private String sourceDir;
private String targetDir;
Segmenter( String source, String target ) {
sourceDir = source;
targetDir = target;
}
publicvoid segment() {
segmentDir( sourceDir, targetDir );
}
publicvoid 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 );
}
}
}
publicvoid 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();
}
}
publicstaticvoid 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;
publicclass TextCategorizationTest {
publicstaticvoid 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("nnImported data:nn" + 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("nnFiltered data:nn" + dataFiltered);
dataFiltered.setClassIndex( 0 );
// train NaiveBayes and output model
NaiveBayes classifier = new NaiveBayes();
/*classifier.buildClassifier(dataFiltered);
System.out.println("nnClassifier model:nn" + 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,所以不显示。别激动,呵呵。
-------------------------------下面的内容为李向东添加:
说明:上面这段代码的最后5句,其实是用weka的评估算法对naiveBayes的分类效果进行评估。如果只想利用naiveBayes对训练集进行训练并保存得到的分类模型,代码可以这么写:
// train NaiveBayes and output model
NaiveBayes classifier = new NaiveBayes();
classifier.buildClassifier(dataFiltered);
// serialize model
SerializationHelper.write("\some\where\naivebayes.model", classifier);
这样,当你有个实例需要调用训练好的模型进行分类时,可以这么写:
// deserialize model
Classifier classifier = (Classifier) weka.core.SerializationHelper.read("\some\where\naivebayes.model");
//prepare the sample
String samplefile = "\some\where\test\singlesample.arff";
BufferedReader reader = new BufferedReader(new FileReader(samplefile));
ArffReader arff = new ArffReader(reader);
Instances sampledata = arff.getData();
sampledata.setClassIndex(0);
//classify
double prediction = classifier.classifyInstance(sampledata.instance(0));
String category = sampledata.classAttribute().value((int)prediction);
System.out.println("the sample is belong to: " + category);
但注意,仅当sampledata中的分类枚举信息是正确的,上面的语句才能返回正确的字符串值。
Archiver|手机版|科学网 ( 京ICP备07017567号-12 )
GMT+8, 2024-5-6 08:12
Powered by ScienceNet.cn
Copyright © 2007- 中国科学报社