scikit中的OneVsRestClassifier和MultiOutputClassifier有什么区别?

时间:2017-03-15 19:54:21

标签: python scikit-learn classification multilabel-classification multiclass-classification

有人可以解释(例如可能)scikit-learn中OneVsRestClassifierMultiOutputClassifier之间的差异是什么?

我阅读了文档,我知道我们使用了:

  • OneVsRestClassifier - 当我们想要进行多类或多标签分类时,它的策略包括 每个类适合一个分类器 。对于每个分类器,该类适用于所有其他类。 (这很清楚,这意味着多类/多标记分类的问题被分解为多个二元分类问题。)
  • MultiOutputClassifier - 当我们想要进行多目标分类时(这是什么?),它的策略包括 为每个目标拟合一个分类器 (目标意味着什么?)

我已经使用OneVsRestClassifier进行多标记分类,我可以理解它是如何工作的,但后来我找到了MultiOutputClassifier,并且无法理解它与OneVsRestClassifier的工作方式有何不同。

2 个答案:

答案 0 :(得分:22)

多类分类

为了更好地说明这些差异,我们假设您的目标是将SO问题分类为n_classes个不同的互斥类。为了简化本例,我们只考虑四个类,即'Python''Java''C++''Other language'。让我们假设您有一个仅由六个SO问题组成的数据集,这些问题的类标签存储在数组y中,如下所示:

import numpy as np
y = np.asarray(['Java', 'C++', 'Other language', 'Python', 'C++', 'Python'])

上述情况通常称为多类分类(也称为多项分类)。为了适应分类器并通过scikit-learn库验证模型,您需要将文本类标签转换为数字标签。为此,您可以使用LabelEncoder

from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
y_numeric = le.fit_transform(y)

这是数据集标签的编码方式:

In [220]: y_numeric
Out[220]: array([1, 0, 2, 3, 0, 3], dtype=int64)

这些数字表示以下数组的索引:

In [221]: le.classes_
Out[221]: 
array(['C++', 'Java', 'Other language', 'Python'], 
      dtype='|S14')

一个重要的特殊情况是只有两个类,即n_classes = 2。这通常称为二进制分类

多标签分类

现在让我们假设您希望使用n_classes二元分类器池执行此类多类分类,n_classes不同类的数量。这些二元分类器中的每一个都决定项是否属于特定类。在这种情况下,您不能将类标签编码为从0n_classes - 1的整数,您需要创建一个二维指标矩阵。请考虑样本n属于k类。然后,指标矩阵的[n, k]条目为1,行n中的其余元素为0。重要的是要注意,如果类不是互斥的,则连续可以有多个1。这种方法被命名为多标记分类,可以通过MultiLabelBinarizer轻松实现:

from sklearn.preprocessing import MultiLabelBinarizer
mlb = MultiLabelBinarizer()
y_indicator = mlb.fit_transform(y[:, None])

指标如下所示:

In [225]: y_indicator
Out[225]: 
array([[0, 1, 0, 0],
       [1, 0, 0, 0],
       [0, 0, 1, 0],
       [0, 0, 0, 1],
       [1, 0, 0, 0],
       [0, 0, 0, 1]])

以及1' s实际上是此数组的索引的列号:

In [226]: mlb.classes_
Out[226]: array(['C++', 'Java', 'Other language', 'Python'], dtype=object)

多输出分类

如果您想同时根据两个不同的标准对特定的SO问题进行分类,例如语言和应用程序,该怎么办?在这种情况下,您打算执行多输出分类。为简单起见,我将只考虑三个应用程序类,即'Computer Vision''Speech Processing'和'Other application'。数据集的标签数组应为二维:

y2 = np.asarray([['Java', 'Computer Vision'],
                 ['C++', 'Speech Recognition'],
                 ['Other language', 'Computer Vision'],
                 ['Python', 'Other Application'],
                 ['C++', 'Speech Recognition'],
                 ['Python', 'Computer Vision']])

同样,我们需要将文本类标签转换为数字标签。据我所知,这个功能尚未在scikit-learn中实现,因此您需要编写自己的代码。 This thread描述了一些聪明的方法,但就本帖而言,以下单行应该足够了:

y_multi = np.vstack((le.fit_transform(y2[:, i]) for i in range(y2.shape[1]))).T

编码标签如下所示:

In [229]: y_multi
Out[229]: 
array([[1, 0],
       [0, 2],
       [2, 0],
       [3, 1],
       [0, 2],
       [3, 0]], dtype=int64)

每列中值的含义可以从以下数组中推断出来:

In [230]: le.fit(y2[:, 0]).classes_
Out[230]: 
array(['C++', 'Java', 'Other language', 'Python'], 
      dtype='|S18')

In [231]: le.fit(y2[:, 1]).classes_
Out[231]: 
array(['Computer Vision', 'Other Application', 'Speech Recognition'], 
      dtype='|S18')

答案 1 :(得分:1)

这是@tonechas 答案的扩展。在阅读本文之前阅读该答案。 OVR 仅在每个标签是二进制标签/类(也称为二进制多标签)时才支持多标签,即样本属于该标签或不属于该标签。当目标是多输出(也称为多类多标签)时,即当每个样本可以属于标签内的任何一个类时,它将不起作用。对于后一种情况,您需要使用 sklearn 多输出分类器。

换句话说,当您的目标变量看起来像这样时,sklearn OVR 不起作用,

y_true = np.arr([[2, 1, 0],
                 [0, 2, 1],
                 [1, 2, 4]])

其中 label1 有 4 个类 [0, 1, 2, 3]; label2 有 3 个类 [0, 1, 2]; label3 有 5 个类 [0, 1, 2 , 3, 4]。例如:第一个样本在 label1 中属于第 2 类,在 label2 中属于第 1 类,在 label3 中属于第 0 类。 把它想象成标签不是互斥的,而每个标签中的类是互斥的。

Sklearn OVR 将在以下情况下工作

y_true = np.arr([[0, 1, 1],
                 [0, 0, 1],
                 [1, 1, 0]])

其中 label1 labe2, label3 每个只有 2 个类。因此,样本要么属于该标签,要么不属于该标签。例如:第一个样本属于 label1 和 label2。

很抱歉,我找不到此类用例的真实示例。