4.4分类器-All Alike, Yet Different
在本章中,我们将从上一章 Getting data in shape 中继续我们的工作,以便复制Haxby等人(2001年)的研究。对于本教程,有一个小帮手函数,可以在以下情况下手动生成我们所生成的数据集:
原始研究采用所谓的1-最近邻分类器,使用相关性作为距离测量。在pymvpa中,这种类型的分类器由knn类提供,这使得可以指定所需的参数。
k最近邻分类器基于 a training dataset 训练数据集中的每个样本的样本相似性来执行分类。
k 的值指定要导出预测的邻居数,
dfx 设置确定邻居的距离度量,
voting 从分配给这些邻居的目标集中选择单个标签的策略。
现在我们有了一个分类器实例,通过将DataSet传递给它的train()方法,可以轻松地对其进行训练。
经过训练的分类器随后可用于对未标记的样本进行分类。分类性能可以通过将这些预测与目标标签进行比较来评估。
>>> predictions = clf.predict(ds.samples)
>>> np.mean(predictions == ds.sa.targets)
1.0
我们看到,分类器在我们的数据集上表现得非常好-它甚至连一个预测错误都没有。然而,大部分时间我们对分类器对其训练的同一数据集的预测精度没有特别感兴趣。
相反,我们感兴趣的是分类器对新的、没见过的数据的可推广性。这将允许我们原则上使用它为未标记的数据分配标签。因为我们只有一个数据集,因此需要将其拆分为(至少)两个部分来实现这一点。在最初的研究中,haxby和他的同事将数据集分为奇数和偶数运行的激活模式。我们的DataSet在runtype SAMPLE属性中具有以下信息:
>>> print ds.sa.runtype
['even' 'even' 'even' 'even' 'even' 'even' 'even' 'even' 'odd' 'odd' 'odd'
'odd' 'odd' 'odd' 'odd' 'odd']
使用此属性,我们现在可以很容易地将数据集分割为一半。pymvpa数据集可以用类似于numpy的ndarray的方式分割。以下调用选择样本子集(即数据集的行),其中runtype属性的值是字符串“偶数”或“奇数”。
>>> ds_split1 = ds[ds.sa.runtype == 'odd']
>>> len(ds_split1)
8
>>> ds_split2 = ds[ds.sa.runtype == 'even']
>>> len(ds_split2)
8
现在我们可以重复上述步骤:利用train()处理一半的数据集,利用predict()处理另一半,人工计算预测精度。然而,更方便的方法是让分类器为我们这样做。pymvpa中的许多对象支持一个后处理步骤,我们可以使用该步骤从实际结果中计算一些内容。下面的示例计算分类器预测和存储在数据集中的目标值之间的失配误差。为了进行这项工作,我们不再调用分类器的predict()方法,而是直接“call”classififier 直接与测试数据集联系起来。在PyMVPA中,这是一个非常常见的使用模式,我们在本教程的过程中可以看到很多。请注意,我们现在计算error,因此较低的值表示更准确的分类。
>>> clf.set_postproc(BinaryFxNode(mean_mismatch_error, 'targets'))
>>> clf.train(ds_split2)
>>> err = clf(ds_split1)
>>> print np.asscalar(err)
0.125
在这种情况下,我们选择数据集的哪一半用于培训,哪一半用于测试完全是任意的,因此我们也可以在交换角色后估计传输错误:
>>> clf.train(ds_split1)
>>> err = clf(ds_split2)
>>> print np.asscalar(err)
0.0
我们认为,平均分类器误差非常低,并且我们获得了与原始研究中报告的结果相当的精度水平。
4.4.1 交叉验证
我们刚刚做的是手动将数据集分为培训和测试数据集的组合,给定特定的示例属性-在这种情况下,激活模式or sample样本来自偶数运行或者奇数运行。我们对每个分割进行分类分析,以估计分类器模型的性能。一般来说,这种方法称为交叉验证,涉及将数据集划分为多对子集,根据某些标准选择样本组,并通过在分割中的第一个数据集上对分类器性能进行培训,并根据同一拆分测试第二数据集来估计分类器性能
PyMVPA提供了一种方法,以允许完全的交叉验证过程完全自动运行,而无需手动拆分数据集。使用CrossValidation类,通过指定应在每个数据集拆分上计算什么度量以及应如何生成数据集拆分,来设置交叉验证。通常计算的度量是我们在上一节中已经查看过的传输错误。第二个元素是一个用于数据集的 generator 生成器,是pYMVPA中的另一个非常常见的工具。以下示例使用HalfPartitioner,半分区器,一个生成器,作用是在使用数据集调用时,将其关联的所有样本标记为数据集的第一个或第二个半部分。这基于指定的示例属性的值-在这种情况下运行类型-非常类似于我们之前执行的手动数据集拆分。HalfPartitioner半分区器将确保随后将样本分配给两部分,即第一生成数据集中前半部分的样本将位于第二生成数据集的后半部分。使用这两种技术,我们可以轻松地复制手工交叉验证-重用现有的分类器,但不需要定制的后处理步骤。
>>> # disable post-processing again
>>> clf.set_postproc(None)
>>> # dataset generator
>>> hpart = HalfPartitioner(attr='runtype')
>>> # complete cross-validation facility
>>> cv = CrossValidation(clf, hpart)
一旦创建了cv对象,就可以使用DataSet调用它,就像我们以前对分类器所做的那样。它将在内部执行所有数据集分区,将每个生成的数据集拆分为训练和测试集(基于分区),并对分类器进行反复训练和测试。最后,它将返回所有交叉验证折叠的结果。
>>> cv_results = cv(ds)
>>> np.mean(cv_results)
0.0625
实际上,交叉验证结果作为另一个数据集返回,每个数据集具有每个折叠的一个样本和每个折叠计算的传输误差的单个特征。
>>> len(cv_results)
2
>>> cv_results.samples
array([[ 0. ],
[ 0.125]])
4.4.2任何分类器,真的
迄今为止开发的用于分析的所有代码的简短总结如下:
>>> clf = kNN(k=1, dfx=one_minus_correlation, voting='majority')
>>> cvte = CrossValidation(clf, HalfPartitioner(attr='runtype'))
>>> cv_results = cvte(ds)
>>> np.mean(cv_results)
0.0625
看看这个小代码片段,我们可以很好地看到交叉验证的分类分析的逻辑部分。
1.加载数据;
2.选择分类器;
3.设置错误函数
4.在交叉验证过程中评估错误
5.检查结果
我们以前选择的分类器是以复制Haxby等人的意图为指导的。(2001),但是如果我们想尝试一种不同的算法呢?在这种情况下,pymvpa的另一个好特性开始发挥作用。所有分类器都实现了一个公共接口,使得它们可以很容易地互换,而不需要调整分析代码的任何其他部分。例如,如果我们想在我们的示例数据集中尝试流行的svm(支持向量机),它如下所示:
>>> clf = LinearCSVMC()
>>> cvte = CrossValidation(clf, HalfPartitioner(attr='runtype'))
>>> cv_results = cvte(ds)
>>> np.mean(cv_results)
0.1875
我们使用流行的llinear SVM 算法,在内部使用流行的libsvm库(请注意,pYMVPA提供了额外的SVM实现),而不是KNN算法,而是创建了一个线性SVM分类器。其余代码保持相同。SVM及其默认设置似乎比简单的KNN分类器要稍微差一些。我们很快就会回到分类器了。让我们首先看看这个分析的剩余部分。
我们已经知道CrossValidation可用于计算错误。迄今为止,我们只使用实际目标与分类器预测之间的平均失配数作为误差函数(缺省值为默认值)。但是,PyMVPA在 errorfx 模块中提供了许多可选功能,但是指定自定义函数也是微不足道的。例如,如果我们不想报告错误,而是精度,我们可以执行以下操作: