1.12 多类和多标签算法¶
警告 scikit-learn中的所有分类器都可以开箱即用进行多分类。除非您想尝试不同的多类策略,否则无需使用
sklearn.multiclass
模块。
sklearn.multiclass
模块通过将 multiclass
和multilabel
分类问题分解为二分类问题,实现了meta-estimators(元估计)。 multioutput
regression(多输出回归)也被支持。
多分类:具有两个以上类的分类任务。每个样本只能标记为一个类。
例如,使用从一组水果图像中提取的特征进行分类,其中每一幅图像都可能是一个橙子、一个苹果或一个梨。每个图像就是一个样本,并被标记为三个可能的类之一。多类分类的假设是,每个样本分配给一个标签并且只有一个标签--例如,一个样本不能同时是一个梨和一个苹果。
有效的 multiclass 的
type_of_target
(y
) 表述是:包含两个以上离散值的一维数组或列向量。三个样本的向量
y
的例子:>>> import numpy as np
>>> y = np.array(['apple', 'pear', 'apple'])
>>> print(y)
['apple' 'pear' 'apple']稀疏的binary矩阵, 形状是
(n_samples, n_classes)
, 每行有一个元素, 每一列代表一个类。这里有一个例子, 一个稀疏的binary矩阵, 对应于三个样本的y
, 它的列的顺序是橙子、苹果和梨子:>>> from scipy import sparse
>>> row_ind = np.array([0, 1, 2])
>>> col_ind = np.array([1, 2, 1])
>>> y_sparse = sparse.csr_matrix((np.ones(3), (row_ind, col_ind)))
>>> print(y_sparse)
(0, 1) 1.0
(1, 2) 1.0
(2, 1) 1.0
多标签分类:分类任务用
n_class中
的x
标签标记每个样本,其中x
可以包含0到n_class
。这可以看作是预测不相互排斥的样本的属性。形式上,为每个样本分配二值化输出的每个类。正类用1表示,负类用0或-1表示。因此,它可以与运行n_class
二分类任务相媲美,例如使用sklearn.multioutput.MultiOutputClassifier
。这种方法独立地处理每个标签,而多标签分类器可以同时处理多个类,同时考虑它们之间的相关行为。例如,与文本文档或视频相关的主题预测。这些文件或视频的主题可以是关于“宗教”、“政治”、“金融”或“教育”其中之一, 也可以是其中的几个,甚至全部都行 。
多标签 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]]稀疏矩阵形式中相同
y
的一个例子:>>> y_sparse = sparse.csr_matrix(y)
>>> print(y_sparse)
(0, 0) 1
(0, 3) 1
(1, 2) 1
(1, 3) 1多输出回归:预测每个样本的多个数值属性。每个属性都是一个数值变量,每个样本预测的属性数大于或等于2。一些支持多输出回归的估计器比只运行
n_output
估计器的速度更快。例如,利用在某一地点获得的数据,以度为单位预测风速和风向。每个样本都是在一个地点获得的数据,每个样本的风速和风向都将被输出。
multioutput 多输出
y
的一个有效表示是浮点数组成的形状是(n_samples, n_classes)
的稠密矩阵。连续变量的一种列的合并。3个样本的y
示例:>>> y = np.array([[31.4, 94], [40.5, 109], [25.0, 30]])
>>> print(y)
[[ 31.4 94. ]
[ 40.5 109. ]
[ 25. 30. ]]多输出-多类分类(也称为多任务分类):用一组非二值化属性标记每个样本的分类任务。属性的数量和类的数量都大于2。因此,单个估计器处理多个联合分类任务。这既是对只考虑二值属性的多标签分类任务的推广,也是对多类分类任务的推广,其中只考虑一个属性。
例如,对一组水果图像的属性“水果种类”和“颜色”进行分类。属性“水果种类”有可能的类别有:“苹果”,“梨”和“橙子”。属性“颜色”有可能的类别有:“绿色”、“红色”、“黄色”和“橙色”。每个样本都是一个水果的图像,两个属性的标签都是输出的,每个标签都是相应属性的可能类别之一。
多输出 multioutput
y
的一个有效表示类标签形状是(n_samples, n_classes)
的稠密矩阵。一维的多类变量的一种列的合并。3个样本的y
示例:>>> y = np.array([['apple', 'green'], ['orange', 'orange'], ['pear', 'green']])
>>> print(y)
[['apple' 'green']
['orange' 'orange']
['pear' 'green']]注意,所有处理多输出-多类(也称为多任务分类)任务的分类器,都支持将多标签分类任务作为特例。多任务分类类似于具有不同模型构想的多输出分类任务。有关更多信息,请参见相关的估值器文档。
所有scikit-learn分类器都能够进行多类分类,但是sklearn.multiclass
提供的 meta-estimators 允许改变它们处理两个以上类的方式,因为这可能会影响分类器的性能(无论是泛化误差还是所需的计算资源)。
摘要
以下是 scikit-learn按策略分组支持的分类器总结;如果要使用这些方法之一,则不需要该类中的元估计器,除非您希望使用自定义的多类行为:
固有的多类分类器:
sklearn.naive_bayes.BernoulliNB
sklearn.tree.DecisionTreeClassifier
sklearn.tree.ExtraTreeClassifier
sklearn.ensemble.ExtraTreesClassifier
sklearn.naive_bayes.GaussianNB
sklearn.neighbors.KNeighborsClassifier
sklearn.semi_supervised.LabelPropagation
sklearn.semi_supervised.LabelSpreading
sklearn.discriminant_analysis.LinearDiscriminantAnalysis
sklearn.svm.LinearSVC
(setting multi_class=”crammer_singer”)sklearn.linear_model.LogisticRegression
(setting multi_class=”multinomial”)sklearn.linear_model.LogisticRegressionCV
(setting multi_class=”multinomial”)sklearn.neural_network.MLPClassifier
sklearn.neighbors.NearestCentroid
sklearn.discriminant_analysis.QuadraticDiscriminantAnalysis
sklearn.neighbors.RadiusNeighborsClassifier
sklearn.ensemble.RandomForestClassifier
sklearn.linear_model.RidgeClassifier
sklearn.linear_model.RidgeClassifierCV
One-Vs-One 的多分类:
sklearn.svm.NuSVC
sklearn.svm.SVC
.sklearn.gaussian_process.GaussianProcessClassifier
(setting multi_class = “one_vs_one”)
One-Vs-The-Rest 的多分类:
sklearn.ensemble.GradientBoostingClassifier
sklearn.gaussian_process.GaussianProcessClassifier
(setting multi_class = “one_vs_rest”)sklearn.svm.LinearSVC
(setting multi_class=”ovr”)sklearn.linear_model.LogisticRegression
(setting multi_class=”ovr”)sklearn.linear_model.LogisticRegressionCV
(setting multi_class=”ovr”)sklearn.linear_model.SGDClassifier
sklearn.linear_model.Perceptron
sklearn.linear_model.PassiveAggressiveClassifier
支持多标签:
支持多类多输出:
警告:目前,
sklearn.metrics
中没有评估方法能够支持多输出多类分类任务。
1.12.1 多标签分类格式
在多标签学习中,二分类任务的联合集合伴随标签的二值化数组表示:每个样本是一个形状为(n_sames,n_class)的数组的一行, 该行只有两个值:举一个例子, 即非零元素,对应于标签的子集。形如np.array([[1, 0, 0], [0, 1, 1], [0, 0, 0]])
的数组在第一个样本中表示属于标签0,第二个样本中表示属于标签1和2,在第三个样本中没有属于任何标签。
通过一组标签集合来产生多标签数据可能更为直观。 MultiLabelBinarizer
转换器可用于标签集合和指示格式集合之间的转换。
>>> from sklearn.preprocessing import MultiLabelBinarizer
>>> y = [[2, 3, 4], [2], [0, 1, 3], [0, 1, 2, 3, 4], [0, 1, 2]]
>>> MultiLabelBinarizer().fit_transform(y)
array([[0, 0, 1, 1, 1],
[0, 0, 1, 0, 0],
[1, 1, 0, 1, 0],
[1, 1, 1, 1, 1],
[1, 1, 1, 0, 0]])
1.12.2. One-Vs-The-Rest
这种策略,也称为one-vs-all,是在OneVsRestClassifier
中实现的。该策略包括为每个类拟合一个分类器。对于每个分类器,将一个类与其他所有类进行分类拟合的。除了它的计算效率(只需要n_classes
个分类器)之外,这种方法的一个优点是它的可解释性。由于每个类都由一个分类器表示,并且只有一个分类器,因此可以通过检查其对应的分类器来获得有关该类的知识。这是最常用的策略,也是一个公平的默认选择。
1.12.2.1 多类学习
下面是使用 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])
1.12.2.2 多标签学习
OneVsRestClassifier
还支持多标签分类。要使用此特性,向分类器提供一个指示矩阵,其中单元格[i,j]表示样本 i 中的第 j 个标签。
示例 |
---|
多标签分类 |
1.12.3 One-Vs-One
OneVsOneClassifier
是对每一对类构造一个分类器。在预测时,选择得票最多的类别。在票数相等的两个类别中,它选择具有最高总分类置信度的类别,方法是对由底层二分类器计算的对分类置信度进行求和。
由于它需要拟合n_classes * (n_classes - 1) / 2
个分类器,这种方法通常比one-vs-the-rest要慢,原因就在于其复杂度O(n_classes^2) 。然而,这个方法也有优点,比如说是在没有很好的缩放 n_samples
数据的核方法中。这是因为每个单独的学习问题只涉及一小部分数据,而对于一个one-vs-the-rest,完整的数据集将会被使用 n_classes
次。决策函数是one-versus-one分类器单调变换的结果。
1.12.3.1 多类学习
下面是使用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])
参考
“Pattern Recognition and Machine Learning. Springer”, Christopher M. Bishop, page 183, (First Edition)
1.12.4 误差校正输出代码
基于Output-code的策略与 one-vs-the-rest 和 one-vs-one 的策略有很大的不同。使用这些策略,每个类都表示在欧氏空间中,其中每个维度只能是0或1。换句话说,每个类都由二进制代码(0和1的数组)表示。跟踪每个类的位置/代码的矩阵称为代码簿( code book)。代码大小是上述空间的维数。直观地说,每个类都应该尽可能地用唯一的代码来表示,并且应该设计一本好的code book来优化分类的准确性。在这个实现中,我们只使用[3]中提倡的随机生成的code book,尽管将来可能会添加更详细的方法。
在训练的时候,在 code book 中每位上训练一个二分类器。在预测时,使用分类器在类空间中投影新的点,并选择最靠近该点的类。
在 OutputCodeClassifier
中,code_size
属性允许用户控制将要使用的分类器的数量。它是类别总数的百分比。
在0到1之间的数字需要的分类器比 one-vs-the-rest 少。理论上,log2(n_classes) / n_classes
足以明确地表示每个类。然而,在实践中,由于log2(n_classes)
比n_class小得多,所以它可能不会带来很好的准确性。
比 1 大的数字比 one-vs-the-rest 需要更多的分类器。在这种情况下,一些分类器在理论上会纠正其他分类器的错误,因此命名为 “error-correcting” 。然而在实际上这通常不会发生,因为许多分类器的错误通常意义上来说是相关的。error-correcting output codes 和 bagging 有一个相似的作用效果。
1.12.4.1 多类学习
下面是使用Output-Codes进行多类学习的一个例子:
>>> 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. [3] “The error coding method and PICTs”, James G., Hastie T., Journal of Computational and Graphical statistics 7, 1998.
“The Elements of Statistical Learning”, Hastie T., Tibshirani R., Friedman J., page 606 (second-edition) 2008.
1.12.5 多输出回归
多输出回归支持 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.6 多输出分类
多输出分类支持能够添加到任何带有 MultiOutputClassifier
标志的分类器中。这种方法为每一个目标训练一个分类器。 这就允许产生多目标变量分类器。这种类的目的是扩展评估器用于评估一系列目标函数 (f1,f2,f3…,fn) ,在单个X预测矩阵上训练以预测一系列响应 (y1,y2,y3…,yn)。
下面是多输出分类的一个例子:
>>> 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=-1)
>>> 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]])
1.12.7 分类链
Classifier chains(参见ClassifierChain
)是一种将多个二分类器组合成一个能够利用目标之间相关性的单一多标签模型的方法。
对于有N个类的多标签分类问题,N 个二分类器被分配一个介于0到N-1之间的整数。这些整数定义了链中模型的顺序。然后,每个分类器在可用的训练数据和真实的类标签上训练, 模型被分配了较小的数值。
当预测时,真正的标签将不可用。相反,每个模型的预测被传递到链中的后续模型,作为特征使用。
显然,链的顺序很重要。链中的第一个模型没有关于其他标签的信息,而链中的最后一个模型具有指示所有其他标签存在的特性。一般情况下,人们不知道模型在链中的最优排序,所以通常许多随机有序链是合适的,它们的预测是平均了的。
参考
Jesse Read, Bernhard Pfahringer, Geoff Holmes, Eibe Frank,
“Classifier Chains for Multi-label Classification”, 2009.
1.12.8 回归链
回归链(参见RegressorChain
)类似于分类链,它是将多个回归合并成一个能够利用目标间相关性的单一多目标模型的一种方法。