Java 数据科学秘籍(二)
四、从数据中学习——第一部分
在本章中,我们将介绍以下配方:
- 创建并保存属性关系文件格式文件
- 交叉验证机器学习模型
- 分类看不见的测试数据
- 用过滤的分类器对看不见的测试数据进行分类
- 生成线性回归模型
- 生成逻辑回归模型
- 使用 KMeans 算法对数据进行聚类
- 来自类的聚类数据
- 从数据中学习关联规则
- 使用低级方法、过滤方法和元分类器方法选择特征/属性
简介
在本章和接下来的章节中,我们将介绍使用机器学习技术从数据中学习模式的方法。这些模式是至少三个关键机器学习任务的关注中心:分类、回归和聚类。分类是从一个名义类别预测一个值的任务。与分类相反,回归模型试图从数值类中预测值。最后,聚类是根据数据点的接近程度对其进行分组的技术。
有许多基于 Java 的工具、工作台、库和 API 可以用于前面提到的机器学习领域的研究和开发。最流行的工具之一是怀卡托知识分析环境 ( Weka ),这是一个在 GNU 通用公共许可证下授权的自由软件。它是用 Java 编写的,拥有非常好的数据准备和过滤选项、带有可定制参数设置的经典机器学习算法以及强大的数据可视化选项。此外,除了易于使用的 Java 库,它还有一个非常方便的图形用户界面 ( GUI )供非 Java 用户使用。
在本章中,我们的重点将是演示如何进行常规的数据科学活动,例如为工具准备数据集、为不同类型的机器学习任务生成模型,以及使用 Weka 进行模型性能评估。
注意
请注意,本章配方中的代码不会实现任何异常处理,因此,catch 块故意留空。异常处理完全取决于用户和他/她的需求。
创建并保存属性关系文件格式(ARFF)文件
Weka 的原生文件格式叫做属性关系文件格式 ( ARFF )。ARFF 文件有两个逻辑部分。第一部分叫做头,第二部分叫做数据 。**头部分有三个必须出现在 ARFF 文件中的物理部分——关系的名称、属性或特征以及它们的数据类型和范围。数据部分有一个物理部分,也必须存在以生成机器学习模型。ARFF 文件的头部分如下所示:
% 1\. Title: Iris Plants Database
%
% 2\. Sources:
% (a) Creator: R.A. Fisher
% (b) Donor: Michael Marshall (MARSHALL%PLU@io.arc.nasa.gov)
% (c) Date: July, 1988
%
@RELATION iris
@ATTRIBUTE sepallength NUMERIC
@ATTRIBUTE sepalwidth NUMERIC
@ATTRIBUTE petallength NUMERIC
@ATTRIBUTE petalwidth NUMERIC
@ATTRIBUTE class {Iris-setosa,Iris-versicolor,Iris-virginica}
这里,以%符号开始的行表示注释。关系的名称由关键字@RELATION表示。以@ATTRIBUTE关键字开始的下几行表示特性或属性。在示例中,关系的名称是iris,数据集有五个属性——前四个属性是数字类型,最后一个属性是数据点的类,这是一个带有三个类值的名义属性。
ARFF 文件的数据部分如下所示:
@DATA
5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
5.4,3.9,1.7,0.4,Iris-setosa
4.6,3.4,1.4,0.3,Iris-setosa
该示例显示数据部分以关键字@DATA开始,然后包含逗号分隔的属性值。逗号分隔值的顺序应该与属性部分中属性的顺序一致。
注意
@RELATION、@ATTRIBUTE和@DATA声明不区分大小写。
要了解更多关于 ARFF 文件格式、Weka 和稀疏 ARFF 文件中支持的属性类型,请参考www.cs.waikato.ac.nz/ml/weka/arf…。
为了执行本章中的配方,我们需要以下内容:
为了开发我们的代码,我们将使用 Eclipse IDE,为了成功地执行本章中的所有代码,我们将把 Weka JAR 文件添加到一个项目中。为此,请按照下列步骤操作:
-
To download Weka, go to www.cs.waikato.ac.nz/ml/weka/dow… and you will find download options for Windows, Mac, and other operating systems such as Linux. Read through the options carefully and download the appropriate version.
注意
在撰写本书时,3.9.0 是开发人员的最新版本,由于作者已经在他的 64 位 Windows 机器上安装了 1.8 版本的 JVM,他选择下载一个用于 64 位 Windows 的自解压可执行文件,而不使用 Java VM,如下图所示:
-
下载完成后,双击可执行文件并按照屏幕上的说明进行操作。你需要安装 Weka 的完整版本。
-
安装完成后,不要运行该软件。相反,转到安装它的目录,找到 Weka 的 Java 归档文件(
weka.jar)。将这个文件作为外部库添加到您的 Eclipse 项目中:
Tip
如果你因为某种原因需要下载旧版本的 Weka,都可以在sourceforge.net/projects/we…找到。请注意,旧版本中的许多方法可能已被废弃,因此不再受支持。
怎么做...
-
We will be keeping all our codes in a
main()method instead of creating a method. Therefore, create a class and amainmethod:public class WekaArffTest { public static void main(String[] args) throws Exception {注意,
main方法将包含与 Weka 库相关的代码,因此会抛出异常 -
创建两个数组列表。第一个
ArrayList将包含属性,第二个ArrayList将包含类值。因此,第一个ArrayList的泛型将是 Attribute 类型的(事实上,它是一个 Weka 类来建模属性),而第二个ArrayList的泛型可以是 string 来表示类标签:ArrayList<Attribute> attributes; ArrayList<String> classVals; -
接下来,创建一个实例对象。该对象将为 ARFF 文件的
@DATA部分中的实例建模;@DATA部分中的每一行都是一个实例:Instances data; -
创建一个双数组。该数组将包含属性的值:
double[] values; -
现在是设置属性的时候了。我们将创建 ARFF 文件的
@ATTRIBUTE部分。首先,实例化属性:attributes = new ArrayList<Attribute>(); -
接下来,我们将创建一个名为 age 的数字属性,并将它添加到我们的属性
ArrayList:attributes.add(new Attribute("age")); -
我们现在将创建一个名为 name 的字符串属性,并将它添加到我们的属性
ArrayList中。然而,在此之前,我们将创建一个 String 类型的空的ArrayList,并将NULL赋给它。这个空的ArrayList将在Attribute类的构造函数中使用,以表明 name 是一个字符串类型属性,而不是名义属性,就像类属性:ArrayList<String> empty = null; attributes.add(new Attribute("name", empty)); -
Weka 也支持日期类型属性。接下来,我们将创建一个 dob 属性来表示出生日期:
attributes.add(new Attribute("dob", "yyyy-MM-dd")); -
然后我们将实例化类值
ArrayList,并将创建五个类值-class1、class2、class3、class4和class5:classVals = new ArrayList<String>(); for (int i = 0; i < 5; i++){ classVals.add("class" + (i + 1)); } -
有了这些类值,我们接下来将创建一个属性,并将其添加到我们的属性
ArrayList:
```java
Attribute classVal = new Attribute("class", classVals);
attributes.add(classVal);
```
11. 通过这一行代码,我们已经完成了 ARFF 文件的@ATTRIBUTE部分的创建。接下来,我们将填充@DATA部分。
12. 首先,我们将创建一个名为MyRelation(这是我们的 ARFF 文件的@RELATION部分中的参数)的实例对象,以及所有属性:
```java
data = new Instances("MyRelation", attributes, 0);
```
13. 我们将使用前面创建的 double 数组为我们的四个属性生成四个值;我们将指定年龄、姓名、出生日期和类值(随机选择,在本例中没有意义):
```java
values = new double[data.numAttributes()];
values[0] = 35;
values[1] = data.attribute(1).addStringValue("John Doe");
values[2] = data.attribute(2).parseDate("1981-01-20");
values[3] = classVals.indexOf("class3");
```
14. 然后,我们将这些值添加到我们的数据部分:
```java
data.add(new DenseInstance(1.0, values));
```
15. 同样,我们将为我们的数据部分创建第二个实例,如下所示:
```java
values = new double[data.numAttributes()];
values[0] = 30;
values[1] = data.attribute(1).addStringValue("Harry Potter");
values[2] = data.attribute(2).parseDate("1986-07-05");
values[3] = classVals.indexOf("class1");
data.add(new DenseInstance(1.0, values));
```
16. 如果我们想在某个地方保存 ARFF 文件,添加以下代码段:
```java
BufferedWriter writer = new BufferedWriter(new
FileWriter("c:/training.arff"));
writer.write(data.toString());
writer.close();
```
17. 我们刚刚创建的 ARFF 文件的全部内容也可以显示在控制台输出上:
```java
System.out.println(data);
```
18. 此时,关闭方法和类:
}
}
食谱的完整代码如下:
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.ArrayList;
import weka.core.Attribute;
import weka.core.DenseInstance;
import weka.core.Instances;
public class WekaArffTest {
public static void main(String[] args) throws Exception {
ArrayList<Attribute> attributes;
ArrayList<String> classVals;
Instances data;
double[] values;
// Set up attributes
attributes = new ArrayList<Attribute>();
// Numeric attribute
attributes.add(new Attribute("age"));
// String attribute
ArrayList<String> empty = null;
attributes.add(new Attribute("name", empty));
// Date attribute
attributes.add(new Attribute("dob", "yyyy-MM-dd"));
classVals = new ArrayList<String>();
for (int i = 0; i < 5; i++){
classVals.add("class" + (i + 1));
}
Attribute classVal = new Attribute("class", classVals);
attributes.add(classVal);
// Create Instances object
data = new Instances("MyRelation", attributes, 0);
// Data fill up
// First instance
values = new double[data.numAttributes()];
values[0] = 35;
values[1] = data.attribute(1).addStringValue("John Doe");
values[2] = data.attribute(2).parseDate("1981-01-20");
values[3] = classVals.indexOf("class3");
// add
data.add(new DenseInstance(1.0, values));
// Second instance
values = new double[data.numAttributes()];
values[0] = 30;
values[1] = data.attribute(1).addStringValue("Harry Potter");
values[2] = data.attribute(2).parseDate("1986-07-05");
values[3] = classVals.indexOf("class1");
// add
data.add(new DenseInstance(1.0, values));
//writing arff file to disk
BufferedWriter writer = new BufferedWriter(new
FileWriter("c:/training.arff"));
writer.write(data.toString());
writer.close();
// Output data
System.out.println(data);
}
}
代码的输出如下所示:
@relation MyRelation
@attribute age numeric
@attribute name string
@attribute dob date yyyy-MM-dd
@attribute class {class1,class2,class3,class4,class5}
@data
35,'John Doe',1981-01-20,class3
30,'Harry Potter',1986-07-05,class1
交叉验证机器学习模型
在这个菜谱中,我们将创建四个方法来做四件不同的事情——一个方法将加载一个 ARFF 文件(假设 ARFF 文件已经创建并保存在某个地方);第二种方法将读取 ARFF 文件中的数据,并生成一个机器学习模型(我们任意选择了朴素贝叶斯模型);第三种方法是通过使用序列化来保存模型,最后一种方法是使用 10 重交叉验证来评估 ARFF 文件上的模型。
怎么做...
-
创建两个实例变量。第一个将包含 iris 数据集的所有实例。iris ARFF 数据集可以在您安装的 Weka 目录的 data 文件夹中找到。第二个变量将是一个
NaiveBayes分类器:Instances iris = null; NaiveBayes nb; -
我们的第一个方法是使用
DataSource类加载 iris ARFF 文件,使用类的getDataSet()方法读取内容,并设置类属性的位置。如果用记事本打开iris.arff数据集,会看到 class 属性是最后一个属性,这是约定,不是强制规则。因此,iris.setClassIndex(iris.numAttributes() - 1);已经被用来分配最后一个属性作为类属性。对于 Weka 中的任何分类任务,这都是非常重要的:public void loadArff(String arffInput){ DataSource source = null; try { source = new DataSource(arffInput); iris = source.getDataSet(); if (iris.classIndex() == -1) iris.setClassIndex(iris.numAttributes() - 1); } catch (Exception e1) { } } -
我们的下一个方法是使用
NaiveBayes类的buildClassifier(dataset)方法生成一个NaiveBayes分类器,基于 iris 数据集:public void generateModel(){ nb = new NaiveBayes(); try { nb.buildClassifier(iris); } catch (Exception e) { } } -
Weka 有一个工具可以保存使用 Weka 生成的任何模型。这些保存的模型可以在以后用于分类看不见的、未标记的测试数据。我们需要使用 Weka 的
SerializationHelper类,它有一个名为 write 的特殊方法,该方法将保存用户模型和模型的路径作为其参数:public void saveModel(String modelPath){ try { weka.core.SerializationHelper.write(modelPath, nb); } catch (Exception e) { } } -
我们的最终方法是使用 iris 数据集交叉验证模型的性能评估。为此,我们将使用 10 重交叉验证。如果我们只有少量的数据,这种流行的模型性能评估技术是非常有用的。然而,它也有一些局限性。对这种方法的利弊的讨论超出了本书的范围。感兴趣的读者可以参考https://en . Wikipedia . org/wiki/Cross-validation _(statistics)了解更多详情:
public void crossValidate(){
Evaluation eval = null;
try {
eval = new Evaluation(iris);
eval.crossValidateModel(nb, iris, 10, new Random(1));
System.out.println(eval.toSummaryString());
} catch (Exception e1) {
}
}
第eval.crossValidateModel(nb, iris, 10, new Random(1));行使用模型和数据集作为它的前两个参数。第三个参数表示这是一个 10 重交叉验证。最后一个参数是在过程中引入随机化,这非常重要,因为在大多数情况下,我们数据集中的数据实例不是随机化的。
该配方的完整可执行代码如下:
import java.util.Random;
import weka.classifiers.Evaluation;
import weka.classifiers.bayes.NaiveBayes;
import weka.core.Instances;
import weka.core.converters.ConverterUtils.DataSource;
public class WekaCVTest {
Instances iris = null;
NaiveBayes nb;
public void loadArff(String arffInput){
DataSource source = null;
try {
source = new DataSource(arffInput);
iris = source.getDataSet();
if (iris.classIndex() == -1)
iris.setClassIndex(iris.numAttributes() - 1);
} catch (Exception e1) {
}
}
public void generateModel(){
nb = new NaiveBayes();
try {
nb.buildClassifier(iris);
} catch (Exception e) {
}
}
public void saveModel(String modelPath){
try {
weka.core.SerializationHelper.write(modelPath, nb);
} catch (Exception e) {
}
}
public void crossValidate(){
Evaluation eval = null;
try {
eval = new Evaluation(iris);
eval.crossValidateModel(nb, iris, 10, new Random(1));
System.out.println(eval.toSummaryString());
} catch (Exception e1) {
}
}
public static void main(String[] args){
WekaCVTest test = new WekaCVTest();
test.loadArff("C:/Program Files/Weka-3-6/data/iris.arff");
test.generateModel();
test.saveModel("c:/nb.model");
test.crossValidate();
}
}
代码的输出如下所示:
Correctly Classified Instances 144 96 %
Incorrectly Classified Instances 6 4 %
Kappa statistic 0.94
Mean absolute error 0.0342
Root mean squared error 0.155
Relative absolute error 7.6997 %
Root relative squared error 32.8794 %
Total Number of Instances 150
Tip
在这个食谱中,我们保存了一个机器学习模型。如果您需要加载一个模型,您需要知道使用了哪种类型的学习算法(例如,本菜谱中的 Naive Bayes)来生成模型,以便您可以将模型加载到适当的学习算法对象中。可以使用以下方法加载模型:
public void loadModel(String modelPath){
try {
nb = (NaiveBayes)
weka.core.SerializationHelper.read(modelPath);
} catch (Exception e) {
}
}
分类看不见的测试数据
经典的监督机器学习分类任务是在标记的训练实例上训练分类器,并在看不见的测试实例上应用分类器。这里要记住的关键点是,训练集中的属性数量、它们的类型、它们的名称以及它们在训练数据集中的取值范围(如果它们是常规的名义属性或名义类属性)必须与测试数据集中的完全相同。
准备就绪
在 Weka 中,训练数据集和测试数据集之间可能存在关键差异。测试部分中 ARFF 文件的@DATA部分看起来类似于 ARFF 文件的@DATA部分。它可以具有如下属性值和分类标签:
@DATA
5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
当分类器应用于这种标记的测试数据时,分类器在预测实例的类别时忽略类别标签。还要注意,如果您的测试数据被标记,您可以将您的分类器的预测标签与实际标签进行比较。这使您有机会生成分类器的评估指标。然而,最常见的情况是,您的测试数据没有任何类别信息,并且基于它对训练数据的学习,分类器将预测并分配类别标签。这样一个测试数据集的@DATA 部分将如下所示,其中类标签是未知的,用一个问号(?)表示:
@DATA
5.1,3.5,1.4,0.2,?
4.9,3.0,1.4,0.2,?
4.7,3.2,1.3,0.2,?
Weka 的数据目录不包含任何这样的测试文件。因此,您可以创建自己的类似于iris.arff文件的测试文件。复制以下内容,打开记事本将其放入一个文本文件中,并在您的文件系统中保存为iris-test.arff文件(比如在C:/驱动器中):
@RELATION iris-test
@ATTRIBUTE sepallength REAL
@ATTRIBUTE sepalwidth REAL
@ATTRIBUTE petallength REAL
@ATTRIBUTE petalwidth REAL
@ATTRIBUTE class {Iris-setosa,Iris-versicolor,Iris-virginica}
@DATA
3.1,1.2,1.2,0.5,?
2.3,2.3,2.3,0.3,?
4.2,4.4,2.1,0.2,?
3.1,2.5,1.0,0.2,?
2.8,1.6,2.0,0.2,?
3.0,2.6,3.3,0.3,?
4.5,2.0,3.4,0.1,?
5.3,2.0,3.1,0.2,?
3.2,1.3,2.1,0.3,?
2.1,6.4,1.2,0.1,?
怎么做...
-
We will have the following instance variables:
NaiveBayes nb; Instances train, test, labeled;为了让这个食谱有点挑战性,同时也给我们一个好的学习体验,我们将加载一个先前构建并保存的模型,并将该模型分配给我们的
NaiveBayes分类器。该分类器将应用于未标记的测试实例。测试实例将作为标记实例复制到分类器中。在不改变测试实例的情况下,由分类器预测的类别标签将作为类别标签被分配给相应的标记实例。 -
首先,我们将创建一个方法来加载一个预先构建并保存的模型。事实上,我们可以加载我们在前面名为的配方中构建并保存的分类器,交叉验证一个机器学习模型:
public void loadModel(String modelPath){ try { nb = (NaiveBayes) weka.core.SerializationHelper.read(modelPath); } catch (Exception e) { } } -
然后,我们需要读取训练和测试数据集。作为训练数据集,我们将使用文件系统中 Weka 安装文件的数据目录中的
iris.arff文件。作为测试文件,我们将使用我们之前在菜谱中创建的iris-test.arff文件:public void loadDatasets(String training, String testing){ -
To read the training dataset, we have used Weka's DataSource class. The key advantage in using this class is that it can deal with all file types supported by Weka. However, Weka users can use Java's
BufferedReaderclass also to read the contents of a dataset. In this recipe, just to introduce a new way to read datasets, we will be using theBufferedReaderclass instead of theDataSourceclass.我们将把 ARFF 文件视为常规文件,并使用一个
BufferedReader阅读器来指向训练数据集。然后,读取器将用于使用 Weka 的 instances 类的构造函数创建训练实例。最后,我们将最后一个属性设置为数据集的类属性:BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(training)); train = new Instances (reader); train.setClassIndex(train.numAttributes() -1); } catch (IOException e) { } -
In a similar way, we will be reading the test dataset:
try { reader = new BufferedReader(new FileReader(testing)); test = new Instances (reader); test.setClassIndex(train.numAttributes() -1); } catch (IOException e) { }请注意,您不需要创建新的
BufferedReader对象。 -
最后,关闭打开的
BufferedReader对象并结束方法:try { reader.close(); } catch (IOException e) { } } -
我们的下一个方法是从训练数据中创建一个
NaiveBayes分类器,并将该分类器应用于我们的测试数据集的看不见的、未标记的实例。该方法还将显示由NaiveBayes分类器预测的类值的概率:public void classify(){ try { nb.buildClassifier(train); } catch (Exception e) { } } -
我们将创建带标签的实例,它们是测试实例的副本。我们在前面步骤中生成的分类器预测的标签将被分配给这些实例,而测试实例保持不变:
labeled = new Instances(test); -
现在,对于测试数据集的每个实例,我们将创建一个类标签,这是一个双精度变量。然后,朴素贝叶斯将应用它的
classifyInstance()方法,该方法将一个实例作为参数。类别标签将被分配给类别标签变量,并且该变量的值将被分配为在被标记的实例中的该特定实例的类别标签。换句话说,标记实例中测试实例的?值将被朴素贝叶斯预测的值所替换:for (int i = 0; i < test.numInstances(); i++) { double clsLabel; try { clsLabel = nb.classifyInstance(test.instance(i)); labeled.instance(i).setClassValue(clsLabel); double[] predictionOutput = nb.distributionForInstance(test.instance(i)); double predictionProbability = predictionOutput[1]; System.out.println(predictionProbability); } catch (Exception e) { } } -
最后,我们将在文件系统中写入带标签的测试数据集(即
labeled):
public void writeArff(String outArff){
BufferedWriter writer;
try {
writer = new BufferedWriter(new FileWriter(outArff));
writer.write(labeled.toString());
writer.close();
} catch (IOException e) {
}
}
该配方的完整可执行代码如下:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import weka.classifiers.bayes.NaiveBayes;
import weka.core.Instances;
public class WekaTrainTest {
NaiveBayes nb;
Instances train, test, labeled;
public void loadModel(String modelPath){
try {
nb = (NaiveBayes)
weka.core.SerializationHelper.read(modelPath);
} catch (Exception e) {
}
}
public void loadDatasets(String training, String testing){
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(training));
train = new Instances (reader);
train.setClassIndex(train.numAttributes() -1);
} catch (IOException e) {
}
try {
reader = new BufferedReader(new FileReader(testing));
test = new Instances (reader);
test.setClassIndex(train.numAttributes() -1);
} catch (IOException e) {
}
try {
reader.close();
} catch (IOException e) {
}
}
public void classify(){
try {
nb.buildClassifier(train);
} catch (Exception e) {
}
labeled = new Instances(test);
for (int i = 0; i < test.numInstances(); i++) {
double clsLabel;
try {
clsLabel = nb.classifyInstance(test.instance(i));
labeled.instance(i).setClassValue(clsLabel);
double[] predictionOutput =
nb.distributionForInstance(test.instance(i));
double predictionProbability = predictionOutput[1];
System.out.println(predictionProbability);
} catch (Exception e) {
}
}
}
public void writeArff(String outArff){
BufferedWriter writer;
try {
writer = new BufferedWriter(new FileWriter(outArff));
writer.write(labeled.toString());
writer.close();
} catch (IOException e) {
}
}
public static void main(String[] args) throws Exception{
WekaTrainTest test = new WekaTrainTest();
test.loadModel("path to your Naive Bayes Model");
test.loadDatasets("path to iris.arff dataset", "path to iris-
test.arff dataset");
test.classify();
test.writeArff("path to your output ARFF file");
}
}
在控制台上,您将看到我们的模型预测的概率值:
5.032582653870928E-13
2.1050052853672135E-4
5.177104804026096E-16
1.2459904922893976E-16
3.1771015903129274E-10
0.9999993509430146
0.999999944638627
0.9999999844862647
3.449759371835354E-8
4.0178483420981394E-77
如果您打开由代码生成的 ARFF 文件,该文件包含以前未知实例的类值,您应该会看到如下内容:
@relation iris-test
@attribute sepallength numeric
@attribute sepalwidth numeric
@attribute petallength numeric
@attribute petalwidth numeric
@attribute class {Iris-setosa,Iris-versicolor,Iris-virginica}
@data
3.1,1.2,1.2,0.5,Iris-setosa
2.3,2.3,2.3,0.3,Iris-setosa
4.2,4.4,2.1,0.2,Iris-setosa
3.1,2.5,1,0.2,Iris-setosa
2.8,1.6,2,0.2,Iris-setosa
3,2.6,3.3,0.3,Iris-versicolor
4.5,2,3.4,0.1,Iris-versicolor
5.3,2,3.1,0.2,Iris-versicolor
3.2,1.3,2.1,0.3,Iris-setosa
2.1,6.4,1.2,0.1,Iris-setosa
用过滤的分类器对看不见的测试数据进行分类
很多时候,在开发分类器之前,您需要使用过滤器。该过滤器可用于移除、转换、离散化和添加属性,移除错误分类的实例,随机化或规范化实例,等等。通常的方法是使用 Weka 的 Filter 类,然后用类方法执行一系列过滤。此外,Weka 有一个名为FilteredClassifier的类,它是一个类,用于对通过任意过滤器的数据运行任意分类器。
在这个菜谱中,我们将看到如何同时使用一个过滤器和一个分类器来对看不见的测试例子进行分类。
怎么做...
-
This time, we will be using a Random Forest classifier. As our dataset, we will be using
weather.nominal.arffthat can be found in theDatadirectory of the installed Weka folder in your file system.下面两个将是我们的实例变量:
Instances weather = null; RandomForest rf; -
接下来,在我们的代码中,我们将有一个加载数据集的方法。我们将把
weather.nominal.arff文件的目录路径从我们的驱动方法发送到这个方法。使用 Weka 的DataSource类,我们将读取weather.nominal.arff文件的数据,并将数据集的最后一个属性设置为类属性:public void loadArff(String arffInput){ DataSource source = null; try { source = new DataSource(arffInput); weather = source.getDataSet(); weather.setClassIndex(iris.numAttributes() - 1); } catch (Exception e1) { } } -
接下来,我们将创建这个食谱的核心方法:
public void buildFilteredClassifier(){ -
为了创建这个方法,我们将首先创建一个随机森林分类器:
rf = new RandomForest(); -
我们将创建一个过滤器,从
weather.nominal.arff文件中删除一个特定的属性。为此,我们将使用 Weka 的 Remove 类。下面的代码将创建一个过滤器,用于删除数据集的第一个属性:Remove rm = new Remove(); rm.setAttributeIndices("1"); -
在接下来的几行代码中,创建一个
FilteredClassifier,添加我们在上一步中创建的过滤器,并添加RandomForest分类器:FilteredClassifier fc = new FilteredClassifier(); fc.setFilter(rm); fc.setClassifier(rf); -
使用过滤的分类器,我们可以从名义上的天气数据集构建一个随机的森林分类器。然后,对于名义天气数据集的每个实例,分类器将预测类值。在 try 块中,我们将打印实例的实际值和预测值:
try{
fc.buildClassifier(weather);
for (int i = 0; i < iris.numInstances(); i++){
double pred = fc.classifyInstance(weather.instance(i));
System.out.print("given value: " +
weather.classAttribute().value((int)
weather.instance(i).classValue()));
System.out.println("---predicted value: " +
weather.classAttribute().value((int) pred));
}
} catch (Exception e) {
}
}
食谱的完整代码如下:
import weka.classifiers.meta.FilteredClassifier;
import weka.classifiers.trees.RandomForest;
import weka.core.Instances;
import weka.core.converters.ConverterUtils.DataSource;
import weka.filters.unsupervised.attribute.Remove;
public class WekaFilteredClassifierTest {
Instances weather = null;
RandomForest rf;
public void loadArff(String arffInput){
DataSource source = null;
try {
source = new DataSource(arffInput);
weather = source.getDataSet();
weather.setClassIndex(weather.numAttributes() - 1);
} catch (Exception e1) {
}
}
public void buildFilteredClassifier(){
rf = new RandomForest();
Remove rm = new Remove();
rm.setAttributeIndices("1");
FilteredClassifier fc = new FilteredClassifier();
fc.setFilter(rm);
fc.setClassifier(rf);
try{
fc.buildClassifier(weather);
for (int i = 0; i < weather.numInstances(); i++){
double pred = fc.classifyInstance(weather.instance(i));
System.out.print("given value: " +
weather.classAttribute().value((int)
weather.instance(i).classValue()));
System.out.println("---predicted value: " +
weather.classAttribute().value((int) pred));
}
} catch (Exception e) {
}
}
public static void main(String[] args){
WekaFilteredClassifierTest test = new
WekaFilteredClassifierTest();
test.loadArff("C:/Program Files/Weka-3-
6/data/weather.nominal.arff");
test.buildFilteredClassifier();
}
}
代码的输出如下所示:
given value: no---predicted value: yes
given value: no---predicted value: no
given value: yes---predicted value: yes
given value: yes---predicted value: yes
given value: yes---predicted value: yes
given value: no---predicted value: yes
given value: yes---predicted value: yes
given value: no---predicted value: yes
given value: yes---predicted value: yes
given value: yes---predicted value: yes
given value: yes---predicted value: yes
given value: yes---predicted value: yes
given value: yes---predicted value: yes
given value: no---predicted value: yes
生成线性回归模型
大多数线性回归模型遵循一个通用的模式-将有许多自变量共同产生一个结果,这是一个因变量。例如,我们可以生成一个回归模型,根据房屋的不同属性/特征(主要是数字、真实值)来预测房屋的价格,例如房屋的平方英尺大小、卧室数量、卫生间数量、位置的重要性等等。
在这个食谱中,我们将使用 Weka 的线性回归分类器来生成回归模型。
怎么做...
-
In this recipe, the linear regression model we will be creating is based on the
cpu.arffdataset, which can be found in thedatadirectory of the Weka installation directory.我们的代码将有两个实例变量:第一个变量将包含
cpu.arff文件的数据实例,第二个变量将是我们的线性回归分类器:Instances cpu = null; LinearRegression lReg ; -
接下来,我们将创建一个方法来加载 ARFF 文件,并将 ARFF 文件的最后一个属性指定为它的类属性:
public void loadArff(String arffInput){ DataSource source = null; try { source = new DataSource(arffInput); cpu = source.getDataSet(); cpu.setClassIndex(cpu.numAttributes() - 1); } catch (Exception e1) { } } -
我们将创建一种方法来构建线性回归模型。为此,我们只需调用线性回归变量的
buildClassifier()方法。模型可以作为参数直接发送给System.out.println():
public void buildRegression(){
lReg = new LinearRegression();
try {
lReg.buildClassifier(cpu);
} catch (Exception e) {
}
System.out.println(lReg);
}
食谱的完整代码如下:
import weka.classifiers.functions.LinearRegression;
import weka.core.Instances;
import weka.core.converters.ConverterUtils.DataSource;
public class WekaLinearRegressionTest {
Instances cpu = null;
LinearRegression lReg ;
public void loadArff(String arffInput){
DataSource source = null;
try {
source = new DataSource(arffInput);
cpu = source.getDataSet();
cpu.setClassIndex(cpu.numAttributes() - 1);
} catch (Exception e1) {
}
}
public void buildRegression(){
lReg = new LinearRegression();
try {
lReg.buildClassifier(cpu);
} catch (Exception e) {
}
System.out.println(lReg);
}
public static void main(String[] args) throws Exception{
WekaLinearRegressionTest test = new WekaLinearRegressionTest();
test.loadArff("path to the cpu.arff file");
test.buildRegression();
}
}
代码的输出如下所示:
Linear Regression Model
class =
0.0491 * MYCT +
0.0152 * MMIN +
0.0056 * MMAX +
0.6298 * CACH +
1.4599 * CHMAX +
-56.075
生成逻辑回归模型
Weka 有一个名为 logistic 的类,可用于构建和使用带有岭估计的多项式 Logistic 回归模型。虽然最初的逻辑回归不处理实例权重,但是 Weka 中的算法已经被修改来处理实例权重。
在这个配方中,我们将使用 Weka 在 iris 数据集上生成一个逻辑回归模型。
怎么做...
-
We will be generating a logistic regression model from the
irisdataset, which can be found in thedatadirectory in the installed folder of Weka.我们的代码将有两个实例变量:一个包含 iris 数据集的数据实例,另一个是逻辑回归分类器:
Instances iris = null; Logistic logReg ; -
我们将使用一个方法来加载和读取数据集,以及分配它的类属性(
iris.arff文件的最后一个属性):public void loadArff(String arffInput){ DataSource source = null; try { source = new DataSource(arffInput); iris = source.getDataSet(); iris.setClassIndex(iris.numAttributes() - 1); } catch (Exception e1) { } } -
接下来,我们将创建食谱中最重要的方法,该方法从
iris数据集构建逻辑回归分类器:
public void buildRegression(){
logReg = new Logistic();
try {
logReg.buildClassifier(iris);
} catch (Exception e) {
}
System.out.println(logReg);
}
该配方的完整可执行代码如下:
import weka.classifiers.functions.Logistic;
import weka.core.Instances;
import weka.core.converters.ConverterUtils.DataSource;
public class WekaLogisticRegressionTest {
Instances iris = null;
Logistic logReg ;
public void loadArff(String arffInput){
DataSource source = null;
try {
source = new DataSource(arffInput);
iris = source.getDataSet();
iris.setClassIndex(iris.numAttributes() - 1);
} catch (Exception e1) {
}
}
public void buildRegression(){
logReg = new Logistic();
try {
logReg.buildClassifier(iris);
} catch (Exception e) {
}
System.out.println(logReg);
}
public static void main(String[] args) throws Exception{
WekaLogisticRegressionTest test = new
WekaLogisticRegressionTest();
test.loadArff("path to the iris.arff file ");
test.buildRegression();
}
}
代码的输出如下所示:
Logistic Regression with ridge parameter of 1.0E-8
Coefficients...
Class
Variable Iris-setosa Iris-versicolor
===============================================
sepallength 21.8065 2.4652
sepalwidth 4.5648 6.6809
petallength -26.3083 -9.4293
petalwidth -43.887 -18.2859
Intercept 8.1743 42.637
Odds Ratios...
Class
Variable Iris-setosa Iris-versicolor
===============================================
sepallength 2954196659.8892 11.7653
sepalwidth 96.0426 797.0304
petallength 0 0.0001
petalwidth 0 0
Tip
对配方结果的解释超出了本书的范围。感兴趣的读者可以在这里看到关于堆栈溢出的讨论:http://Stack Overflow . com/questions/19136213/how-to-interpret-WEKA-logistic-regression-output
使用 KMeans 算法聚类数据点
在这个菜谱中,我们将使用 KMeans 算法对数据集的数据点进行聚类或分组。
怎么做...
-
We will be using the cpu dataset to cluster its data points based on a simple KMeans algorithm. The cpu dataset can be found in the
datadirectory of the installed folder in the Weka directory.我们将有两个实例变量,就像前面的食谱一样。第一个变量将包含 cpu 数据集的数据点,第二个变量将是我们简单的 KMeans clusterer:
Instances cpu = null; SimpleKMeans kmeans; -
然后,我们将创建一个方法来加载 cpu 数据集,并读取其内容。请注意,由于聚类是一种无监督的方法,我们不需要指定数据集的 class 属性:
public void loadArff(String arffInput){ DataSource source = null; try { source = new DataSource(arffInput); cpu = source.getDataSet(); } catch (Exception e1) { } } -
接下来,我们将创建我们的方法来开发集群器:
public void clusterData(){ -
我们实例化集群,并将 seed 的值设置为 10。种子将用于生成一个随机数,它取一个整数值:
kmeans = new SimpleKMeans(); kmeans.setSeed(10); -
然后,我们告诉集群器保持数据实例的顺序不变。如果您觉得不需要维护数据集中实例的顺序,可以将
setPreserveInstancesOrder()方法的参数设置为 false。我们还将集群的数量设置为 10。最后,我们从 cpu 数据集构建集群:try { kmeans.setPreserveInstancesOrder(true); kmeans.setNumClusters(10); kmeans.buildClusterer(cpu); -
接下来,我们使用一个
for循环,通过简单的 KMeans 算法获得每个实例和分配给它们的集群号:
int[] assignments = kmeans.getAssignments();
int i = 0;
for(int clusterNum : assignments) {
System.out.printf("Instance %d -> Cluster %d\n", i,
clusterNum);
i++;
}
} catch (Exception e1) {
}
食谱的完整代码如下:
import weka.clusterers.SimpleKMeans;
import weka.core.Instances;
import weka.core.converters.ConverterUtils.DataSource;
public class WekaClusterTest {
Instances cpu = null;
SimpleKMeans kmeans;
public void loadArff(String arffInput){
DataSource source = null;
try {
source = new DataSource(arffInput);
cpu = source.getDataSet();
} catch (Exception e1) {
}
}
public void clusterData(){
kmeans = new SimpleKMeans();
kmeans.setSeed(10);
try {
kmeans.setPreserveInstancesOrder(true);
kmeans.setNumClusters(10);
kmeans.buildClusterer(cpu);
int[] assignments = kmeans.getAssignments();
int i = 0;
for(int clusterNum : assignments) {
System.out.printf("Instance %d -> Cluster %d\n", i,
clusterNum);
i++;
}
} catch (Exception e1) {
}
}
public static void main(String[] args) throws Exception{
WekaClusterTest test = new WekaClusterTest();
test.loadArff("path to cpu.arff file");
test.clusterData();
}
}
cpu.arff文件有 209 个数据实例。前 10 个的输出如下:
Instance 0 -> Cluster 7
Instance 1 -> Cluster 5
Instance 2 -> Cluster 5
Instance 3 -> Cluster 5
Instance 4 -> Cluster 1
Instance 5 -> Cluster 5
Instance 6 -> Cluster 5
Instance 7 -> Cluster 5
Instance 8 -> Cluster 4
Instance 9 -> Cluster 4
聚类来自类的数据
如果你有一个包含类的数据集,这对于无监督学习来说是一种不寻常的情况,Weka 有一种方法叫做从类中聚类。在这种方法中,Weka 首先忽略类属性并生成聚类。然后在测试阶段,它根据每个集群内的类属性的多数值将类分配给集群。我们将在这份食谱中介绍这种方法。
怎么做...
-
In this recipe, we will use a dataset with class values for instances. We will use a
weather.nominal.arfffile, which can be found in the data directory of the installed Weka directory.在我们的代码中,我们将有两个实例变量。第一个变量将包含数据集的实例,第二个变量将包含一个期望最小化聚类器:
Instances weather = null; EM clusterer; -
接下来,我们将加载数据集,读取它,并将最后一个索引设置为它的类索引:
public void loadArff(String arffInput){ DataSource source = null; try { source = new DataSource(arffInput); weather = source.getDataSet(); weather.setClassIndex(weather.numAttributes() - 1); } catch (Exception e1) { } } -
然后,我们将在这个配方中创建我们的关键方法,它将从类:
public void generateClassToCluster(){中生成集群
-
为此,我们将首先创建一个移除过滤器。此过滤器将用于从数据集中移除类属性,因为 Weka 在聚类过程中会忽略此属性:
Remove filter = new Remove(); filter.setAttributeIndices("" + (weather.classIndex() + 1)); -
然后,我们将把过滤器应用到我们的数据集:
try { filter.setInputFormat(weather); -
我们将获得没有类变量的数据集,并可以从数据中创建一个期望最大化聚类器:
Instances dataClusterer = Filter.useFilter(weather, filter); clusterer = new EM(); clusterer.buildClusterer(dataClusterer); -
然后,我们将使用原始数据集的类来评估集群:
ClusterEvaluation eval = new ClusterEvaluation(); eval.setClusterer(clusterer); eval.evaluateClusterer(weather); -
最后,我们将在控制台上打印聚类结果:
System.out.println(eval.clusterResultsToString());
} catch (Exception e) {
}
}
食谱的完整代码如下:
import weka.clusterers.ClusterEvaluation;
import weka.clusterers.EM;
import weka.core.Instances;
import weka.core.converters.ConverterUtils.DataSource;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.Remove;
public class WekaClassesToClusterTest {
Instances weather = null;
EM clusterer;
public void loadArff(String arffInput){
DataSource source = null;
try {
source = new DataSource(arffInput);
weather = source.getDataSet();
weather.setClassIndex(weather.numAttributes() - 1);
} catch (Exception e1) {
}
}
public void generateClassToCluster(){
Remove filter = new Remove();
filter.setAttributeIndices("" + (weather.classIndex() + 1));
try {
filter.setInputFormat(weather);
Instances dataClusterer = Filter.useFilter(weather, filter);
clusterer = new EM();
clusterer.buildClusterer(dataClusterer);
ClusterEvaluation eval = new ClusterEvaluation();
eval.setClusterer(clusterer);
eval.evaluateClusterer(weather);
System.out.println(eval.clusterResultsToString());
} catch (Exception e) {
}
}
public static void main(String[] args){
WekaClassesToClusterTest test = new WekaClassesToClusterTest();
test.loadArff("path to weather.nominal.arff file");
test.generateClassToCluster();
}
}
从数据中学习关联规则
关联规则学习是一种机器学习技术,用于发现数据集中各种特征或变量之间的关联和规则。统计学中的一种类似技术被称为相关性,这在第 3 章、中有所介绍,统计分析数据,但是关联规则学习在决策制定中更有用。例如,通过分析大型超市数据,机器学习学习者可以发现,如果一个人买了洋葱、西红柿、鸡肉馅饼和蛋黄酱,她很可能会买包子(来做汉堡)。
在这个菜谱中,我们将看到如何使用 Weka 从数据集中学习关联规则。
准备就绪
我们将使用超市数据集,该数据集可以在我们安装的 Weka 目录的data目录中找到。数据集中的实例总数为 4,627 个,每个实例有 217 个二进制属性。属性的值为true或missing。有一个名为total的名义类属性,如果交易少于 100 美元,其值为low,如果交易多于 100 美元,其值为high。
怎么做...
-
声明两个实例变量来包含超市数据集的数据并表示先验学习者:
Instances superMarket = null; Apriori apriori; -
创建一个方法来加载数据集并读取它。对于这个配方,不需要设置数据集的 class 属性:
public void loadArff(String arffInput){ DataSource source = null; try { source = new DataSource(arffInput); superMarket = source.getDataSet(); } catch (Exception e1) { } } -
创建一个方法来实例化先验学习者。然后,该方法从给定的数据集构建关联。最后,在控制台上显示学员:
public void generateRule(){
apriori = new Apriori();
try {
apriori.buildAssociations(superMarket);
System.out.println(apriori);
} catch (Exception e) {
}
}
Tip
先验学习者产生的规则的默认数量被设置为 10。如果您需要生成更多的规则,您可以在构建关联之前输入以下代码行,其中n是一个整数,表示规则learn-apriori.setNumRules(n);的数量
食谱的完整代码如下:
import weka.associations.Apriori;
import weka.core.Instances;
import weka.core.converters.ConverterUtils.DataSource;
public class WekaAssociationRuleTest {
Instances superMarket = null;
Apriori apriori;
public void loadArff(String arffInput){
DataSource source = null;
try {
source = new DataSource(arffInput);
superMarket = source.getDataSet();
} catch (Exception e1) {
}
}
public void generateRule(){
apriori = new Apriori();
try {
apriori.buildAssociations(superMarket);
System.out.println(apriori);
} catch (Exception e) {
}
}
public static void main(String args[]){
WekaAssociationRuleTest test = new WekaAssociationRuleTest();
test.loadArff("path to supermarket.arff file");
test.generateRule();
}
}
先验学习者发现的规则如下:
1\. biscuits=t frozen foods=t fruit=t total=high 788 ==> bread and cake=t 723 <conf:(0.92)> lift:(1.27) lev:(0.03) [155] conv:(3.35)
2\. baking needs=t biscuits=t fruit=t total=high 760 ==> bread and cake=t 696 <conf:(0.92)> lift:(1.27) lev:(0.03) [149] conv:(3.28)
3\. baking needs=t frozen foods=t fruit=t total=high 770 ==> bread and cake=t 705 <conf:(0.92)> lift:(1.27) lev:(0.03) [150] conv:(3.27)
4\. biscuits=t fruit=t vegetables=t total=high 815 ==> bread and cake=t 746 <conf:(0.92)> lift:(1.27) lev:(0.03) [159] conv:(3.26)
5\. party snack foods=t fruit=t total=high 854 ==> bread and cake=t 779 <conf:(0.91)> lift:(1.27) lev:(0.04) [164] conv:(3.15)
6\. biscuits=t frozen foods=t vegetables=t total=high 797 ==> bread and cake=t 725 <conf:(0.91)> lift:(1.26) lev:(0.03) [151] conv:(3.06)
7\. baking needs=t biscuits=t vegetables=t total=high 772 ==> bread and cake=t 701 <conf:(0.91)> lift:(1.26) lev:(0.03) [145] conv:(3.01)
8\. biscuits=t fruit=t total=high 954 ==> bread and cake=t 866 <conf:(0.91)> lift:(1.26) lev:(0.04) [179] conv:(3)
9\. frozen foods=t fruit=t vegetables=t total=high 834 ==> bread and cake=t 757 <conf:(0.91)> lift:(1.26) lev:(0.03) [156] conv:(3)
10\. frozen foods=t fruit=t total=high 969 ==> bread and cake=t 877 <conf:(0.91)> lift:(1.26) lev:(0.04) [179] conv:(2.92)
使用低级方法、过滤方法和元分类器方法选择特征/属性
特征选择是一个重要的机器学习过程,它从一组属性中识别数据集中最重要的属性,因此,如果基于所选属性生成分类器,该分类器会比包含所有属性的分类器产生更好的结果。
在 Weka 中,有三种选择属性的方式。该方法将使用 Weka 中可用的所有三种属性选择技术:低级属性选择方法、使用过滤器的属性选择和使用元分类器的属性选择。
准备就绪
配方将选择可以在 Weka 安装目录的data目录中找到的iris数据集的重要属性。
要执行属性选择,需要两个元素:搜索方法和评估方法。在我们的配方中,我们将使用最佳优先搜索作为我们的搜索方法,以及一个名为基于相关性的特征子集选择的子集评估方法。
怎么做...
-
声明一个实例变量来保存 iris 数据集中的数据。为
NaiveBayes分类器声明另一个变量:Instances iris = null; NaiveBayes nb; -
创建一个方法来加载我们的数据集。该方法还将读取数据实例,并将数据集的最后一个属性设置为类属性:
public void loadArff(String arffInput){ DataSource source = null; try { source = new DataSource(arffInput); iris = source.getDataSet(); iris.setClassIndex(iris.numAttributes() - 1); } catch (Exception e1) { } } -
我们将从简单开始——我们将创建一个使用 Weka 的低级属性选择方法的方法:
public void selectFeatures(){ -
创建一个
AttributeSelection对象:AttributeSelection attSelection = new AttributeSelection(); -
接下来,为搜索和评估器创建对象,并为属性
selection对象:CfsSubsetEval eval = new CfsSubsetEval(); BestFirst search = new BestFirst(); attSelection.setEvaluator(eval); attSelection.setSearch(search);设置评估器和搜索对象
-
Then, use the attribute selection object to select attributes from the iris dataset using the search and evaluator. We will get the index of the attributes that are selected by this technique and will display the selected attribute numbers (the attribute numbers start from 0):
try { attSelection.SelectAttributes(iris); int[] attIndex = attSelection.selectedAttributes(); System.out.println(Utils.arrayToString(attIndex)); } catch (Exception e) { } }该方法的输出如下:
2, 3, 4输出意味着属性选择技术从虹膜数据集的所有属性中选择属性号 2、3 和 4。
-
现在,我们将创建一个方法,实现基于过滤器选择属性的第二种技术:
public void selectFeaturesWithFilter(){ -
创建属性选择过滤器。注意,这个过滤器的包不是我们在这个配方的第一个方法中使用的包:
weka.filters.supervised.attribute.AttributeSelection filter = new weka.filters.supervised.attribute.AttributeSelection(); -
接下来,为搜索和评估器创建对象,并为过滤器设置评估器和搜索对象:
CfsSubsetEval eval = new CfsSubsetEval(); BestFirst search = new BestFirst(); filter.setEvaluator(eval); filter.setSearch(search); -
Then, apply the filter to the iris dataset, and retrieve new data using the
useFilter()of the Filter class, which takes the dataset and filter as its two arguments. This is something different than what we saw in the previous method. This is very useful if we want to create a new ARFF file by selecting the attributes selected by the filtering technique on the fly:
```java
try {
filter.setInputFormat(iris);
Instances newData = Filter.useFilter(iris, filter);
System.out.println(newData);
} catch (Exception e) {
}
}
```
从控制台的输出中,我们可以看到 ARFF 文件数据的属性部分现在包含以下条目:
```java
@attribute petallength numeric
@attribute petalwidth numeric
@attribute class {Iris-setosa,Iris-versicolor,Iris-virginica}
```
这意味着与类属性一起列出的两个属性是由我们刚刚使用的属性选择方法选择的。
11. 最后,我们将创建一个方法,在将数据集交给分类器之前选择属性(在我们的例子中,它是一个NaiveBayes分类器):
```java
public void selectFeaturesWithClassifiers(){
```
12. 在将数据传递给NaiveBayes分类器:
```java
AttributeSelectedClassifier classifier = new
AttributeSelectedClassifier();
```
之前,创建一个元分类器来减少数据的维数(即选择属性)
13. 创建一个评估器、一个搜索对象和一个NaiveBayes分类器:
```java
CfsSubsetEval eval = new CfsSubsetEval();
BestFirst search = new BestFirst();
nb = new NaiveBayes();
```
14. 将评估器、搜索对象和NaiveBayes分类器设置为元分类器:
```java
classifier.setClassifier(nb);
classifier.setEvaluator(eval);
classifier.setSearch(search);
```
15. 现在,我们将使用元分类器技术选择的属性来评估NaiveBayes分类器的性能。注意,在这个例子中,元分类器选择的属性就像一个黑盒。在评估中,使用了 10 重交叉验证:
Evaluation evaluation;
try {
evaluation = new Evaluation(iris);
evaluation.crossValidateModel(classifier, iris, 10, new
Random(1));
System.out.println(evaluation.toSummaryString());
} catch (Exception e) {
}
}
食谱的完整代码如下:
import java.util.Random;
import weka.attributeSelection.AttributeSelection;
import weka.attributeSelection.BestFirst;
import weka.attributeSelection.CfsSubsetEval;
import weka.classifiers.Evaluation;
import weka.classifiers.bayes.NaiveBayes;
import weka.classifiers.meta.AttributeSelectedClassifier;
import weka.core.Instances;
import weka.core.Utils;
import weka.core.converters.ConverterUtils.DataSource;
import weka.filters.Filter;
public class WekaFeatureSelectionTest {
Instances iris = null;
NaiveBayes nb;
public void loadArff(String arffInput){
DataSource source = null;
try {
source = new DataSource(arffInput);
iris = source.getDataSet();
iris.setClassIndex(iris.numAttributes() - 1);
} catch (Exception e1) {
}
}
public void selectFeatures(){
AttributeSelection attSelection = new AttributeSelection();
CfsSubsetEval eval = new CfsSubsetEval();
BestFirst search = new BestFirst();
attSelection.setEvaluator(eval);
attSelection.setSearch(search);
try {
attSelection.SelectAttributes(iris);
int[] attIndex = attSelection.selectedAttributes();
System.out.println(Utils.arrayToString(attIndex));
} catch (Exception e) {
}
}
public void selectFeaturesWithFilter(){
weka.filters.supervised.attribute.AttributeSelection filter = new
weka.filters.supervised.attribute.AttributeSelection();
CfsSubsetEval eval = new CfsSubsetEval();
BestFirst search = new BestFirst();
filter.setEvaluator(eval);
filter.setSearch(search);
try {
filter.setInputFormat(iris);
Instances newData = Filter.useFilter(iris, filter);
System.out.println(newData);
} catch (Exception e) {
}
}
public void selectFeaturesWithClassifiers(){
AttributeSelectedClassifier classifier = new
AttributeSelectedClassifier();
CfsSubsetEval eval = new CfsSubsetEval();
BestFirst search = new BestFirst();
nb = new NaiveBayes();
classifier.setClassifier(nb);
classifier.setEvaluator(eval);
classifier.setSearch(search);
Evaluation evaluation;
try {
evaluation = new Evaluation(iris);
evaluation.crossValidateModel(classifier, iris, 10, new
Random(1));
System.out.println(evaluation.toSummaryString());
} catch (Exception e) {
}
}
public static void main(String[] args){
WekaFeatureSelectionTest test = new WekaFeatureSelectionTest();
test.loadArff("C:/Program Files/Weka-3-6/data/iris.arff");
test.selectFeatures();
test.selectFeaturesWithFilter();
test.selectFeaturesWithClassifiers();
}
该方法的输出如下:
Correctly Classified Instances 144 96 %
Incorrectly Classified Instances 6 4 %
Kappa statistic 0.94
Mean absolute error 0.0286
Root mean squared error 0.1386
Relative absolute error 6.4429 %
Root relative squared error 29.4066 %
Total Number of Instances 150
注意
以下频道有关于使用 Weka 完成许多不同机器学习任务的教程,包括其 API 和 GUI:www.youtube.com/c/rushdisha…。
五、从数据中学习——第二部分
在本章中,我们将介绍以下配方:
- 使用 Java 机器学习库在数据上应用机器学习
- 数据集导入和导出
- 聚类和评估
- 分类
- 交叉验证和延期测试
- 特征评分
- 特征选择
- 使用斯坦福分类器对数据点进行分类
- 使用大量在线分析对数据点进行分类
简介
在第 4 章、从数据中学习-第 1 部分中,我们使用 Weka 机器学习工作台进行不同的分类、聚类、关联规则挖掘、特征选择等。我们在那一章还提到,Weka 并不是唯一用 Java 编写的从数据中学习模式的工具。还有其他工具可以执行类似的任务。这类工具的例子包括但不限于 Java 机器学习 ( Java-ML )库、海量在线分析 ( MOA )和斯坦福机器学习库。
在这一章中,我们将关注这些其他工具的一点一滴,以对数据进行机器学习分析。
使用 Java 机器学习(Java-ML)库对数据应用机器学习
Java 机器学习 ( Java-ML )库是标准机器学习算法的集合。与 Weka 不同,该库没有任何 GUI,因为它主要面向软件开发人员。Java-ML 的一个特别有利的特性是,它对每种类型的算法都有一个公共接口,因此,算法的实现相当容易和直接。对该库的支持是它的另一个关键特性,因为源代码有很好的文档记录,因此是可扩展的,并且有大量的代码示例和教程,可以使用该库完成各种机器学习任务。java-ml.sourceforge.net/[网站](java-ml.sourceforge.net/)有关于图书馆的所有细…
在这个菜谱中,我们将使用这个库来完成以下任务:
- 数据集导入和导出
- 聚类和评估
- 分类
- 交叉验证和延期测试
- 特征评分
- 特征选择
准备就绪
为了执行此配方,我们需要以下内容:
-
In this recipe, we will be using the 0.1.7 version of the library. Download this version from sourceforge.net/projects/ja…:
-
The file you downloaded is a compressed zip file. Extract the files to a directory. The directory structure looks like the following:
我们需要将
javaml-0.1.7.jar文件作为一个外部 JAR 文件包含在 Eclipse 项目中,我们将使用它来实现菜谱。 -
The directory also has a folder named
lib. By opening the lib folder, we will see that it contains several other JAR files:这些 JAR 文件是 Java-ML 的依赖项,因此也必须作为外部 JAR 文件包含在项目中:
-
In our recipe, we will also be using a sample Iris dataset that is compatible with Java-ML's native file format. Iris and other data files, however, do not come with the library's distribution; they need to be downloaded from a different repository. To download the datasets, go to java-l.sourceforge.net/content/dat…. Java-ML has two types of datasets: 111 small UCI datasets and 7 large UCI datasets. For your practice, it is highly recommended to download both types of datasets. For the recipe, click 111 small UCI datasets and you will be prompted for its download:
-
下载完成后,解压缩文件夹。您将看到在这个分布中有 111 个文件夹,每个文件夹代表一个数据集。找到 iris 数据集文件夹并将其打开。您将看到有两个数据文件和一个名称文件。在我们的食谱中,我们将使用
iris.data文件。需要注意到该文件的路径,因为我们将在菜谱中使用该路径:
注意
如果您使用任何 UCI 数据集,您需要相应地注明并提供对原始作品的引用。详情可在archive.ics.uci.edu/ml/citation…找到。
怎么做...
-
创建一个名为
JavaMachineLearning的类。我们将使用一个主方法来实现所有的机器学习任务。该方法将抛出一个IOException:public class JavaMachineLearning { public static void main(String[] args) throws IOException{ -
First, we will be reading the iris dataset using Java-ML's
FileHandlerclass'sloadDataset()method:Dataset data = FileHandler.loadDataset(new File("path to your iris.data"), 4, ",");该方法的参数是到
dataset的路径、类属性的位置和分隔值的分隔符。可以用任何标准文本编辑器阅读dataset。属性的起始索引是 0,irisdataset的第五个属性是 class 属性。因此,第二个参数设置为 4。同样,在我们的例子中,数据值由逗号分隔。因此,第三个参数被设置为逗号。文件的内容被带到一个Dataset对象中。 -
通过简单地将对象传递给
System.out.println()方法:System.out.println(data);来打印
dataset内容 -
代码的部分输出如下:
[{[5.1, 3.5, 1.4, 0.2];Iris-setosa}, {[4.9, 3.0, 1.4, 0.2];Iris-setosa}, {[4.7, 3.2, 1.3, 0.2];Iris-setosa}, {[4.6, 3.1, 1.5, 0.2];Iris-setosa}, {[5.0, 3.6, 1.4, 0.2];Iris- setosa}, {[5.4, 3.9, 1.7, 0.4];Iris-setosa}, {[4.6, 3.4, 1.4, 0.3];Iris-setosa}, {[5.0, 3.4, 1.5, 0.2];Iris- setosa}, {[4.4, 2.9, 1.4, 0.2];Iris-setosa}, ...] -
If at any point, you need to export your
datasetfrom the .data format to a .txt format, Java-ML has a very simple way to accomplish it using itsexportDataset()method of theFileHandlerclass. The method takes the data and the output file as its parameter. The following line of code creates a text file in theC:/drive with the contents of the irisdataset:FileHandler.exportDataset(data, new File("c:/javaml- output.txt"));上述代码生成的文本文件的部分输出如下:
Iris-setosa 5.1 3.5 1.4 0.2 Iris-setosa 4.9 3.0 1.4 0.2 Iris-setosa 4.7 3.2 1.3 0.2 Iris-setosa 4.6 3.1 1.5 0.2 ...................................注意
对于 Java-ML 生成的数据文件,有两点需要注意。首先,类值是第一个属性,其次,这些值不再像在。数据文件;相反,它们由制表符分隔。
-
要读取上一步创建的数据文件,我们可以再次使用
loadDataset()方法。但是这次参数的值会不同:data = FileHandler.loadDataset(new File("c:/javaml- output.txt"), 0,"\t"); -
If we print the data:
System.out.println(data);然后,它将与我们在步骤 3 中看到的输出相同:
[{[5.1, 3.5, 1.4, 0.2];Iris-setosa}, {[4.9, 3.0, 1.4, 0.2];Iris-setosa}, {[4.7, 3.2, 1.3, 0.2];Iris-setosa}, {[4.6, 3.1, 1.5, 0.2];Iris-setosa}, {[5.0, 3.6, 1.4, 0.2];Iris- setosa}, {[5.4, 3.9, 1.7, 0.4];Iris-setosa}, {[4.6, 3.4, 1.4, 0.3];Iris-setosa}, {[5.0, 3.4, 1.5, 0.2];Iris- setosa}, {[4.4, 2.9, 1.4, 0.2];Iris-setosa}, ...] -
Java-ML 提供了非常简单的接口来应用聚类、显示聚类和评估聚类。我们将在菜谱中使用
KMeans聚类。创建一个KMeans集群器:Clusterer km = new KMeans(); -
使用
cluster()方法向集群器提供数据。结果将是数据点的多个聚类(或多个数据集)。将结果放入一个数组Dataset:Dataset[] clusters = km.cluster(data); -
If you want to see the data points in each cluster, use a for loop to iterate over the array of datasets:
```java
for(Dataset cluster:clusters){
System.out.println("Cluster: " + cluster);
}
```
该步骤中代码的部分输出如下:
```java
Cluster: [{[6.3, 3.3, 6.0, 2.5];Iris-virginica}, {[7.1, 3.0,
5.9, 2.1];Iris-virginica}, ...]
Cluster: [{[5.5, 2.3, 4.0, 1.3];Iris-versicolor}, {[5.7, 2.8,
4.5, 1.3];Iris-versicolor}, ...]
Cluster: [{[5.1, 3.5, 1.4, 0.2];Iris-setosa}, {[4.9, 3.0, 1.4,
0.2];Iris-setosa}, ...]
Cluster: [{[7.0, 3.2, 4.7, 1.4];Iris-versicolor}, {[6.4, 3.2,
4.5, 1.5];Iris-versicolor}, ...]
```
从输出中,我们可以看到 KMeans 算法从 iris 数据集创建了四个聚类。
11. 误差平方和是衡量聚类器性能的指标之一。我们将使用ClusterEvaluation类来测量聚类的误差:
```java
ClusterEvaluation sse = new SumOfSquaredErrors();
```
12. 接下来,我们简单地将聚类发送给对象的 score 方法,以获得聚类的误差平方和:
```java
double score = sse.score(clusters);
```
13. Print the error score:
```java
System.out.println(score);
```
输出中将显示以下内容:
```java
114.9465465309897
```
这是 iris 数据集的 KMeans 聚类的误差平方和。
14. Java-ML 中的分类也非常容易,只需要几行代码。下面的代码创建了一个 K 近邻 ( KNN )分类器。分类器将基于来自五个最近邻居的多数投票来预测看不见的数据点的标签。buildClassifier()方法用于训练一个分类器,该分类器将数据集(在我们的例子中是 iris)作为参数:
```java
Classifier knn = new KNearestNeighbors(5);
knn.buildClassifier(data);
```
15. After a model is built, the recipe will then continue to evaluate the model. We will see two evaluation methods that can be accomplished using Java-ML:
* k 倍交叉验证和
* 坚持测试
16. 对于 KNN 分类器的 k-fold 交叉验证,我们将使用分类器创建一个CrossValidation实例。CrossValidation类有一个名为crossValidation()的方法,它将数据集作为参数。该方法返回一个 map,它的第一个参数是 object,第二个参数是 evaluation metric:
```java
CrossValidation cv = new CrossValidation(knn);
Map<Object, PerformanceMeasure> cvEvaluation =
cv.crossValidation(data);
```
17. Now that we have the cross-validation results, we can simply print them by using the following:
```java
System.out.println(cvEvaluation);
```
这将显示每个类别的真阳性、假阳性、真阴性和假阴性:
```java
{Iris-versicolor=[TP=47.0, FP=1.0, TN=99.0, FN=3.0], Iris-
virginica=[TP=49.0, FP=3.0, TN=97.0, FN=1.0], Iris-setosa=
[TP=50.0, FP=0.0, TN=100.0, FN=0.0]}
```
18. In order to do a held-out testing, we need to have a test dataset. Unfortunately, we do not have any test dataset for iris. Therefore, we will be using the same iris.data file (that was used to train our KNN classifier) as our test dataset. But note that in real life, you will have a test dataset with the exact number of attributes as in your training dataset, while the labels of the data points will be unknown.
首先,我们加载测试数据集:
```java
Dataset testData = FileHandler.loadDataset(new File("path to
your iris.data "), 4, ",");
```
然后,我们使用以下代码获得分类器对测试数据的性能:
```java
Map<Object, PerformanceMeasure> testEvaluation =
EvaluateDataset.testDataset(knn, testData);
```
19. Then, we can simply print the results for each class by iterating over the map object:
```java
for(Object classVariable:testEvaluation.keySet()){
System.out.println(classVariable + " class has " +
testEvaluation.get(classVariable).getAccuracy());
}
```
前面的代码将打印每个类的 KNN 分类器的精度:
```java
Iris-versicolor class has 0.9666666666666667
Iris-virginica class has 0.9666666666666667
Iris-setosa class has 1.0
```
20. Feature scoring is a key aspect of machine learning to reduce dimensionality. In Java-ML, we will be implementing the following method that generates a score for a given attribute:
```java
public double score(int attIndex);
```
首先,创建一个特征评分算法实例。在我们的配方中,我们将使用增益比算法:
```java
GainRatio gainRatio = new GainRatio();
```
21. 接下来,将算法应用于数据:
```java
gainRatio.build(data);
```
22. Finally, print the scores of each feature using a for loop and by iterating through sending the attribute index to the score() method, one by one:
```java
for (int i = 0; i < gainRatio.noAttributes(); i++){
System.out.println(gainRatio.score(i));
}
```
虹膜数据集的特征得分如下:
```java
0.2560110727706682
0.1497001925156687
0.508659832906763
0.4861382158327255
```
23. We can also rank features based on some feature-ranking algorithms. To do this, we will be implementing the rank() method of Java-ML that works in a similar way like the score() method--both take the index of an attribute:
```java
public int rank(int attIndex);
```
创建一个特征排序算法实例。在我们的例子中,我们将依赖于 SVM 的基于递归消除特征方法的特征排序。构造函数的参数表示将被消除的排名最差的特征的百分比:
```java
RecursiveFeatureEliminationSVM featureRank = new
RecursiveFeatureEliminationSVM(0.2);
```
* 接下来,对数据集应用算法:
```java
featureRank.build(data);
```
* 最后,使用 for 循环并通过将属性索引依次发送给`rank()`方法进行迭代来打印每个特性的排名:
```java
for (int i = 0; i < featureRank.noAttributes(); i++){
System.out.println(featureRank.rank(i));
}
```
* iris 数据集的特征排序如下:
```java
3
2
0
1
```
24. 而对于特征的评分和排序,我们得到单个特征的信息,当我们应用 Java-ML 的特征子集选择时,我们只得到从数据集中选择的特征子集。
首先,创建一个特征选择算法。在我们的配方中,我们将使用一种使用greedy方法的正向选择方法。在选择特征的过程中,我们需要一个距离度量,在我们的例子中是皮尔逊相关度量。构造函数的第一个参数代表子集中要选择的属性数:
GreedyForwardSelection featureSelection = new
GreedyForwardSelection(5, new PearsonCorrelationCoefficient());
-
然后,将算法应用于数据集:
featureSelection.build(data); -
最后,您可以轻松打印算法选择的特征:
System.out.println(featureSelection.selectedAttributes());
要素的输出子集如下:
[0]
食谱的完整代码如下:
import java.io.File;
import java.io.IOException;
import java.util.Map;
import net.sf.javaml.classification.Classifier;
import net.sf.javaml.classification.KNearestNeighbors;
import net.sf.javaml.classification.evaluation.CrossValidation;
import net.sf.javaml.classification.evaluation.EvaluateDataset;
import net.sf.javaml.classification.evaluation.PerformanceMeasure;
import net.sf.javaml.clustering.Clusterer;
import net.sf.javaml.clustering.KMeans;
import net.sf.javaml.clustering.evaluation.ClusterEvaluation;
import net.sf.javaml.clustering.evaluation.SumOfSquaredErrors;
import net.sf.javaml.core.Dataset;
import net.sf.javaml.distance.PearsonCorrelationCoefficient;
import net.sf.javaml.featureselection.ranking.
RecursiveFeatureEliminationSVM;
import net.sf.javaml.featureselection.scoring.GainRatio;
import net.sf.javaml.featureselection.subset.GreedyForwardSelection;
import net.sf.javaml.tools.data.FileHandler;
public class JavaMachineLearning {
public static void main(String[] args) throws IOException{
Dataset data = FileHandler.loadDataset(new File("path to
iris.data"), 4, ",");
System.out.println(data);
FileHandler.exportDataset(data, new File("c:/javaml-
output.txt"));
data = FileHandler.loadDataset(new File("c:/javaml-output.txt"),
0,"\t");
System.out.println(data);
//Clustering
Clusterer km = new KMeans();
Dataset[] clusters = km.cluster(data);
for(Dataset cluster:clusters){
System.out.println("Cluster: " + cluster);
}
ClusterEvaluation sse= new SumOfSquaredErrors();
double score = sse.score(clusters);
System.out.println(score);
//Classification
Classifier knn = new KNearestNeighbors(5);
knn.buildClassifier(data);
//Cross validation
CrossValidation cv = new CrossValidation(knn);
Map<Object, PerformanceMeasure> cvEvaluation =
cv.crossValidation(data);
System.out.println(cvEvaluation);
//Held-out testing
Dataset testData = FileHandler.loadDataset(new File("path to
iris.data"), 4, ",");
Map<Object, PerformanceMeasure> testEvaluation =
EvaluateDataset.testDataset(knn, testData);
for(Object classVariable:testEvaluation.keySet()){
System.out.println(classVariable + " class has
"+testEvaluation.get(classVariable).getAccuracy());
}
//Feature scoring
GainRatio gainRatio = new GainRatio();
gainRatio.build(data);
for (int i = 0; i < gainRatio.noAttributes(); i++){
System.out.println(gainRatio.score(i));
}
//Feature ranking
RecursiveFeatureEliminationSVM featureRank = new
RecursiveFeatureEliminationSVM(0.2);
featureRank.build(data);
for (int i = 0; i < featureRank.noAttributes(); i++){
System.out.println(featureRank.rank(i));
}
//Feature subset selection
GreedyForwardSelection featureSelection = new
GreedyForwardSelection(5, new
PearsonCorrelationCoefficient());
featureSelection.build(data);
System.out.println(featureSelection.selectedAttributes());
}
}
使用斯坦福分类器分类数据点
斯坦福分类器是斯坦福大学自然语言处理小组开发的机器学习分类器。软件用 Java 实现,作为它的分类器,软件使用最大熵。最大熵相当于多类逻辑回归模型,只是参数设置略有不同。使用斯坦福分类器的优势在于,软件中使用的技术与谷歌或亚马逊使用的基本技术相同。
准备就绪
在这个食谱中,我们将使用斯坦福分类器,根据它使用最大熵的学习来分类数据点。我们将使用 3.6.0 版本的软件。详情请参考nlp.stanford.edu/software/cl…。要运行这个食谱的代码,您需要 Java 8。为了执行此配方,我们需要执行以下操作:
-
Go to nlp.stanford.edu/software/cl…, and download version 3.6.0. This is the latest version at the time of writing of this book. The software distribution is a compressed zip file:
-
Once downloaded, decompress the files. You will see a list of files and folders as follows:
stanford-classifier-3.6.0.jar文件需要包含在您的 Eclipse 项目中:该发行版还有一个名为 examples 的文件夹。我们将在食谱中使用这个文件的内容。示例文件夹包含两个数据集:奶酪疾病数据集和虹膜数据集。每个数据集包含三个相关联的文件:一个训练文件(带有。train 扩展名)、一个测试文件(带有。测试扩展名),以及一个属性(带有
.prop扩展名)。在我们的食谱中,我们将使用奶酪疾病数据集:如果打开
cheeseDisease.train文件,内容如下:2 Back Pain 2 Dissociative Disorders 2 Lipoma 1 Blue Rathgore 2 Gallstones 1 Chevrotin des Aravis 2 Pulmonary Embolism 2 Gastroenteritis 2 Ornithine Carbamoyltransferase Deficiency Disease ............第一列表示 1 或 2,是数据实例的类,而第二列是字符串值,是名称。类别 1 表示其后的名称是奶酪的名称,类别 2 表示其后的名称是疾病的名称。对数据集应用监督分类的目标是构建一个分类器,将奶酪名称与疾病名称分开。
注意
数据集中的列由制表符分隔。这里,只有一个类列和一个预测列。这是训练分类器的最低要求。但是,您可以拥有任意数量的预测列并指定它们的角色。
-
Cheese2007.prop文件是数据集的属性文件。您需要理解该文件的内容,因为我们的配方中的代码将联系该文件,以获得关于类特性、分类器要使用的特性类型、控制台上的显示格式、分类器的参数等等的必要信息。因此,我们现在将检查该文件的内容。
属性文件的前几行列出了特性选项。带#的行表示注释。这些行告诉分类器在学习过程中使用类特征(在训练文件中是第 1 列)。它还提供信息,例如分类器将使用 N 元语法特征,其中 N 元语法的最小长度为 1,最大长度为 4。分类器在计算 N-gram 和 10、20 和 30 的装箱长度时也将使用前缀和后缀:
# # Features
# useClassFeature=true
1.useNGrams=true
1.usePrefixSuffixNGrams=true
1.maxNGramLeng=4
1.minNGramLeng=1
1.binnedLengths=10,20,30
下一个重要的行块是映射,其中属性文件向分类器传达评估的基础事实将是列 0,并且需要对列 1 进行预测:
# # Mapping
# goldAnswerColumn=0
displayedColumn=1
接下来,属性文件保存最大熵分类器的优化参数:
#
# Optimization
# intern=true
sigma=3
useQN=true
QNsize=15
tolerance=1e-4
最后,属性文件包含训练和测试文件路径的条目:
# Training input
# trainFile=./examples/cheeseDisease.train
testFile=./examples/cheeseDisease.test
怎么做...
-
对于食谱,我们将在项目中创建一个类。我们将只使用一种
main()方法来演示分类。该方法将抛出一个异常:public class StanfordClassifier { public static void main(String[] args) throws Exception { -
斯坦福分类器是在
ColumnDataClassifier类中实现的。通过为奶酪疾病数据集提供属性文件的路径来创建分类器:ColumnDataClassifier columnDataClassifier = new ColumnDataClassifier("examples/cheese2007.prop"); -
接下来,使用训练数据构建分类器。
Classifier类的泛型是<String, String>,因为第一列是类,第二列是奶酪/疾病的名称。注意,即使 class 列的标称值是 1 和 2,它们也被视为字符串:Classifier<String,String> classifier = columnDataClassifier.makeClassifier (columnDataClassifier.readTrainingExamples ("examples/cheeseDisease.train")); -
Finally, iterate through each line of the test dataset. The test dataset is similar to the training dataset: the first column is the actual class and the second column is the name. A first few lines of the test dataset are as follows:
2 鹦鹉热
2 库欣综合征
2 内斜视
2 黄疸,新生儿
2 胸腺瘤...............
它是如何工作的...
列数据分类器被应用到测试集的每一行,结果被发送到一个Datum对象。分类器预测Datum对象的类别,并在控制台上打印预测结果:
for (String line : ObjectBank.getLineIterator("examples/cheeseDisease.test", "utf-8")) { Datum<String,String> d = columnDataClassifier.makeDatumFromLine(line); System.out.println(line + " ==> " + classifier.classOf(d)); }
控制台上的输出如下(输出被截断):
2 Psittacosis ==> 2 2 Cushing Syndrome ==> 2 2 Esotropia ==> 2 2 Jaundice, Neonatal ==> 2 2 Thymoma ==> 2 1 Caerphilly ==> 1 2 Teratoma ==> 2 2 Phantom Limb ==> 1 2 Iron Overload ==> 1 ...............
第一列是实际类别,第二列是名称,==>符号右侧的值是分类器预测的类别。食谱的完整代码如下:
import edu.stanford.nlp.classify.Classifier;
import edu.stanford.nlp.classify.ColumnDataClassifier;
import edu.stanford.nlp.ling.Datum;
import edu.stanford.nlp.objectbank.ObjectBank;
public class StanfordClassifier {
public static void main(String[] args) throws Exception { ColumnDataClassifier columnDataClassifier = new ColumnDataClassifier("examples/cheese2007.prop"); Classifier<String,String> classifier = columnDataClassifier.makeClassifier(columnDataClassifier.readTrainingExamples("examples/cheeseDisease.train"));
for (String line : ObjectBank.getLineIterator("examples/cheeseDisease.test", "utf-8")) { Datum<String,String> d = columnDataClassifier.makeDatumFromLine(line); System.out.println(line + " ==> " + classifier.classOf(d));
}
}
}
该方法不演示斯坦福分类器模型的加载和保存。如果有兴趣,可以看看发行版中的ClassifierDemo.java文件。
使用海量在线分析(MOA)对数据点进行分类
海量在线分析或 MOA 与 Weka 相关,但它具有更大的可扩展性。它是一个著名的用于数据流挖掘的 Java 工作台。有了强大的社区,MOA 实现了分类、聚类、回归、概念漂移识别和推荐系统。MOA 的其他主要优势是其可被开发者扩展的能力以及与 Weka 进行双向交互的能力。
准备就绪
为了执行此配方,我们需要以下内容:
-
MOA can be downloaded from sourceforge.net/projects/mo…, which eventually is accessible from the MOA getting started webpage at moa.cms.waikato.ac.nz/getting-sta…:
这会将一个名为
moa-release-2016.04.zip的 zip 文件下载到您的系统中。把它保存在你喜欢的任何地方。 -
Once downloaded, extract the files. You will see files and folders as follows:
-
您需要将
moa.jar文件作为项目的外部库:
怎么做...
-
首先,创建一个类和一个带两个参数的方法:第一个参数表示我们将要处理的实例数量,第二个参数表示我们是否要测试分类器:
public class MOA { public void run(int numInstances, boolean isTesting){ -
创建一个
HoeffdingTree分类器:Classifier learner = new HoeffdingTree();注意
恐鸟实现了以下分类器:朴素贝叶斯、赫夫丁树、赫夫丁选项树、赫夫丁自适应树、装袋、增压、使用阿德温的装袋、杠杆装袋、SGD、感知器、斯佩加索斯.
-
接下来,创建一个随机径向基函数流。
-
准备要使用的流:
stream.prepareForUse(); -
设置对数据流头的引用。使用
getHeader()方法可以找到数据流的头:learner.setModelContext(stream.getHeader()); -
然后,准备要使用的分类器:
learner.prepareForUse(); -
声明两个变量,用于跟踪样本数和正确分类的样本数:
int numberSamplesCorrect = 0; int numberSamples = 0; -
声明另一个变量来跟踪分类所花费的时间:
long evaluateStartTime = TimingUtils.getNanoCPUTimeOfCurrentThread(); -
接下来,循环执行,直到流中有更多的实例,并且被分类的样本数没有达到实例总数。在循环中,获取流的每个实例的数据。然后,检查分类器是否正确地对实例进行了分类。如果是,将变量
numberSamplesCorrect增加 1。仅当测试打开时才选中此项(通过此方法的第二个参数)。 -
然后,通过增加样本数来转到下一个样本,并使用下一个训练实例来训练您的学员:
```java
while (stream.hasMoreInstances() && numberSamples < numInstances)
{
Instance trainInst = stream.nextInstance().getData();
if (isTesting)
{
if (learner.correctlyClassifies(trainInst)){
numberSamplesCorrect++;
}
}
numberSamples++; learner.trainOnInstance(trainInst);
}
```
11. 计算精度:
```java
double accuracy = 100.0 * (double) numberSamplesCorrect/
(double) numberSamples;
```
12. 此外,计算分类所需的时间:
```java
double time = TimingUtils.nanoTimeToSeconds(TimingUtils.
getNanoCPUTimeOfCurrentThread()- evaluateStartTime);
```
13. 最后,显示这些评估指标并关闭方法:
```java
System.out.println(numberSamples + " instances processed with "
+ accuracy + "% accuracy in "+time+" seconds."); }
```
14. 要执行该方法,可以有如下的main()方法。关闭您的班级:
public static void main(String[] args) throws IOException { MOA
exp = new MOA(); exp.run(1000000, true); } }
该配方的完整代码如下:
import moa.classifiers.trees.HoeffdingTree;
import moa.classifiers.Classifier;
import moa.core.TimingUtils;
import moa.streams.generators.RandomRBFGenerator;
import com.yahoo.labs.samoa.instances.Instance;
import java.io.IOException;
public class MOA {
public void run(int numInstances, boolean isTesting){
Classifier learner = new HoeffdingTree();
RandomRBFGenerator stream = new RandomRBFGenerator();
stream.prepareForUse();
learner.setModelContext(stream.getHeader());
learner.prepareForUse();
int numberSamplesCorrect = 0;
int numberSamples = 0;
long evaluateStartTime = Tim-
ingUtils.getNanoCPUTimeOfCurrentThread();
while (stream.hasMoreInstances() && numberSamples <
numIn-stances) {
Instance trainInst =
stream.nextInstance().getData();
if (isTesting) {
if
(learner.correctlyClassifies(trainInst)){
numberSamplesCorrect++;
}
}
numberSamples++;
learner.trainOnInstance(trainInst);
}
double accuracy = 100.0 * (double)
numberSamplesCorrect/ (double) numberSamples;
double time = Tim-in-
gUtils.nanoTimeToSeconds(TimingUtils.
getNanoCPUTimeOfCurrentThread()- evaluateStartTime);
System.out.println(numberSamples + " instances
processed with " + accuracy + "% accuracy in "+time+"
seconds.");
}
public static void main(String[] args) throws IOException {
MOA exp = new MOA();
exp.run(1000000, true);
}
}
菜谱中代码的输出如下所示(输出可能因机器而异):
1000000 instances processed with 91.0458% accuracy in 6.769871032 seconds.
使用木兰对多标记数据点进行分类
到目前为止,我们已经看到了多类分类,其目的是将一个数据实例分类到几个类中的一个。多标签数据实例是可以有多个类或标签的数据实例。到目前为止,我们使用的机器学习工具无法处理具有多个目标类这一特征的数据点。
为了对多标记数据点进行分类,我们将使用一个名为 Mulan 的开源 Java 库。Mulan 实现了各种分类、排序、特征选择和模型评估。由于木兰没有 GUI,使用它的唯一方法是通过命令行或使用它的 API。在这份食谱中,我们将把重点限制在使用两个不同分类器对多标记数据集进行分类和分类评估上。
准备就绪
为了执行此配方,我们需要以下内容:
- 首先,下载花木兰。在我们的食谱中,我们将使用它的 1.5 版本。库的压缩文件可以在https://SourceForge . net/projects/Mulan/files/Mulan-1-5/Mulan-1 . 5 . 0 . zip/download找到,可以通过mulan.sourceforge.net/download.ht…访问。
- Unzip the compressed files. You will see a data folder as follows. Take a look inside the
distfolder: - You will see three files there. Among the three files, there is another compressed file named
Mulan-1.5.0.zip. Unzip the files: - This time, you will see three or four JAR files. From the four JAR files, we will be using the JAR files highlighted in the following image:
- Add these three JAR files into your project as external libraries:
- 木兰分布的数据文件夹包含一个多标签数据集示例。在我们的食谱中,我们将使用
emotions.arff文件和emotions.xml文件:
在开始我们的食谱之前,让我们先来看看木兰的数据集格式。Mulan 需要两个文件来指定多标签数据集。第一个是 ARFF 文件(看看第四章,从数据中学习——第一部分)。标签应该被指定为具有两个值“0”和“1”的标称属性,其中前者表示标签不存在,后者表示标签存在。以下示例演示了数据集有三个数字要素,每个实例可以有五个类或标签。在@data部分,前三个值代表实际的特征值,然后我们有五个 0 或 1 表示类的存在或不存在:
@relation MultiLabelExample
@attribute feature1 numeric
@attribute feature2 numeric
@attribute feature3 numeric
@attribute label1 {0, 1}
@attribute label2 {0, 1}
@attribute label3 {0, 1}
@attribute label4 {0, 1}
@attribute label5 {0, 1}
@data
2.3,5.6,1.4,0,1,1,0,0
另一方面,XML 文件指定标签以及它们之间的任何层次关系。以下示例是与上一示例相对应的 XML 文件:
<labels >
<label name="label1"></label>
<label name="label2"></label>
<label name="label3"></label>
<label name="label4"></label>
<label name="label5"></label>
</labels>
更多详情,请参见mulan.sourceforge.net/format.html。
怎么做...
-
创建一个类和一个方法。我们将在
main()方法中编写所有的代码:public class Mulan { public static void main(String[] args){ -
创建一个数据集并将
emotions.arff和emotions.xml文件读取到数据集:MultiLabelInstances dataset = null; try { dataset = new MultiLabelInstances("path to emotions.arff", "path to emotions.xml"); } catch (InvalidDataFormatException e) { } -
接下来,我们将创建一个
RAkEL分类器和一个MLkNN分类器。请注意,RAkEL是一个元分类器,这意味着它可以有一个多标签学习器,它通常与LabelPowerset算法一起使用。LabelPowerset是一种基于变换的算法,可以将单标签分类器(在我们的例子中,J48)作为参数。MLkNN是一个自适应分类器,基于 k 近邻:RAkEL learner1 = new RAkEL(new LabelPowerset(new J48())); MLkNN learner2 = new MLkNN(); -
创建一个评估器来评估分类性能:
Evaluator eval = new Evaluator(); -
因为我们将对两个分类器进行评估,所以我们需要声明一个可以有多个评估结果的变量:
MultipleEvaluation results; -
我们将进行 10 倍交叉验证评估。因此,声明一个你要创建的折叠数的变量:
int numFolds = 10; -
接下来,评估你的第一个学员并显示结果:
results = eval.crossValidate(learner1, dataset, numFolds); System.out.println(results); -
最后,评估你的第二个学员并显示结果:
results = eval.crossValidate(learner2, dataset, numFolds); System.out.println(results); -
关闭方法和类:
} }
食谱的完整代码如下:
import mulan.classifier.lazy.MLkNN;
import mulan.classifier.meta.RAkEL;
import mulan.classifier.transformation.LabelPowerset;
import mulan.data.InvalidDataFormatException;
import mulan.data.MultiLabelInstances;
import mulan.evaluation.Evaluator;
import mulan.evaluation.MultipleEvaluation;
import weka.classifiers.trees.J48;
public class Mulan {
public static void main(String[] args){
MultiLabelInstances dataset = null;
try {
dataset = new MultiLabelInstances("path to emo-
tions.arff", "path to emotions.xml");
} catch (InvalidDataFormatException e) {
}
RAkEL learner1 = new RAkEL(new LabelPowerset(new
J48()));
MLkNN learner2 = new MLkNN();
Evaluator eval = new Evaluator();
MultipleEvaluation results;
int numFolds = 10;
results = eval.crossValidate(learner1, dataset, num-
Folds);
System.out.println(results);
results = eval.crossValidate(learner2, dataset, num-
Folds);
System.out.println(results);
}
}
代码的输出将是您选择的两个学习者的表现:
Fold 1/10
Fold 2/10
Fold 3/10
Fold 4/10
Fold 5/10
Fold 6/10
Fold 7/10
Fold 8/10
Fold 9/10
Fold 10/10
Hamming Loss: 0.2153±0.0251
Subset Accuracy: 0.2562±0.0481
Example-Based Precision: 0.6325±0.0547
Example-Based Recall: 0.6307±0.0560
Example-Based F Measure: 0.5990±0.0510
Example-Based Accuracy: 0.5153±0.0484
Example-Based Specificity: 0.8607±0.0213
........................................
Fold 1/10
Fold 2/10
Fold 3/10
Fold 4/10
Fold 5/10
Fold 6/10
Fold 7/10
Fold 8/10
Fold 9/10
Fold 10/10
Hamming Loss: 0.1951±0.0243
Subset Accuracy: 0.2831±0.0538
Example-Based Precision: 0.6883±0.0655
Example-Based Recall: 0.6050±0.0578
Example-Based F Measure: 0.6138±0.0527
Example-Based Accuracy: 0.5326±0.0515
Example-Based Specificity: 0.8994±0.0271
........................................