lixiangdong的个人博客分享 http://blog.sciencenet.cn/u/lixiangdong

博文

Weka中文文本分类示例

已有 9582 次阅读 2012-5-7 15:31 |个人分类:weka|系统分类:科研笔记| 博客, 中文文本分类, 屈伟

根据 屈伟博客 编写。 原文 http://quweiprotoss.blog.163.com/blog/static/40882883201103051150347/

         第零步,准备你需要的工具,weka.jarlucene-core.jarIKAnalyzer.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这个类当然是要用第四步修改的类,不然出来的全是乱码。你可以把dawDatadataFilted打印出来看一下。

         最后我想解释一下产生的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中的分类枚举信息是正确的,上面的语句才能返回正确的字符串值。



https://wap.sciencenet.cn/blog-713110-568176.html


下一篇:基于weka的中文文本分类:对任意单个文本进行分类
收藏 IP: 61.163.70.*| 热度|

0

该博文允许注册用户评论 请点击登录 评论 (2 个评论)

数据加载中...

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-5-6 08:12

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部