Matrice de confusion
Pourquoi apprendre ça ?
La matrice de confusion est la représentation la plus complète des performances d'un classifieur. Elle montre non seulement combien d'exemples sont correctement classés, mais aussi comment les erreurs sont distribuées entre les classes.
Analogie
Imagine un examen médical distinguant 3 maladies A, B, C. La matrice de confusion est le tableau complet : combien de vrais cas de A ont été correctement identifiés, combien ont été confondus avec B, etc. La diagonale contient les succès, le reste les erreurs.
Construction et lecture d'une matrice de confusion
Théorie
Pour classes, la matrice de confusion est définie par :
La diagonale contient les vrais positifs de chaque classe.
Normalisation : diviser chaque ligne par sa somme donne les taux de rappel par classe.
Métriques multi-classes :
- Accuracy :
- Précision par classe : (proportion correcte parmi les prédits de classe )
- Rappel par classe : (proportion correcte parmi les vrais de classe )
- F1 macro : moyenne des F1 par classe
- F1 micro : F1 calculé sur les TP/FP/FN globaux
Matrice de confusion multi-classes
import numpy as np
from sklearn.metrics import confusion_matrix, classification_report
np.random.seed(42)
n = 300
y_true = np.repeat([0, 1, 2], n // 3)
y_pred = y_true.copy()
# 15% d'erreurs classe 1 → prédite comme 2
masque_1 = y_true == 1
y_pred[masque_1] = np.where(np.random.rand(masque_1.sum()) < 0.15, 2, 1)
# 10% d'erreurs classe 2 → prédite comme 1
masque_2 = y_true == 2
y_pred[masque_2] = np.where(np.random.rand(masque_2.sum()) < 0.10, 1, 2)
cm = confusion_matrix(y_true, y_pred)
print("Matrice de confusion :")
print(cm)
cm_norm = cm.astype(float) / cm.sum(axis=1, keepdims=True)
print("\nNormalisée (rappel par classe) :")
print(np.round(cm_norm, 2))
print("\n" + classification_report(y_true, y_pred,
target_names=['A', 'B', 'C']))
Checkpoint
Dans une matrice de confusion normalisée par ligne, que représente la valeur diagonale M_ii ?
Micro vs Macro F1 et AUC-ROC
Théorie
Micro F1 : agrège tous les TP/FP/FN avant de calculer le F1
Macro F1 : calcule le F1 par classe puis fait la moyenne
Micro F1 est influencé par les classes fréquentes. Macro F1 traite toutes les classes également.
Courbe ROC et AUC :
- Axe X : taux de faux positifs (FPR = FP/(FP+TN))
- Axe Y : taux de vrais positifs (TPR = rappel = TP/(TP+FN))
- AUC : probabilité qu'un positif aléatoire reçoive un score plus élevé qu'un négatif aléatoire
- AUC = 0.5 : classifieur aléatoire ; AUC = 1.0 : parfait
AUC-ROC et micro/macro F1
import numpy as np
from sklearn.metrics import (roc_auc_score, roc_curve, f1_score)
# Dataset binaire pour ROC
np.random.seed(0)
n = 500
y_true_bin = np.random.binomial(1, 0.3, n)
y_prob = y_true_bin * 0.7 + np.random.beta(2, 5, n) * 0.3
auc = roc_auc_score(y_true_bin, y_prob)
print(f"AUC-ROC : {auc:.4f}")
fpr, tpr, seuils = roc_curve(y_true_bin, y_prob)
youden_idx = (tpr - fpr).argmax()
print(f"Seuil optimal (Youden) : {seuils[youden_idx]:.3f}")
print(f"TPR={tpr[youden_idx]:.3f}, FPR={fpr[youden_idx]:.3f}")
# Micro vs Macro F1
y_true_mc = np.array([0,0,0,1,1,1,2,2,2,0,1,2])
y_pred_mc = np.array([0,0,1,1,1,2,2,0,2,0,1,2])
f1_macro = f1_score(y_true_mc, y_pred_mc, average='macro')
f1_micro = f1_score(y_true_mc, y_pred_mc, average='micro')
f1_class = f1_score(y_true_mc, y_pred_mc, average=None)
print(f"F1 macro : {f1_macro:.4f}")
print(f"F1 micro : {f1_micro:.4f}")
print(f"F1 par classe : {np.round(f1_class, 3)}")
Détecter les confusions systématiques
import numpy as np
from sklearn.metrics import confusion_matrix
# Classificateur de sentiment : négatif, neutre, positif
y_true = np.array([0,0,0,0,1,1,1,1,2,2,2,2,0,0,1,1,2,2])
y_pred = np.array([0,0,1,0,1,1,0,1,2,2,1,2,0,1,1,2,2,2])
labels = ['négatif', 'neutre', 'positif']
cm = confusion_matrix(y_true, y_pred)
# Confusions principales (hors diagonale)
cm_off = cm.copy()
np.fill_diagonal(cm_off, 0)
i, j = np.unravel_index(cm_off.argmax(), cm_off.shape)
print(f"Confusion principale : {labels[i]} -> prédit comme {labels[j]} ({cm[i,j]} fois)")
# Précision et rappel par classe
for k in range(len(labels)):
prec = cm[k,k] / cm[:,k].sum() if cm[:,k].sum() > 0 else 0
rec = cm[k,k] / cm[k,:].sum() if cm[k,:].sum() > 0 else 0
print(f"{labels[k]:10s} — Précision: {prec:.2%}, Rappel: {rec:.2%}")
Ne pas interpréter l'AUC seul pour les datasets déséquilibrés
L'AUC-ROC peut rester élevée même avec un dataset très déséquilibré. Pour les datasets déséquilibrés, préférez l'AUC-PR (Area Under Precision-Recall Curve) ou l'Average Precision (AP), plus sensibles aux performances sur la classe minoritaire.
Checkpoint
Pour un dataset très déséquilibré (1% positifs, 99% négatifs), quelle métrique est la plus informative ?
À retenir
- Matrice de confusion M_ij : nombre d'exemples de classe i prédits comme classe j
- Normalisation par ligne = rappel par classe ; par colonne = précision par classe
- Micro F1 favorise les classes fréquentes ; Macro F1 traite toutes les classes également
- AUC-ROC : probabilité qu'un positif soit mieux classé qu'un négatif
- Pour datasets déséquilibrés : préférer F1 macro ou AUC-PR à accuracy et AUC-ROC seuls
F-beta, courbe précision-rappel et matrices de coûts
Théorie
F-beta score — généralisation du F1 :
- : F1 standard, équilibre précision et rappel
- (ex. ) : favorise le rappel — utile quand les faux négatifs coûtent cher (dépistage médical)
- (ex. ) : favorise la précision — utile quand les faux positifs coûtent cher (filtre spam)
Courbe précision-rappel (PR curve) :
En faisant varier le seuil de décision :
- Seuil élevé peu de positifs prédits haute précision, faible rappel
- Seuil bas beaucoup de positifs prédits faible précision, haut rappel
Average Precision (AP) = aire sous la courbe PR. Plus fiable qu'AUC-ROC pour les datasets très déséquilibrés car elle est sensible aux performances sur la classe minoritaire.
Matrices de coûts asymétriques :
Dans certains contextes, FP et FN n'ont pas le même coût :
| Contexte | FP coûte... | FN coûte... | Stratégie | |---|---|---|---| | Dépistage cancer | Anxiété, examens inutiles | Maladie non détectée | Rappel élevé, | | Filtre spam | Email légitime bloqué | Spam dans la boîte | Précision élevée, | | Fraude bancaire | Transaction valide bloquée | Fraude non détectée | Rappel élevé selon pertes |
Coût total :
Le seuil optimal est celui qui minimise , pas nécessairement celui qui maximise l'accuracy ou le F1.
Compromis précision-rappel selon le seuil
Un modèle de détection de maladie rare (prévalence 2%) à différents seuils :
| Seuil | Précision | Rappel | F1 | F2 () | |---|---|---|---|---| | 0.8 | 0.95 | 0.40 | 0.56 | 0.46 | | 0.5 | 0.70 | 0.85 | 0.77 | 0.82 | | 0.2 | 0.30 | 0.98 | 0.46 | 0.71 |
En médecine, manquer un cas positif (FN) est bien pire que diagnostiquer à tort (FP). On préfère le seuil 0.5 selon le F2, qui reflète mieux cet objectif que le F1.
Calcul F2 au seuil 0.5 :
Checkpoint
Un filtre anti-spam doit minimiser les emails légitimes bloqués (faux positifs). Quel F-beta convient le mieux ?
Checkpoint
Sur la courbe précision-rappel, que se passe-t-il quand on augmente le seuil de décision ?
À retenir
- F-beta : favorise le rappel (coût FN élevé, ex. médecine) ; favorise la précision (coût FP élevé, ex. spam)
- Courbe PR : précision vs rappel selon le seuil — l'AP (aire sous la courbe) est plus robuste qu'AUC-ROC sur données déséquilibrées
- Coût asymétrique : — choisir le seuil qui minimise le coût total
- AUC-ROC = 0.5 : classifieur aléatoire ; AUC-ROC = 1 : classifieur parfait
- Matrice de confusion : outil de base — diagonale = vrais prédits par classe ; hors diagonale = erreurs
- Micro vs Macro F1 : micro favorise les classes fréquentes ; macro traite toutes les classes à égalité