Limiter le surapprentissage
- Leçon 13
- 65 min
- Module 4 : Diagnostiquer les problèmes d'entraînement
Leçon 13 sur 23 · L'apprentissage profond et les transformeurs expliquésLimiter le surapprentissage
1. Ce qu'est un réseau de neurones
2. Les tenseurs et PyTorch
3. Comment les réseaux de neurones apprennent
4. Diagnostiquer les problèmes d'entraînement
5. Représenter les séquences et le sens
6. Assembler un transformeur
7. Utiliser des modèles préentraînés
8. Sous le capot : les mathématiquesFacultatif
Ce que vous saurez faire
Empêchez le routeur d’apprendre ses billets de soutien d’entraînement par cœur. Vous verrez le surapprentissage dans la perte de validation, même quand l’exactitude le cache. Vous le limiterez avec l’arrêt anticipé, le dropout (désactivation aléatoire de neurones) et la décroissance des poids. Vous déciderez aussi quand créer de nouveaux exemples d’entraînement à partir des anciens sans risque.
Carnet JupyterFaites cette leçon dans un carnet Jupyter
Le carnet (notebook) Jupyter contient tous les blocs de code, les tâches et les vérifications de cette leçon. Il entraîne le grand routeur avec et sans chaque mesure, conserve la meilleure époque et répète la mesure de Tomás avec le dropout activé. Ses cellules de préparation téléchargent les billets et ticketnet.py, donc aucune installation locale n’est nécessaire. Environ 35 minutes; les entraînements prennent environ 1 minute sur un processeur gratuit de Colab.
Le carnet est en anglais. Colab et Kaggle ouvrent votre propre copie. Il faut un compte Google ou Kaggle gratuit. Vous êtes bloqué ? Consultez le carnet de solution.
Avant de commencerCe qu’il faut savoir et avoir sous la main
Ce que vous devez savoir
- Le surapprentissage et le sous-apprentissage, présentés avec les trois élèves de Contrôler la complexité dans le cours d’apprentissage automatique.
- La régularisation comme pénalité sur les grands poids, dans Autres limites : taille des feuilles et régularisation.
- Pourquoi chaque examen des données de validation les utilise un peu, dans Données d’entraînement, de validation et de test.
model.eval()ettorch.no_grad(), ainsi que la meilleure époque, dans Lots, époques et validation.- Les trois formes de courbes et le taux d’apprentissage par défaut d’Adam, dans Taux d’apprentissage et optimiseurs.
Ce qu’il vous faut sur votre ordinateur (seulement pour l’option locale; le carnet n’en a pas besoin)
- Le dossier
ticket-routeravecdata/,ticketnet.pyet PyTorch, de Installer PyTorch, ainsi que les tenseursx_train,y_train,x_valid,y_validetvocabdu premier bloc de code de Taux d’apprentissage et optimiseurs. Exécutez ce bloc à nouveau si vous commencez une nouvelle session.
Tous les nombres de cette page proviennent d’exécutions réelles avec PyTorch 2.14.1 sur un processeur. Les cinq entraînements de 40 époques prennent environ 40 secondes au total.
L’élève qui apprend le corrigé
Section intitulée « L’élève qui apprend le corrigé »Vous connaissez cette histoire du cours d’apprentissage automatique. Une élève apprend les méthodes. Une autre apprend chaque exercice par cœur, avec sa réponse, et ne sait plus quoi faire devant de nouvelles questions d’examen. Un modèle qui fait la deuxième chose fait du surapprentissage. Il apprend les détails et les particularités accidentelles de ses exemples d’entraînement, puis fonctionne moins bien sur de nouveaux exemples. Le cours Apprentissage automatique : du problème à un modèle fiable limitait ce problème en limitant la profondeur d’un arbre et par la régularisation, une pénalité sur les grands poids.
Un réseau de neurones peut très bien jouer le rôle de « l’élève qui apprend le corrigé », car il a beaucoup de poids. Ses mesures de contrôle fonctionnent un peu différemment. L’une d’elles, le dropout, n’a pas d’équivalent dans le cours d’apprentissage automatique. Voyons d’abord le problème avec les billets.
Le problème : un routeur plus grand qui empire pendant l’entraînement
Section intitulée « Le problème : un routeur plus grand qui empire pendant l’entraînement »Tomás pense qu’un réseau plus grand doit être meilleur. Il donne au routeur 512 neurones cachés au lieu de 64 et l’entraîne pendant 40 époques avec le taux d’apprentissage par défaut d’Adam. Il utilise "adamw", la version d’Adam nécessaire pour la décroissance des poids, plus loin dans cette leçon. Sans décroissance des poids, elle équivaut à Adam.
set_seed(0)big = BagOfWordsNet(len(vocab), hidden=512) # 8 times more hidden neurons than beforeprint("parameters:", count_parameters(big))history = train(big, x_train, y_train, x_valid, y_valid, epochs=40, optimizer="adamw", log=None)for row in history: if row["epoch"] in (1, 2, 4, 7, 10, 20, 30, 40): print(f"epoch {row['epoch']:>2}: train loss {row['train_loss']:.4f}, accuracy {row['train_acc']:.2%}" f" | valid loss {row['valid_loss']:.4f}, accuracy {row['valid_acc']:.1%}")La sortie :
parameters: 1469445epoch 1: train loss 0.1588, accuracy 97.47% | valid loss 0.1437, accuracy 98.3%epoch 2: train loss 0.0815, accuracy 98.51% | valid loss 0.0909, accuracy 99.2%epoch 4: train loss 0.0293, accuracy 99.53% | valid loss 0.0730, accuracy 98.0%epoch 7: train loss 0.0080, accuracy 99.96% | valid loss 0.0628, accuracy 97.8%epoch 10: train loss 0.0034, accuracy 99.98% | valid loss 0.0714, accuracy 97.8%epoch 20: train loss 0.0006, accuracy 100.00% | valid loss 0.0874, accuracy 97.5%epoch 30: train loss 0.0002, accuracy 100.00% | valid loss 0.0971, accuracy 97.5%epoch 40: train loss 0.0001, accuracy 100.00% | valid loss 0.1059, accuracy 97.3%Le réseau a 1 469 445 paramètres pour 4 696 billets d’entraînement : plus de 300 paramètres par billet. Il classe correctement tous les billets d’entraînement pour la première fois à l’époque 9. À partir de l’époque 11, il le fait toujours, y compris pour les 70 billets dont l’étiquette est erronée dans le fichier. Sa perte d’entraînement descend à 0,0001. C’est de la mémorisation : pour donner une forte probabilité à une mauvaise étiquette, il doit se souvenir de ce billet précis.
L’exactitude le cache; la perte le montre
Section intitulée « L’exactitude le cache; la perte le montre »Regardez maintenant l’exactitude de validation. Elle atteint son maximum à l’époque 2 (99,2 %). À partir de l’époque 4, elle reste entre 97,3 % et 98,3 %. Rien de spectaculaire ne se produit. Une personne qui surveille seulement l’exactitude ne voit donc aucun problème.
Description textuelle du graphique
Un graphique en courbes de l’époque 1 à 40, avec une exactitude de 90 % à 100 %. La courbe commence à 98,3 %, atteint un sommet de 99,2 % à l’époque 2, puis passe à 98,8 % à l’époque 3. À partir de l’époque 4, elle reste presque plate entre 97,3 % et 98,3 %, puis descend progressivement à 97,3 % à l’époque 40.
La perte de validation raconte une autre histoire. Elle descend à 0,063 à l’époque 7, puis remonte à 0,106 à l’époque 40 : une hausse de 70 %. Pourquoi la perte change-t-elle alors que l’exactitude ne change pas? La perte mesure à quel point le routeur est sûr de la bonne équipe, pas seulement si son premier choix est correct. Priya compte les billets de validation mal classés aux époques 7 et 40. Elle mesure aussi la probabilité que le routeur attribue à ses mauvaises réponses :
def wrong_and_sure(model): """How many validation tickets are wrong, and how sure the network is of those wrong answers.""" model.eval() with torch.no_grad(): probs = model(x_valid).softmax(1) wrong = probs.argmax(1) != y_valid return int(wrong.sum()), round(float(probs.max(1).values[wrong].mean()), 3)
set_seed(0)at_epoch_7 = BagOfWordsNet(len(vocab), hidden=512)train(at_epoch_7, x_train, y_train, x_valid, y_valid, epochs=7, optimizer="adamw", log=None)print("epoch 7: wrong tickets and mean probability of the wrong answer", wrong_and_sure(at_epoch_7))print("epoch 40: wrong tickets and mean probability of the wrong answer", wrong_and_sure(big))La sortie :
epoch 7: wrong tickets and mean probability of the wrong answer (13, 0.681)epoch 40: wrong tickets and mean probability of the wrong answer (16, 0.816)À l’époque 40, il classe mal trois billets de plus, mais le plus grand changement concerne les probabilités. À l’époque 7, le routeur attribue en moyenne 0,681 à ses mauvaises réponses : il hésite quand il se trompe. À l’époque 40, il leur attribue 0,816. Le routeur en surapprentissage devient de plus en plus sûr de tout, même quand il se trompe.
Omar demande ce que cela signifie pour les clients. « Un routeur qui sait quand il hésite peut envoyer ces billets à une personne, dit Priya. Celui en surapprentissage est sûr de ses erreurs. Les billets vont donc directement dans la mauvaise file d’attente. » Voilà pourquoi vous surveillez la perte de validation pour détecter le surapprentissage, et présentez l’exactitude aux gens.
Comment détecter le surapprentissage
Section intitulée « Comment détecter le surapprentissage »Revenons à l’élève qui apprend le corrigé. Son enseignante ne peut pas voir dans sa tête. Elle peut seulement comparer deux résultats : celui des exercices pratiqués et celui d’un examen préparatoire avec de nouvelles questions. Un réseau vous donne les deux mêmes résultats après chaque époque : la perte d’entraînement et la perte de validation. Vous les avez vus dans Lire les deux courbes. Ici, vous les lisez pour le grand réseau de Tomás. Vous ajoutez aussi deux signaux : l’écart entre les courbes et la confiance du routeur quand il se trompe.
L’écart entre les deux pertes
Section intitulée « L’écart entre les deux pertes »La perte qui compte est celle des billets que les clients écriront demain. Personne ne peut la mesurer, car ces billets n’existent pas encore. Elle porte un nom, la perte attendue, et deux pertes mesurables la remplacent :
Ici, est le routeur avec ses poids, et est la perte d’un billet avec sa bonne équipe : l’entropie croisée de La perte : à quel point le réseau se trompe-t-il?. est la perte moyenne pour tous les billets que la boutique pourrait recevoir. Le symbole signifie « la moyenne de tous ces billets ». Le chapeau sur signifie « estimé à partir d’un échantillon » : la moyenne des billets d’entraînement ou des billets de validation.
L’écart de généralisation est : à quel point le routeur fonctionne moins bien sur de nouveaux billets que sur ceux utilisés pour apprendre. La perte de validation remplace . L’écart mesurable est donc :
Vérifiez avec les nombres de l’entraînement. À l’époque 7, l’écart est de 0,0628 − 0,0080 = 0,0548. À l’époque 40, il est de 0,1059 − 0,0001 = 0,1058. La perte de validation est une estimation juste seulement tant que les données de validation ne servent pas à choisir le routeur. Choisir l’époque avec ces données les utilise un peu. Les données de test restent donc intactes.
Priya entraîne de nouveau le routeur et enregistre cinq signaux après chaque époque. train() appelle la fonction fournie comme log après chaque époque. Elle prend donc ses mesures à cet endroit :
def watch(epochs=40): """Train the large router again and record five signals after every epoch.""" set_seed(0) model = BagOfWordsNet(len(vocab), hidden=512) rows = []
def after_epoch(row): # train() calls this after each epoch train_loss, _ = evaluate(model, x_train, y_train) # not rounded, unlike row["train_loss"] valid_loss, _ = evaluate(model, x_valid, y_valid) wrong, sure = wrong_and_sure(model) # from the code block above with torch.no_grad(): worst = nn.functional.cross_entropy(model(x_valid), y_valid, reduction="none").max().item() rows.append({**row, "gap": valid_loss - train_loss, "sure_when_wrong": sure, "worst": worst})
train(model, x_train, y_train, x_valid, y_valid, epochs=epochs, optimizer="adamw", log=after_epoch) return rows
rows = watch()for row in rows: if row["epoch"] in (1, 2, 5, 7, 9, 20, 40): print(f"epoch {row['epoch']:>2}: train {row['train_loss']:.4f} valid {row['valid_loss']:.4f}" f" gap {row['gap']:+.4f} accuracy {row['valid_acc']:.2%}" f" sure when wrong {row['sure_when_wrong']:.2f} worst ticket {row['worst']:.2f}")La sortie :
epoch 1: train 0.1588 valid 0.1437 gap -0.0151 accuracy 98.33% sure when wrong 0.55 worst ticket 3.29epoch 2: train 0.0815 valid 0.0909 gap +0.0093 accuracy 99.17% sure when wrong 0.53 worst ticket 2.86epoch 5: train 0.0173 valid 0.0661 gap +0.0488 accuracy 98.33% sure when wrong 0.69 worst ticket 2.83epoch 7: train 0.0080 valid 0.0628 gap +0.0548 accuracy 97.83% sure when wrong 0.68 worst ticket 4.08epoch 9: train 0.0042 valid 0.0735 gap +0.0692 accuracy 97.67% sure when wrong 0.74 worst ticket 6.27epoch 20: train 0.0006 valid 0.0874 gap +0.0868 accuracy 97.50% sure when wrong 0.79 worst ticket 9.96epoch 40: train 0.0001 valid 0.1059 gap +0.1058 accuracy 97.33% sure when wrong 0.82 worst ticket 12.79Explorer
Regardez le grand routeur s'entraîner, époque par époque
Prédisez d’abord : Vous voyez les courbes seulement jusqu'à l'époque actuelle, comme pendant l'entraînement. L'écart est positif dès l'époque 2. À quelle époque arrêteriez-vous, et pourquoi?
- Époque
- 40
- Perte d’entraînement
- 0,0001
- Perte de validation
- 0,1059
- Écart
- +0,1058
- Exactitude en validation
- 97,33 %
- Confiance quand c’est faux
- 0,82
- Perte du pire billet
- 12,79
Époque 40 : la perte de validation (0,1059) est au-dessus de son minimum (0,0628 à l’époque 7) depuis 33 époques. Avec une patience de 5, l’arrêt précoce a arrêté l’entraînement après l’époque 12 et a gardé les poids de l’époque 7. L’exactitude bouge à peine : 97,33 %, 3 billets mal classés de plus qu’à l’époque 7. Mais le routeur est plus sûr de ses mauvaises réponses : 0,82 contre 0,68.
- Perte d’entraînement (tirets)
- Perte de validation
- Écart entre les deux
Description textuelle de l’explorateur
L’outil d’exploration a une commande : un curseur pour choisir l’époque de 1 à 40. Avec JavaScript, il commence à l’époque 1 et le graphique affiche les courbes seulement jusqu’à l’époque choisie. Sans JavaScript, il affiche les 40 époques. Le graphique du haut montre la perte d’entraînement (en pointillés) et la perte de validation de 0 à 0,16. La zone entre les courbes est ombrée. Un point noir marque la perte de validation la plus basse jusqu’ici. Le graphique du bas montre l’écart, de −0,02 à 0,12. Sept indicateurs affichent les valeurs à l’époque choisie. Ce sont l’époque, les deux pertes, l’écart et l’exactitude de validation. Les deux derniers sont la confiance moyenne pour les mauvaises réponses de validation et la perte du pire billet.
À l’époque 1, la perte d’entraînement est de 0,1588 et la perte de validation de 0,1437. L’écart (−0,0151) est donc négatif. Des époques 2 à 7, les deux pertes diminuent et l’écart augmente (de 0,0093 à 0,0548). Chacune de ces époques établit un nouveau minimum de perte de validation. L’époque 7 atteint le minimum de l’entraînement (0,0628). Après l’époque 7, une bande grise commence : la perte de validation dépasse 0,0628 à chaque époque suivante. Avec une patience de 5, l’arrêt anticipé arrête l’entraînement après l’époque 12. À l’époque 40, les indicateurs affichent une perte d’entraînement de 0,0001, une perte de validation de 0,1059 et un écart de 0,1058. L’exactitude de validation passe de 97,83 % à l’époque 7 à 97,33 % à l’époque 40. La confiance pour les mauvaises réponses passe de 0,68 à 0,82, et la perte du pire billet de 4,08 à 12,79.
L’écart augmente avant le début du problème
Section intitulée « L’écart augmente avant le début du problème »Un écart qui augmente semble être un signal d’alarme. Priya compte les changements des deux signaux :
def rises(values): """How many times a value is higher than the one before it.""" return sum(b > a for a, b in zip(values, values[1:]))
gaps = [row["gap"] for row in rows]best = min(rows, key=lambda row: row["valid_loss"])after_best = [row["valid_loss"] for row in rows[best["epoch"] - 1:]]print("gap positive from epoch", next(row["epoch"] for row in rows if row["gap"] > 0))print("gap rises in", rises(gaps), "of", len(gaps) - 1, "steps")print("lowest validation loss:", best["valid_loss"], "at epoch", best["epoch"])print("after it, the validation loss rises in", rises(after_best), "of", len(after_best) - 1, "steps")La sortie :
gap positive from epoch 2gap rises in 34 of 39 stepslowest validation loss: 0.0628 at epoch 7after it, the validation loss rises in 28 of 33 stepsL’écart augmente à 34 des 39 étapes, à partir de l’époque 2. Mais des époques 2 à 7, la perte de validation diminue encore : le routeur s’améliore toujours sur les nouveaux billets. L’écart augmente parce que le routeur apprend ses billets d’entraînement plus vite que le schéma général. Presque tous les réseaux qui apprennent font cela. Un écart qui augmente ne suffit donc pas pour décider d’arrêter.
Le signal est la perte de validation qui commence à remonter. Elle atteint son minimum à l’époque 7 et le dépasse à chaque époque suivante. Elle augmente à 28 des 33 étapes qui suivent. C’est exactement ce que surveille l’arrêt anticipé. La taille de l’écart vous renseigne quand même : elle montre la marge d’amélioration possible avec une mesure de contrôle. L’entraînement avec dropout, plus loin dans cette leçon, a un écart de 0,031 à sa meilleure époque, contre 0,055 ici.
Pourquoi la perte le montre et l’exactitude le cache
Section intitulée « Pourquoi la perte le montre et l’exactitude le cache »Vous avez vu la confiance pour les mauvaises réponses à deux époques plus haut. Le prochain graphique la montre à chaque époque. Elle augmente alors que l’exactitude bouge à peine.
- Pour les mauvaises réponses de validation
- Pour les bonnes réponses de validation
Description textuelle du graphique
Un graphique en courbes de l’époque 1 à 40, avec une confiance de 0,5 à 1. La confiance pour les bonnes réponses commence à 0,90 et monte à 0,98 à l’époque 7, puis à 0,99 à l’époque 40. La confiance pour les mauvaises réponses commence à 0,55 et passe à 0,53 à l’époque 2. Elle est de 0,68 à l’époque 7, de 0,79 à l’époque 20 et de 0,82 à l’époque 40. Elle augmente avec de petites fluctuations, car seulement 10 à 16 billets sont mal classés à chaque époque.
Pourquoi une erreur commise avec confiance change-t-elle autant la perte? La perte d’un billet est l’opposé du logarithme de la probabilité de sa bonne équipe :
Une probabilité de 0,5 coûte −ln 0,5 = 0,69. Le pire billet de validation à l’époque 7 a une perte de 4,08 : sa bonne équipe reçoit une probabilité de . À l’époque 40, la perte du pire billet est de 12,79 : une probabilité d’environ 0,000003. La perte n’a pas de plafond. Une erreur commise avec confiance coûte donc de plus en plus cher. Ce seul billet ajoute 12,79 ÷ 600 = 0,021 à la perte moyenne de validation. C’est le tiers de toute la perte de la meilleure époque. L’exactitude le compte comme un seul billet mal classé sur 600, à l’époque 7 comme à l’époque 40.
Une liste de vérification : comment détecter le surapprentissage
Section intitulée « Une liste de vérification : comment détecter le surapprentissage »- Surveillez la perte de validation après chaque époque, pas seulement à la fin. Gardez l’époque où elle est la plus basse; l’arrêt anticipé le fait pour vous.
- Comparez la perte d’entraînement à la perte de validation. Toutes deux élevées : sous-apprentissage. Un écart qui augmente pendant que la perte de validation diminue : le routeur apprend encore. La perte de validation augmente pendant que la perte d’entraînement diminue : surapprentissage.
- Regardez la confiance pour les erreurs, ainsi que les pertes des pires billets, pas seulement l’exactitude. Un routeur qui devient sûr de ses erreurs mémorise.
- Vérifiez avec plusieurs graines avant de conclure. Cette section utilise une seule graine et une seule division des données. Avec 600 billets de validation, un billet représente 0,17 point de pourcentage. Des différences de 1 à 3 billets sont donc du bruit. Recommencez l’entraînement avec deux ou trois autres graines. Si la perte de validation remonte près de la même époque chaque fois, la tendance est réelle.
L’arrêt anticipé : garder la meilleure époque
Section intitulée « L’arrêt anticipé : garder la meilleure époque »Le pain au four est prêt à un moment précis. Sortez-le trop tôt et il n’est pas cuit; laissez-le jusqu’à la fin de la minuterie et il brûle. Un boulanger attentif surveille le pain, pas la minuterie.
L’arrêt anticipé fait la même chose avec l’entraînement. Après chaque époque, mesurez la perte de validation. Gardez une copie des poids de la meilleure époque jusqu’ici. Si la perte ne s’améliore pas pendant quelques époques, arrêtez et utilisez la copie. Le nombre d’époques d’attente s’appelle la patience. Il faut de la patience, car une mauvaise époque peut être due au hasard.
Voici la règle appliquée aux pertes de validation réelles des époques 5 à 12 de l’entraînement de Tomás, avec une patience de 5 :
| Époque | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
|---|---|---|---|---|---|---|---|---|
| Perte de validation | 0,0661 | 0,0644 | 0,0628 | 0,0728 | 0,0735 | 0,0714 | 0,0746 | 0,0757 |
| Époques sans nouveau minimum | 0 | 0 | 0 | 1 | 2 | 3 | 4 | 5 : arrêt |
L’époque 7 est la meilleure. Les époques 8 à 12 sont toutes moins bonnes, donc l’entraînement s’arrête après l’époque 12. Il conserve les poids de l’époque 7 : perte de validation de 0,063 et 97,8 % de bonnes réponses. Il économise 28 époques de travail et donne le meilleur routeur de cet entraînement. Vous avez vu la meilleure époque dans Lots, époques et validation; l’arrêt anticipé rend ce choix automatique.
Le coût : les données de validation choisissent maintenant l’époque. Elles sont donc un peu plus utilisées, comme l’expliquait le cours d’apprentissage automatique. C’est une raison de plus de garder les données de test pour la fin.
Dropout : personne ne peut compter sur un seul collègue
Section intitulée « Dropout : personne ne peut compter sur un seul collègue »Imaginez une équipe où n’importe quel membre peut s’absenter n’importe quel jour. Personne ne peut confier une tâche à un seul collègue, car celui-ci pourrait être absent. Tout le monde apprend donc une partie de chaque tâche, et l’équipe fonctionne chaque jour. Le dropout (dropout, désactivation aléatoire de neurones) entraîne un réseau de la même façon.
Le problème qu’il résout : avec 512 neurones cachés, quelques neurones peuvent se spécialiser dans certains billets de soutien d’entraînement. Un neurone peut apprendre « cette combinaison exacte de mots correspond à la garantie ». C’est vrai pour un seul billet du fichier, mais inutile pour tout nouveau billet. Les autres neurones comptent alors sur lui.
L’idée : pendant l’entraînement, désactivez chaque neurone caché au hasard, avec une probabilité p, pour chaque lot. Un neurone désactivé produit 0 pour ce lot. Les autres ne peuvent donc pas compter sur lui. Les neurones qui restent actifs sont multipliés par 1 ÷ (1 − p). Le signal total reste ainsi à peu près le même. Le nom décrit ce qui arrive : des neurones quittent le réseau pendant une étape. À l’évaluation, tous les neurones sont actifs et rien n’est multiplié.
nn.Dropout fait cela. Voici son effet sur les sorties de quatre neurones cachés, avec p = 0.5 :
torch.manual_seed(3)drop = nn.Dropout(p=0.5) # switch off each neuron with chance 0.5h = torch.tensor([0.8, 0.2, 0.5, 0.6]) # the outputs of 4 hidden neuronsdrop.train() # training mode: dropout is onprint("training, pass 1:", [round(v, 2) for v in drop(h).tolist()])print("training, pass 2:", [round(v, 2) for v in drop(h).tolist()])drop.eval() # evaluation mode: dropout is offprint("evaluation: ", [round(v, 2) for v in drop(h).tolist()])La sortie :
training, pass 1: [1.6, 0.4, 0.0, 1.2]training, pass 2: [0.0, 0.0, 1.0, 1.2]evaluation: [0.8, 0.2, 0.5, 0.6]Au passage 1, le troisième neurone est désactivé, et les trois autres sont doublés (1 ÷ (1 − 0,5) = 2) : 0,8 devient 1,6. Au passage 2, les deux premiers sont désactivés. En mode évaluation, les sorties passent sans changement. Le BagOfWordsNet du cours contient un nn.Dropout après sa ReLU, et dropout=0.5 l’active.
Description textuelle du diagramme
Trois panneaux montrent le même petit réseau. Il contient trois neurones d’entrée à gauche, cinq neurones cachés au milieu et deux neurones de sortie à droite. Dans le premier panneau, « Entraînement, lot 1 », le dropout désactive deux neurones cachés : le deuxième et le quatrième. Ce sont des cercles pointillés avec une croix, sans lignes entrantes ni sortantes. La note indique : 2 neurones cachés sur 5 désactivés. Dans le deuxième panneau, « Entraînement, lot 2 », trois autres neurones cachés sont désactivés : le premier, le troisième et le cinquième. Dans le troisième panneau, « Évaluation : model.eval() », les cinq neurones cachés sont actifs. Chaque entrée se connecte à chaque neurone caché, et chaque neurone caché se connecte aux deux sorties.
Son coût : chaque étape d’entraînement utilise seulement une partie du réseau. L’entraînement demande donc plus d’époques pour atteindre la même perte d’entraînement. Vous devez aussi désactiver le dropout quand vous mesurez ou utilisez le réseau. Le cas d’échec ci-dessous montre ce qui arrive si vous l’oubliez.
Décroissance des poids : un petit coût pour chaque grand poids
Section intitulée « Décroissance des poids : un petit coût pour chaque grand poids »La régression logistique du cours d’apprentissage automatique utilisait la régularisation : une pénalité sur les grands poids, réglée avec C. Les réseaux utilisent la même idée, appelée décroissance des poids. À chaque étape, l’optimiseur réduit aussi un peu chaque poids vers 0. Un poids reste grand seulement si les données continuent de le faire augmenter. Le réseau utilise donc de grands poids seulement quand plusieurs billets concordent, et non pour un seul billet.
Un tout petit exemple montre cette réduction. À chaque étape, AdamW multiplie chaque poids par 1 − taux d’apprentissage × décroissance des poids. Prenez un poids de 4, un taux d’apprentissage de 0,1 et une décroissance des poids de 0,5. Chaque étape multiplie le poids par 1 − 0,05 = 0,95. Sans poussée des données, le poids diminue : 4, puis 3,8, puis 3,61, puis 3,43. Il décroît, ce qui donne son nom à la méthode. Avec les vrais réglages (taux d’apprentissage de 0,001, décroissance des poids de 0,05), le facteur est de 0,99995 par étape. C’est très doux, mais cela agit aux 74 étapes de chaque époque.
Dans PyTorch, il suffit d’un argument : weight_decay=0.05, avec l’optimiseur AdamW. Utilisez AdamW, pas Adam, pour la décroissance des poids. Adam ordinaire ajoute la décroissance à la pente, puis divise par la taille du pas de chaque poids. La décroissance devient donc inégale.
Comparer les moyens de contrôle sur le routeur
Section intitulée « Comparer les moyens de contrôle sur le routeur »Priya entraîne le grand routeur trois fois pendant 40 époques. Le premier essai n’a aucun contrôle, le deuxième utilise un dropout de 0,5, et le troisième, une décroissance des poids de 0,05. Tout le reste est identique.
controls = {"no control": dict(dropout=0.0, weight_decay=0.0), "dropout 0.5": dict(dropout=0.5, weight_decay=0.0), "weight decay 0.05": dict(dropout=0.0, weight_decay=0.05)}results = {}for name, c in controls.items(): set_seed(0) model = BagOfWordsNet(len(vocab), hidden=512, dropout=c["dropout"]) results[name] = train(model, x_train, y_train, x_valid, y_valid, epochs=40, optimizer="adamw", weight_decay=c["weight_decay"], log=None) best = min(results[name], key=lambda row: row["valid_loss"]) last = results[name][-1] print(f"{name:<18} best epoch {best['epoch']:>2}: loss {best['valid_loss']:.3f}, accuracy {best['valid_acc']:.1%}" f" | epoch 40: loss {last['valid_loss']:.3f}, accuracy {last['valid_acc']:.1%}")La sortie :
no control best epoch 7: loss 0.063, accuracy 97.8% | epoch 40: loss 0.106, accuracy 97.3%dropout 0.5 best epoch 6: loss 0.056, accuracy 98.7% | epoch 40: loss 0.104, accuracy 97.0%weight decay 0.05 best epoch 7: loss 0.063, accuracy 97.8% | epoch 40: loss 0.098, accuracy 97.2%- Aucun contrôle
- Dropout de 0,5
- Décroissance des poids de 0,05
Description textuelle du graphique
Un graphique linéaire des époques 1 à 40, avec une perte de validation de 0 à 0,2. Les trois courbes descendent rapidement aux premières époques. Le dropout commence plus haut (0,193), mais atteint le point le plus bas du graphique : 0,056 à l’époque 6. Sans contrôle et avec la décroissance des poids, la perte atteint 0,063 à l’époque 7. Ces deux courbes se superposent presque. Après l’époque 7, les trois courbes remontent lentement. À l’époque 40 : aucun contrôle 0,106, dropout 0,104, décroissance des poids 0,098.
Lisez ces résultats honnêtement. Le dropout a aidé un peu : la perte la plus basse (0,056 contre 0,063) et 98,7 % de bonnes réponses. Cela représente environ 5 billets de plus sur 600. Une décroissance des poids de 0,05 n’a presque rien changé à la meilleure époque. Elle a seulement ralenti un peu la hausse : 0,098 contre 0,106 à l’époque 40. Et aucun contrôle n’a arrêté la hausse : chaque essai présente du surapprentissage après l’époque 6 ou 7. Le contrôle qui conserve le bon routeur est l’arrêt anticipé.
Priya combine donc le dropout et l’arrêt anticipé. Elle trouve la meilleure époque de l’essai avec dropout, puis entraîne de nouveau le routeur pendant exactement ce nombre d’époques. train() part de la même graine aléatoire. Le deuxième essai répète donc le premier, époque par époque, et s’arrête à la meilleure :
best = min(results["dropout 0.5"], key=lambda row: row["valid_loss"])print("best epoch:", best["epoch"])set_seed(0)router = BagOfWordsNet(len(vocab), hidden=512, dropout=0.5)train(router, x_train, y_train, x_valid, y_valid, epochs=best["epoch"], optimizer="adamw", log=None)loss, acc = evaluate(router, x_valid, y_valid)print(f"stopped at epoch {best['epoch']}: valid loss {loss:.3f}, accuracy {acc:.1%}")La sortie :
best epoch: 6stopped at epoch 6: valid loss 0.056, accuracy 98.7%Sur les réseaux plus grands, on enregistre les poids à chaque nouvelle meilleure époque plutôt que d’entraîner deux fois. Enregistrer et examiner les résultats montre comment enregistrer les poids.
Gardez les résultats en perspective. Le meilleur routeur de cette leçon atteint 98,7 % en validation. Les outils du cours d’apprentissage automatique, TF-IDF et la régression logistique, ont atteint 98,8 % sur les mêmes billets. Ils n’avaient aucune époque à surveiller. Les contrôles rendent le réseau fiable. Ils ne le rendent pas meilleur qu’un modèle plus simple sur ces données.
Quand utiliser chaque moyen. Utilisez toujours l’arrêt anticipé : il est gratuit et demande seulement la perte de validation que vous calculez déjà. Ajoutez du dropout (0,1 à 0,5) quand un grand réseau présente du surapprentissage tôt. Le dropout fait aussi partie de la plupart des blocs de transformeur, comme le montre le module 6. Ajoutez une décroissance des poids (0,01 à 0,1 avec AdamW) comme léger complément. Une forte décroissance des poids pousse le réseau vers le sous-apprentissage. Aucun de ces moyens ne remplace des données plus nombreuses et de meilleure qualité.
Augmentation des données : seulement si l’étiquette reste vraie
Section intitulée « Augmentation des données : seulement si l’étiquette reste vraie »Une enfant apprend le mot « chat » à partir de quelques images. Montrez-lui la même image dans un miroir : c’est encore un chat. L’image miroir est donc un exemple supplémentaire gratuit. L’augmentation des données crée de nouveaux exemples d’entraînement à partir des anciens, avec des changements qui conservent la bonne réponse. Plus d’exemples rendent la mémorisation plus difficile.
Pour les images, plusieurs changements conservent l’étiquette : une chemise retournée de gauche à droite reste une chemise. L’étude de cas Classer des images de vêtements utilise ce retournement et mesure son effet.
Le texte est différent, car un seul mot peut changer le sens. Une augmentation courante du texte remplace un mot par un autre du même genre. Voici un remplacement et les vraies réponses du routeur :
def route(text): router.eval() with torch.no_grad(): probs = router(bag_of_words([{"text": text}], vocab)).softmax(1)[0] return TEAMS[int(probs.argmax())], round(float(probs.max()), 3)
for text in ["Where is my parcel? It is three days late.", "Where is my refund? It is three days late."]: print(route(text), text)La sortie :
('delivery', 0.934) Where is my parcel? It is three days late.('delivery', 0.875) Where is my refund? It is three days late.Le premier billet concerne la livraison. Le deuxième, non : selon la politique de Grace, un remboursement manquant va aux retours ou au paiement, jamais à la livraison. Une augmentation qui remplace « colis » par « remboursement » en gardant l’étiquette delivery (livraison) introduit une mauvaise étiquette dans les données d’entraînement. Regardez aussi la réponse du routeur : il indique déjà delivery (0,875) pour le billet de remboursement. C’est probablement parce que « retard » et « jours » sont des mots associés à la livraison. Des copies augmentées avec la mauvaise étiquette lui apprendraient davantage cette erreur.
Pour le texte, utilisez donc seulement des changements vérifiés par une personne qui connaît la politique. Par exemple, un autre numéro de commande ou une faute de frappe. Vérifiez aussi à la main un échantillon des nouveaux exemples, comme Grace vérifierait une nouvelle règle d’acheminement.
Erreur courante
Mesurer avec le dropout encore actif
Symptôme : Tomás fait l’erreur de Lots, époques et validation, cette fois avec le code. Il vérifie le routeur trois fois sur un billet de paiement et obtient trois probabilités différentes pour payment (paiement). « Le réseau est aléatoire », dit-il. Il a entraîné un petit routeur avec un dropout de 0,5 pendant 8 époques, puis a oublié model.eval() :
set_seed(0)small = BagOfWordsNet(len(vocab), dropout=0.5) # 64 hidden neurons, dropout 0.5train(small, x_train, y_train, x_valid, y_valid, epochs=8, log=None)ticket = bag_of_words(valid_rows[:1], vocab) # one payment ticket, shape (1, 2864)small.train() # Tomás's mistake: dropout is still onwith torch.no_grad(): for run in range(3): print("training mode, payment:", round(float(small(ticket).softmax(1)[0, 2]), 3))small.eval() # the right way to measurewith torch.no_grad(): for run in range(3): print("evaluation mode, payment:", round(float(small(ticket).softmax(1)[0, 2]), 3))La sortie :
training mode, payment: 0.996training mode, payment: 1.0training mode, payment: 0.983evaluation mode, payment: 0.999evaluation mode, payment: 0.999evaluation mode, payment: 0.999Cause : en mode entraînement, le dropout désactive une moitié différente des neurones cachés à chaque passage. Le même billet reçoit donc une réponse différente chaque fois. train() laisse le réseau en mode entraînement. Sur l’ensemble des données de validation, l’effet est petit, mais réel. À l’époque 40, le grand routeur avec dropout de cette leçon a obtenu 97,17 % en mode entraînement. Il a obtenu 97,0 % en mode évaluation. Un score qui change quand vous relancez le code ne peut être comparé à rien.
Correction : appelez model.eval() avant de mesurer ou d’utiliser le réseau, et model.train() avant de l’entraîner de nouveau. Mesurez à l’intérieur de torch.no_grad(). La fonction evaluate() du cours fait les deux. Le symptôme à chercher : des réponses qui changent entre les exécutions pour la même entrée.
Ce que cela signifie pour un vrai service d’IA : un routeur déployé en mode entraînement donne des réponses différentes au même billet. Son exactitude mesurée ne correspond pas à ce qu’il fait. Placez model.eval() dans le code qui charge le modèle, pas dans la mémoire d’une personne.
Exercice guidé : choisir l’époque d’arrêt
Section intitulée « Exercice guidé : choisir l’époque d’arrêt »- Exécutez le bloc de code des trois contrôles. Vous devriez voir les trois lignes de sortie ci-dessus, avec les meilleures époques 7, 6 et 7.
- Affichez les pertes de validation de l’essai avec dropout pour les époques 5 à 12 :
[row["valid_loss"] for row in results["dropout 0.5"][4:12]]. Vous devriez voir[0.0611, 0.0561, 0.0565, 0.0575, 0.0576, 0.0615, 0.0585, 0.0591]. - Appliquez à la main l’arrêt anticipé avec une patience de 5. Notez la meilleure époque et l’époque où l’entraînement s’arrête.
- Exécutez le bloc de code d’arrêt anticipé. Vous devriez voir
stopped at epoch 6: valid loss 0.056, accuracy 98.7%(arrêt à l’époque 6 : perte de validation de 0,056, exactitude de 98,7 %).
Afficher un exemple de réponse
La meilleure perte de validation est de 0,0561 à l’époque 6. Les époques 7 à 11 donnent toutes une perte supérieure : 0,0565, 0,0575, 0,0576, 0,0615 et 0,0585. Après l’époque 11, cinq époques se sont écoulées sans nouveau minimum. L’entraînement s’arrête donc. Le routeur conserve les poids de l’époque 6 : perte de validation de 0,056 et 98,7 % de bonnes réponses.
À vous de jouer : une décroissance des poids beaucoup plus forte
Section intitulée « À vous de jouer : une décroissance des poids beaucoup plus forte »Une décroissance des poids de 0,05 n’a presque rien changé. Prédisez ce qui arrive avec 0,5 et avec 5 : la meilleure perte de validation diminue-t-elle, et la meilleure époque change-t-elle? Exécutez ensuite le code :
for weight_decay in [0.5, 5.0]: # 10 and 100 times stronger than 0.05 set_seed(0) model = BagOfWordsNet(len(vocab), hidden=512) history = train(model, x_train, y_train, x_valid, y_valid, epochs=40, optimizer="adamw", weight_decay=weight_decay, log=None) best, last = min(history, key=lambda row: row["valid_loss"]), history[-1] print(f"weight decay {weight_decay:<4} best epoch {best['epoch']:>2}: loss {best['valid_loss']:.3f}," f" accuracy {best['valid_acc']:.1%} | epoch 40: loss {last['valid_loss']:.3f}," f" train accuracy {last['train_acc']:.1%}")Afficher la sortie et la réponse
weight decay 0.5 best epoch 7: loss 0.065, accuracy 98.2% | epoch 40: loss 0.085, train accuracy 100.0%weight decay 5.0 best epoch 29: loss 0.098, accuracy 98.8% | epoch 40: loss 0.108, train accuracy 99.4%Avec 0,5, la hausse après la meilleure époque est plus faible : 0,085 à l’époque 40, contre 0,106 sans contrôle. Mais la meilleure perte n’est pas meilleure. Avec 5, le réseau ne peut plus mémoriser : l’exactitude d’entraînement s’arrête à 99,4 %, et la meilleure époque passe à 29. Mais sa meilleure perte, 0,098, est bien pire que 0,063. Les poids restent si petits que le routeur est aussi moins sûr de ses bonnes réponses. Il obtient pourtant 98,8 % de bonnes réponses. Une forte décroissance des poids remplace le surapprentissage par le sous-apprentissage, et la perte de validation montre ce compromis.
Vérification des connaissances
Section intitulée « Vérification des connaissances »Vérifiez votre compréhension
Entre l'époque 7 et l'époque 40, l'exactitude de validation du grand routeur baisse seulement de 97,8 % à 97,3 %. Sa perte de validation monte pourtant de 0,063 à 0,106. Expliquez comment les deux peuvent être vrais.
Afficher la réponse
L’exactitude compte seulement si l’équipe classée en premier est la bonne. La perte mesure aussi la probabilité que le routeur attribue à la bonne équipe. Après l’époque 7, le routeur devient de plus en plus sûr de chaque réponse, même des mauvaises. La probabilité moyenne de ses mauvaises réponses passe de 0,681 à 0,816. Les mauvaises réponses confiantes font augmenter rapidement la perte. Le nombre de mauvaises réponses change pourtant de seulement trois billets. Surveillez donc la perte pour repérer le surapprentissage.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Prédisez : les pertes de validation des époques 1 à 8 sont 0,20, 0,12, 0,09, 0,10, 0,08, 0,09, 0,11, 0,12. Avec une patience de 2, à quelle époque l'entraînement s'arrête-t-il, et de quelle époque conservez-vous les poids?
Afficher la réponse
La meilleure perte est de 0,09 à l’époque 3, puis de 0,08 à l’époque 5. L’époque 4 (0,10) était une époque sans nouveau minimum. Mais l’époque 5 a établi un nouveau minimum, donc le compte recommence. Les époques 6 (0,09) et 7 (0,11) sont deux époques sans nouveau minimum. L’entraînement s’arrête donc après l’époque 7, et vous conservez les poids de l’époque 5.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Grace veut plus de billets d'entraînement pour l'équipe de garantie. Tomás propose de copier chaque billet de garantie et de remplacer le nom du produit par un autre produit. Par exemple, remplacer bouilloire par hamac. Est-ce une augmentation sûre? Que vérifieriez-vous?
Afficher la réponse
Probablement sûre : selon la politique, l’équipe dépend de ce qui s’est passé (un défaut après utilisation), pas du produit. Donc, « la bouilloire a cessé de fonctionner après un mois » et « le hamac a cessé de fonctionner après un mois » relèvent tous deux de la garantie. Mais vérifiez un échantillon à la main : certains remplacements rendent un billet étrange, comme un hamac qui « cesse de chauffer ». Ne remplacez jamais les mots qui déterminent l’équipe, comme « arrivé brisé » par « brisé après utilisation », ou « colis » par « remboursement ».
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Récapitulatif
- Un grand réseau peut mémoriser ses billets d’entraînement. Avec 1 469 445 paramètres, il a atteint 100 % en entraînement, y compris les 70 mauvaises étiquettes. Pendant ce temps, la perte de validation est passée de 0,063 (époque 7) à 0,106 (époque 40).
- L’exactitude cache le surapprentissage; la perte le montre. Le routeur en surapprentissage est sûr de ses mauvaises réponses (0,816 contre 0,681). Une erreur confiante peut coûter une perte de 12,79 (−ln p).
- Détectez-le à partir des faits : l’écart (perte de validation − perte d’entraînement) augmente dès l’époque 2. La perte de validation continue pourtant de diminuer jusqu’à l’époque 7. Arrêtez quand la perte de validation remonte, pas selon l’écart; vérifiez avec plusieurs graines aléatoires.
- L’arrêt anticipé conserve l’époque ayant la plus faible perte de validation. Une patience de quelques époques permet d’ignorer une mauvaise époque. Le dropout désactive des neurones cachés au hasard pendant l’entraînement, pour qu’aucun neurone ne soit indispensable (meilleur résultat : 98,7 %). La décroissance des poids réduit un peu chaque poids à chaque étape; 0,05 a peu changé les résultats ici.
- L’augmentation des données est sûre seulement si l’étiquette reste vraie : une chemise retournée reste une chemise. Mais remplacer « colis » par « remboursement » change l’équipe.
- Mesurez avec
model.eval()ettorch.no_grad(): si le dropout reste actif, le même billet reçoit une réponse différente chaque fois.
Ensuite, une liste de vérification pour les cas où un entraînement échoue sans que vous sachiez pourquoi.
Leçon suivante : Déboguer l'expérience