WEKA入门
WEKA是一个用于机器学习和数据挖掘的综合工作台。它的主要优势在于分类领域,许多主要的机器学习方法都是在一个干净的、面向对象的Java类层次结构中实现的。还实现了回归、关联规则挖掘、时间序列预测和聚类算法。
本文档简要介绍了如何从命令行界面使用WEKA。我们将从描述基本概念和思想开始。然后,我们将描述weka.filter包,该包用于转换输入数据,例如,用于预处理、转换、特征生成等。接下来,我们将考虑一些生成分类模型的机器学习算法。在此基础上,给出了一些应用实例。
请注意,在WEKA安装目录的doc目录中,你可以找到WEKA中所有Java类的文档。准备使用它,因为本简介并不完整。如果你想知道到底发生了什么,请看一下源代码,这些源代码可以在weka-src.jar中找到,并可以通过Java开发工具包中的JAR实用程序解压缩。
点击这里进入WEKA入门英文版。
基本概念
数据集
一组数据项,即数据集,是机器学习的一个非常基本的概念。数据集大致相当于二维电子表格或数据库表。在WEKA中,它由Instance类实现。数据集是示例的集合,每个示例都是类实例。每个实例都由许多属性组成,其中任何属性都可以是名义的(=预定义的值列表中的一个)、数值(=实数或整数)或字符串(=用“双引号”括起来的任意长字符列表)。WEKA还支持日期属性和关系属性。Instance类的外部表示是一个ARFF文件,该文件由描述属性类型的头和逗号分隔列表形式的数据组成。这里有一个简短的注释示例。可以在此处找到ARFF文件格式的完整说明。
% This is a toy example, the UCI weather dataset. % Any relation to real weather is purely coincidental.
数据集开头的注释行应该指示其来源、上下文和含义。
@relation golfWeatherMichigan_1988/02/10_14days
在这里,我们声明数据集的内部名称。尽量做到描述性。
@attribute outlook {sunny, overcast rainy} @attribute windy {TRUE, FALSE}
在这里,我们定义了两个名义属性,outlook
和windy
。前者有三个值:sunny, overcast rainy
;后者两个:TRUE
和FALSE
。带有特殊字符、逗号或空格的标称值括在‘单引号’中。
@attribute temperature numeric @attribute humidity numeric
这些行定义了两个数字属性。
@attribute play {yes, no}
最后一个属性是用于预测的默认目标或类变量。在我们的示例中,它是一个具有两个值的名义属性,这使得这是一个二进制分类问题。
@data sunny,FALSE,85,85,no sunny,TRUE,80,90,no overcast,FALSE,83,86,yes rainy,FALSE,70,96,yes rainy,FALSE,68,80,yes
数据集的其余部分由标记@data
组成,后跟属性的逗号分隔值–每个示例一行。在我们的例子中,有五个例子。
给定ARFF文件的一些基本统计和验证可以通过weka.core.Instances的main()例程获得。实例:
java weka.core.Instances data/soybean.arff
weka.core
提供了一些其他有用的例程,例如Converters.C45Loader和Converters.CSVLoader,可以分别用于转换C45数据集和逗号/制表符分隔的数据集,例如:
java weka.core.converters.CSVLoader data.csv > data.arff java weka.core.converters.C45Loader c45_filestem > data.arff
分类器
WEKA中的任何分类或回归算法都派生自抽象的分类器类。令人惊讶的是,基本分类器只需要很少的东西:从训练数据集生成分类器模型的例程(=buildclassfier
)和为给定实例生成分类的另一个例程(=classfyInstance
),或者为实例的所有类生成概率分布(=distributionForInstance
)。
分类器模型是从预测器属性到类属性的任意复杂映射。此映射或模型的具体形式和创建因分类器而异。例如,ZeroR的模型只由单个值组成:在分类问题中是最常见的类,或者在预测数值时是所有数值的中位数(=回归学习)。ZeroR是一个微不足道的分类器,但它给出了给定数据集的性能下限,应该由更复杂的分类器显著提高。因此,这是一个合理的测试,可以在不考虑其他属性的情况下预测类的好坏。
稍后,我们将详细解释如何解释分类器的输出–现在只关注分层交叉验证一节中正确分类的实例,并注意当我们使用大豆(soybean)数据时,它如何从ZeroR改进到J48:
java weka.classifiers.rules.ZeroR -t soybean.arff java weka.classifiers.trees.J48 -t soybean.arff
有多种方法可以确定分类器的性能。最简单的测量方法是计算测试数据集中正确预测的示例所占的比例。该值是分类精度,也是1-ErrorRate。这两个术语都在文学中使用。
最简单的评估情况是当我们使用相互独立的训练集和测试集时。这被称为坚持估计。为了估计这些性能估计中的方差,可以通过重复对相同的数据集进行采样来计算坚持估计–即,随机地洗牌,然后用特定比例的示例将其分成训练集和测试集,收集测试集上的所有估计,并计算精度的平均和标准偏差。
一种更精细的方法是k-折交叉验证。这里,指定了折叠数k。数据集被随机洗牌,然后被分成大小相等的k个倍数。在每次迭代中,一个折叠用于测试,其他k-1个折叠用于训练分类器。测试结果被收集起来,并汇集(或平均)在所有褶皱上。这给出了准确性的交叉验证估计。折叠可以是纯随机的,也可以稍加修改以在每个折叠中创建与完整数据集中相同的类分布。在后一种情况下,交叉验证称为分层。留一法(LOO)交叉验证表示k等于示例数量。出于需要,loo cv必须是非分层的,即测试集中的类分布与训练数据中的类分布不同。因此,Loo CV在极少数情况下会产生误导性结果。然而,它在处理小数据集时仍然非常有用,因为它利用了来自数据集的最大量的训练数据。
WEKA过滤器
weka.filter包包含转换数据集的Java类–通过删除或添加属性、重采样数据集、删除示例等等。这个软件包为数据预处理提供了有用的支持,这是机器学习中的重要一步。
所有过滤器都提供了用于指定输入数据集的命令行选项-i和用于指定输出数据集的-o选项。如果没有给出这些参数中的任何一个,则指定标准输入RESP。在管道内使用的输出。其他参数特定于每个过滤器,可以通过-h找到,就像任何其他类一样。weka.filter包被组织为监督过滤和非监督过滤,这两个过滤又细分为实例过滤和属性过滤。我们将分别讨论这四个小节中的每一个。
weka.filters.supervised
在weka的Java类层次结构中,weka.filters.supervised下面的类用于监督过滤,即利用类信息。对于这些过滤器,必须通过-c提供类属性的索引来分配类。
属性
离散化用于根据类信息,通过Fayyad&Irani的MDL方法,或可选的使用Kononeko的MDL方法,将数值属性离散化为名义属性。一些学习模式或分类器只能处理名义数据,例如 rules.Prism;在某些情况下,离散化还可以减少学习时间,并有助于克服过度拟合。
java weka.filters.supervised.attribute.Discretize -i data/iris.arff -o iris-nom.arff -c last java weka.filters.supervised.attribute.Discretize -i data/cpu.arff -o cpu-classvendor-nom.arff -c first
NominalToBinary将所有名义属性编码为二进制(二值)属性,这些属性可用于将数据集转换为纯数字表示,例如,通过多维缩放进行可视化。
java weka.filters.supervised.attribute.NominalToBinary -i data/contact-lenses.arff -o contact-lenses-bin.arff -c last
注意,WEKA中的大多数分类器在内部使用转换过滤器,例如Logistic和SMO,因此你可能不必显式地使用这些过滤器。
实例
重采样创建给定数据集的分层子样本。这意味着整个类分布大致保留在样本中。对均匀类分布的偏向可以通过-B指定。
java weka.filters.supervised.instance.Resample -i data/soybean.arff -o soybean-5%.arff -c last -Z 5 java weka.filters.supervised.instance.Resample -i data/soybean.arff -o soybean-uniform-5%.arff -c last -Z 5 -B 1
StratifiedRemoveFolds创建给定数据集的分层交叉验证折叠。这意味着在默认情况下,类分布大致保留在每个折叠中。下面的示例将soybean.arff分为分层训练和测试数据集,后者包含25%(=1/4)的数据。
java weka.filters.supervised.instance.StratifiedRemoveFolds -i data/soybean.arff -o soybean-train.arff \ -c last -N 4 -F 1 -V java weka.filters.supervised.instance.StratifiedRemoveFolds -i data/soybean.arff -o soybean-test.arff \ -c last -N 4 -F 1
weka.filters.unsupervised
在WEKA的Java类层次结构中,weka.filters.unSupervised下面的类用于无监督过滤,例如,重采样的非分层版本。不应在此处分配类。
属性
StringToWordVector将字符串属性转换为单词向量,例如,为每个单词创建一个属性,该属性对字符串中的存在或单词计数(-C)进行编码。-W可以用来设置单词数量的近似限制。当指定一个类时,该限制将分别应用于每个类。此筛选器可用于文本挖掘。
Obfuscate(模糊)重命名数据集名称、所有属性名称和标称属性值。这是为了在不泄露受限信息的情况下交换敏感数据集。
Remove用于从数据集中显式删除属性,例如,用于删除iris数据集的属性:
java weka.filters.unsupervised.attribute.Remove -R 1-2 -i data/iris.arff -o iris-simplified.arff java weka.filters.unsupervised.attribute.Remove -V -R 3-last -i data/iris.arff -o iris-simplified.arff
实例
Resample(重采样)将创建给定数据集的非分层子采样。它在不考虑类别信息的情况下执行随机采样。否则,它就等同于它的监督变体。
java weka.filters.unsupervised.instance.Resample -i data/soybean.arff -o soybean-5%.arff -Z 5
RemoveFold创建给定数据集的交叉验证文件夹。不保留类分布。下面的示例将soybean.arff分为训练数据集和测试数据集,后者包含25%(=1/4)的数据。
java weka.filters.unsupervised.instance.RemoveFolds -i data/soybean.arff -o soybean-train.arff -c last -N 4 -F 1 -V java weka.filters.unsupervised.instance.RemoveFolds -i data/soybean.arff -o soybean-test.arff -c last -N 4 -F 1
RemoveWithValues根据属性的值筛选实例。
java weka.filters.unsupervised.instance.RemoveWithValues -i data/soybean.arff \ -o soybean-without_herbicide_injury.arff -V -C last -L 19
weka.classifiers
分类器是WEKA的核心。分类器有很多常见的选择,其中大部分都与评估目的有关。我们将把重点放在最重要的事情上。像往常一样,所有其他参数包括分类器特定参数都可以通过-h找到。
参数 | 描述 |
---|---|
-t | 指定训练文件(ARFF格式) |
-T | 以(ARFF格式)指定测试文件。如果缺少此参数,将执行交叉验证(默认值:10倍cv) |
-x | 此参数确定交叉验证的折叠次数。只有缺少-T时才会执行cv。 |
-c | 正如我们已经从weka.filter部分了解到的那样,该参数使用基于1的索引设置类变量。 |
-d | 通过此参数可以保存训练后的模型。每个分类器对模型有不同的二进制格式,因此只能由兼容数据集上的ct同一个分类器读回。只保存训练集上的模型,不保存通过交叉验证生成的多个模型。 |
-l | 加载以前保存的模型,通常用于测试以前未看到的新数据。在这种情况下,应该指定一个兼容的测试文件,也就是说,以相同的顺序指定相同的特性。 |
-p | 如果指定了测试文件,则此参数将显示所有测试实例的预测和一个属性(0表示无)。 |
-o | 此参数关闭模型描述的可读输出。对于支持向量机或NaiveBayes,这是有意义的,除非你想解析和可视化大量信息。 |
我们现在给出WEKA中选定分类器的简短列表:
trees.J48
C4.5决策树学习器的克隆。bayes.NaiveBayes
一个初级的贝叶斯学习者。-K打开数值属性的核密度估计,这通常会提高性能。meta.ClassificationViaRegression
-Wfunctions.LinearRegression
多响应线性回归。functions.Logistic
Logistic回归。functions.SMO
基于伪最小优化算法的支持向量机(线性、多项式和RBF核)〔Platt,1998〕。默认为线性核支持向量机,-E 5-C 10给出了多项式核为5且lambda=10的支持向量机。lazy.KStar
基于实例的学习者。-E自动设置混合熵,这通常是首选的。lazy.IBk
基于实例的固定邻域学习者。-K设置邻居的数目。IB1相当于IBk -K 1。rules.JRip
RIPPER规则学习器的克隆。
基于一个简单的例子,我们现在将解释一个典型分类器weka.classifiers.trees.J48的输出。从命令行考虑以下调用,或启动WEKA explorer并在weather.numeric.arff上训练J48:
java weka.classifiers.trees.J48 -t data/weather.numeric.arff
J48 pruned tree ------------------ outlook = sunny | humidity <= 75: yes (2.0) | humidity > 75: no (3.0) outlook = overcast: yes (4.0) outlook = rainy | windy = TRUE: no (2.0) | windy = FALSE: yes (3.0) Number of Leaves : 5 Size of the tree : 8
除非你指定-o,否则第一部分是训练集模型的人类可读形式。在本例中,它是一个决策树。Outlook位于树的根,并决定第一个决策。每片叶子末尾(括号)里的数字告诉我们这片叶子里有多少例子。如果有一片或多片叶子不是纯的(=全部属于同一类),则在/slash/之后还会给出错误分类的示例的数量。
Time taken to build model: 0.05 seconds Time taken to test model on training data: 0 seconds
如你所见,决策树学习速度相当快,评估速度甚至更快。
== Error on training data == Correctly Classified Instance 14 100 % Incorrectly Classified Instances 0 0 % Kappa statistic 1 Mean absolute error 0 Root mean squared error 0 Relative absolute error 0 % Root relative squared error 0 % Total Number of Instances 14 == Detailed Accuracy By Class == TP Rate FP Rate Precision Recall F-Measure Class 1 0 1 1 1 yes 1 0 1 1 1 no == Confusion Matrix == a b <-- classified as 9 0 | a = yes 0 5 | b = no
这很无聊:我们的分类器是完美的,至少在训练数据上——所有实例都被正确分类,所有错误都为零。通常情况下,训练集的准确性过于乐观。类别和混淆矩阵的详细精度是相似的。
== Stratified cross-validation == Correctly Classified Instances 9 64.2857 % Incorrectly Classified Instances 5 35.7143 % Kappa statistic 0.186 Mean absolute error 0.2857 Root mean squared error 0.4818 Relative absolute error 60 % Root relative squared error 97.6586 % Total Number of Instances 14 == Detailed Accuracy By Class == TP Rate FP Rate Precision Recall F-Measure Class 0.778 0.6 0.7 0.778 0.737 yes 0.4 0.222 0.5 0.4 0.444 no == Confusion Matrix == a b <-- classified as 7 2 | a = yes 3 2 | b = no
分层交叉验证描绘了一幅更真实的画面。准确率在64%左右。kappa统计量衡量预测与真类的一致性——1.0表示完全一致。所示的误差值,例如均方误差的根,指示由分类模型生成的概率估计的准确性。
混淆矩阵通常称为列联表。在我们的例子中,我们有两个类,因此一个2x2混淆矩阵,矩阵可以任意大。正确分类的实例数是矩阵中对角线的总和;所有其他实例都被错误分类(类“a”被错误分类为“b”正好两次,类“b”被错误分类为“a”三次)。
真阳性率(TP)是指被分类为x类的实例在所有真正具有x类的实例中所占的比例,即正确捕获了多少类。这相当于召回。在混淆矩阵中,这是对角线元素除以相关行的和,即7/(7+2)=0.778(对于类yes)和2/(3+2)=0.4(对于类no)。
误报率(FP)是指在所有不属于x类的示例中,被分类为x类但属于不同类的示例所占的比例。在矩阵中,这是x类的列和减去对角线元素,除以所有其他类的行和;即3/5=0.6表示类是,2/9=0.222表示类否。
精度(Precision)是在所有分类为x类的示例中,真正具有x类的示例所占的比例。在矩阵中,这是对角线元素除以相关列上的和,即7/(7+3)=0.7表示是类,2/(2+2)=0.5表示否类。
F度量是简单的 2precision Recall/(Precision+Recall),一种精确和召回的组合度量。
这些度量对于比较分类器是有用的。但是,如果需要有关分类器预测的更详细信息,-p#只输出每个测试实例的预测,以及一个基于一个属性id的范围(0表示无)。让我们看看下面的例子。我们假设soybean-train.arff
和soybean-test.arff
是通过weka.filters.supervised.instance.StratifiedRemoveFolds
构建的,如前一个示例所示。
java weka.classifiers.bayes.NaiveBayes -K -t soybean-train.arff -T soybean-test.arff -p 0
0 diaporthe-stem-canker 0.9999672587892333 diaporthe-stem-canker 1 diaporthe-stem-canker 0.9999992614503429 diaporthe-stem-canker 2 diaporthe-stem-canker 0.999998948559035 diaporthe-stem-canker 3 diaporthe-stem-canker 0.9999998441238833 diaporthe-stem-canker 4 diaporthe-stem-canker 0.9999989997681132 diaporthe-stem-canker 5 rhizoctonia-root-rot 0.9999999395928124 rhizoctonia-root-rot 6 rhizoctonia-root-rot 0.999998912860593 rhizoctonia-root-rot 7 rhizoctonia-root-rot 0.9999994386283236 rhizoctonia-root-rot ...
每行中的值由一个空格分隔。字段是基于零的测试实例id,后跟预测的类值、预测的置信度(预测类的估计概率)和真类。所有这些都是正确的分类,所以让我们看看几个错误的分类。
32 phyllosticta-leaf-spot 0.7789710144361445 brown-spot ... 39 alternarialeaf-spot 0.6403333824349896 brown-spot ... 44 phyllosticta-leaf-spot 0.893568420641914 brown-spot ... 46 alternarialeaf-spot 0.5788190397739439 brown-spot ... 73 brown-spot 0.4943768155314637 alternarialeaf-spot
在每一种情况下,都会出现一个错误的分类,主要是在alternarialeaf-spot和brown-spot之间。信任度似乎低于正确分类,因此对于实际应用程序来说,输出不知道低于某个阈值可能是有意义的。WEKA还输出一个尾随的换行符。
如果我们通过-p选择了一系列属性,例如-p first last
,那么在parantesses
中,所提到的属性随后将作为逗号分隔值输出。但是,第一列中基于零的实例id提供了一种更安全的方法来确定测试实例。
通常,如果你对分类器进行较长时间的测试,你将执行以下操作(对于csh):
java -Xmx1024m weka.classifiers.trees.J48 -t data.arff -k -d J48-data.model >&! J48-data.out &
最大堆大小的-Xmx1024m参数使Java存储对象的Java堆的最大大小增加到1024兆字节。不涉及开销,只会给堆留下更多的空间。k标志为你提供一些额外的性能统计信息。如果你的模型运行良好,那么通过-d保存它是有意义的-你可以稍后删除它!隐式交叉验证比训练集精度能更合理地估计未知数据的期望精度。标准错误和输出的输出都应该被重定向,这样就可以得到分类器的错误和正常输出。最后一个&在后台启动任务。通过top监视你的任务,如果你注意到硬盘一直在工作(对于linux),这可能意味着你的任务需要太多内存,无法及时完成考试。;-)在这种情况下,切换到更快的分类器或使用过滤器,例如,用于重新采样以减小数据集的大小,或使用StratifiedRemoveFolds创建训练集和测试集-对于大多数分类器,训练比测试花费更多时间。
那么,现在你已经做了很多实验——哪个分类器最好?尝试
cat *.out | grep -A 3 "Stratified" | grep "^Correctly"
……这会给你所有交叉验证的准确性。如果交叉验证的准确度与训练集的准确度大致相同,则这表示分类器可能没有过度拟合训练集。
假设你找到了最好的分类器。要将其应用于新的数据集,请使用
java weka.classifiers.trees.J48 -l J48-data.model -T new-data.arff
你必须使用相同的分类器来加载模型,但不需要设置任何选项。只需通过-T添加新的测试文件。如果需要,-p first last将输出所有具有分类和置信度分数的测试实例,然后输出所有属性值,这样你就可以分别查看每个错误。
下面更复杂的csh脚本创建用于学习曲线的数据集,从给定的数据集创建75%的训练集和25%的测试集,然后依次将测试集减小因子1.2(83%),直到它的大小也减小到25%。所有这些都重复了30次,使用不同的随机重新排序(-S),结果被写入不同的目录。WEKA中的Experimenter GUI可以用来设计和运行类似的实验。
#!/bin/csh foreach f ($*) set run=1 while ( $run <= 30 ) mkdir $run >&! /dev/null java weka.filters.supervised.instance.StratifiedRemoveFolds -N 4 -F 1 -S $run -c last -i ../$f -o $run/t_$f java weka.filters.supervised.instance.StratifiedRemoveFolds -N 4 -F 1 -S $run -V -c last -i ../$f -o $run/t0$f foreach nr (0 1 2 3 4 5) set nrp1=$nr @ nrp1++ java weka.filters.supervised.instance.Resample -S 0 -Z 83 -c last -i $run/t$nr$f -o $run/t$nrp1$f end echo Run $run of $f done. @ run++ end end
如果使用元分类器,即其选项包括分类器规范(例如StackingC或ClassificationViaRegression)的分类器,则必须注意不要混合参数。例如,
java weka.classifiers.meta.ClassificationViaRegression -W weka.classifiers.functions.LinearRegression -S 1 \ -t data/iris.arff -x 2
为-S 1
提供了一个非法选项异常。此参数用于线性回归,而不是通过回归进行分类,但WEKA本身并不知道这一点。澄清这种情况的一种方法是将分类器规范(包括所有参数)括在“双引号”中,如下所示:
java weka.classifiers.meta.ClassificationViaRegression -W "weka.classifiers.functions.LinearRegression -S 1" \ -t data/iris.arff -x 2
但是,这并不总是有效的,这取决于选项处理是如何在顶级分类器中实现的。虽然对于堆栈来说,这种方法可以很好地工作,但是对于classficationViaRegpression,情况就不是这样了。。我们得到一条可疑的错误消息,即找不到类weka.classifiers.functions.LinearRegression -S 1
。幸运的是,还有另一种方法:后面给出的所有参数——都由第一个子分类器处理;另一种——让我们为第二个子分类器指定参数等等。
java weka.classifiers.meta.ClassificationViaRegression -W weka.classifiers.functions.LinearRegression \ -t data/iris.arff -x 2 -- -S 1
在某些情况下,这两种方法必须混合使用,例如:
java weka.classifiers.meta.Stacking -B "weka.classifiers.lazy.IBk -K 10" \ -M "weka.classifiers.meta.ClassificationViaRegression -W weka.classifiers.functions.LinearRegression -- -S 1" \ -t data/iris.arff -x 2
请注意,虽然classficationViaRegpression支持--参数,但堆栈本身不支持。