Scikit-learn predict_proba 给出错误答案
Posted
技术标签:
【中文标题】Scikit-learn predict_proba 给出错误答案【英文标题】:Scikit-learn predict_proba gives wrong answers 【发布时间】:2013-06-05 18:06:46 【问题描述】:这是How to know what classes are represented in return array from predict_proba in Scikit-learn的后续问题
在那个问题中,我引用了以下代码:
>>> 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)]
所以......如果解释正确,这个答案表示该点可能是“橙色”(由于数据量很小,置信度相当低)。但直观地说,这个结果显然是不正确的,因为给出的点与“apple”的训练数据相同。可以肯定的是,我也测试了相反的结果:
>>> 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 的结果一致?
【问题讨论】:
【参考方案1】:predict_probas
正在使用 libsvm 的 Platt 缩放功能来调用概率,请参阅:
因此,超平面预测和概率校准确实可能不一致,尤其是当您的数据集中只有 2 个样本时。奇怪的是,在这种情况下,libsvm 为缩放概率所做的内部交叉验证并没有(显式地)失败。也许这是一个错误。必须深入研究 libsvm 的 Platt 缩放代码才能了解发生了什么。
【讨论】:
只是补充一点:原则上,交叉验证应该与大 n 的决策边界一致。【参考方案2】:如果您使用svm.LinearSVC()
作为估计器,并使用.decision_function()
(类似于 svm.SVC 的 .predict_proba())将结果从最可能的类排序到最不可能的类。这与.predict()
功能一致。另外,这个估计器速度更快,并且与svm.SVC()
给出几乎相同的结果
对您来说唯一的缺点可能是.decision_function()
给出了一个介于 -1 和 3 之间的有符号值,而不是概率值。但它与预测一致。
【讨论】:
这很有趣 Bilal...我实际上并不需要概率来实现我的目的,只需要排序。我想这就是我正在寻找的答案。 是的,很有趣。我有同样的问题并使用这种方法进行订购。它给了我比 predict_proba() 更好的结果 请注意,LinearSVC()
将产生与 SVC(kernel='linear')
类似的预测,但不会产生与 SVC(kernel='rbf')
类似的预测,后者是 SVC
的默认内核。
给我与 predict_proba 相同的结果
据说.decision_function()
产生置信度。对我来说,它不在[-1,3]
中。见***.com/questions/26478000/…【参考方案3】:
这里值得深思。我想我实际上让 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 Top 1 准确率 = 1.0
虽然无法让它为我自己的数据工作:(
【讨论】:
以上是关于Scikit-learn predict_proba 给出错误答案的主要内容,如果未能解决你的问题,请参考以下文章