Le machine learning en partant de zéro · Leçon 3.6 · 19 sur 33 leçons prêtes · 12 min
Arrêt précoce
En bref
Un réseau entraîné trop longtemps mémorise les étiquettes fausses. Avec 20 % d’étiquettes au hasard, 100 époques font passer le test de 86,1 % à 76,3 %.
Échauffement Deux questions de leçons précédentes. Une minute, sans jamais bloquer la leçon.
Un réseau de neurones apprend par époques : une époque est un passage sur tous les exemples d’entraînement. Après chaque époque, la perte d’entraînement baisse en général. La question de la leçon 3.1 revient sous une autre forme : combien d’époques, c’est trop ? L’arrêt précoce y répond avec l’ensemble de validation : l’entraînement s’arrête quand la perte de validation cesse de baisser, et le modèle revient à sa meilleure époque.
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 : Elle passe de 86,1 % à 76,3 %, parce que le réseau mémorise aussi les étiquettes fausses
Les poids de l’époque 28 obtiennent 86,1 % sur les 997 images de test, après 100 époques 76,3 % : 9,7 points de moins. Pendant ce temps, la perte de validation monte de 0,48 à 0,84, et des 51 étiquettes fausses, le réseau finit par toutes les répéter.
À vous de jouer
- Le labo s’ouvre sur les étiquettes propres. Passez aux étiquettes au hasard et gardez une patience de 5. À quelle époque l’entraînement s’arrête-t-il, quels poids sont gardés, et est-ce la meilleure époque de tout le parcours ?
- Montez la patience à 10. Qu’est-ce qui change dans l’époque gardée et dans l’exactitude au test ?
- Revenez aux étiquettes propres. Combien coûte maintenant l’entraînement jusqu’au bout ?
Une fois vérifié dans le lab, mettons un nom sur ce que vous voyez.
L’idée en quelques mots
Au début de l’entraînement, le réseau apprend ce que beaucoup d’images ont en commun : la forme d’un zéro, d’un huit, d’un sept. Les pertes d’entraînement et de validation baissent ensemble. Puis le réseau se met à ajuster des exemples isolés, y compris ceux dont l’étiquette est fausse. La perte d’entraînement continue de baisser, la perte de validation commence à monter. C’est le surapprentissage de la leçon 3.1, mais le long des époques au lieu du nombre de paramètres.
L’arrêt précoce surveille la perte de validation. Il retient l’époque à la perte la plus basse et compte les époques sans amélioration. Quand il y en a autant que la patience, l’entraînement s’arrête et le réseau revient aux poids de la meilleure époque. Dans le lab, avec une patience de 10, l’entraînement s’arrête à l’époque 38 et l’on garde les poids de l’époque 28 : 86,1 % au test au lieu de 76,3 % après 100 époques.
Trop peu de patience nuit aussi. La courbe de validation tremble d’une époque à l’autre. Avec une patience de 5, l’entraînement s’arrête dès l’époque 23 et garde les poids de l’époque 18 (85,0 % au test), alors que la meilleure était l’époque 28.
Jak to policzono
- Chiffres : Optical Recognition of Handwritten Digits, UCI, licence CC BY 4.0. 1797 images de 8×8 pixels.
- Découpage stratifié : 299 images pour apprendre, 501 pour la validation, 997 pour le test. Dans la version bruitée, chaque étiquette d’entraînement est remplacée, avec une probabilité de 20 %, par un chiffre au hasard : 51 étiquettes ont changé (un chiffre au hasard tombe parfois sur le vrai).
- Réseau 64 → 32 → 10, ReLU, entropie croisée, SGD avec un momentum de 0,9, pas de 0,05, lots de 32 images, 100 époques. Perte de validation calculée après chaque époque.
- La validation et le test ont les vraies étiquettes. Le bruit est seulement dans les données d’entraînement.
Pourquoi ça marche
Un exemple à l’étiquette fausse est difficile pour le réseau : l’image ressemble à un trois, mais l’étiquette dit « sept ». Pour réduire la perte sur un tel exemple, le réseau doit apprendre une règle qui ne convient qu’à lui. Il le fait en général tard, une fois les motifs faciles et communs acquis. Dans le lab, le réseau répète 10 des 51 étiquettes fausses à l’époque 28, et les 51 après 100 époques. Sur ses propres données d’entraînement, il a alors 99,7 % d’exactitude : il est d’accord même avec les étiquettes fausses.
Les règles ajustées à des étiquettes fausses gâchent les réponses sur de nouvelles images. L’ensemble de validation a les vraies étiquettes, donc sa perte monte précisément quand le réseau commence à apprendre le bruit. C’est pour cela que c’est elle, et non la perte d’entraînement, qui dit quand s’arrêter.
Ce n’est pas un hasard d’un seul passage. Dans le code plus bas, la même expérience tourne dans scikit-learn sur 10 autres découpages et tirages du bruit : la meilleure époque tombe entre 13 et 23, et entraîner jusqu’à 100 époques fait baisser l’exactitude de test dans chacun des 10 passages, en moyenne de 87,2 % à 76,4 %. La meilleure époque du lab (28) sort de cet intervalle, car elle dépend aussi des poids de départ et de l’ordre des lots : le même réseau que dans le lab, avec 9 autres graines aléatoires, est au mieux entre les époques 12 et 25, et entraîner jusqu’à 100 époques fait baisser l’exactitude de test à chaque fois.
Erreur fréquente
Vrai ou faux : « entraîner plus longtemps donne toujours un meilleur modèle ». Faux. Entraîner plus longtemps colle toujours mieux aux données d’entraînement, mais pas aux nouvelles. Avec des étiquettes au hasard, 100 époques donnent 99,7 % d’exactitude à l’entraînement et 76,3 % au test, alors que s’arrêter à l’époque 28 donne 86,1 % au test.
Une remarque dans l’autre sens : avec des étiquettes propres, entraîner jusqu’au bout ne gâche presque rien. La perte de validation la plus basse arrive à l’époque 30 (93,9 % au test), et après 100 époques l’exactitude est de 94,5 %. L’arrêt précoce ne sauve pas toujours l’exactitude, mais il fait toujours gagner du temps et protège contre des données moins bonnes que vous ne le pensez.
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
- Une époque est un passage sur les données d’entraînement ; la perte d’entraînement baisse presque toujours, la perte de validation non.
- L’arrêt précoce interrompt l’entraînement quand la perte de validation ne s’améliore pas pendant quelques époques (la patience) et revient aux meilleurs poids.
- Entraîner trop longtemps sur des étiquettes fausses apprend le bruit au réseau : le test passe de 86,1 % à 76,3 %.
- Trop peu de patience arrête l’entraînement sur un tremblement fortuit de la courbe.
Pour aller plus loin : formule, code, pratique
Les maths
Perte de validation après l’époque e : L_val(e) = −(1/m) · Σᵢ log p_e(yᵢ | xᵢ) sur m images de validation (entropie croisée).
La règle avec une patience P : e = argmin_{e ≤ t} L_val(e) ; l’entraînement s’arrête à la première époque t telle que t − e ≥ P et renvoie les poids w(e). Si une telle époque n’arrive pas, il renvoie w(e) sur tout le parcours.
L’arrêt précoce agit un peu comme la pénalité de la leçon 3.4. Avec une descente de gradient à petit pas, les poids partent près de zéro et s’en éloignent à chaque époque. Moins d’époques, ce sont des poids plus petits : pour un modèle linéaire à perte quadratique, s’arrêter après t pas a à peu près le même effet que ridge avec un α de l’ordre de 1 / (pas · t).
En code (Python)
La même expérience dans scikit-learn : 10 découpages et tirages du bruit différents, un réseau 64 → 32 → 10 entraîné époque par époque, en gardant les poids de l’époque à la perte de validation la plus basse.
import copy
import numpy as np
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import log_loss
X, y = load_digits(return_X_y=True)
X = X / 16
def run(seed): # 300 chiffres pour apprendre, 500 pour la validation, les 997 autres pour le test
used, te = train_test_split(np.arange(len(y)), train_size=800, stratify=y, random_state=seed)
tr, va = train_test_split(used, test_size=500, stratify=y[used], random_state=seed)
rng = np.random.default_rng(seed)
noisy = y[tr].copy()
flip = rng.random(len(tr)) < 0.2 # 20 % des étiquettes d’entraînement remplacées par un chiffre au hasard
noisy[flip] = rng.integers(0, 10, flip.sum())
net = MLPClassifier(hidden_layer_sizes=(32,), solver="sgd", learning_rate_init=0.05, momentum=0.9, batch_size=32, alpha=0, random_state=seed)
best, best_loss = None, np.inf
for epoch in range(1, 101):
net.partial_fit(X[tr], noisy, classes=np.arange(10))
loss = log_loss(y[va], net.predict_proba(X[va]), labels=np.arange(10))
if loss < best_loss:
best, best_loss, best_epoch = copy.deepcopy(net), loss, epoch # l’arrêt précoce garde les poids de la meilleure époque
return best_epoch, best.score(X[te], y[te]), net.score(X[te], y[te])
r = np.array([run(s) for s in range(10)])
print(f"10 splits and noise draws: lowest validation loss at epoch {r[:, 0].min():.0f} to {r[:, 0].max():.0f}; test at that epoch {r[:, 1].mean():.1%}, after 100 epochs {r[:, 2].mean():.1%}; lower after 100 in {(r[:, 2] < r[:, 1]).sum()} of 10")
Sortie
10 splits and noise draws: lowest validation loss at epoch 13 to 23; test at that epoch 87.2%, after 100 epochs 76.4%; lower after 100 in 10 of 10
Les découpages, les poids de départ et le bruit diffèrent de ceux du lab, donc les nombres aussi. L’image est la même : la meilleure époque arrive tôt (entre 13 et 23), et entraîner jusqu’à 100 époques fait baisser l’exactitude de test dans les 10 passages, en moyenne de 87,2 % à 76,4 %.
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 Keras :
EarlyStopping(monitor="val_loss", patience=10, restore_best_weights=True). Sansrestore_best_weights=True, on garde les poids de la dernière époque, pas ceux de la meilleure. - Dans PyTorch, vous écrivez la boucle vous-même : après chaque époque, calculez la perte de validation et gardez une copie des poids quand elle est la plus basse.
- Dans scikit-learn :
MLPClassifier(early_stopping=True, n_iter_no_change=...)met de côté une partie des données d’entraînement pour la validation ; il surveille l’exactitude de validation, pas la perte. En gradient boosting, la même idée s’appellen_iter_no_changeouearly_stopping_rounds. - Le nombre d’époques choisi en validation est lui aussi un hyperparamètre : le score de validation à la meilleure époque est un peu gonflé (leçon 3.5), donc faites l’évaluation finale sur un test mis de côté.
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 :