我正在尝试使用Weka的Java API创建一个“自动训练”,但我觉得我做错了什么。每当我通过Weka的界面使用MultiLayerPerceptron进行10折交叉验证或66%的百分比分割测试我的ARFF文件时,我会得到一些满意的结果(约90%),但当我通过Weka的API测试同一文件时,每次测试都基本返回0%的匹配度(每一行都返回false)。
这是Weka GUI的输出:
=== 测试分割的评估 ====== 摘要 ===
正确分类的实例 78 91.7647 %错误分类的实例 7 8.2353 %Kappa统计量 0.8081平均绝对误差 0.0817均方根误差 0.24 相对绝对误差 17.742 %根相对平方误差 51.0603 %总实例数 85
=== 按类别详细准确度 ===
真正率 假正率 精确度 召回率 F-Measure ROC面积 类别 0.885 0.068 0.852 0.885 0.868 0.958 1 0.932 0.115 0.948 0.932 0.94 0.958 0加权平均 0.918 0.101 0.919 0.918 0.918 0.958
=== 混淆矩阵 ===
a b <-- 分类为 23 3 | a = 1 4 55 | b = 0
这是我在Java中使用的代码(实际上是在.NET中使用IKVM):
var classifier = new weka.classifiers.functions.MultilayerPerceptron();classifier.setOptions(weka.core.Utils.splitOptions("-L 0.7 -M 0.3 -N 75 -V 0 -S 0 -E 20 -H a")); //这些是测试在Weka GUI下运行时的相同选项(默认选项)string trainingFile = Properties.Settings.Default.WekaTrainingFile; //使用与Weka浏览器测试相同的文件路径weka.core.Instances data = null;data = new weka.core.Instances(new java.io.BufferedReader(new java.io.FileReader(trainingFile))); //加载文件data.setClassIndex(data.numAttributes() - 1); //将最后一列设置为类别属性cl.buildClassifier(data);var tmp = System.IO.Path.GetTempFileName(); //创建一个临时文件,用于创建一个包含我想要测试的实例的单行ARFF文件,该实例取自之前加载的ARFF文件using (var f = System.IO.File.CreateText(tmp)){ //长代码从数据库读取数据并重新生成行,模拟来自我真正想要测试的源的数据}var dataToTest = new weka.core.Instances(new java.io.BufferedReader(new java.io.FileReader(tmp)));dataToTest.setClassIndex(dataToTest.numAttributes() - 1);double prediction = 0;for (int i = 0; i < dataToTest.numInstances(); i++){ weka.core.Instance curr = dataToTest.instance(i); weka.core.Instance inst = new weka.core.Instance(data.numAttributes()); inst.setDataset(data); for (int n = 0; n < data.numAttributes(); n++) { weka.core.Attribute att = dataToTest.attribute(data.attribute(n).name()); if (att != null) { if (att.isNominal()) { if ((data.attribute(n).numValues() > 0) && (att.numValues() > 0)) { String label = curr.stringValue(att); int index = data.attribute(n).indexOfValue(label); if (index != -1) inst.setValue(n, index); } } else if (att.isNumeric()) { inst.setValue(n, curr.value(att)); } else { throw new InvalidOperationException("未处理的属性类型!"); } } } prediction += cl.classifyInstance(inst);}//预测在这里总是0,我的ARFF文件有两个类别:0和1,92个零和159个一
有趣的是,如果我将分类器更改为NaiveBayes,结果与通过Weka GUI进行的测试相匹配
回答:
您使用的是读取ARFF文件的过时方法。请参阅此文档。尝试使用以下方法:
import weka.core.converters.ConverterUtils.DataSource; ... DataSource source = new DataSource("/some/where/data.arff"); Instances data = source.getDataSet();
请注意,该文档还展示了如何直接连接到数据库,并绕过创建临时ARFF文件。您还可以从数据库读取数据,并手动创建实例来填充Instances对象。
最后,如果简单地将代码顶部的分类器类型更改为NaiveBayes就解决了问题,那么请检查Weka GUI中MultilayerPerceptron的选项,看看它们是否与默认值不同(不同的设置可能会导致同一种分类器类型产生不同的结果)。
更新:看起来您在代码中使用的数据与Weka GUI中使用的数据不同(来自数据库与原始训练文件的一个折叠);也可能是数据库中的特定数据实际上对MLP分类器看起来像是class 0
。为了验证是否是这种情况,您可以使用Weka界面将训练ARFF文件分割成训练/测试集,然后在您的代码中重复原始实验。如果结果与GUI相同,那么数据有问题。如果结果不同,那么我们需要更仔细地查看代码。您将调用的函数是这个(来自文档):
public Instances trainCV(int numFolds, int numFold)