L’accuracy (proportion de bonnes prédictions) est intuitive, mais elle peut être trompeuse. Prenons un problème de détection de fraude avec 10 % de cas positifs :
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.dummy import DummyClassifier
X, y = make_classification(n_samples=3000, n_features=12, n_informative=6, weights=[0.9],
class_sep=1.5, random_state=0)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, stratify=y, random_state=0)
naif = DummyClassifier(strategy="most_frequent").fit(X_train, y_train)
print(naif.score(X_test, y_test)) # environ 0.90 sans rien apprendre !
Un modèle qui répond toujours « pas de fraude » obtient 90 % d’accuracy… et ne détecte aucune fraude. Il faut des métriques qui regardent chaque type d’erreur.
La matrice de confusion
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix
clf = make_pipeline(StandardScaler(), LogisticRegression()).fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(confusion_matrix(y_test, y_pred))
# [[663 9] lignes = classes réelles, colonnes = classes prédites
# [ 28 50]]
En binaire, avec la classe positive 1 :
| prédit 0 | prédit 1 | |
|---|---|---|
| réel 0 | VN : vrais négatifs (663) | FP : faux positifs (9) |
| réel 1 | FN : faux négatifs (28) | VP : vrais positifs (50) |
ConfusionMatrixDisplay.from_estimator(clf, X_test, y_test) en donne une version graphique.
Précision, rappel, F1
- Précision = VP / (VP + FP) = 50 / 59 ≈ 0,85 : quand le modèle lance une alerte, il a raison 85 % du temps.
- Rappel (sensibilité) = VP / (VP + FN) = 50 / 78 ≈ 0,64 : le modèle détecte 64 % des fraudes.
- F1 = 2 × précision × rappel / (précision + rappel) ≈ 0,73 : la moyenne harmonique, élevée seulement si les deux le sont.
from sklearn.metrics import precision_score, recall_score, f1_score, classification_report
print(precision_score(y_test, y_pred), recall_score(y_test, y_pred), f1_score(y_test, y_pred))
print(classification_report(y_test, y_pred, digits=3))
classification_report affiche ces métriques pour chaque classe, ainsi que les moyennes macro (moyenne simple des classes, qui donne autant de poids à la classe rare) et weighted (pondérée par l’effectif). La balanced accuracy (balanced_accuracy_score), moyenne des rappels de chaque classe, vaut 0,5 pour le modèle naïf : elle ne se laisse pas tromper par le déséquilibre.
Quelle métrique choisir ?
Tout dépend du coût des erreurs :
- Dépistage médical, fraude : un faux négatif coûte cher → privilégier le rappel (tout en surveillant la précision).
- Filtre anti-spam, recommandation : un faux positif agace l’utilisateur → privilégier la précision.
- Compromis équilibré : le F1.
- Classes équilibrées et erreurs de coût similaire : l’accuracy reste acceptable.
Pour les problèmes multiclasses, precision_score(..., average="macro") ou "weighted" résume les classes en un nombre. Fixez la métrique avant de comparer des modèles, en accord avec les équipes métier : changer de métrique après coup pour flatter un modèle est une forme de biais.
Notez enfin que ces métriques dépendent du seuil de décision (0,5 par défaut). La leçon suivante montre comment les évaluer indépendamment du seuil, puis comment le choisir.
Exercices
Les solutions et les tests restent sur le serveur. Dans l'espace étudiant, ton code est exécuté et corrigé, et le tuteur donne des indices avant la solution.
Démasquer le modèle naïf
Entraînez un
DummyClassifier(strategy='most_frequent'). Stockez son accuracy de test dansacc_naifet son rappel sur la classe 1 dansrappel_naif(utilisezrecall_score).from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split X, y = make_classification(n_samples=3000, n_features=12, n_informative=6, weights=[0.9], class_sep=1.5, random_state=0) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, stratify=y, random_state=0) from sklearn.dummy import DummyClassifier from sklearn.metrics import recall_score acc_naif = ... rappel_naif = ...Matrice de confusion et métriques
Entraînez
clf(pipelineStandardScaler+LogisticRegression()), prédisez le test, puis extrayezvn, fp, fn, vpde la matrice de confusion. Calculez à la mainprecision = vp / (vp + fp)etrappel = vp / (vp + fn), et stockez le F1 calculé avecf1_scoredansf1.from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split X, y = make_classification(n_samples=3000, n_features=12, n_informative=6, weights=[0.9], class_sep=1.5, random_state=0) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, stratify=y, random_state=0) from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.metrics import confusion_matrix, f1_score clf = ... # vn, fp, fn, vp = ... precision = ... rappel = ... f1 = ...Choisir la métrique
Une banque veut détecter le maximum de transactions frauduleuses, quitte à vérifier quelques transactions légitimes. Quelle métrique privilégier ?
- La précision
- Le rappel
- L'accuracy
- Le R²
Quiz de fin de leçon
Dans confusion_matrix de scikit-learn, que représentent les lignes ?
- Les classes prédites
- Les classes réelles
- Les seuils
- Les features
Le rappel mesure :
- La part des alertes qui sont justes
- La part des vrais positifs détectés
- La part de bonnes prédictions
- L'aire sous la courbe ROC
Que vaut la balanced accuracy d’un modèle qui prédit toujours la classe majoritaire en binaire ?
- 0
- 0.5
- L'accuracy
- 1
La suite du cours
- Courbes ROC et précision-rappel, choix du seuil15 min
- Stratégies de validation croisée15 min
- Recherche d'hyperparamètres : GridSearchCV et RandomizedSearchCV20 min
- Diagnostiquer un modèle : courbes et fuites de données20 min