Le machine learning en partant de zéro · Leçon 2.5 · 12 sur 33 leçons prêtes · 12 min
Classes déséquilibrées
En bref
Si une classe est rare, l’exactitude cache les pertes et le modèle apprend à dire « la classe fréquente ». Rappel, exactitude équilibrée et poids le corrigent.
Échauffement Deux questions de leçons précédentes. Une minute, sans jamais bloquer la leçon.
Dans la leçon 1.3, « toujours bénigne » avait une exactitude élevée et ne trouvait personne. Là, la classe rare n’était rare que dans le test. Aujourd’hui, elle est rare dans les données d’apprentissage, comme dans un vrai dépistage, la fraude ou les pannes. Vous verrez ce que cela fait au modèle, comment le mesurer et ce qu’on peut réparer sans nouvelles données.
Choisissez une réponse et votre degré de certitude.
Répondez pour débloquer le labo.
Labo interactif
Ce labo interactif fonctionne dans un navigateur avec JavaScript activé. Le texte qui l’entoure décrit ce qu’il montre.
Voir la réponse
Bonne réponse : Le rappel, de plus de 20 points, et l’exactitude d’environ 6 points seulement
Le rappel passe de 83,0 % à 61,5 % (moyenne de 5 tirages des malignes conservées), et l’exactitude seulement de 91,5 % à 85,6 %. En validation, 89 des 142 biopsies sont bénignes, et le modèle les reconnaît avec de plus en plus d’assurance ; l’exactitude voit donc à peine la perte.
À vous de jouer
- Sans poids, déplacez le curseur de 159 à 8 malignes. Quelle ligne baisse le plus vite : le rappel ou l’exactitude ?
- À 8 malignes, activez les poids de classe. Combien de rappel revient, et combien manque encore par rapport à toutes les données ?
- Regardez la P(maligne) moyenne prédite. Comment change-t-elle quand les malignes disparaissent ?
Une fois vérifié dans le lab, nommons ce que vous voyez.
L’idée en quelques mots
Les classes sont déséquilibrées quand l’une est beaucoup plus rare que l’autre. Le modèle apprend alors deux choses à la fois : à quoi ressemblent les biopsies malignes et à quel point elles sont rares. Cette deuxième leçon agit comme un préjugé : quand les mesures sont floues, le modèle choisit « bénigne » de plus en plus souvent. La P(maligne) moyenne prédite passe de 0,36 à 0,24 quand il reste 8 malignes pour apprendre au lieu de 159.
L’exactitude le voit à peine. Le rappel passe de 83,0 % à 61,5 %, l’exactitude seulement de 91,5 % à 85,6 %. C’est pourquoi, avec une classe rare, on donne le rappel de la classe rare ou l’exactitude équilibrée, la moyenne des rappels des deux classes. « Toujours bénigne » a une exactitude équilibrée de 0,5, quelles que soient les proportions.
La première correction est simple : les poids de classe. Une erreur sur la classe rare coûte à l’apprentissage autant de fois plus que la classe est plus rare (class_weight='balanced'). Avec 8 malignes, les poids font passer le rappel de 61,5 % à 80,0 %, et l’exactitude monte même, à 91,4 %. Par rapport à toutes les données avec poids (86,8 %), il manque encore quelque chose : les poids changent les proportions, ils n’ajoutent pas d’exemples.
Jak to policzono
- Biopsies : Breast Cancer Wisconsin (Diagnostic), UCI, licence CC BY 4.0. 569 biopsies, 10 mesures « mean ».
- Partie de validation : 25 % des biopsies, stratifiée (142 : 53 malignes, 89 bénignes), la même dans tous les réglages. Pour apprendre, 268 bénignes et de 159 à 8 malignes ; sous 159, c’est la moyenne de 5 tirages des malignes conservées.
- Modèle : standardisation sur les données d’apprentissage et régression logistique (C = 1), avec ou sans poids de classe ; seuil 0,5.
Pourquoi ça marche
La régression logistique cherche le modèle qui se trompe le moins en moyenne sur les données d’apprentissage. Avec 268 bénignes et 8 malignes, les malignes ne font que 2,9 % de la pénalité totale. Le modèle déplace la frontière du côté des malignes : il sacrifie quelques malignes pour se tromper moins souvent sur les bénignes. Les poids de classe égalisent la pénalité : les 8 malignes pèsent ensemble autant que les 268 bénignes.
Un découpage pourrait tomber ainsi par hasard. Dans le code ci-dessous, la même expérience tourne sur 50 découpages différents : le rappel passe en moyenne de 89,3 % à 67,3 %, l’exactitude de 93,5 % à 87,7 %, et les poids avec 8 malignes donnent 84,0 %. Le rappel baisse plus que l’exactitude dans 50 découpages sur 50, et les poids le font remonter dans chacun d’eux.
L’exactitude peut même monter sans que le modèle change du tout. Dans le widget de l’Atlas sur l’exactitude équilibrée, un modèle faible à deux mesures note les mêmes biopsies tandis que les malignes passent de 212 à 20. Son exactitude monte de 75,9 % à 83,8 %, et l’exactitude équilibrée ne bouge pas : 0,728 et 0,726. « Toujours bénigne » avec 20 malignes atteint 94,7 % d’exactitude.
Erreur fréquente
Vrai ou faux : « SMOTE aide toujours ». Faux. SMOTE fabrique des exemples synthétiques de la classe rare sur les segments entre des exemples réels et leurs plus proches voisins, jusqu’à ce que les deux classes aient la même taille. Cela déplace la frontière de décision comme les poids de classe : le modèle dit « maligne » plus souvent. Dans le widget de l’Atlas sur SMOTE (357 bénignes, 30 malignes, deux mesures, validation croisée 5 × 5), le modèle sans correction trouve 60,0 % des malignes avec une précision de 90,0 %. SMOTE fait monter la détection à 86,7 %, mais la précision tombe à 42,9 %. Les poids de classe donnent le même rappel avec une précision de 40,2 %, et la simple duplication avec 39,6 %. C’est un tirage de 30 malignes, celui où la chute est la plus forte : en moyenne sur 20 tirages, la détection passe de 68,7 % à 91,9 % et la précision tombe de 91,0 % à 54,7 %. Elle tombe dans chacun d’eux. SMOTE n’ajoute aucune connaissance, il change seulement les proportions. Que cela aide dépend du coût des erreurs : plus de détections au prix de nombreuses fausses alertes.
Un avertissement : la correction ne s’applique qu’aux données d’apprentissage. Des exemples synthétiques ou dupliqués en validation ou en test donneraient un score qui ne veut rien dire.
Quiz
Trois questions pour vérifier vos acquis. Choisissez une réponse ou dépliez la solution sous chaque question. Au clavier : les flèches choisissent, Entrée ou Espace valide.
À retenir
- Quand une classe est rare, l’exactitude cache les pertes : donnez le rappel de la classe rare ou l’exactitude équilibrée.
- Un modèle apprend aussi les proportions des classes ; avec une classe rare à l’apprentissage, il choisit de plus en plus la classe fréquente.
- Les poids de classe et SMOTE déplacent la frontière : plus de détections au prix de fausses alertes, sans nouvelle connaissance.
- N’appliquez les corrections qu’aux données d’apprentissage et vérifiez le résultat sur une validation intacte, idéalement sur de nombreux découpages.
Pour aller plus loin : formule, code, pratique
Les maths
Exactitude = (TP + TN) / n, rappel = TP / (TP + FN), spécificité = TN / (TN + FP), exactitude équilibrée = (rappel + spécificité) / 2. « Toujours bénigne » a un rappel de 0 et une spécificité de 1, donc une exactitude équilibrée de (0 + 1) / 2 = 0,5.
La régression logistique minimise la perte logistique moyenne L = Σᵢ wᵢ · ℓ(yᵢ, pᵢ) / Σᵢ wᵢ. Sans poids wᵢ = 1, donc les 8 malignes font 8 / 276 ≈ 2,9 % de la perte totale. Les poids « balanced » valent w = n / (2 · n_classe) : 276 / 16 = 17,25 pour une maligne et 276 / 536 ≈ 0,51 pour une bénigne, donc chaque classe pèse au total n / 2 = 138.
En code (Python)
La même expérience dans scikit-learn sur 50 découpages différents : toutes les bénignes et 159 ou 8 malignes pour apprendre, avec et sans poids de classe, évaluée sur la partie de validation intacte.
import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import recall_score, accuracy_score
X, y = load_breast_cancer(return_X_y=True)
X, y = X[:, :10], 1 - y # les 10 mesures « mean », comme dans le lab ; 1 = maligne
def run(seed, m, weights):
tr, va = train_test_split(np.arange(len(y)), test_size=0.25, stratify=y, random_state=seed)
mal = np.random.default_rng(seed).choice(tr[y[tr] == 1], m, replace=False)
keep = np.r_[tr[y[tr] == 0], mal] # toutes les bénignes + m malignes pour apprendre
clf = make_pipeline(StandardScaler(), LogisticRegression(class_weight="balanced" if weights else None))
pred = clf.fit(X[keep], y[keep]).predict(X[va]) # la partie de validation ne change jamais
return recall_score(y[va], pred), accuracy_score(y[va], pred)
for m, w in ((159, False), (8, False), (8, True)):
rec, acc = np.mean([run(s, m, w) for s in range(50)], axis=0)
print(f"{m} malignant{' + class weights' if w else ''}: recall {rec:.1%}, accuracy {acc:.1%} (mean of 50 splits)")
drop = np.array([np.subtract(run(s, 159, False), run(s, 8, False)) for s in range(50)])
gain = np.array([run(s, 8, True)[0] - run(s, 8, False)[0] for s in range(50)])
print(f"recall fell more than accuracy in {(drop[:, 0] > drop[:, 1]).sum()} of 50 splits; weights raised recall in {(gain > 0).sum()}")
Sortie
159 malignant: recall 89.3%, accuracy 93.5% (mean of 50 splits)
8 malignant: recall 67.3%, accuracy 87.7% (mean of 50 splits)
8 malignant + class weights: recall 84.0%, accuracy 91.0% (mean of 50 splits)
recall fell more than accuracy in 50 of 50 splits; weights raised recall in 50
Le lab montre un découpage et 5 tirages, le code 50 découpages, donc les nombres diffèrent : sur le découpage du lab, le rappel avec toutes les données vaut 83,0 %, en moyenne 89,3 %. La conclusion est la même dans chaque découpage.
Télécharger le notebook (.ipynb). Ouvrez-le dans Jupyter ou importez-le dans Google Colab ; il télécharge lui-même les données manquantes.
Pour les praticiens
- Dans scikit-learn, les poids viennent de
class_weight="balanced"(régression logistique, SVM, arbres, forêts) et l’exactitude équilibrée debalanced_accuracy_score. Donnez aussi le rappel et la précision de la classe rare. - Découpez avec stratification (
stratify=y,StratifiedKFold), pour que la classe rare arrive dans chaque partie. - SMOTE et la duplication se trouvent dans la bibliothèque imbalanced-learn. Utilisez-les dans son
Pipeline, pour qu’ils n’agissent que sur les parties d’apprentissage en validation croisée. - Les poids de classe et un seuil plus bas font une chose semblable : ils déplacent la décision vers la classe rare. Le seuil peut se choisir en validation sur la courbe PR (leçon 2.3), sans réentraîner le modèle.
Cela vous a-t-il aidé ?
Merci ! Cela nous aide à nous améliorer.
Aller plus loin dans l’Atlas
Articles de l’encyclopédie sur les notions de cette leçon :