1.12. 多类别和多输出算法#
用户指南的这一部分涵盖了与多学习问题相关的功能,包括多类别(multiclass)、多标签(multilabel)和多输出(multioutput)分类和回归。
本节中的模块实现了元估计器(meta-estimators),这些估计器需要在其构造函数中提供一个基础估计器。元估计器扩展了基础估计器的功能,以支持多学习问题,通过将多学习问题转换为一组更简单的问题,然后为每个问题拟合一个估计器来实现。
本节涵盖了两个模块:sklearn.multiclass 和 sklearn.multioutput。下表展示了每个模块负责的问题类型,以及每个模块提供的相应元估计器。
下表提供了问题类型之间差异的快速参考。更详细的解释可以在本指南的后续部分中找到。
目标数量 |
目标基数 |
有效的 |
|
|---|---|---|---|
多类别分类 |
1 |
>2 |
‘multiclass’ |
多标签分类 |
>1 |
2 (0 or 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(大多数求解器)linear_model.LogisticRegressionCV(大多数求解器)
支持多标签
支持多类别-多输出
1.12.1. 多类别分类#
警告
scikit-learn 中的所有分类器都开箱即用地支持多类别分类。您不需要使用 sklearn.multiclass 模块,除非您想尝试不同的多类别策略。
多类别分类是一种具有两个以上类别的分类任务。每个样本只能被标记为一个类别。
例如,使用从一组水果图像中提取的特征进行分类,其中每张图像可能是橙子、苹果或梨。每张图像是一个样本,并被标记为 3 个可能的类别之一。多类别分类假设每个样本被分配到一个且只有一个标签 - 一个样本不能同时是梨和苹果。
虽然所有 scikit-learn 分类器都能够进行多类别分类,但 sklearn.multiclass 提供的元估计器允许更改它们处理两个以上类别的方式,因为这可能会影响分类器性能(无论是在泛化误差还是所需计算资源方面)。
1.12.1.1. 目标格式#
多类别(multiclass) y 的有效表示形式(根据 type_of_target)是
包含两个以上离散值的一维或列向量。一个包含 4 个样本的向量
y示例>>> import numpy as np >>> y = np.array(['apple', 'pear', 'apple', 'orange']) >>> print(y) ['apple' 'pear' 'apple' 'orange']
形状为
(n_samples, n_classes)的密集或稀疏 二元(binary) 矩阵,每行一个样本,其中每列代表一个类别。一个包含 4 个样本的密集和稀疏 二元(binary) 矩阵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#
一对剩余(one-vs-rest)策略,也称为一对所有(one-vs-all)策略,在 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)。然而,对于诸如核算法(kernel algorithms)等与 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])
References
“Pattern Recognition and Machine Learning. Springer”, Christopher M. Bishop, page 183, (First Edition)
1.12.1.4. OutputCodeClassifier#
基于纠错输出码(Error-Correcting Output Code)的策略与一对剩余和一对一有很大不同。使用这些策略,每个类别都在一个欧几里得空间中表示,其中每个维度只能是 0 或 1。另一种说法是,每个类别都由一个二进制码(0 和 1 的数组)表示。跟踪每个类别位置/代码的矩阵称为码本(code book)。码本大小是上述空间的维度。直观上,每个类别都应由尽可能唯一的代码表示,并且应设计一个好的码本来优化分类准确性。在此实现中,我们仅使用随机生成的码本,如 [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])
References
“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. 多标签分类#
多标签分类(与多输出分类密切相关)是一种分类任务,它使用 n_classes 个可能的类别中的 m 个标签来标记每个样本,其中 m 可以是 0 到 n_classes(含)之间的任意值。这可以被认为是预测样本的非互斥属性。正式地,为每个样本的每个类别分配一个二元输出。正类别用 1 表示,负类别用 0 或 -1 表示。因此,这与运行 n_classes 个二元分类任务相当,例如使用 MultiOutputClassifier。这种方法独立处理每个标签,而多标签分类器可以同时处理多个类别,考虑到它们之间的相关行为。
例如,预测文本文档或视频的相关主题。文档或视频可能涉及“宗教”、“政治”、“金融”或“教育”中的一个、几个或所有主题类别。
1.12.2.1. 目标格式#
多标签(multilabel) y 的有效表示形式是形状为 (n_samples, n_classes) 的密集或稀疏 二元(binary) 矩阵。每列代表一个类别。每行中的 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#
分类器链(Classifier chains)(参见 ClassifierChain)是一种将多个二元分类器组合成单个多标签模型的方法,该模型能够利用目标之间的相关性。
对于具有 N 个类别的多标签分类问题,N 个二元分类器被分配一个介于 0 和 N-1 之间的整数。这些整数定义了链中模型的顺序。然后,每个分类器根据可用的训练数据以及分配了较低编号的类别的真实标签进行拟合。
在预测时,真实标签将不可用。相反,每个模型的预测结果将传递给链中的后续模型作为特征使用。
显然,链的顺序很重要。链中的第一个模型没有关于其他标签的信息,而链中的最后一个模型具有指示所有其他标签存在的特征。通常,人们不知道链中模型的最佳排序,因此通常会拟合许多随机排序的链,并对其预测结果取平均值。
References
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. 目标格式#
多输出(multioutput) y 的有效表示形式是形状为 (n_samples, n_classes) 的类别标签密集矩阵。一维 多类别(multiclass) 变量的按列连接。一个包含 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. 目标格式#
多输出(multioutput) y 的有效表示形式是形状为 (n_samples, n_output) 的浮点数密集矩阵。 连续(continuous) 变量的按列连接。一个包含 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#
回归器链(Regressor chains)(参见 RegressorChain)与 ClassifierChain 类似,是一种将多个回归器组合成单个多目标模型的方法,该模型能够利用目标之间的相关性。