Scikit-learn的predict_proba给出了错误的答案

50
This is a follow-up question from 如何知道Scikit-learn中predict_proba返回数组中表示哪些类的问题
在那个问题中,我引用了以下代码:
>>> import sklearn
>>> sklearn.__version__
'0.13.1'
>>> from sklearn import svm
>>> model = svm.SVC(probability=True)
>>> X = [[1,2,3], [2,3,4]] # feature vectors
>>> Y = ['apple', 'orange'] # classes
>>> model.fit(X, Y)
>>> model.predict_proba([1,2,3])
array([[ 0.39097541,  0.60902459]])

我发现在这个问题中,这个结果代表了每个类别的点属于该类别的概率,按照model.classes_给出的顺序。
>>> zip(model.classes_, model.predict_proba([1,2,3])[0])
[('apple', 0.39097541289393828), ('orange', 0.60902458710606167)]

所以……如果正确解释的话,这个答案表示这个点可能是一个“橙子”(由于数据量非常少,置信度较低)。但直觉上来看,这个结果显然是不正确的,因为给出的点与“苹果”的训练数据完全相同。为了确保,我也进行了反向测试:

>>> zip(model.classes_, model.predict_proba([2,3,4])[0])
[('apple', 0.60705475211840931), ('orange', 0.39294524788159074)]

再次强调,显然是错误的,但方向相反。

最后,我尝试了距离更远的点。

>>> X = [[1,1,1], [20,20,20]] # feature vectors
>>> model.fit(X, Y)
>>> zip(model.classes_, model.predict_proba([1,1,1])[0])
[('apple', 0.33333332048410247), ('orange', 0.66666667951589786)]

模型再次预测错误的概率。但是,model.predict函数得到了正确的结果!

>>> model.predict([1,1,1])[0]
'apple'

现在,我记得在文档中读到过predict_proba对于小数据集不准确的内容,尽管我似乎找不到它了。这是预期的行为吗,还是我做错了什么?如果这是预期的行为,那么为什么predict和predict_proba函数会在输出上产生分歧?更重要的是,数据集需要多大才能信任predict_proba的结果?
-------- 更新 --------
好的,所以我对此进行了更多的“实验”:predict_proba的行为严重依赖于“n”,但没有任何可预测的方式!
>>> def train_test(n):
...     X = [[1,2,3], [2,3,4]] * n
...     Y = ['apple', 'orange'] * n
...     model.fit(X, Y)
...     print "n =", n, zip(model.classes_, model.predict_proba([1,2,3])[0])
... 
>>> train_test(1)
n = 1 [('apple', 0.39097541289393828), ('orange', 0.60902458710606167)]
>>> for n in range(1,10):
...     train_test(n)
... 
n = 1 [('apple', 0.39097541289393828), ('orange', 0.60902458710606167)]
n = 2 [('apple', 0.98437355278112448), ('orange', 0.015626447218875527)]
n = 3 [('apple', 0.90235408180319321), ('orange', 0.097645918196806694)]
n = 4 [('apple', 0.83333299908143665), ('orange', 0.16666700091856332)]
n = 5 [('apple', 0.85714254878984497), ('orange', 0.14285745121015511)]
n = 6 [('apple', 0.87499969631893626), ('orange', 0.1250003036810636)]
n = 7 [('apple', 0.88888844127886335), ('orange', 0.11111155872113669)]
n = 8 [('apple', 0.89999988018127364), ('orange', 0.10000011981872642)]
n = 9 [('apple', 0.90909082368682159), ('orange', 0.090909176313178491)]

我该如何在我的代码中安全使用这个函数?至少有哪些n的取值可以保证它与model.predict的结果一致?

3个回答

26

predict_probas使用libsvm的Platt scaling特性来进行概率校准,详见:

因此,如果您的数据集中只有2个样本,那么超平面预测和概率校准可能会产生不一致。奇怪的是,libsvm用于比例缩放的内部交叉验证在这种情况下并没有失败(明确地)。也许这是一个错误,需要深入研究libsvm的Platt scaling代码以了解发生了什么。


2
只是补充一下:原则上,对于大的n,交叉验证应该与决策边界一致。 - Andreas Mueller

21
如果你使用svm.LinearSVC()作为估计器,并且使用.decision_function()(类似于svm.SVC的.predict_proba())对结果进行排序,从最有可能的类到最不可能的类。这与.predict()函数一致。而且,这个估计器更快,而且几乎与svm.SVC()的结果相同。
唯一的缺点就是.decision_function()会给出一个带符号的值,例如在-1和3之间,而不是概率值。但它与预测是一致的。

这很有趣,Bilal...实际上我并不需要概率,只需要排序。我想这就是我要找的答案。 - Alex
是的,很有趣。我遇到了同样的问题,并使用了这种排序方法。它给我的结果比predict_proba()更好。 - Bilal Dadanlar
5
иҜ·жіЁж„ҸпјҢLinearSVC()дјҡдә§з”ҹзұ»дјјдәҺSVC(kernel='linear')дҪҶдёҚеҗҢдәҺSVC(kernel='rbf')зҡ„йў„жөӢз»“жһңпјҢеҗҺиҖ…жҳҜSVCзҡ„й»ҳи®ӨеҶ…ж ёгҖӮ - ogrisel
给我相同的predict_proba结果。 - Ezzat
据说.decision_function()提供置信度。但对我来说,它不在[-1,3]范围内。请参见https://dev59.com/NV8d5IYBdhLWcg3wv0Yg - gr4nt3d

0

思考的食物。我认为我实际上已经让predict_proba按原样工作了。请参见以下代码...

# Test data
TX = [[1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15], [16,17,18], [19,20,21], [22,23,24]]
TY = ['apple', 'orange', 'grape', 'kiwi', 'mango','peach','banana','pear']

VX2 = [[16,17,18], [19,20,21], [22,23,24], [13,14,15], [10,11,12], [7,8,9], [4,5,6], [1,2,3]]
VY2 = ['peach','banana','pear','mango', 'kiwi', 'grape', 'orange','apple']

VX2_df = pd.DataFrame(data=VX2) # convert to dataframe
VX2_df = VX2_df.rename(index=float, columns={0: "N0", 1: "N1", 2: "N2"})
VY2_df = pd.DataFrame(data=VY2) # convert to dataframe
VY2_df = VY2_df.rename(index=float, columns={0: "label"})

# NEW - in testing
def train_model(classifier, feature_vector_train, label, feature_vector_valid, valid_y, valid_x, is_neural_net=False):

    # fit the training dataset on the classifier
    classifier.fit(feature_vector_train, label)

    # predict the top n labels on validation dataset
    n = 5
    #classifier.probability = True
    probas = classifier.predict_proba(feature_vector_valid)
    predictions = classifier.predict(feature_vector_valid)

    #Identify the indexes of the top predictions
    #top_n_predictions = np.argsort(probas)[:,:-n-1:-1]
    top_n_predictions = np.argsort(probas, axis = 1)[:,-n:]

    #then find the associated SOC code for each prediction
    top_socs = classifier.classes_[top_n_predictions]

    #cast to a new dataframe
    top_n_df = pd.DataFrame(data=top_socs)

    #merge it up with the validation labels and descriptions
    results = pd.merge(valid_y, valid_x, left_index=True, right_index=True)
    results = pd.merge(results, top_n_df, left_index=True, right_index=True)

    conditions = [
        (results['label'] == results[0]),
        (results['label'] == results[1]),
        (results['label'] == results[2]),
        (results['label'] == results[3]),
        (results['label'] == results[4])]
    choices = [1, 1, 1, 1, 1]
    results['Successes'] = np.select(conditions, choices, default=0)

    print("Top 5 Accuracy Rate = ", sum(results['Successes'])/results.shape[0])
    print("Top 1 Accuracy Rate = ", metrics.accuracy_score(predictions, valid_y))

train_model(naive_bayes.MultinomialNB(), TX, TY, VX2, VY2_df, VX2_df)

输出: 前5准确率 = 1.0 前1准确率 = 1.0

但是对于我的数据无法使其工作 :(


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接