Weka集成分类器
package cn.edu.xmu.bdm.wekainjava.test; import java.io.File; import weka.classifiers.Classifier; import weka.classifiers.Evaluation; import weka.classifiers.bayes.NaiveBayes; import weka.classifiers.functions.LibSVM; import weka.classifiers.meta.Vote; import weka.core.Instances; import weka.core.SelectedTag; import cn.edu.xmu.bdm.wekainjava.utils.WekaFactory; import cn.edu.xmu.bdm.wekainjava.utils.WekaFactoryImpl; public class EnsembleTest { public static void main(String[] args) throws Exception { // LibSVM classifier = new LibSVM(); File trainFile = new File( "C://Program Files//Weka-3-6//data//segment-challenge.arff"); File testFile = new File( "C://Program Files//Weka-3-6//data//segment-test.arff"); /** * 1. 获取weka工厂类 */ WekaFactory wi = WekaFactoryImpl.getInstance(); /** * 3. 从工厂中获取训练样本和测试样本实例 */ Instances instancesTrain = wi.getInstance(trainFile); instancesTrain.setClassIndex(instancesTrain.numAttributes() - 1); Instances instancesTest = wi.getInstance(testFile); instancesTest.setClassIndex(instancesTest.numAttributes() - 1); /** * 2. 从工厂中获取分类器 具体使用哪一种特定的分类器可以选择 这样就构建了一个简单的分类器 */ Classifier j48 = (Classifier) wi.getClassifier(LibSVM.class); Classifier naiveBayes = (Classifier)wi.getClassifier(NaiveBayes.class); Classifier libSVM = (Classifier)wi.getClassifier(LibSVM.class); /** * 2.1 设置集成分类器 */ Classifier[] cfsArray = new Classifier[3]; cfsArray[0] = j48; cfsArray[1] = naiveBayes; cfsArray[2] = libSVM; /** * 2.2 定制集成分类器的决策方式 * AVERAGE_RULE * PRODUCT_RULE * MAJORITY_VOTING_RULE * MIN_RULE * MAX_RULE * MEDIAN_RULE * 它们具体的工作方式,参考weka的说明文档。 * 通常情况下选择的是多数投票的决策规则 */ Vote ensemble = new Vote(); SelectedTag tag = new SelectedTag(Vote.MAJORITY_VOTING_RULE, Vote.TAGS_RULES); ensemble.setCombinationRule(tag); ensemble.setClassifiers(cfsArray); //设置随机数种子 ensemble.setSeed(2); //训练ensemble分类器 ensemble.buildClassifier(instancesTrain); /** * 5. 从工厂中获取使用Evaluation,测试样本测试分类器的学习效果 */ double sum = instancesTrain.numInstances(); Evaluation testingEvaluation = wi.getEvaluation(ensemble, instancesTest); int length = instancesTest.numInstances(); for (int i = 0; i < length; i++) { // 通过这个方法来用每个测试样本测试分类器的效果 testingEvaluation.evaluateModelOnceAndRecordPrediction(ensemble, instancesTest.instance(i)); } // double[][] confusionMatrix = testingEvaluation.confusionMatrix(); // for (int i = 0; i < confusionMatrix.length; i++) { // double[] ds = confusionMatrix[i]; // for (int j = 0; j < ds.length; j++) { // System.out.print(ds[j]); // } // System.out.println(); // } System.out.println(testingEvaluation.toSummaryString()); System.out.println(testingEvaluation.toMatrixString()); System.out.println(testingEvaluation.toClassDetailsString()); // System.out.println(testingEvaluation.toCumulativeMarginDistributionString()); System.out.println("分类器的正确率:" + (1 - testingEvaluation.errorRate())); } }
网址:http://irwenqiang.iteye.com/blog/1408438
个人推荐的Weka教程,包含了数据格式、数据准备、分类和聚类Demo
WEKA的全名是怀卡托智能分析环境(Waikato Environment for Knowledge Analysis),它的源代码可通过http://www.cs.waikato.ac.nz/ml/weka得到
同时weka也是新西兰的一种鸟名,而WEKA的主要开发者来自新西兰。
WEKA作为一个公开的数据挖掘工作平台,集合了大量能承担数据挖掘任务的机器学习算法,包括对数据进行预处理,分类,回归、聚类、关联规则以及在新的交互式界面上的可视化。如果想自己实现数据挖掘算法的话,可以看一看weka的接口文档。在weka中集成自己的算法甚至借鉴它的方法自己实现可视化工具并不是件很困难的事情。
我学习的过程中会持续把有用的教材上传...
------
调用weka API写了两个java程序,很基本:
- package cn.edu.xmu.bdm.wekainjava.test;
- import java.io.FileReader;
- import weka.core.Instances;
- /**
- * desc:试试Weka中最基本最重要的Instance类
- * <code>InstanceTest</code>
- * @version 1.0 2011/12/13
- * @author chenwq
- *
- */
- publicclass InstanceTest {
- publicstatic Instances getFileInstances(String fileName) throws Exception {
- FileReader frData = new FileReader(fileName);
- Instances data = new Instances(frData);
- return data;
- }
- publicstaticvoid main(String[] args) throws Exception {
- Instances instances = getFileInstances("C:\\Program Files\\Weka-3-6\\data\\bank-data-final.arff");
- // 把数据集全部输入出
- // System.out.println( instances );
- // 用numInstances可以获得数据集中有多少样本
- for (int i = 0; i < instances.numInstances(); i++) {
- // instance( i )是得到第i个样本
- System.out.println(instances.instance(i));
- }
- }
- }
package cn.edu.xmu.bdm.wekainjava.test; import java.io.FileReader; import weka.core.Instances; /** * desc:试试Weka中最基本最重要的Instance类 * <code>InstanceTest</code> * @version 1.0 2011/12/13 * @author chenwq * */ public class InstanceTest { public static Instances getFileInstances(String fileName) throws Exception { FileReader frData = new FileReader(fileName); Instances data = new Instances(frData); return data; } public static void main(String[] args) throws Exception { Instances instances = getFileInstances("C:\\Program Files\\Weka-3-6\\data\\bank-data-final.arff"); // 把数据集全部输入出 // System.out.println( instances ); // 用numInstances可以获得数据集中有多少样本 for (int i = 0; i < instances.numInstances(); i++) { // instance( i )是得到第i个样本 System.out.println(instances.instance(i)); } } }
- package cn.edu.xmu.bdm.wekainjava.test;
- /**
- * desc:试试Weka的决策树类
- * <code>J48Test</code>
- * @version 1.0 2011/12/13
- * @author chenwq
- *
- */
- import java.io.File;
- import java.io.IOException;
- import weka.classifiers.Classifier;
- import weka.classifiers.trees.J48;
- import weka.core.Instances;
- import weka.core.converters.ArffLoader;
- publicclass J48Test {
- /**
- * @param args
- * @throws Exception
- */
- publicstaticvoid main(String[] args) throws Exception {
- Classifier m_classifier = new J48();
- File inputFile = new File("C:\\Program Files\\Weka-3-6\\data\\cpu.with.vendor.arff");//训练语料文件
- ArffLoader atf = new ArffLoader();
- atf.setFile(inputFile);
- Instances instancesTrain = atf.getDataSet(); // 读入训练文件
- inputFile = new File("C:\\Program Files\\Weka-3-6\\data\\cpu.with.vendor.arff");//测试语料文件
- atf.setFile(inputFile);
- Instances instancesTest = atf.getDataSet(); // 读入测试文件
- instancesTest.setClassIndex(0); //设置分类属性所在行号(第一行为0号),instancesTest.numAttributes()可以取得属性总数
- double sum = instancesTest.numInstances(),//测试语料实例数
- right = 0.0f;
- instancesTrain.setClassIndex(0);
- m_classifier.buildClassifier(instancesTrain); //训练
- for(int i = 0;i<sum;i++)//测试分类结果
- {
- if(m_classifier.classifyInstance(instancesTest.instance(i))==instancesTest.instance(i).classValue())//如果预测值和答案值相等(测试语料中的分类列提供的须为正确答案,结果才有意义)
- {
- right++;//正确值加1
- }
- }
- System.out.println("J48 classification precision:"+(right/sum));
- }
- }
参考网址:http://irwenqiang.iteye.com/blog/1308834#bc2281931
参考三:http://quweiprotoss.blog.163.com/blog/static/40882883201103051150347/
时不时有人问我有中文的文本数据,怎么分类的问题。我以前给别人做过一个中文分类的程序,预处理部分是用wvtool写的,不过交易的时候,对方说我不能将有关的代码公布出来,所以我也就没有贴出来。现在我写一篇只用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;
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("\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,所以不显示。别激动,呵呵。