Comment calculer la précision, le rappel, l'exactitude et le score f1 pour le cas multiclasse avec scikit learn?

109

Je travaille sur un problème d'analyse des sentiments, les données ressemblent à ceci:

label instances
    5    1190
    4     838
    3     239
    1     204
    2     127

Donc mes données sont déséquilibrées puisque 1190 instancessont étiquetées avec 5. Pour la classification Im utilisant le SVC de scikit . Le problème est que je ne sais pas comment équilibrer mes données de la bonne manière afin de calculer avec précision la précision, le rappel, l'exactitude et le score f1 pour le cas multiclasse. J'ai donc essayé les approches suivantes:

Première:

    wclf = SVC(kernel='linear', C= 1, class_weight={1: 10})
    wclf.fit(X, y)
    weighted_prediction = wclf.predict(X_test)

print 'Accuracy:', accuracy_score(y_test, weighted_prediction)
print 'F1 score:', f1_score(y_test, weighted_prediction,average='weighted')
print 'Recall:', recall_score(y_test, weighted_prediction,
                              average='weighted')
print 'Precision:', precision_score(y_test, weighted_prediction,
                                    average='weighted')
print '\n clasification report:\n', classification_report(y_test, weighted_prediction)
print '\n confussion matrix:\n',confusion_matrix(y_test, weighted_prediction)

Seconde:

auto_wclf = SVC(kernel='linear', C= 1, class_weight='auto')
auto_wclf.fit(X, y)
auto_weighted_prediction = auto_wclf.predict(X_test)

print 'Accuracy:', accuracy_score(y_test, auto_weighted_prediction)

print 'F1 score:', f1_score(y_test, auto_weighted_prediction,
                            average='weighted')

print 'Recall:', recall_score(y_test, auto_weighted_prediction,
                              average='weighted')

print 'Precision:', precision_score(y_test, auto_weighted_prediction,
                                    average='weighted')

print '\n clasification report:\n', classification_report(y_test,auto_weighted_prediction)

print '\n confussion matrix:\n',confusion_matrix(y_test, auto_weighted_prediction)

Troisième:

clf = SVC(kernel='linear', C= 1)
clf.fit(X, y)
prediction = clf.predict(X_test)


from sklearn.metrics import precision_score, \
    recall_score, confusion_matrix, classification_report, \
    accuracy_score, f1_score

print 'Accuracy:', accuracy_score(y_test, prediction)
print 'F1 score:', f1_score(y_test, prediction)
print 'Recall:', recall_score(y_test, prediction)
print 'Precision:', precision_score(y_test, prediction)
print '\n clasification report:\n', classification_report(y_test,prediction)
print '\n confussion matrix:\n',confusion_matrix(y_test, prediction)


F1 score:/usr/local/lib/python2.7/site-packages/sklearn/metrics/classification.py:676: DeprecationWarning: The default `weighted` averaging is deprecated, and from version 0.18, use of precision, recall or F-score with multiclass or multilabel data or pos_label=None will result in an exception. Please set an explicit value for `average`, one of (None, 'micro', 'macro', 'weighted', 'samples'). In cross validation use, for instance, scoring="f1_weighted" instead of scoring="f1".
  sample_weight=sample_weight)
/usr/local/lib/python2.7/site-packages/sklearn/metrics/classification.py:1172: DeprecationWarning: The default `weighted` averaging is deprecated, and from version 0.18, use of precision, recall or F-score with multiclass or multilabel data or pos_label=None will result in an exception. Please set an explicit value for `average`, one of (None, 'micro', 'macro', 'weighted', 'samples'). In cross validation use, for instance, scoring="f1_weighted" instead of scoring="f1".
  sample_weight=sample_weight)
/usr/local/lib/python2.7/site-packages/sklearn/metrics/classification.py:1082: DeprecationWarning: The default `weighted` averaging is deprecated, and from version 0.18, use of precision, recall or F-score with multiclass or multilabel data or pos_label=None will result in an exception. Please set an explicit value for `average`, one of (None, 'micro', 'macro', 'weighted', 'samples'). In cross validation use, for instance, scoring="f1_weighted" instead of scoring="f1".
  sample_weight=sample_weight)
 0.930416613529

Cependant, je reçois des avertissements comme celui-ci:

/usr/local/lib/python2.7/site-packages/sklearn/metrics/classification.py:1172:
DeprecationWarning: The default `weighted` averaging is deprecated,
and from version 0.18, use of precision, recall or F-score with 
multiclass or multilabel data or pos_label=None will result in an 
exception. Please set an explicit value for `average`, one of (None, 
'micro', 'macro', 'weighted', 'samples'). In cross validation use, for 
instance, scoring="f1_weighted" instead of scoring="f1"

Comment puis-je gérer correctement mes données déséquilibrées afin de calculer correctement les métriques du classificateur?

nouveau_avec_python
la source
Alors pourquoi ne pas ajouter un averageparamètre dans le troisième cas?
yangjie
1
@yangjie je ne sais pas. Je viens de vérifier la documentation mais je ne comprends pas comment utiliser correctement les métriques pour les données déséquilibrées. Pourriez-vous fournir une explication plus large et un exemple ?. Merci!
new_with_python

Réponses:

164

Je pense qu'il y a beaucoup de confusion sur les poids utilisés pour quoi. Je ne suis pas sûr de savoir précisément ce qui vous dérange donc je vais aborder différents sujets, soyez indulgents avec moi;).

Poids de classe

Les poids du class_weightparamètre sont utilisés pour entraîner le classificateur . Ils ne sont utilisés dans le calcul d'aucune des métriques que vous utilisez : avec des poids de classe différents, les nombres seront différents simplement parce que le classificateur est différent.

Fondamentalement, dans chaque classificateur scikit-learn, les poids de classe sont utilisés pour indiquer à votre modèle l'importance d'une classe. Cela signifie que pendant la formation, le classificateur fera des efforts supplémentaires pour classer correctement les classes avec des poids élevés.
La façon dont ils font cela est spécifique à l'algorithme. Si vous voulez des détails sur la façon dont cela fonctionne pour SVC et que le document n'a pas de sens pour vous, n'hésitez pas à le mentionner.

Les métriques

Une fois que vous avez un classificateur, vous voulez savoir comment il fonctionne. Ici , vous pouvez utiliser les paramètres que vous avez mentionnés: accuracy, recall_score, f1_score...

Habituellement, lorsque la distribution des classes est déséquilibrée, la précision est considérée comme un mauvais choix car elle donne des scores élevés aux modèles qui ne font que prédire la classe la plus fréquente.

Je ne détaillerai pas toutes ces métriques mais noter que, à l'exception de accuracy, elles sont naturellement appliquées au niveau de la classe: comme vous pouvez le voir dans ce printrapport de classification, elles sont définies pour chaque classe. Ils s'appuient sur des concepts tels que true positivesou false negativequi nécessitent de définir quelle classe est la classe positive .

             precision    recall  f1-score   support

          0       0.65      1.00      0.79        17
          1       0.57      0.75      0.65        16
          2       0.33      0.06      0.10        17
avg / total       0.52      0.60      0.51        50

L'avertissement

F1 score:/usr/local/lib/python2.7/site-packages/sklearn/metrics/classification.py:676: DeprecationWarning: The 
default `weighted` averaging is deprecated, and from version 0.18, 
use of precision, recall or F-score with multiclass or multilabel data  
or pos_label=None will result in an exception. Please set an explicit 
value for `average`, one of (None, 'micro', 'macro', 'weighted', 
'samples'). In cross validation use, for instance, 
scoring="f1_weighted" instead of scoring="f1".

Vous obtenez cet avertissement parce que vous utilisez le score f1, le rappel et la précision sans définir comment ils doivent être calculés! La question pourrait être reformulée: à partir du rapport de classification ci-dessus, comment sortez-vous un nombre global pour le score f1? Vous pourriez:

  1. Prenez la moyenne du score f1 pour chaque classe: c'est le avg / totalrésultat ci-dessus. C'est aussi appelé calcul de la moyenne macro .
  2. Calculez le score f1 en utilisant le nombre global de vrais positifs / faux négatifs, etc. (vous additionnez le nombre de vrais positifs / faux négatifs pour chaque classe). Moyenne micro Aka .
  3. Calculez une moyenne pondérée du score f1. L'utilisation 'weighted'dans scikit-learn pesera le score f1 par le support de la classe: plus une classe a d'éléments, plus le score f1 pour cette classe est important dans le calcul.

Ce sont 3 des options de scikit-learn, l'avertissement est là pour dire que vous devez en choisir une . Vous devez donc spécifier un averageargument pour la méthode de score.

Celui que vous choisissez dépend de la façon dont vous souhaitez mesurer les performances du classificateur: par exemple, le macro-moyennage ne prend pas en compte le déséquilibre de classe et le score f1 de la classe 1 sera tout aussi important que le score f1 de la classe 5. Si vous utilisez la moyenne pondérée, vous aurez plus d'importance pour la classe 5.

Toute la spécification des arguments dans ces métriques n'est pas très claire dans scikit-learn pour le moment, elle s'améliorera dans la version 0.18 selon la documentation. Ils suppriment certains comportements standard non évidents et émettent des avertissements pour que les développeurs le remarquent.

Calcul des scores

La dernière chose que je veux mentionner (n'hésitez pas à l'ignorer si vous en êtes conscient), c'est que les scores ne sont significatifs que s'ils sont calculés sur des données que le classificateur n'a jamais vues . Ceci est extrêmement important car tout score obtenu sur les données utilisées pour ajuster le classificateur est totalement hors de propos.

Voici un moyen de le faire en utilisant StratifiedShuffleSplit, qui vous donne un fractionnement aléatoire de vos données (après mélange) qui préserve la distribution des étiquettes.

from sklearn.datasets import make_classification
from sklearn.cross_validation import StratifiedShuffleSplit
from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score, classification_report, confusion_matrix

# We use a utility to generate artificial classification data.
X, y = make_classification(n_samples=100, n_informative=10, n_classes=3)
sss = StratifiedShuffleSplit(y, n_iter=1, test_size=0.5, random_state=0)
for train_idx, test_idx in sss:
    X_train, X_test, y_train, y_test = X[train_idx], X[test_idx], y[train_idx], y[test_idx]
    svc.fit(X_train, y_train)
    y_pred = svc.predict(X_test)
    print(f1_score(y_test, y_pred, average="macro"))
    print(precision_score(y_test, y_pred, average="macro"))
    print(recall_score(y_test, y_pred, average="macro"))    

J'espère que cela t'aides.

ldirer
la source
Pour une multiclasse, comment spécifier un poids de classe? Par exemple, que signifie une class_weight={1:10}donnée qui a 3 classes?
Aziz Javed
Est-il possible d'obtenir des scores de précision par étiquette?
Ankur Sinha
Pouvez-vous expliquer plus clairement comment le micro fonctionne. Aussi, vous ne mentionnez rien sur le binaire
humble
Pour moi, le shuffle stratifié créait des problèmes, alors je suis revenu au partage train-test comme il le montrait ValueError: The least populated class in y has only 1 member, which is too few. The minimum number of labels for any class cannot be less than 2.. Cela fonctionne bien avec la division train-test, mais quelqu'un peut-il m'aider pourquoi je reçois cette erreur avec SSS? Merci.
Akash Kandpal
HI j'ai testé votre code mais j'ai ce message d'erreur C: \ Users \\ Anaconda3 \ lib \ site-packages \ sklearn \ metrics \ classification.py: 976: DeprecationWarning: À partir de la version 0.18, l'entrée binaire ne sera pas gérée spécialement lors de l'utilisation précision moyenne / rappel / score F. Veuillez utiliser average = 'binary' pour signaler uniquement les performances de classe positives. 'performance de classe positive.', DeprecationWarning)
Chedi Bechikh
73

Beaucoup de réponses très détaillées ici, mais je ne pense pas que vous répondiez aux bonnes questions. Si je comprends bien la question, il y a deux préoccupations:

  1. Comment marquer un problème multiclasse?
  2. Comment gérer les données déséquilibrées?

1.

Vous pouvez utiliser la plupart des fonctions de notation de scikit-learn aussi bien avec des problèmes multiclasses qu'avec des problèmes à classe unique. Ex.:

from sklearn.metrics import precision_recall_fscore_support as score

predicted = [1,2,3,4,5,1,2,1,1,4,5] 
y_test = [1,2,3,4,5,1,2,1,1,4,1]

precision, recall, fscore, support = score(y_test, predicted)

print('precision: {}'.format(precision))
print('recall: {}'.format(recall))
print('fscore: {}'.format(fscore))
print('support: {}'.format(support))

De cette façon, vous vous retrouvez avec des nombres tangibles et interprétables pour chacune des classes.

| Label | Precision | Recall | FScore | Support |
|-------|-----------|--------|--------|---------|
| 1     | 94%       | 83%    | 0.88   | 204     |
| 2     | 71%       | 50%    | 0.54   | 127     |
| ...   | ...       | ...    | ...    | ...     |
| 4     | 80%       | 98%    | 0.89   | 838     |
| 5     | 93%       | 81%    | 0.91   | 1190    |

Ensuite...

2.

... vous pouvez dire si les données déséquilibrées sont même un problème. Si le score des classes les moins représentées (classes 1 et 2) est inférieur à celui des classes avec plus d'échantillons d'apprentissage (classes 4 et 5), vous savez que les données déséquilibrées sont en fait un problème et vous pouvez agir en conséquence, comme décrit dans certaines des autres réponses de ce fil. Cependant, si la même distribution de classe est présente dans les données que vous souhaitez prédire, vos données d'entraînement déséquilibrées sont un bon représentant des données et, par conséquent, le déséquilibre est une bonne chose.

wonderkid2
la source
1
Excellent post et bien dit. Merci
Alvis
1
Hé juste une question de suivi: comment avez-vous imprimé les étiquettes en utilisant precision_recall_fscore_support? Les étiquettes sont-elles imprimées sur commande?
BigD
@BigD Ouais, voir scikit-learn.org/stable/modules/generated/… tout en bas. Définissez average=Noneet définissez les étiquettes, puis vous obtenez la métrique que vous recherchez, pour chacune de vos étiquettes spécifiées.
wonderkid2
Est-il possible d'obtenir des scores de précision par étiquette?
Ankur Sinha
@trollster Je ne suis pas sûr de ce que tu veux dire? N'est-ce pas ce que j'indique dans les scores de précision par étiquette de réponse?
wonderkid2
16

Question posée

Pour répondre à la question «quelle métrique utiliser pour la classification multi-classes avec des données déséquilibrées»: Macro-F1-mesure. La précision de macro et le rappel de macro peuvent également être utilisés, mais ils ne sont pas aussi facilement interprétables que pour la classificaion binaire, ils sont déjà incorporés dans la mesure F, et les métriques en excès compliquent la comparaison des méthodes, le réglage des paramètres, etc.

Les micro-moyennes sont sensibles au déséquilibre des classes: si votre méthode, par exemple, fonctionne bien pour les étiquettes les plus courantes et en perturbe totalement les autres, les métriques micro-moyennes donnent de bons résultats.

La moyenne de pondération n'est pas bien adaptée aux données déséquilibrées, car elle pondère par le nombre d'étiquettes. De plus, il est trop difficilement interprétable et impopulaire: par exemple, il n'est pas fait mention d'une telle moyenne dans l' enquête très détaillée suivante que je recommande vivement de parcourir:

Sokolova, Marina et Guy Lapalme. "Une analyse systématique des mesures de performance pour les tâches de classification." Traitement et gestion de l'information 45.4 (2009): 427-437.

Question spécifique à l'application

Cependant, pour revenir à votre tâche, je rechercherais 2 sujets:

  1. métriques couramment utilisées pour votre tâche spécifique - cela permet (a) de comparer votre méthode avec d'autres et de comprendre si vous faites quelque chose de mal, et (b) de ne pas explorer cela par vous-même et de réutiliser les découvertes de quelqu'un d'autre;
  2. coût des différentes erreurs de vos méthodes - par exemple, le cas d'utilisation de votre application peut reposer uniquement sur des évaluations 4 et 5 étoiles - dans ce cas, une bonne métrique ne doit compter que ces 2 étiquettes.

Métriques couramment utilisées. Comme je peux le déduire après avoir parcouru la littérature, il existe 2 principales mesures d'évaluation:

  1. Précision , qui est utilisée, par exemple dans

Yu, April et Daryl Chang. «Prédiction de sentiment multiclasse à l'aide de Yelp Business».

( lien ) - notez que les auteurs travaillent avec presque la même répartition des notes, voir la figure 5.

Pang, Bo et Lillian Lee. "Voir les étoiles: Exploiter les relations de classe pour la catégorisation des sentiments par rapport aux échelles de notation." Actes de la 43e réunion annuelle de l'Association for Computational Linguistics. Association pour la linguistique computationnelle, 2005.

( lien )

  1. MSE (ou, moins souvent, Erreur absolue moyenne - MAE ) - voir, par exemple,

Lee, Moontae et R. Grafe. "Analyse des sentiments multiclasses avec des critiques de restaurants." Projets finaux de CS N 224 (2010).

( lien ) - ils explorent à la fois la précision et la MSE, considérant que cette dernière est meilleure

Pappas, Nikolaos, Rue Marconi et Andrei Popescu-Belis. "Expliquer les étoiles: apprentissage multi-instances pondéré pour l'analyse des sentiments basée sur les aspects." Actes de la conférence 2014 sur les méthodes empiriques dans le traitement du langage naturel. N ° EPFL-CONF-200899. 2014.

( lien ) - ils utilisent scikit-learn pour l'évaluation et les approches de base et déclarent que leur code est disponible; cependant, je ne le trouve pas, donc si vous en avez besoin, écrivez une lettre aux auteurs, le travail est assez récent et semble être écrit en Python.

Coût des différentes erreurs . Si vous vous souciez davantage d'éviter les grossières maladresses, par exemple, donner un avis de 1 étoile à 5 étoiles ou quelque chose du genre, regardez MSE; si la différence compte, mais pas tellement, essayez MAE, car elle ne met pas la différence au carré; sinon restez avec Précision.

À propos des approches, pas des métriques

Essayez des approches de régression, par exemple SVR , car elles surpassent généralement les classificateurs Multiclass comme SVC ou OVA SVM.

Nikita Astrakhantsev
la source
13

Tout d'abord, il est un peu plus difficile d'utiliser simplement une analyse de comptage pour savoir si vos données sont déséquilibrées ou non. Par exemple: 1 observation positive sur 1000 n'est qu'un bruit, une erreur ou une percée scientifique? On ne sait jamais.
Il est donc toujours préférable d'utiliser toutes vos connaissances disponibles et de choisir son statut avec tous les sages.

D'accord, et si c'est vraiment déséquilibré?
Encore une fois, regardez vos données. Parfois, vous pouvez trouver une ou deux observations multipliées par cent. Parfois, il est utile de créer ces fausses observations à une classe.
Si toutes les données sont propres, l'étape suivante consiste à utiliser des pondérations de classe dans le modèle de prédiction.

Alors qu'en est-il des métriques multiclasses?
D'après mon expérience, aucune de vos mesures n'est généralement utilisée. Il y a deux principales raisons.
Premièrement: il est toujours préférable de travailler avec des probabilités qu'avec des prédictions solides (car sinon, comment pourriez-vous séparer les modèles avec une prédiction de 0.9 et 0.6 s'ils vous donnent tous les deux la même classe?)
Et deuxièmement: il est beaucoup plus facile de comparer vos modèles de prédiction et de construire de nouveaux ceux qui dépendent d'une seule bonne métrique.
D'après mon expérience, je pourrais recommander logloss ou MSE (ou simplement dire erreur au carré).

Comment corriger les avertissements sklearn?
Simplement (comme yangjie l'a remarqué) écrasez le averageparamètre avec l'une de ces valeurs: 'micro'(calculer les métriques globalement), 'macro'(calculer les métriques pour chaque étiquette) ou 'weighted'(comme la macro mais avec des pondérations automatiques).

f1_score(y_test, prediction, average='weighted')

Tous vos avertissements sont venus après l' appel de fonctions métriques par défaut avec la averagevaleur 'binary'qui ne convient pas pour la prédiction multiclassent.
Bonne chance et amusez-vous avec l'apprentissage automatique!

Edit:
J'ai trouvé une autre recommandation du répondant pour passer aux approches de régression (par exemple SVR) avec laquelle je ne peux pas être d'accord. Autant que je me souvienne, il n'y a même pas de régression multiclasse. Oui, il existe une régression à étiquettes multiples qui est très différente et oui, il est possible dans certains cas de basculer entre la régression et la classification (si les classes sont triées) mais c'est assez rare.

Ce que je recommanderais (dans le cadre de scikit-learn) est d'essayer un autre outil de classification très puissant: le renforcement du gradient , la forêt aléatoire (mon préféré), KNeighbors et bien d'autres.

Après cela, vous pouvez calculer la moyenne arithmétique ou géométrique entre les prédictions et la plupart du temps, vous obtiendrez un résultat encore meilleur.

final_prediction = (KNNprediction * RFprediction) ** 0.5
Vlad Mironov
la source
1
> "basculer entre la régression et la classification (si les classes sont en quelque sorte triées) mais c'est assez rare" C'est le cas: 5> 4> 3> 2> 1. Je vous suggère de jeter un œil aux articles pour cette tâche - il y a de nombreuses approches de régression et de classification de la tâche (parfois dans le même travail).
Nikita Astrakhantsev
Alors ce n'est même pas une classification multiclasse mais une simple régression.
Vlad Mironov
Oui, en interne ou du point de vue ML, c'est une régression, mais à l'étape finale, nous convertissons les résultats de la régression en étiquettes, donc c'est une classification multiclasse - du point de vue de l'utilisateur ou de l'application.
Nikita Astrakhantsev