1.12. 多类别和多输出算法#
用户指南的这一部分涵盖了与多学习问题相关的功能,包括 多类别、多标签 和 多输出 分类和回归。
本节中的模块实现了 元估计器,这些估计器需要在它们的构造函数中提供一个基础估计器。元估计器扩展了基础估计器的功能以支持多学习问题,这是通过将多学习问题转换为一组更简单的问题来实现的,然后为每个问题拟合一个估计器。
本节涵盖了两个模块:sklearn.multiclass
和 sklearn.multioutput
。下图展示了每个模块负责的问题类型,以及每个模块提供的相应元估计器。
下表提供了关于问题类型之间差异的快速参考。本指南后续部分将提供更详细的解释。
目标数量 |
目标基数 |
||
---|---|---|---|
多类别分类 |
1 |
>2 |
‘multiclass’ |
多标签分类 |
>1 |
2(0 或 1) |
‘multilabel-indicator’ |
多类别-多输出分类 |
>1 |
>2 |
‘multiclass-multioutput’ |
多输出回归 |
>1 |
连续 |
‘continuous-multioutput’ |
以下是 scikit-learn 估计器按策略分组的摘要,这些估计器具有内置的多学习支持。如果您使用的是这些估计器之一,则不需要本节提供的元估计器。但是,元估计器可以提供超出内置功能的额外策略。
固有地多类别
svm.LinearSVC
(设置 multi_class=”crammer_singer”)linear_model.LogisticRegression
(使用大多数求解器)linear_model.LogisticRegressionCV
(使用大多数求解器)
多类别作为一对一
gaussian_process.GaussianProcessClassifier
(设置 multi_class = “one_vs_one”)
多类别作为一对多
gaussian_process.GaussianProcessClassifier
(设置 multi_class = “one_vs_rest”)svm.LinearSVC
(设置 multi_class=”ovr”)linear_model.LogisticRegression
(大多数求解器)
支持多标签
支持多类别-多输出
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) (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。
示例
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])
参考文献
“模式识别与机器学习。施普林格”,克里斯托弗·M·毕肖普,第 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 的数字将比一对多方法需要更多的分类器。在这种情况下,理论上一些分类器将纠正其他分类器犯的错误,因此得名“纠错”。然而,在实践中,这可能不会发生,因为分类器错误通常是相关的。纠错输出码与装袋具有类似的效果。
以下是使用输出码进行多类学习的示例
>>> 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])
参考文献
“通过纠错输出码解决多类学习问题”,Dietterich T.,Bakiri G.,人工智能研究杂志 2,1995 年。
“统计学习的要素”,Hastie T.,Tibshirani R.,Friedman J.,第 606 页(第二版),2008 年。
1.12.2. 多标签分类#
多标签分类(与多输出分类密切相关)是一种分类任务,它使用 n_classes
个可能的类别中的 m
个标签对每个样本进行标记,其中 m
可以是 0 到 n_classes
(含)。这可以被认为是预测样本的属性,这些属性不是相互排斥的。正式地说,对于每个样本,为每个类别分配一个二元输出。正类别用 1 表示,负类别用 0 或 -1 表示。因此,它类似于运行 n_classes
个二元分类任务,例如使用 MultiOutputClassifier
。这种方法独立地处理每个标签,而多标签分类器可能会同时处理多个类别,从而考虑它们之间的相关行为。
例如,预测与文本文档或视频相关的主题。文档或视频可能与“宗教”、“政治”、“金融”或“教育”中的一个主题类别、多个主题类别或所有主题类别相关。
1.12.2.1. 目标格式#
多标签 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)
(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,“用于多标签分类的分类器链”,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
,是一种将多个回归组合成单个多目标模型的方法,该模型能够利用目标之间的相关性。