web-dev-qa-db-fra.com

Python: comment normaliser une matrice de confusion?

J'ai calculé une matrice de confusion pour mon classificateur en utilisant la méthode confusion_matrix () du package sklearn. Les éléments diagonaux de la matrice de confusion représentent le nombre de points pour lesquels l'étiquette prédite est égale à l'étiquette vraie, tandis que les éléments hors diagonale sont ceux qui sont mal étiquetés par le classificateur.

Je voudrais normaliser ma matrice de confusion afin qu'elle ne contienne que des nombres entre 0 et 1. Je voudrais lire le pourcentage d'échantillons correctement classés de la matrice.

J'ai trouvé plusieurs méthodes pour normaliser une matrice (normalisation de lignes et de colonnes) mais je ne connais pas grand-chose aux mathématiques et je ne sais pas si c'est la bonne approche. Quelqu'un peut-il aider s'il vous plaît?

26
Kaly

Je suppose que M[i,j] signifie Element of real class i was classified as j. Si c'est l'inverse, vous devrez transposer tout ce que je dis. Je vais également utiliser la matrice suivante pour des exemples concrets:

1 2 3
4 5 6
7 8 9

Il y a essentiellement deux choses que vous pouvez faire:

Trouver comment chaque classe a été classée

La première chose que vous pouvez demander est le pourcentage d'éléments de la classe réelle i ici classés comme chaque classe. Pour ce faire, nous prenons une ligne fixant le i et divisons chaque élément par la somme des éléments de la ligne. Dans notre exemple, les objets de la classe 2 sont classés en classe 1 4 fois, sont correctement classés en classe 2 5 fois et sont classés en classe 3 6 fois. Pour trouver les pourcentages, nous divisons tout simplement par la somme 4 + 5 + 6 = 15

4/15 of the class 2 objects are classified as class 1
5/15 of the class 2 objects are classified as class 2
6/15 of the class 2 objects are classified as class 3

Trouver quelles classes sont responsables de chaque classification

La deuxième chose que vous pouvez faire est d'examiner chaque résultat de votre classificateur et de demander combien de ces résultats proviennent de chaque classe réelle. Cela va être similaire à l'autre cas, mais avec des colonnes au lieu de lignes. Dans notre exemple, notre classificateur renvoie "1" 1 fois lorsque la classe d'origine est 1, 4 fois lorsque la classe d'origine est 2 et 7 fois lorsque la classe d'origine est 3. Pour trouver les pourcentages, nous divisons par la somme 1 + 4 + 7 = 12

1/12 of the objects classified as class 1 were from class 1
4/12 of the objects classified as class 1 were from class 2
7/12 of the objects classified as class 1 were from class 3

-

Bien sûr, les deux méthodes que j'ai données ne s'appliquent qu'à une seule colonne de ligne à la fois et je ne sais pas si ce serait une bonne idée de modifier réellement votre matrice de confusion sous cette forme. Cependant, cela devrait donner les pourcentages que vous recherchez.

8
hugomg

Supposer que

>>> y_true = [0, 0, 1, 1, 2, 0, 1]
>>> y_pred = [0, 1, 0, 1, 2, 2, 1]
>>> C = confusion_matrix(y_true, y_pred)
>>> C
array([[1, 1, 1],
       [1, 2, 0],
       [0, 0, 1]])

Ensuite, pour savoir combien d'échantillons par classe ont reçu leur étiquette correcte, vous devez

>>> C / C.astype(np.float).sum(axis=1)
array([[ 0.33333333,  0.33333333,  1.        ],
       [ 0.33333333,  0.66666667,  0.        ],
       [ 0.        ,  0.        ,  1.        ]])

La diagonale contient les valeurs requises. Une autre façon de les calculer est de réaliser que ce que vous calculez est le rappel par classe:

>>> from sklearn.metrics import precision_recall_fscore_support
>>> _, recall, _, _ = precision_recall_fscore_support(y_true, y_pred)
>>> recall
array([ 0.33333333,  0.66666667,  1.        ])

De même, si vous divisez par la somme sur axis=0, vous obtenez la précision (fraction des prédictions de classe -k qui ont une étiquette de vérité au sol k):

>>> C / C.astype(np.float).sum(axis=0)
array([[ 0.5       ,  0.33333333,  0.5       ],
       [ 0.5       ,  0.66666667,  0.        ],
       [ 0.        ,  0.        ,  0.5       ]])
>>> prec, _, _, _ = precision_recall_fscore_support(y_true, y_pred)
>>> prec
array([ 0.5       ,  0.66666667,  0.5       ])
20
Fred Foo

De la documentation sklearn (exemple de tracé)

cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]

où cm est la matrice de confusion fournie par sklearn.

7
Antoni

La matrice sortie par la fonction confusion_matrix() de sklearn est telle que

C_ {i, j} est égal au nombre d'observations connues pour être dans le groupe i mais prévues pour être dans le groupe j

donc pour obtenir les pourcentages pour chaque classe (souvent appelés spécificité et sensibilité dans la classification binaire), vous devez normaliser par ligne: remplacez chaque élément d'une ligne par lui-même divisé par la somme des éléments de cette ligne.

Notez que sklearn a une fonction de résumé disponible qui calcule les métriques à partir de la matrice de confusion: classification_report . Il produit de la précision et du rappel plutôt que de la spécificité et de la sensibilité, mais ceux-ci sont souvent considérés comme plus informatifs en général (en particulier pour la classification multi-classe déséquilibrée.)

7
damienfrancois

Il existe une bibliothèque fournie par scikit-learn lui-même pour tracer des graphiques. Il est basé sur matplotlib et devrait déjà être installé pour continuer.

pip install scikit-plot

Maintenant, il suffit de définir le paramètre normaliser sur true:

import scikitplot as skplt 
skplt.metrics.plot_confusion_matrix(Y_TRUE, Y_PRED, normalize=True)
0
Pranzell

Je pense que la façon la plus simple de le faire est de faire:

c = sklearn.metrics.confusion_matrix(y, y_pred)
normed_c = (c.T / c.astype(np.float).sum(axis=1)).T
0
Ignacio Peletier

En utilisant Seaborn, vous pouvez facilement imprimer une matrice normalisée ET assez confuse avec un plan de santé:

enter image description here

from sklearn.metrics import confusion_matrix
import seaborn as sns

cm = confusion_matrix(y_test, y_pred)
# Normalise
cmn = cm.astype('float') / 
cm.sum(axis=1)[:, np.newaxis]
fig, ax = plt.subplots(figsize=(10,10))
sns.heatmap(cmn, annot=True, fmt='.2f', xticklabels=target_names, yticklabels=target_names)
plt.ylabel('Actual')
plt.xlabel('Predicted')
plt.show(block=False)
0