Scikit学习SVC决策功能并进行预测


71

我正在尝试理解Decision_function和Predict之间的关系,它们是SVC的实例方法(http://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html)。到目前为止,我已经收集到决策函数返回类之间的成对得分。我的印象是,预测会选择最大化其成对成绩的课程,但我对此进行了测试,得出了不同的结果。这是我用来尝试理解两者之间关系的代码。首先,我生成了成对分数矩阵,然后打印出最大成对分数的类,该类不同于clf.predict预测的类。

        result = clf.decision_function(vector)[0]
        counter = 0
        num_classes = len(clf.classes_)
        pairwise_scores = np.zeros((num_classes, num_classes))
        for r in xrange(num_classes):
            for j in xrange(r + 1, num_classes):
                pairwise_scores[r][j] = result[counter]
                pairwise_scores[j][r] = -result[counter]
                counter += 1

        index = np.argmax(pairwise_scores)
        class = index_star / num_classes
        print class
        print clf.predict(vector)[0]

有谁知道这些预测和决策函数之间的关系?


1
“决策函数返回类之间的成对分数”是不正确的。它应该是“每个类的分数”,如在文档页面上写的那样decision_function:“样本X与分离超平面的距离”。
justhalf

7
@justhalf:不,OP是正确的。sklearn.svm.SVC默认情况下使用成对(一对一对)分解,并为每个样本返回到所有n(n-1)/ 2个超平面的距离。
Fred Foo

1
糟糕,是的,我记得在某个地方读过该书。但是被文档误导了。抱歉!
justhalf

在尝试回答之后,我认为bcorso的回答应该是答案。这种关系实际上是基于他从C ++实现中翻译的代码decision = decision_function(params, sv, nv, a, b, X); votes = [(i if decision[p] > 0 else j) for p,(i,j) in enumerate((i,j) for i in range(len(cs)) for j in range(i+1,len(cs)))]。最高的票数votes基本上是什么predict
埃里克·普拉顿

Answers:


46

我不完全理解您的代码,但让我们来看一下您引用的文档页面的示例:

import numpy as np
X = np.array([[-1, -1], [-2, -1], [1, 1], [2, 1]])
y = np.array([1, 1, 2, 2])
from sklearn.svm import SVC
clf = SVC()
clf.fit(X, y) 

现在,我们将决策函数和预测应用于样本:

clf.decision_function(X)
clf.predict(X)

我们得到的输出是:

array([[-1.00052254],
       [-1.00006594],
       [ 1.00029424],
       [ 1.00029424]])
array([1, 1, 2, 2])

这很容易解释:Desion函数告诉我们分类器生成的超平面在哪一侧(以及距离我们有多远)。然后,根据该信息,估算器用相应的标签标记示例。


23
这很容易解释,因为这是一个二进制示例。在多类情况下,SVC.decision_function变得更加复杂。
Fred Foo

24

对于那些感兴趣的人,我将发布一个predict从C ++(在此处)转换为python的函数的快速示例:

# I've only implemented the linear and rbf kernels
def kernel(params, sv, X):
    if params.kernel == 'linear':
        return [np.dot(vi, X) for vi in sv]
    elif params.kernel == 'rbf':
        return [math.exp(-params.gamma * np.dot(vi - X, vi - X)) for vi in sv]

# This replicates clf.decision_function(X)
def decision_function(params, sv, nv, a, b, X):
    # calculate the kernels
    k = kernel(params, sv, X)

    # define the start and end index for support vectors for each class
    start = [sum(nv[:i]) for i in range(len(nv))]
    end = [start[i] + nv[i] for i in range(len(nv))]

    # calculate: sum(a_p * k(x_p, x)) between every 2 classes
    c = [ sum(a[ i ][p] * k[p] for p in range(start[j], end[j])) +
          sum(a[j-1][p] * k[p] for p in range(start[i], end[i]))
                for i in range(len(nv)) for j in range(i+1,len(nv))]

    # add the intercept
    return [sum(x) for x in zip(c, b)]

# This replicates clf.predict(X)
def predict(params, sv, nv, a, b, cs, X):
    ''' params = model parameters
        sv = support vectors
        nv = # of support vectors per class
        a  = dual coefficients
        b  = intercepts 
        cs = list of class names
        X  = feature to predict       
    '''
    decision = decision_function(params, sv, nv, a, b, X)
    votes = [(i if decision[p] > 0 else j) for p,(i,j) in enumerate((i,j) 
                                           for i in range(len(cs))
                                           for j in range(i+1,len(cs)))]

    return cs[max(set(votes), key=votes.count)]

有一个很大的输入参数predictdecision_function,但要注意的是,这些都是内部由模型调用时使用predict(X)。实际上,拟合后,您可以在模型内部访问所有参数:

# Create model
clf = svm.SVC(gamma=0.001, C=100.)

# Fit model using features, X, and labels, Y.
clf.fit(X, y)

# Get parameters from model
params = clf.get_params()
sv = clf.support_vectors
nv = clf.n_support_
a  = clf.dual_coef_
b  = clf._intercept_
cs = clf.classes_

# Use the functions to predict
print(predict(params, sv, nv, a, b, cs, X))

# Compare with the builtin predict
print(clf.predict(X))

1
y!感谢您的回答。但是,我尝试了您的解决方案,结果却有所不同……
lilouch

嗨,bcorso!谢谢您的回答,但正如@lilouch指针指出的那样,我无法获得相同的值。决策函数描述为$ \ langle \ mathbf {w},\ mathbf {x} \ rangle + b $,对于正类别,此值必须大于1,对于负类别,此值必须小于-1。问题是我无法弄清楚如何在新示例和超平面向量之间进行点积运算。你能帮我吗?
vhcandido

sklearn似乎具有对double_coef和intercept的两个互补对,更改a = clf.dual_coef_a = clf._dual_coef_和的输出decision_function相同clf._decision_function,并且的结果predict也与clf.predict
TurtleIzzy

19

在datascience.sx上,对于多类一对一场景有一个非常好的问答

我有一个带有标签“ A”,“ B”,“ C”,“ D”的多类SVM分类器。

这是我正在运行的代码:

>>>print clf.predict([predict_this])
['A']
>>>print clf.decision_function([predict_this])
[[ 185.23220833   43.62763596  180.83305074  -93.58628288   62.51448055  173.43335293]]

如何使用决策函数的输出以最高的概率预测类(A / B / C / D),并且在可能的情况下,它的价值如何?我已经访问了https://stackoverflow.com/a/20114601/7760998,但它是针对二进制分类器的,找不到很好的资源来解释形状为ovo(一对一)的多类分类器的Decision_function的输出。

编辑:

上面的示例适用于“ A”类。对于另一个输入,分类器预测为“ C”,并在decision_function中给出以下结果

[[ 96.42193513 -11.13296606 111.47424538 -88.5356536 44.29272494 141.0069203 ]]

对于分类器预测为“ C”的另一种不同输入,decision_function提供了以下结果:

[[ 290.54180354 -133.93467605  116.37068951 -392.32251314 -130.84421412   284.87653043]]

如果是ovr(相对于其余),则通过选择值较高的那个会变得更容易,但是在ovo(一对1)(n * (n - 1)) / 2中,结果列表中会有值。

如何根据决策函数推论选择哪个类别?

回答

您的链接具有足够的资源,因此让我们看一下:

当您调用Decision_function()时,您将从每个成对分类器获取输出(总共n *(n-1)/ 2个数字)。请参见“用于模式分类的支持向量机”的第127和128页。

单击“第127和128页”链接(此处未显示,但在Stackoverflow答案中)。您应该看到:

在此处输入图片说明

  • Python的SVM实现使用一对一。这正是本书所要讨论的。
  • 对于每个成对比较,我们测量决策函数
  • 决策函数只是常规的二进制SVM决策边界

与您的问题有什么关系?

  • clf.decision_function()将为每个成对比较提供$ D $
  • 得票最多的班级获胜

例如,

[[96.42193513 -11.13296606 111.47424538 -88.5356536 44.29272494 141.0069203]]

正在比较:

[AB,AC,AD,BC,BD,CD]

我们用符号标记每个标签。我们得到:

[A,C,A,C,B,C]

例如,96.42193513为正,因此A是AB的标签。

现在我们有三个C,C将是您的预测。如果对其他两个示例重复我的过程,您将获得Python的预测。试试看!


截距值(b)是否应该从点积中加上或减去?我查看了Wikipedia,它在减去,但在文章中在添加。这有那么重要吗?我很担心,因为我计算的是w.x + b而不是的决策函数w.x - b
fabda01

虽然您可以从原始的角度出发,但直观地讲,使用+ b代替-b应该会导致b倒置。这应该不是真正的问题。
serv-inc

18

调用时decision_function(),您将从每个成对的分类器获得输出(总共n *(n-1)/ 2个数字)。请参见“用于模式分类的支持向量机”的第127和128页

每个分类器对正确答案是一票(基于该分类器输出的符号);predict()返回投票最多的班级。


2
谢谢罗曼!我对此进行了测试,在大多数情况下,它看起来像是预测是选择获得最多选票的课程。最初我做错了,是选择累积利润率得分最高的课程。

3

他们可能有一些复杂的数学关系。但是,如果使用decision_functioninLinearSVC分类器,则两者之间的关系会更加清晰!因为这样decision_function会给您每个班级标签的分数(与SVC不同),并且预测会给班级提供最佳分数。


1

Predict()遵循成对投票方案,该方案返回所有成对比较中投票数最多的类。当两个类别的分数相同时,将返回索引最低的类别。

在下面的Python示例中,将此投票方案应用于(n *(n-1)/ 2对分),由一对1 Decision_function()返回。

from sklearn import svm
from sklearn import datasets
from numpy import argmax, zeros
from itertools import combinations

# do pairwise comparisons, return class with most +1 votes
def ovo_vote(classes, decision_function):
    combos = list(combinations(classes, 2))
    votes = zeros(len(classes))
    for i in range(len(decision_function[0])):
        if decision_function[0][i] > 0:
            votes[combos[i][0]] = votes[combos[i][0]] + 1
        else:
            votes[combos[i][1]] = votes[combos[i][1]] + 1
    winner = argmax(votes)
    return classes[winner]

# load the digits data set
digits = datasets.load_digits()

X, y = digits.data, digits.target

# set the SVC's decision function shape to "ovo"
estimator = svm.SVC(gamma=0.001, C=100., decision_function_shape='ovo')

# train SVC on all but the last digit
estimator.fit(X.data[:-1], y[:-1])

# print the value of the last digit
print("To be classified digit: ", y[-1:][0])

# print the predicted class
pred = estimator.predict(X[-1:])
print("Perform classification using predict: ", pred[0])

# get decision function
df = estimator.decision_function(X[-1:])

# print the decision function itself
print("Decision function consists of",len(df[0]),"elements:")
print(df)

# get classes, here, numbers 0 to 9
digits = estimator.classes_

# print which class has most votes
vote = ovo_vote(digits, df)
print("Perform classification using decision function: ", vote)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.