ML Atlas

Le machine learning en partant de zéro · Leçon 3.2 · 15 sur 33 leçons prêtes · 10 min

Validation et validation croisée

En bref

Choisir ses réglages avec le jeu de test le gâche. La validation croisée teste un modèle sur chaque partie des données tour à tour et montre si son score tient.

Échauffement Deux questions de leçons précédentes. Une minute, sans jamais bloquer la leçon.
De la leçon 3.1 : Surapprentissage : quand un modèle apprend par cœur Pourquoi davantage de données d’entraînement rendent-elles généralement un modèle souple moins risqué ?

Voir la réponse

Bonne réponse : Plus de points compensent le bruit : la courbe ne suit plus les points isolés

Le bruit de chaque point demeure, mais sur beaucoup de points il se compense, si bien que l’ajustement suit la vraie tendance.

De la leçon 2.6 : Métrique et seuil selon le coût des erreurs Un oubli coûte 10, une fausse alerte 1, et le modèle est calibré. Quel seuil découle de la théorie de la décision ?

Voir la réponse

Bonne réponse : 1 / (1 + 10) ≈ 0,09, car une alerte est rentable dès un risque faible

Une alerte est rentable quand p · 10 > (1 − p) · 1, c’est-à-dire p > 1/11. Dans le lab, le seuil 0,09 coûte 125. Le seuil le moins cher, 0,06, ne coûte 111 que sur les scores qui ont servi à le choisir.

Dans la leçon précédente, nous avons choisi le meilleur degré de polynôme en jetant un œil au jeu de test. Cela semblait anodin, mais réfléchissez à ce que cela signifie : nous avons essayé plusieurs modèles et gardé celui que le jeu de test préférait. Le jeu de test n’est plus un examen inédit – il est devenu un juge que nous avons consulté pendant la préparation. Comment choisir des réglages sans gâcher notre seule mesure honnête ?

Prédisez, puis vérifiez Vous faites passer le nombre de plis k de 2 à 10 (178 vins au total). Que devient l’écart entre le pire et le meilleur pli ?
Quel est votre degré de certitude ?

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 : Il augmente, car chaque pli de test contient moins de vins

Pour k = 2, de 94,4 % à 97,8 % (3,4 points). Pour k = 10, de 83,3 % à 100,0 % (16,7 points), car un pli de test compte 17 ou 18 vins et une erreur pèse plus de 5 points. La moyenne ne bouge presque pas (96,1 % et 96,7 %).

À vous de jouer

  • Réglez le curseur de k sur sa plus petite valeur et lisez l’exactitude de chaque pli. À quel point les plis diffèrent-ils les uns des autres ?
  • Augmentez k cran par cran. Observez la moyenne et la dispersion, et remarquez que chaque pli contient désormais moins de vins.
  • Avec un grand k, cherchez des plis à l’exactitude inhabituellement basse ou haute. Avec seulement une poignée de vins dans un pli, une seule erreur fait beaucoup bouger son score.
  • Comparez la dispersion aux différences entre les réglages que vous voudriez ajuster. Un petit gain sur la moyenne est-il plus grand que la dispersion ?

L’idée en quelques mots

Voyons maintenant ce que vous venez de manipuler, en termes simples.

La première solution est une troisième pile de données. Répartissez les lignes en jeux d’entraînement, de validation et de test. Entraînez les modèles candidats sur le jeu d’entraînement, comparez-les sur le jeu de validation, choisissez le gagnant, et seulement ensuite ouvrez le jeu de test, une seule fois, pour annoncer le score final. Le jeu de validation absorbe tous les coups d’œil, pour que le jeu de test n’ait pas à le faire.

Le problème : chaque découpage coûte des données. Avec un petit jeu de données, un jeu de validation assez grand pour être fiable laisse trop peu de lignes pour l’entraînement, et la leçon 2 a montré qu’un petit jeu donne un score instable. La validation croisée résout ce problème en réutilisant astucieusement les données.

Imaginez que vous jugiez une nouvelle cuisinière. Goûter un seul plat vous apprend peu de chose : c’était peut-être son meilleur, peut-être son pire. Alors vous lui demandez plusieurs plats, des jours différents, et vous regardez la moyenne et à quel point ils varient. La validation croisée fait la même chose avec un modèle.

Pourquoi ça marche

Dans la validation croisée à k plis (k-fold), les données (hors jeu de test final) sont coupées en k parts égales appelées plis (folds). Le modèle est entraîné k fois. À chaque fois, un pli est réservé à la validation et les k − 1 autres servent à l’entraînement. Au bout du compte, chaque ligne a servi exactement une fois à la validation et k − 1 fois à l’entraînement. Chaque entraînement voit cette part des données :

part d'entraînement = (k − 1) / k

Le résultat n’est pas un score mais k scores, un par pli. Leur moyenne est une estimation plus fiable que n’importe quel découpage unique, car elle ne dépend pas d’une répartition chanceuse ou malchanceuse des lignes. Leur dispersion est tout aussi précieuse : elle montre à quel point le score dépend des lignes que le modèle a reçues par hasard. Deux modèles dont les moyennes diffèrent de moins que cette dispersion ne peuvent pas vraiment être départagés.

Le choix de k est un compromis. Un petit k (disons 2) entraîne chaque modèle sur la moitié des données seulement, si bien que chaque modèle est plus faible que celui que vous construirez au final, et l’estimation est pessimiste. Un grand k entraîne sur presque toutes les données, mais chaque pli est petit, donc les scores de chaque pli deviennent plus bruités, et le calcul prend k fois plus de temps qu’un ajustement unique. Les valeurs 5 ou 10 sont un compromis courant.

L’exemple de cette leçon est le jeu de données Wine : 178 vins d’une même région d’Italie, issus de trois cépages différents, chacun décrit par 13 résultats d’analyse chimique. La tâche consiste à distinguer les trois cépages.

Erreur fréquente

La validation croisée ne vous protège que si tout ce qui est appris à partir des données se passe à l’intérieur de chaque pli. Si vous normalisez les variables ou sélectionnez les « meilleures » variables sur toutes les lignes d’abord, puis faites la validation croisée, des informations des plis de validation ont déjà fui dans l’entraînement, et les scores sont trop optimistes. La seconde erreur est d’ajuster des dizaines de réglages par validation croisée et d’annoncer le meilleur score validé comme résultat final. Après tant de comparaisons, le gagnant a en partie eu de la chance ; c’est pourquoi un jeu de test séparé, utilisé une seule fois, reste important.

Avec une évaluation honnête dans notre boîte à outils, nous pouvons explorer des modèles plus puissants sans nous leurrer. Dans la prochaine leçon, nous rencontrons le plus lisible d’entre eux : l’arbre de décision.

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.

1. Dans une validation croisée à 5 plis, combien de fois chaque ligne sert-elle à la validation ?
Voir la réponse

Bonne réponse : Exactement une fois, dans un seul pli

Chaque ligne appartient à un pli, et chaque pli sert de jeu de validation dans exactement l’une des cinq exécutions.

2. Le modèle A a une exactitude moyenne en validation croisée légèrement supérieure à celle du modèle B, mais la dispersion entre les plis est bien plus grande que la différence. Qu’en conclure ?
Voir la réponse

Bonne réponse : Les données ne montrent aucune différence fiable entre les deux modèles

Quand l’écart entre les moyennes est plus petit que la variation entre les plis, il peut facilement être dû au hasard.

3. Pourquoi normaliser les variables sur toutes les lignes avant la validation croisée est-il une erreur ?
Voir la réponse

Bonne réponse : Des infos des plis de validation fuient dans l’entraînement : scores trop optimistes

Toute étape qui apprend à partir des données doit être ajustée sur les seuls plis d’entraînement, sinon les plis de validation ne sont plus inédits.

À retenir

  • Choisir ses réglages avec le jeu de test le gâche ; utilisez plutôt un jeu de validation ou la validation croisée.
  • Dans la validation croisée à k plis, chaque ligne sert exactement une fois à la validation.
  • La moyenne des scores des plis estime la performance ; la dispersion montre à quel point elle dépend du découpage.
  • Toute préparation fondée sur les données doit se faire à l’intérieur de chaque pli pour éviter les fuites.

Pour aller plus loin : formule, code, pratique

Les maths

Dans la validation croisée à k plis, les n lignes sont coupées en k plis. Chaque ajustement s’entraîne sur n · (k − 1) / k lignes et teste sur les n / k restantes. Le score est la moyenne sur les plis : CV = (1/k) · Σ score_f. La dispersion est leur écart type s = √( Σ (score_f − CV)² / (k − 1) ).

Un pli de m lignes de test donne une exactitude qui évolue par pas de 1 / m. Si l’exactitude vraie est p, son erreur standard est d’environ √( p · (1 − p) / m ). Elle grandit quand m diminue, c’est pourquoi chaque pli devient plus bruité quand k augmente, même si la moyenne devient moins pessimiste. Avec 178 vins et k = 10, une erreur dans un pli de 17 ou 18 vins coûte 5,6 à 5,9 points.

Les scores des plis ne sont pas tout à fait indépendants : les jeux d’entraînement se recouvrent. Donc s / √k sous-estime l’incertitude réelle de la moyenne.

En code (Python)

Les 178 vins avec les 5 plus proches voisins et des variables standardisées. Pour voir le motif derrière le mélange unique du lab, l’écart entre le pire et le meilleur pli est moyenné sur 20 mélanges. Les plis sont stratifiés et mélangés avec une graine, donc les nombres ne sont pas les 3,4 et 16,7 points du lab :

import numpy as np
from sklearn.datasets import load_wine
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

X, y = load_wine(return_X_y=True)
model = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=5))  # le scaler est réajusté dans chaque pli
for k in (2, 10):
    runs = [cross_val_score(model, X, y, cv=StratifiedKFold(k, shuffle=True, random_state=s)) for s in range(20)]
    gap = np.mean([a.max() - a.min() for a in runs])
    print(f"k={k:2d}: mean accuracy {np.mean(runs):.3f}, average worst-to-best gap {gap:.3f}")

Sortie

k= 2: mean accuracy 0.956, average worst-to-best gap 0.020
k=10: mean accuracy 0.966, average worst-to-best gap 0.113

La moyenne bouge à peine (0,956 et 0,966), mais l’écart moyen entre le pire et le meilleur pli passe de 0,020 à 0,113 quand k passe de 2 à 10.

Pour les praticiens
  • Ajustez chaque étape apprise (mise à l’échelle, imputation, sélection de variables) à l’intérieur de chaque pli. Dans scikit-learn, mettez-les dans une Pipeline et passez-la à cross_val_score.
  • Pour la classification, utilisez des plis stratifiés, afin que chaque pli contienne toutes les classes. cross_val_score le fait par défaut pour les classifieurs quand cv est un nombre, mais ce défaut ne mélange pas.
  • Mélangez avant de couper les plis si les lignes sont ordonnées, par exemple par classe ou par date. load_wine est trié par classe, donc passez StratifiedKFold(k, shuffle=True, random_state=0), comme ci-dessus. Pour les séries temporelles, ne mélangez pas : utilisez un découpage progressif.
  • Si des lignes appartiennent à des groupes (plusieurs lignes par patient), gardez chaque groupe dans un seul pli avec GroupKFold. Sinon, le même patient se retrouve à la fois dans l’entraînement et dans la validation.
  • Donnez la moyenne et la dispersion. Si deux modèles diffèrent de moins que la dispersion, ne dites pas que l’un des deux est meilleur.

Aller plus loin dans l’Atlas

Articles de l’encyclopédie sur les notions de cette leçon :