使用 OneVsRestClassifier 时 sklearn.svm.SVC 的哪个决策函数形状?

Posted

技术标签:

【中文标题】使用 OneVsRestClassifier 时 sklearn.svm.SVC 的哪个决策函数形状?【英文标题】:Which decision_function_shape for sklearn.svm.SVC when using OneVsRestClassifier? 【发布时间】:2017-09-16 06:10:08 【问题描述】:

我正在做多标签分类,试图预测问题的正确标签:

(X = 问题,y = X 中每个问题的标签列表)。

我想知道,sklearn.svm.SVC 的哪个decision_function_shape 应该与OneVsRestClassifier 一起使用?

从文档中我们可以看出decision_function_shape 可以有两个值'ovo''ovr'

decision_function_shape : ‘ovo’, ‘ovr’ or None, default=None

是否像所有其他分类器一样返回形状为 (n_samples, n_classes) 的 one-vs-rest ('ovr') 决策函数,还是返回原始的 具有形状的 libsvm 的一对一('ovo')决策函数 (n_samples, n_classes * (n_classes - 1) / 2)。 None 的默认值将 当前表现为“ovo”以实现向后兼容性并提高 弃用警告,但将在 0.19 中更改“ovr”。

但我还是不明白两者有什么区别:

# First decision_function_shape set to 'ovo'
estim = OneVsRestClassifier(SVC(kernel='linear', decision_function_shape ='ovo'))

# Second decision_function_shape set to 'ovr'
estim = OneVsRestClassifier(SVC(kernel='linear', decision_function_shape ='ovr'))

multi-label classification 问题应该使用哪个decision_function_shape

编辑: Question 提出类似的问题而没有答案。

【问题讨论】:

您可以同时尝试这两种方法,然后检查哪一种可以为您的特定数据提供更好的结果。 【参考方案1】:

我认为应该使用哪个问题最好留给情境。这很容易成为您的 GridSearch 的一部分。但是凭直觉,我会觉得就差异而言,您将做同样的事情。这是我的推理:

OneVsRestClassifier 旨在独立地对每个类与所有其他类进行建模,并为每种情况创建一个分类器。我理解这个过程的方式是OneVsRestClassifier 抓取一个类,并为一个点是否是那个类创建一个二进制标签。然后这个标签被输入到你选择使用的任何估计器中。我相信混淆之处在于SVC 也允许您做出相同的选择,但实际上对于此实现,选择将无关紧要,因为您将始终只将两个类输入SVC

这是一个例子:

from sklearn.datasets import load_iris
from sklearn.multiclass import OneVsRestClassifier
from sklearn.svm import SVC

data = load_iris()

X, y = data.data, data.target
estim1 = OneVsRestClassifier(SVC(kernel='linear', decision_function_shape='ovo'))
estim1.fit(X,y)

estim2 = OneVsRestClassifier(SVC(kernel='linear', decision_function_shape='ovr'))
estim2.fit(X,y)

print(estim1.coef_ == estim2.coef_)
array([[ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True]], dtype=bool)

因此,您可以看到两个模型构建的所有三个估计器的系数都相等。假设这个数据集只有 150 个样本和 3 个类,因此对于更复杂的数据集,这些结果可能会有所不同,但这是一个简单的概念证明。

【讨论】:

这里补充一下,SVC内部会一直使用OvO,decision_function_shape只是为了兼容,不用于拟合数据。所以coef_ 总是相等的。我已经在the linked question 中回答了这个问题【参考方案2】:

决策函数的形状不同,因为ovo 为每个2 对类组合 训练一个分类器,而ovr 为每个类训练一个分类器,以适应所有其他类。 p>

我能找到的最好的例子是found here on http://scikit-learn.org:

SVCNuSVC 实施“一对一”方法(Knerr 等人, 1990)用于多类分类。如果n_class 是 类,然后构造n_class * (n_class - 1) / 2分类器 每一个都训练来自两个类的数据。提供一致的 与其他分类器的接口,decision_function_shape 选项 允许聚合“一对一”分类器的结果 到形状为 (n_samples, n_classes) 的决策函数

>>> X = [[0], [1], [2], [3]]
>>> Y = [0, 1, 2, 3]
>>> clf = svm.SVC(decision_function_shape='ovo')
>>> clf.fit(X, Y) 
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
    decision_function_shape='ovo', degree=3, gamma='auto', kernel='rbf',
    max_iter=-1, probability=False, random_state=None, shrinking=True,
    tol=0.001, verbose=False)
>>> dec = clf.decision_function([[1]])
>>> dec.shape[1] # 4 classes: 4*3/2 = 6
6
>>> clf.decision_function_shape = "ovr"
>>> dec = clf.decision_function([[1]])
>>> dec.shape[1] # 4 classes
4

简单来说这是什么意思?

要了解n_class * (n_class - 1) / 2 的含义,请使用itertools.combinations 生成两类组合。

def ovo_classifiers(classes):
    import itertools
    n_class = len(classes)
    n = n_class * (n_class - 1) / 2
    combos = itertools.combinations(classes, 2)
    return (n, list(combos))

>>> ovo_classifiers(['a', 'b', 'c'])
(3.0, [('a', 'b'), ('a', 'c'), ('b', 'c')])
>>> ovo_classifiers(['a', 'b', 'c', 'd'])
(6.0, [('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'd')])

多标签分类应该使用哪个估计器?

在您的情况下,您有一个带有多个标签的问题(例如 *** 上的此处)。如果您提前知道标签(类),我可能会建议 OneVsRestClassifier(LinearSVC()),但您可以尝试 DecisionTreeClassifier 或 RandomForestClassifier(我认为):

import pandas as pd
from sklearn.preprocessing import MultiLabelBinarizer
from sklearn.svm import SVC, LinearSVC
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.pipeline import Pipeline
from sklearn.multiclass import OneVsRestClassifier, OneVsOneClassifier

df = pd.DataFrame(
  'Tags': [['python', 'pandas'], ['c#', '.net'], ['ruby'],
           ['python'], ['c#'], ['sklearn', 'python']],
  'Questions': ['This is a post about python and pandas is great.',
           'This is a c# post and i hate .net',
           'What is ruby on rails?', 'who else loves python',
           'where to learn c#', 'sklearn is a python package for machine learning'],
                  columns=['Questions', 'Tags'])

X = df['Questions']
mlb = MultiLabelBinarizer()
y = mlb.fit_transform(df['Tags'].values)

pipeline = Pipeline([
  ('vect', CountVectorizer(token_pattern='|'.join(mlb.classes_))),
  ('linear_svc', OneVsRestClassifier(LinearSVC()))
  ])
pipeline.fit(X, y)

final = pd.DataFrame(pipeline.predict(X), index=X, columns=mlb.classes_)

def predict(text):
  return pd.DataFrame(pipeline.predict(text), index=text, columns=mlb.classes_)

test = ['is python better than c#', 'should i learn c#',
        'should i learn sklearn or tensorflow',
        'ruby or c# i am a dinosaur',
        'is .net still relevant']
print(predict(test))

输出:

                                      .net  c#  pandas  python  ruby  sklearn
is python better than c#                 0   1       0       1     0        0
should i learn c#                        0   1       0       0     0        0
should i learn sklearn or tensorflow     0   0       0       0     0        1
ruby or c# i am a dinosaur               0   1       0       0     1        0
is .net still relevant                   1   0       0       0     0        0

【讨论】:

很好的答案。您能否解释一下为什么要使用 LinearSVC() 而不是 SVC(kernel='linear') 来解决这个特定问题?它们不是基本相同,只是根据文档有一些实现差异吗? (我只知道 LinearSVC() 的一个优点——它可以更好地适应更大的数据集)

以上是关于使用 OneVsRestClassifier 时 sklearn.svm.SVC 的哪个决策函数形状?的主要内容,如果未能解决你的问题,请参考以下文章

使用 OneVsRestClassifier 时全为零

OneVsRestClassifier 的损失函数

如何用 OneVsRestClassifier 做 GridSearchCV?

如何使用带有 SVC 估计器的 OneVsRestClassifier 的 GridSearchCV?

OneVsRestClassifier(svm.SVC()).predict() 给出连续值

Scikit-learn 多输出分类器使用:GridSearchCV、Pipeline、OneVsRestClassifier、SGDClassifier