web-dev-qa-db-fra.com

Scikit-learn predict_proba donne de mauvaises réponses

Ceci est une question de suivi de Comment savoir quelles classes sont représentées dans le tableau de retour de predite_proba dans Scikit-learn

Dans cette question, j'ai cité le code suivant:

>>> 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]])

J'ai découvert dans cette question que ce résultat représente la probabilité du point appartenant à chaque classe, dans l'ordre donné par model.classes_

>>> Zip(model.classes_, model.predict_proba([1,2,3])[0])
[('Apple', 0.39097541289393828), ('orange', 0.60902458710606167)]

Donc ... cette réponse, si elle est interprétée correctement, dit que le point est probablement un "orange" (avec une confiance assez faible, en raison de la petite quantité de données). Mais intuitivement, ce résultat est évidemment incorrect, car le point donné était identique aux données d'entraînement pour "Apple". Pour être sûr, j'ai également testé l'inverse:

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

Encore une fois, évidemment incorrect, mais dans l'autre sens.

Enfin, je l'ai essayé avec des points beaucoup plus éloignés.

>>> 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)]

Encore une fois, le modèle prédit les mauvaises probabilités. MAIS, la fonction model.predict a raison!

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

Maintenant, je me souviens avoir lu quelque chose dans les documents à propos de la prédiction de predict_proba pour les petits ensembles de données, bien que je n'arrive pas à le retrouver. Est-ce le comportement attendu ou est-ce que je fais quelque chose de mal? Si ceci IS le comportement attendu, alors pourquoi la fonction Predict et Predict_Proba ne correspondent-elles pas à la sortie? Et surtout, quelle taille doit avoir l'ensemble de données avant de pouvoir faire confiance aux résultats de Predict_Proba?

-------- MISE À JOUR --------

D'accord, j'ai donc fait quelques "expériences" supplémentaires: le comportement de predire_proba dépend fortement de "n", mais pas de manière prévisible!

>>> 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)]

Comment dois-je utiliser cette fonction en toute sécurité dans mon code? À tout le moins, existe-t-il une valeur de n pour laquelle il sera garanti d'être en accord avec le résultat de model.predict?

39
Alex

si vous utilisez svm.LinearSVC() comme estimateur, et .decision_function() (qui est comme .predict_proba () de svm.SVC pour trier les résultats de la classe la plus probable à la classe la moins probable. cela correspond à la fonction .predict(). De plus, cet estimateur est plus rapide et donne presque les mêmes résultats avec svm.SVC()

le seul inconvénient pour vous pourrait être que .decision_function() donne une valeur signée sth comme entre -1 et 3 au lieu d'une valeur de probabilité. mais il est d'accord avec la prédiction.

19
Bilal Dadanlar

predict_probas utilise la fonction de mise à l'échelle Platt de libsvm pour étalonner les probabilités, voir:

Ainsi, en effet, les prédictions d'hyperplan et l'étalonnage proba peuvent être en désaccord, surtout si vous n'avez que 2 échantillons dans votre jeu de données. Il est étrange que la validation croisée interne effectuée par libsvm pour mettre à l'échelle les probabilités n'échoue pas (explicitement) dans ce cas. C'est peut-être un bug. Il faudrait plonger dans le code de mise à l'échelle Platt de libsvm pour comprendre ce qui se passe.

21
ogrisel

Matière à réflexion ici. Je pense que j'ai réussi à faire pred_proba fonctionner tel quel. Veuillez voir le code ci-dessous ...

# 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)

Sortie: Taux de précision supérieur 5 = 1,0 Taux de précision supérieur 1 = 1,0

Je n'ai pas pu le faire fonctionner pour mes propres données cependant :(

0
Statmonger