1.12. 多类别和多输出算法#

用户指南的本节涵盖了与多学习问题相关的功能,包括多类别多标签多输出分类和回归。

本节中的模块实现了元估计器,这些元估计器需要在其构造函数中提供一个基础估计器。元估计器扩展了基础估计器的功能以支持多学习问题,这是通过将多学习问题转换为一组更简单的问题,然后为每个问题拟合一个估计器来实现的。

本节涵盖两个模块:sklearn.multiclasssklearn.multioutput。下图演示了每个模块负责的问题类型以及每个模块提供的相应元估计器。

../_images/multi_org_chart.png

下表提供了关于问题类型之间差异的快速参考。本指南后续部分将提供更详细的解释。

目标数量

目标基数

有效的 type_of_target

多类别分类

1

>2

‘multiclass’

多标签分类

>1

2(0 或 1)

‘multilabel-indicator’

多类别多输出分类

>1

>2

‘multiclass-multioutput’

多输出回归

>1

连续的

‘continuous-multioutput’

以下是 scikit-learn 估计器的摘要,这些估计器具有内置的多学习支持,按策略分组。如果您使用的是这些估计器之一,则不需要本节提供的元估计器。但是,元估计器可以提供超出内置策略的其他策略。

1.12.1. 多类别分类#

警告

scikit-learn 中的所有分类器都直接支持多类别分类。除非您想尝试不同的多类别策略,否则不需要使用sklearn.multiclass 模块。

多类别分类是一种包含两个以上类别的分类任务。每个样本只能被标记为一个类别。

例如,使用从一组水果图像中提取的特征进行分类,其中每张图像可能是橙子、苹果或梨。每张图像是一个样本,并被标记为 3 个可能类别之一。多类别分类假设每个样本只分配一个标签——例如,一个样本不能同时是梨和苹果。

虽然所有 scikit-learn 分类器都能够进行多类别分类,但sklearn.multiclass 提供的元估计器允许更改它们处理多个类别的方式,因为这可能会影响分类器的性能(就泛化误差或所需的计算资源而言)。

1.12.1.1. 目标格式#

对于type_of_target (y),有效的多类别表示形式为

  • 包含两个以上离散值的 1d 向量或列向量。对于 4 个样本的向量y 的示例:

    >>> import numpy as np
    >>> y = np.array(['apple', 'pear', 'apple', 'orange'])
    >>> print(y)
    ['apple' 'pear' 'apple' 'orange']
    
  • 形状为(n_samples, n_classes)的稠密或稀疏二进制矩阵,每行一个样本,其中每列代表一个类别。对于 4 个样本的稠密和稀疏二进制矩阵y 的示例,其中列分别为苹果、橙子和梨:

    >>> import numpy as np
    >>> from sklearn.preprocessing import LabelBinarizer
    >>> y = np.array(['apple', 'pear', 'apple', 'orange'])
    >>> y_dense = LabelBinarizer().fit_transform(y)
    >>> print(y_dense)
    [[1 0 0]
     [0 0 1]
     [1 0 0]
     [0 1 0]]
    >>> from scipy import sparse
    >>> y_sparse = sparse.csr_matrix(y_dense)
    >>> print(y_sparse)
    <Compressed Sparse Row sparse matrix of dtype 'int64'
        with 4 stored elements and shape (4, 3)>
      Coords    Values
      (0, 0)    1
      (1, 2)    1
      (2, 0)    1
      (3, 1)    1
    

有关LabelBinarizer的更多信息,请参考转换预测目标 (y)

1.12.1.2. OneVsRestClassifier#

一对多策略,也称为一对其余,在OneVsRestClassifier中实现。该策略包括为每个类别拟合一个分类器。对于每个分类器,该类别都针对所有其他类别进行拟合。除了其计算效率(只需要n_classes个分类器)之外,这种方法的优点还在于其可解释性。由于每个类别都由一个且只有一个分类器表示,因此可以通过检查其对应的分类器来了解该类别。这是最常用的策略,也是一个公平的默认选择。

以下是使用 OvR 进行多类别学习的示例:

>>> from sklearn import datasets
>>> from sklearn.multiclass import OneVsRestClassifier
>>> from sklearn.svm import LinearSVC
>>> X, y = datasets.load_iris(return_X_y=True)
>>> OneVsRestClassifier(LinearSVC(random_state=0)).fit(X, y).predict(X)
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])

OneVsRestClassifier 也支持多标签分类。要使用此功能,请向分类器提供一个指示矩阵,其中单元格 [i, j] 表示样本 i 中存在标签 j。

../_images/sphx_glr_plot_multilabel_001.png

示例

1.12.1.3. OneVsOneClassifier#

OneVsOneClassifier 为每一对类别构建一个分类器。在预测时,选择获得最多票数的类别。如果出现平局(两个类别票数相同),则选择通过对底层二元分类器计算的成对分类置信度进行求和而获得最高聚合分类置信度的类别。

由于它需要拟合n_classes * (n_classes - 1) / 2个分类器,因此由于其 O(n_classes^2) 的复杂度,此方法通常比一对其余方法慢。但是,对于像核算法这样的在n_samples方面扩展性不佳的算法,此方法可能更有优势。这是因为每个单独的学习问题只涉及数据的一个小子集,而对于一对其余方法,则n_classes次都使用完整的数据集。决策函数是一对一分类的单调变换的结果。

以下是使用 OvO 进行多类别学习的示例:

>>> from sklearn import datasets
>>> from sklearn.multiclass import OneVsOneClassifier
>>> from sklearn.svm import LinearSVC
>>> X, y = datasets.load_iris(return_X_y=True)
>>> OneVsOneClassifier(LinearSVC(random_state=0)).fit(X, y).predict(X)
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])

参考文献

  • “模式识别与机器学习。Springer”,Christopher M. Bishop,第 183 页,(第一版)

1.12.1.4. OutputCodeClassifier#

基于纠错输出码的策略与一对其余和一对一策略有很大不同。使用这些策略,每个类别都表示在欧几里得空间中,其中每个维度只能为 0 或 1。换句话说,每个类别都由一个二进制码(一个 0 和 1 的数组)表示。跟踪每个类别位置/码的矩阵称为码本。码大小是上述空间的维度。直观地说,每个类别都应该用尽可能唯一的码表示,并且应该设计一个好的码本来优化分类精度。在此实现中,我们只是简单地使用[3]中提倡的随机生成的码本,尽管将来可能会添加更详尽的方法。

在拟合时,为码本中的每个位拟合一个二元分类器。在预测时,使用分类器将新点投影到类别空间中,并选择最接近该点的类别。

OutputCodeClassifier中,code_size属性允许用户控制将使用的分类器数量。它是类别总数的百分比。

介于 0 和 1 之间的数字需要的分类器少于一对其余方法。理论上,log2(n_classes) / n_classes足以明确地表示每个类别。但是,在实践中,这可能不会导致良好的精度,因为log2(n_classes)远小于n_classes

大于1的类别数将需要比一对其余方法更多的分类器。在这种情况下,理论上一些分类器会纠正其他分类器犯的错误,因此得名“纠错”。然而,在实践中,这可能不会发生,因为分类器错误通常是相关的。纠错输出码具有与bagging类似的效果。

以下是使用输出码进行多类别学习的示例

>>> from sklearn import datasets
>>> from sklearn.multiclass import OutputCodeClassifier
>>> from sklearn.svm import LinearSVC
>>> X, y = datasets.load_iris(return_X_y=True)
>>> clf = OutputCodeClassifier(LinearSVC(random_state=0), code_size=2, random_state=0)
>>> clf.fit(X, y).predict(X)
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1,
       1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])

参考文献

  • “Solving multiclass learning problems via error-correcting output codes”,Dietterich T., Bakiri G., Journal of Artificial Intelligence Research 2, 1995。

  • “The Elements of Statistical Learning”,Hastie T., Tibshirani R., Friedman J., page 606 (second-edition), 2008。

1.12.2. 多标签分类#

多标签分类(与多输出分类密切相关)是一种分类任务,它使用m个标签对每个样本进行标记,这些标签来自n_classes个可能的类别,其中m可以是0到n_classes(包含0和n_classes)。这可以被认为是预测样本的属性,这些属性不是相互排斥的。正式地说,对于每个样本,都会为每个类别分配一个二元输出。正类别用1表示,负类别用0或-1表示。因此,它类似于运行n_classes个二元分类任务,例如使用MultiOutputClassifier。这种方法独立地处理每个标签,而多标签分类器可能会同时处理多个类别,从而考虑它们之间的相关行为。

例如,预测与文本文档或视频相关的主题。该文档或视频可能与“宗教”、“政治”、“金融”或“教育”等主题中的一个、几个或所有主题相关。

1.12.2.1. 目标格式#

多标签multilabel y 的有效表示是一个形状为 (n_samples, n_classes) 的稠密或稀疏二元矩阵。每一列代表一个类别。1 表示样本已被标记为正类别的类别。3个样本的稠密矩阵y示例

>>> y = np.array([[1, 0, 0, 1], [0, 0, 1, 1], [0, 0, 0, 0]])
>>> print(y)
[[1 0 0 1]
 [0 0 1 1]
 [0 0 0 0]]

也可以使用MultiLabelBinarizer创建稠密二元矩阵。更多信息,请参考转换预测目标 (y)

相同y的稀疏矩阵形式示例

>>> y_sparse = sparse.csr_matrix(y)
>>> print(y_sparse)
<Compressed Sparse Row sparse matrix of dtype 'int64'
  with 4 stored elements and shape (3, 4)>
  Coords      Values
  (0, 0)      1
  (0, 3)      1
  (1, 2)      1
  (1, 3)      1

1.12.2.2. MultiOutputClassifier#

可以使用MultiOutputClassifier为任何分类器添加多标签分类支持。此策略包括为每个目标拟合一个分类器。这允许多个目标变量分类。此类的目的是扩展估计器,使其能够估计一系列目标函数 (f1,f2,f3…,fn),这些函数在单个 X 预测矩阵上进行训练以预测一系列响应 (y1,y2,y3…,yn)。

您可以在关于多类别多输出分类的部分中找到MultiOutputClassifier的用法示例,因为它将多标签分类推广到多类别输出而不是二元输出。

1.12.2.3. ClassifierChain#

分类器链(参见ClassifierChain)是一种将多个二元分类器组合成单个多标签模型的方法,该模型能够利用目标之间的相关性。

对于具有 N 个类别的多标签分类问题,N 个二元分类器被分配一个介于 0 和 N-1 之间的整数。这些整数定义了链中模型的顺序。然后,每个分类器都根据可用的训练数据加上模型分配了较小编号的类别的真实标签进行拟合。

预测时,真实标签将不可用。取而代之的是,每个模型的预测将传递给链中的后续模型,用作特征。

显然,链的顺序很重要。链中的第一个模型没有关于其他标签的信息,而链中的最后一个模型具有指示所有其他标签存在的特征。通常,人们不知道链中模型的最佳排序,因此通常会拟合许多随机排序的链,并将其预测平均在一起。

参考文献

  • Jesse Read, Bernhard Pfahringer, Geoff Holmes, Eibe Frank, “Classifier Chains for Multi-label Classification”, 2009。

1.12.3. 多类别多输出分类#

多类别多输出分类(也称为多任务分类)是一种分类任务,它使用一组非二元属性对每个样本进行标记。属性的数量和每个属性的类别数量都大于2。因此,单个估计器处理多个联合分类任务。这既是多标签分类任务(只考虑二元属性)的推广,也是多类别分类任务(只考虑一个属性)的推广。

例如,对一组水果图像的“水果类型”和“颜色”属性进行分类。“水果类型”属性可能的类别为:“苹果”、“梨”和“橙子”。“颜色”属性可能的类别为:“绿色”、“红色”、“黄色”和“橙色”。每个样本都是水果的图像,输出两个属性的标签,每个标签都是相应属性的可能类别之一。

请注意,所有处理多类别多输出(也称为多任务分类)任务的分类器都支持多标签分类任务作为特例。多任务分类类似于具有不同模型公式的多输出分类任务。有关更多信息,请参阅相关的估计器文档。

下面是一个多类别多输出分类的例子

>>> from sklearn.datasets import make_classification
>>> from sklearn.multioutput import MultiOutputClassifier
>>> from sklearn.ensemble import RandomForestClassifier
>>> from sklearn.utils import shuffle
>>> import numpy as np
>>> X, y1 = make_classification(n_samples=10, n_features=100,
...                             n_informative=30, n_classes=3,
...                             random_state=1)
>>> y2 = shuffle(y1, random_state=1)
>>> y3 = shuffle(y1, random_state=2)
>>> Y = np.vstack((y1, y2, y3)).T
>>> n_samples, n_features = X.shape # 10,100
>>> n_outputs = Y.shape[1] # 3
>>> n_classes = 3
>>> forest = RandomForestClassifier(random_state=1)
>>> multi_target_forest = MultiOutputClassifier(forest, n_jobs=2)
>>> multi_target_forest.fit(X, Y).predict(X)
array([[2, 2, 0],
       [1, 2, 1],
       [2, 1, 0],
       [0, 0, 2],
       [0, 2, 1],
       [0, 0, 2],
       [1, 1, 0],
       [1, 1, 1],
       [0, 0, 2],
       [2, 0, 0]])

警告

目前,sklearn.metrics 中没有任何指标支持多类别多输出分类任务。

1.12.3.1. 目标格式#

多输出y 的有效表示是一个形状为 (n_samples, n_classes) 的密集类标签矩阵。这是对多个一维多类别变量的列式拼接。3个样本的y示例如下:

>>> y = np.array([['apple', 'green'], ['orange', 'orange'], ['pear', 'green']])
>>> print(y)
[['apple' 'green']
 ['orange' 'orange']
 ['pear' 'green']]

1.12.4. 多输出回归#

多输出回归预测每个样本的多个数值属性。每个属性都是一个数值变量,并且每个样本要预测的属性数量大于或等于2。一些支持多输出回归的估计器比运行 n_output 个估计器要快。

例如,使用在特定位置获得的数据预测风速和风向(以度为单位)。每个样本是在一个位置获得的数据,并且每个样本都会输出风速和风向。

以下回归器原生支持多输出回归:

1.12.4.1. 目标格式#

多输出y 的有效表示是一个形状为 (n_samples, n_output) 的密集浮点数矩阵。这是对多个连续变量的列式拼接。3个样本的y示例如下:

>>> y = np.array([[31.4, 94], [40.5, 109], [25.0, 30]])
>>> print(y)
[[ 31.4  94. ]
 [ 40.5 109. ]
 [ 25.   30. ]]

1.12.4.2. MultiOutputRegressor#

可以使用MultiOutputRegressor 为任何回归器添加多输出回归支持。此策略包括为每个目标拟合一个回归器。由于每个目标都由一个回归器精确表示,因此可以通过检查其对应的回归器来获得关于目标的知识。由于MultiOutputRegressor为每个目标拟合一个回归器,因此它无法利用目标之间的相关性。

下面是一个多输出回归的例子

>>> from sklearn.datasets import make_regression
>>> from sklearn.multioutput import MultiOutputRegressor
>>> from sklearn.ensemble import GradientBoostingRegressor
>>> X, y = make_regression(n_samples=10, n_targets=3, random_state=1)
>>> MultiOutputRegressor(GradientBoostingRegressor(random_state=0)).fit(X, y).predict(X)
array([[-154.75474165, -147.03498585,  -50.03812219],
       [   7.12165031,    5.12914884,  -81.46081961],
       [-187.8948621 , -100.44373091,   13.88978285],
       [-141.62745778,   95.02891072, -191.48204257],
       [  97.03260883,  165.34867495,  139.52003279],
       [ 123.92529176,   21.25719016,   -7.84253   ],
       [-122.25193977,  -85.16443186, -107.12274212],
       [ -30.170388  ,  -94.80956739,   12.16979946],
       [ 140.72667194,  176.50941682,  -17.50447799],
       [ 149.37967282,  -81.15699552,   -5.72850319]])

1.12.4.3. RegressorChain#

回归链(参见RegressorChain)类似于ClassifierChain,它是一种将多个回归组合成单个多目标模型的方法,能够利用目标之间的相关性。