Déboguer l'expérience
- Leçon 14
- 60 min
- Module 4 : Diagnostiquer les problèmes d'entraînement
Leçon 14 sur 23 · L'apprentissage profond et les transformeurs expliquésDéboguer l'expérience
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
Un entraînement peut produire un mauvais routeur sans aucun message d’erreur. Cherchez la cause dans un ordre fixe, de la vérification la moins coûteuse à la plus technique. Corrigez ensuite le problème. Montrez, avec les courbes ou les gradients, que la correction a fonctionné.
Carnet JupyterFaites cette leçon dans un carnet Jupyter
Le carnet (notebook) Jupyter contient tous les blocs de code, tâches et vérifications de cette leçon, ainsi que l’exercice du module. Il écrit le script d’entraînement défectueux de Tomás dans votre dossier. Vous pourrez l’exécuter, trouver les deux bogues et les corriger. Ses cellules de préparation téléchargent les billets de soutien et ticketnet.py, donc aucune configuration locale n’est nécessaire. Prévoyez environ 45 minutes; chaque exécution prend quelques secondes 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
- La rétropropagation comme transmission de la responsabilité vers l’arrière, et
.grad, dans La rétropropagation et autograd. - Les cinq étapes de la boucle d’entraînement, dans La boucle d’entraînement.
- Les trois formes de courbes et le taux d’apprentissage, dans Taux d’apprentissage et optimiseurs, ainsi que le surapprentissage et la perte de validation, dans Limiter le surapprentissage.
- La validation croisée et ses groupes, dans Données d’entraînement, de validation et de test, ainsi que les fuites de données, dans Ajuster les transformations correctement du cours SQL et préparation des données pour l’IA.
Ce qu’il vous faut sur votre ordinateur (seulement pour travailler localement; le carnet n’a besoin de rien de tout cela)
- Le dossier
ticket-routeravecdata/,ticketnet.pyet PyTorch, dans Installer PyTorch, et les tenseurs du premier bloc de code de Taux d’apprentissage et optimiseurs. data/split_manifest.json, la liste des étiquettes erronées introduites volontairement. Si votre dossierdatane le contient pas, téléchargez-le depuis le dépôt des exercices dansticket-router/data.- Pour l’exercice du module : le script de Tomás,
broken_train.py, enregistré dansticket-router.
Tous les nombres de cette page viennent d’exécutions réelles avec PyTorch 2.14.1 sur un processeur.
Un vélo qui roule mal
Section intitulée « Un vélo qui roule mal »Votre vélo est difficile à conduire. Vous pourriez commencer par démonter les engrenages, mais un mécanicien soigneux commence par les pneus. Ils se vérifient rapidement, et les pneus à plat sont fréquents. Ensuite, il vérifie la chaîne, puis les freins qui frottent sur la roue, et seulement après, les engrenages. Chaque vérification coûte peu et permet d’écarter une pièce. Cet ordre fait gagner du temps.
L’entraînement d’un réseau demande la même habitude. Quand une exécution échoue, elle affiche rarement une erreur. La perte est trop élevée, l’exactitude approche celle du hasard, ou une bonne exécution devient soudainement mauvaise. Un même symptôme peut avoir plusieurs causes : étiquettes erronées, entrées défectueuses, bogue dans le code, pas trop grands. Tomás change le taux d’apprentissage, ajoute une couche, puis réessaie. Parfois, cela fonctionne, et personne ne comprend pourquoi. Priya utilise une liste de vérification.
Le problème : un symptôme aux causes multiples
Section intitulée « Le problème : un symptôme aux causes multiples »Voici une exécution qui ressemble d’abord à du surapprentissage. Priya attribue à chaque billet d’entraînement l’équipe d’un autre billet d’entraînement choisi au hasard. Elle entraîne ensuite le routeur pendant 10 époques. Ce test montre à quoi ressemble une exécution avec des étiquettes inutiles.
g = torch.Generator().manual_seed(0)shuffled_labels = y_train[torch.randperm(len(y_train), generator=g)] # each ticket gets a random ticket's teamset_seed(0)model = BagOfWordsNet(len(vocab))history = train(model, x_train, shuffled_labels, x_valid, y_valid, epochs=10, log=None)for row in history[::3]: print(f"epoch {row['epoch']:>2}: train accuracy {row['train_acc']:.1%}, valid accuracy {row['valid_acc']:.1%}")La sortie :
epoch 1: train accuracy 28.4%, valid accuracy 25.5%epoch 4: train accuracy 52.0%, valid accuracy 24.8%epoch 7: train accuracy 68.7%, valid accuracy 20.7%epoch 10: train accuracy 80.0%, valid accuracy 19.2%- Exactitude d'entraînement
- Exactitude de validation
Description textuelle du graphique
Un graphique en courbes sur 10 époques, avec une exactitude de 0 % à 100 %. L’exactitude d’entraînement monte régulièrement de 28,4 % à 80,0 %. L’exactitude de validation commence à 25,5 % et descend lentement à 19,2 %. Cela approche les 20 % obtenus en choisissant au hasard parmi cinq équipes.
Le réseau mémorise 80 % des étiquettes sans valeur, tandis que l’exactitude de validation reste autour de 20 %, le niveau du hasard. Il n’y a rien à apprendre, donc il mémorise. Retenez cette image : l’entraînement progresse, la validation reste au niveau du hasard. Le surapprentissage lui ressemble, mais son exactitude de validation commence à un niveau élevé. Quand la validation ne dépasse jamais le hasard, soupçonnez les étiquettes ou le code avant d’ajouter du dropout (désactivation aléatoire de neurones).
La liste de vérification
Section intitulée « La liste de vérification »Priya suit cet ordre. Les premières vérifications coûtent peu et trouvent les bogues les plus courants; les suivantes demandent plus de connaissances.
Description textuelle du diagramme
La liste commence par « Une exécution échoue et vous ignorez pourquoi ». Vérification 1 : examinez les exemples et les étiquettes; affichez quelques billets avec leurs équipes. Si les étiquettes sont erronées ou décalées, corrigez les étiquettes ou le code qui les associe. Vérification 2 : vérifiez les entrées : formes, plages de valeurs, mots inconnus et vocabulaire construit seulement avec les billets d’entraînement. En cas de fuite, de mauvaise forme ou de valeurs énormes, corrigez la préparation. Vérification 3 : provoquez le surapprentissage d’un tout petit lot : le réseau peut-il atteindre 100 % sur 20 billets? Sinon, le bogue est dans le code. Vérification 4 : vérifiez les gradients, soit la norme du gradient de chaque couche. S’ils sont énormes ou nuls, réduisez le taux d’apprentissage; s’ils sont presque nuls dans les premières couches, changez l’activation. Quand les quatre vérifications réussissent, ajustez le taux d’apprentissage et les moyens de limiter le surapprentissage.
1. Examinez les exemples et les étiquettes
Section intitulée « 1. Examinez les exemples et les étiquettes »Affichez quelques billets d’entraînement à côté de leurs étiquettes, puis lisez-les. Cela prend une minute et révèle les pires bogues : étiquettes d’autres billets, textes vides, mauvaise colonne.
Le fichier contient aussi un problème plus discret. Comme dans un vrai service de soutien, 1,5 % des billets d’entraînement ont une mauvaise équipe : 70 billets. Vous ne pouvez pas lire 4 696 billets pour les trouver. Le réseau peut-il les signaler? Tomás a une idée : « Entraînons le réseau, puis regardons les billets d’entraînement qu’il classe mal. Ce sont les étiquettes suspectes. »
import jsonnoisy = set(json.load(open("data/split_manifest.json"))["noisy_train_ids"]) # the 70 planted wrong labelsset_seed(0)full = BagOfWordsNet(len(vocab))train(full, x_train, y_train, x_valid, y_valid, epochs=20, log=None) # trained on all training rowsfull.eval()with torch.no_grad(): wrong = (full(x_train).argmax(1) != y_train).nonzero().flatten().tolist()print("training tickets the network gets wrong:", len(wrong))print("of these, planted wrong labels:", sum(train_rows[i]["ticket_id"] in noisy for i in wrong))Pour ce cours, le fichier split_manifest.json contient les identifiants des 70 étiquettes erronées introduites volontairement. Vous pouvez ainsi vérifier toute méthode. Un vrai projet n’a pas cette liste.
La sortie :
training tickets the network gets wrong: 2of these, planted wrong labels: 2Le réseau classe mal seulement 2 billets d’entraînement; il ne trouve donc que 2 des 70 étiquettes erronées. Il a mémorisé les 68 autres, comme l’a montré la leçon précédente. Un réseau ne peut pas signaler ses étiquettes d’entraînement erronées s’il les a mémorisées.
La solution vient du cours d’apprentissage automatique : la validation croisée. Divisez les billets d’entraînement en 5 groupes. Entraînez 5 réseaux, chacun sur 4 groupes, puis faites évaluer à chacun le groupe qu’il n’a pas vu. Chaque billet reçoit alors une probabilité d’un réseau qui n’a jamais appris son étiquette. Ce sont des prédictions hors groupe. Une étiquette qu’un tel réseau juge très improbable est suspecte.
order = torch.randperm(len(train_rows), generator=torch.Generator().manual_seed(0))probs = torch.zeros(len(train_rows), len(TEAMS))for k in range(5): # 5 folds held_out = order[k::5] # every 5th ticket: this fold kept = torch.tensor(sorted(set(order.tolist()) - set(held_out.tolist()))) set_seed(k) model = BagOfWordsNet(len(vocab)) train(model, x_train[kept], y_train[kept], x_train[held_out], y_train[held_out], epochs=8, log=None) model.eval() with torch.no_grad(): probs[held_out] = model(x_train[held_out]).softmax(1) # scored by a model that never saw themp_given = probs[torch.arange(len(train_rows)), y_train] # the probability of the label in the filesuspects = p_given.argsort()[:50].tolist() # the 50 least likely labelsfor i in suspects[:3]: r = train_rows[i] print(r["ticket_id"], "label:", r["team"], " network:", TEAMS[int(probs[i].argmax())], f" p(label) {float(p_given[i]):.3f} ", r["text"][:60] + "...")La sortie :
T-60623 label: warranty network: delivery p(label) 0.000 I had a very helpful chat with my neighbour last week, and s...T-60552 label: payment network: returns p(label) 0.000 I got the wrong item for my log store order LK-160935. The b...T-62277 label: warranty network: delivery p(label) 0.000 Hi there! So I was really looking forward to getting my hamm...Le premier billet suspect parle d’un colis laissé par le livreur à la barrière latérale : livraison, pas garantie. Le deuxième concerne un mauvais article à renvoyer : retours, pas paiement. Combien des 50 étiquettes suspectes sont des erreurs introduites volontairement?
print("planted wrong labels:", len(noisy))print("among the 50 suspects:", sum(train_rows[i]["ticket_id"] in noisy for i in suspects))print("among the 100 lowest:", sum(train_rows[i]["ticket_id"] in noisy for i in p_given.argsort()[:100].tolist()))La sortie :
planted wrong labels: 70among the 50 suspects: 49among the 100 lowest: 69Description textuelle du graphique
Un graphique à deux colonnes, sur un axe de 0 à 70. Les erreurs d’entraînement du réseau entraîné sur tous les billets contiennent 2 des 70 étiquettes erronées introduites volontairement. Les 50 étiquettes ayant la plus faible probabilité hors groupe en contiennent 49.
Parmi les 50 étiquettes suspectes, 49 sont des erreurs introduites volontairement. Les 100 étiquettes les moins probables en contiennent 69 sur 70. Le même réseau, utilisé autrement, est passé de 2 à 49. La règle : jugez une étiquette seulement avec un modèle qui ne l’a pas apprise.
Que devrait faire Priya avec les étiquettes suspectes? Pas les supprimer automatiquement. Grace les lit et corrige celles qui sont erronées. Les réseaux hors groupe ne sont pas d’accord avec 180 étiquettes d’entraînement au total. La plupart correspondent à des billets difficiles, pas à des étiquettes erronées. Une personne doit donc décider.
2. Vérifiez les entrées
Section intitulée « 2. Vérifiez les entrées »Examinez ensuite ce que reçoit le réseau. Affichez la forme et la plage des valeurs :
print("input shape:", tuple(x_train.shape), " values from", int(x_train.min()), "to", int(x_train.max()))print("after log1p, the network sees values up to", round(float(torch.log1p(x_train).max()), 2))La sortie :
input shape: (4696, 2864) values from 0 to 16after log1p, the network sees values up to 2.83Un billet contient le même mot 16 fois. Le BagOfWordsNet du cours applique d’abord log1p aux fréquences : le logarithme de 1 + la fréquence. Ainsi, 16 devient 2,83, et un mot répété ne pèse pas 16 fois plus que les autres. Un réseau devient instable quand certaines entrées sont des centaines de fois plus grandes que d’autres. Vérifiez donc la plage chaque fois que vous préparez une nouvelle entrée.
Vérifiez ensuite les fuites de données : des informations des billets de validation ou de test qui ont atteint l’entraînement. Ajuster les transformations correctement, dans le cours SQL et préparation des données pour l’IA, mesurait comment une fuite gonfle un score. Dans un réseau de traitement de texte, le vocabulaire est une étape qui apprend à partir des données. Tomás le construit avec les billets d’entraînement et de validation, « pour avoir plus de mots » :
def unknown_share(rows, vocab): ids = [t for r in rows for t in vocab.encode(r["text"])] return sum(t == 1 for t in ids) / len(ids) # ID 1 is <unk>
leaky = Vocab([r["text"] for r in train_rows + valid_rows]) # the mistake: valid words in the vocabularyprint("train only: ", len(vocab), "words,", f"{unknown_share(valid_rows, vocab):.1%} of valid tokens unknown")print("train + valid:", len(leaky), "words,", f"{unknown_share(valid_rows, leaky):.1%} of valid tokens unknown")La sortie :
train only: 2864 words, 1.2% of valid tokens unknowntrain + valid: 2987 words, 0.8% of valid tokens unknownvocab.encode() transforme un texte en positions de ses jetons dans le vocabulaire, comme stoi dans La propagation avant. Le vocabulaire avec fuite contient 123 mots provenant seulement des billets de validation. Moins de mots de validation sont donc inconnus. Les nouveaux billets de demain n’auront pas cette aide. Ici, l’effet sur le score est faible, mais cela reste une fuite qu’aucune courbe ne montre. Pour la trouver, vérifiez comment chaque entrée a été construite. Toute étape qui apprend à partir des données utilise seulement les billets d’entraînement.
3. Provoquez le surapprentissage d’un tout petit lot
Section intitulée « 3. Provoquez le surapprentissage d’un tout petit lot »Avant un long voyage, vous faites le tour du pâté de maisons en voiture. Si la voiture n’y arrive pas, elle ne fera pas le voyage non plus. Il en va de même pour un réseau. Prenez 20 billets d’entraînement et entraînez le réseau seulement sur eux, plusieurs fois. Un réseau et une boucle d’entraînement qui fonctionnent doivent mémoriser 20 billets :
set_seed(0)tiny_model = BagOfWordsNet(len(vocab))tiny = torch.arange(20) # the first 20 training ticketstiny_history = train(tiny_model, x_train[tiny], y_train[tiny], x_train[tiny], y_train[tiny], epochs=60, batch_size=20, lr=0.01, log=None)for row in tiny_history[::10] + [tiny_history[-1]]: print(f"step {row['epoch']:>2}: loss {row['train_loss']:.4f}, accuracy {row['train_acc']:.0%}")La sortie :
step 1: loss 1.3483, accuracy 80%step 11: loss 0.0256, accuracy 100%step 21: loss 0.0007, accuracy 100%step 31: loss 0.0001, accuracy 100%step 41: loss 0.0000, accuracy 100%step 51: loss 0.0000, accuracy 100%step 60: loss 0.0000, accuracy 100%Le lot contient les 20 billets, donc chaque époque correspond à un pas. La sortie affiche un pas sur dix. Dès le pas 5, le réseau classe correctement les 20 billets, et la perte tombe à 0,0000. Le code peut apprendre. Si cette vérification échoue, aucune quantité de données ni aucun ajustement ne vous aidera. Cherchez le bogue dans le modèle, la perte, l’optimiseur ou le code qui associe les billets aux étiquettes.
La mémorisation était un mauvais résultat dans la leçon précédente; ici, c’est un bon résultat. Sur 20 billets, elle prouve que le réseau et la boucle d’entraînement fonctionnent.
4. Vérifiez les gradients
Section intitulée « 4. Vérifiez les gradients »Les gradients transmettent la responsabilité vers l’arrière dans le réseau, comme dans La rétropropagation et autograd. Leur taille devrait être raisonnable dans chaque couche. Mesurez la norme du gradient de chaque couche : un nombre qui représente la taille de tous les gradients d’une couche. C’est la racine carrée de la somme de leurs carrés. Voyez-la comme la longueur de la flèche qui indique « allez dans cette direction ».
Explosion des gradients. Avec un taux d’apprentissage beaucoup trop élevé, un seul pas déplace les poids très loin. La perte suivante devient alors énorme, tout comme les gradients suivants. Voici Adam avec un taux d’apprentissage de 10, soit 10 000 fois sa valeur par défaut, pendant quatre pas :
set_seed(0)model = BagOfWordsNet(len(vocab))optimizer = torch.optim.Adam(model.parameters(), lr=10) # 10 000 times the defaultloss_fn = nn.CrossEntropyLoss()for step in range(4): batch = torch.arange(step * 64, step * 64 + 64) optimizer.zero_grad() loss = loss_fn(model(x_train[batch]), y_train[batch]) loss.backward() print(f"step {step}: loss {loss.item():10.3f} gradient norm, first layer {model.layers[0].weight.grad.norm().item():8.3f}") optimizer.step()La sortie :
step 0: loss 1.616 gradient norm, first layer 0.354step 1: loss 33688.914 gradient norm, first layer 209.409step 2: loss 24560.604 gradient norm, first layer 121.913step 3: loss 19077.770 gradient norm, first layer 101.325Après un pas, la perte passe de 1,616 à 33 689, et la norme du gradient augmente presque 600 fois. Dans certains réseaux, les nombres augmentent jusqu’à devenir nan (« pas un nombre »); ici, ils restent énormes. La correction : réduisez le taux d’apprentissage. Certains codes d’entraînement limitent aussi la norme du gradient avec torch.nn.utils.clip_grad_norm_. Ainsi, un mauvais lot ne peut pas déplacer les poids très loin.
Disparition des gradients. Le problème inverse survient quand les gradients deviennent minuscules dans les premières couches, qui apprennent alors à peine. Priya construit un empilement profond de 10 couches linéaires, avec une activation après chacune des 9 premières. Elle mesure la norme du gradient de chaque couche après une propagation arrière. Elle essaie la sigmoïde du cours d’apprentissage automatique et la ReLU de ce cours :
def gradient_norms(activation): """A stack of 10 linear layers; one backward pass; the gradient size of each layer.""" set_seed(0) layers = [nn.Linear(len(vocab), 64), activation()] for _ in range(8): layers += [nn.Linear(64, 64), activation()] stack = nn.Sequential(*layers, nn.Linear(64, 5)) loss = nn.CrossEntropyLoss()(stack(torch.log1p(x_train[:256])), y_train[:256]) loss.backward() return [layer.weight.grad.norm().item() for layer in stack if isinstance(layer, nn.Linear)]
for name, activation in [("sigmoid", nn.Sigmoid), ("ReLU", nn.ReLU)]: norms = gradient_norms(activation) print(f"{name:<8} first layer {norms[0]:.2e} last layer {norms[-1]:.2e}")La sortie :
sigmoid first layer 1.45e-08 last layer 6.24e-01ReLU first layer 1.31e-04 last layer 2.05e-021.45e-08 signifie 1,45 × 10⁻⁸, soit 0,0000000145. Avec la sigmoïde, le gradient de la première couche est environ 40 millions de fois plus petit que celui de la dernière. Le graphique montre chaque couche :
- Sigmoïde
- ReLU
Description textuelle du graphique
Un graphique en courbes de la norme du gradient des couches 1 à 10, sur une échelle logarithmique de 10⁻⁸ à 1. La courbe de la sigmoïde descend presque en ligne droite, de 0,62 à la couche 10 à 0,0000000145 à la couche 1. C’est environ 7 fois plus petit par couche. La courbe de ReLU descend de 0,021 à la couche 10 à environ 0,00002 à la couche 2. Elle vaut 0,00013 à la couche 1. Elle diminue aussi, mais le gradient de la première couche est environ 9 000 fois plus grand qu’avec la sigmoïde.
Pourquoi la sigmoïde fait-elle cela? Sa pente ne dépasse jamais 0,25. La rétropropagation multiplie par cette pente à chaque couche en revenant vers l’entrée. Après 9 sigmoïdes, la responsabilité transmise a donc presque disparu. Les premières couches bougent alors à peine. ReLU a une pente de 1 pour toute entrée positive, donc elle perd beaucoup moins. C’est une raison pour laquelle les réseaux profonds utilisent des activations semblables à ReLU. Le module 6 ajoute deux autres protections : les connexions résiduelles et la normalisation, dans chaque bloc du transformeur.
Quand examiner les gradients? Quand la perte bondit ou devient nan, ou quand elle bouge à peine malgré la réussite des vérifications 1 à 3. Affichez la norme de chaque couche pendant quelques pas. Une valeur énorme, nulle ou des millions de fois plus petite que ses voisines indique le problème.
Erreur courante
Ajuster avant de vérifier
Symptôme : le routeur de Tomás reste près de 20 % sur les données de validation. Il essaie cinq taux d’apprentissage, ajoute du dropout et une deuxième couche cachée, et y passe une journée. Rien n’aide.
Cause : les étiquettes de son tenseur d’entraînement appartenaient à d’autres billets. Avec de telles étiquettes, aucun taux d’apprentissage ni aucune architecture ne peut fonctionner. L’exécution ressemble au graphique des étiquettes mélangées ci-dessus : l’exactitude d’entraînement augmente, et l’exactitude de validation reste au niveau du hasard.
Correction : suivez la liste dans l’ordre avant d’ajuster quoi que ce soit. Affichez cinq billets avec leurs étiquettes (vérification 1). Vérifiez ensuite les entrées, provoquez le surapprentissage sur 20 billets, puis examinez les gradients. Chaque vérification prend quelques minutes.
Ce que cela signifie pour un vrai service d’IA : un modèle « difficile à entraîner » cache souvent un bogue de données ou de code. Les ajustements masquent le bogue pendant un temps, puis le bogue est livré avec le modèle.
Exercices du module
Section intitulée « Exercices du module »Corrigez maintenant un vrai problème avec la liste de vérification. Les exemples détaillés ci-dessus montraient chaque vérification et ses résultats intermédiaires; ici, vous les utilisez vous-même.
Tomás a écrit broken_train.py. Ce script entraîne le routeur à sac de mots pendant 8 époques avec Adam. Après chaque époque, il affiche les pertes, les exactitudes et la norme du gradient de la première couche. Il s’exécute sans erreur, mais le routeur fonctionne mal. Le script contient deux bogues.
- Obtenez le script. Dans le carnet (notebook) Jupyter, exécutez la cellule qui écrit
broken_train.py. Sur votre ordinateur, enregistrezbroken_train.pydans votre dossierticket-router. - Exécutez-le :
python broken_train.py(dans le carnet :%run broken_train.py). Cela prend environ 5 secondes. - Lisez la sortie en vous appuyant sur les formes des courbes et les tailles des gradients de ce module. Notez le symptôme et la vérification de la liste qu’il indique.
- Corrigez un bogue, exécutez le script de nouveau et comparez la nouvelle sortie à l’ancienne.
- Répétez les étapes 3 et 4 jusqu’à ce que le routeur atteigne environ 98 % d’exactitude sur les données de validation.
- Rédigez une fiche de diagnostic : pour chaque bogue, notez le symptôme, les preuves (nombres de la sortie), la cause, la correction et les preuves après correction.
Vous devriez voir cette sortie à l’étape 2 :
epoch 1: train loss 77.472 acc 21.4% | valid loss 21.518 acc 21.3% | gradient norm 8.843epoch 2: train loss 40.332 acc 17.2% | valid loss 13.111 acc 17.0% | gradient norm 9.303epoch 3: train loss 2.642 acc 27.4% | valid loss 2.148 acc 27.5% | gradient norm 0.000epoch 4: train loss 1.866 acc 27.4% | valid loss 2.065 acc 27.5% | gradient norm 0.000epoch 5: train loss 1.735 acc 14.5% | valid loss 1.938 acc 14.7% | gradient norm 0.000epoch 6: train loss 1.605 acc 27.4% | valid loss 1.811 acc 27.5% | gradient norm 0.000epoch 7: train loss 1.726 acc 21.4% | valid loss 1.933 acc 21.0% | gradient norm 0.000epoch 8: train loss 1.893 acc 20.1% | valid loss 2.094 acc 20.0% | gradient norm 0.000Afficher le corrigé
Bogue 1, trouvé en premier : le taux d’apprentissage. La première sortie montre une perte de 77,5 après une époque, bien supérieure à 1,61, la perte obtenue au hasard. Ensuite, la norme du gradient reste à 0,000 dès l’époque 3 : plus rien ne peut apprendre. Dès l’époque 3, l’exactitude de validation correspond toujours à la proportion d’une seule équipe : 27,5 % livraison, 14,7 % compte, 21,0 % paiement, 20,0 % retours. Le routeur répond donc la même équipe pour chaque billet de soutien. Le script définit LEARNING_RATE = 10 pour Adam : 10 000 fois la valeur par défaut. Les premiers pas énormes ont poussé 56 des 64 neurones cachés dans un état où ReLU produit 0 pour chaque billet d’entraînement. Ces neurones ne reçoivent aucun gradient, et la norme du gradient de la première couche s’arrondit à 0,000. Correction : LEARNING_RATE = 0.001. La sortie après cette correction :
epoch 1: train loss 1.559 acc 27.6% | valid loss 1.587 acc 27.0% | gradient norm 0.363epoch 2: train loss 1.501 acc 35.1% | valid loss 1.590 acc 23.0% | gradient norm 0.578epoch 3: train loss 1.410 acc 39.9% | valid loss 1.617 acc 23.8% | gradient norm 0.658epoch 4: train loss 1.296 acc 54.0% | valid loss 1.662 acc 21.2% | gradient norm 1.110epoch 5: train loss 1.176 acc 60.1% | valid loss 1.705 acc 20.2% | gradient norm 1.017epoch 6: train loss 1.071 acc 64.7% | valid loss 1.794 acc 20.5% | gradient norm 1.213epoch 7: train loss 0.957 acc 69.4% | valid loss 1.829 acc 20.8% | gradient norm 1.461epoch 8: train loss 0.866 acc 73.6% | valid loss 1.914 acc 20.7% | gradient norm 1.513Les gradients ont maintenant des tailles normales (de 0,36 à 1,5), et la perte d’entraînement diminue. Cette correction a fonctionné. Mais le routeur fonctionne encore mal.
Bogue 2 : les étiquettes ne correspondent pas à leurs billets. L’exactitude d’entraînement monte à 73,6 %. Pendant ce temps, l’exactitude de validation reste près de 20 %, et la perte de validation passe de 1,587 à 1,914. C’est le profil d’étiquettes mélangées : le réseau mémorise des étiquettes qui n’ont aucun sens. La vérification 1 dit : examinez les exemples et les étiquettes. Ajoutez ces lignes après le mélange : elles affichent les cinq premiers billets avec leurs étiquettes.
for i in range(5): # look at the first examples and their labels print(TEAMS[int(y_train[i])], "|", train_rows[int(mix[i])]["text"][:70])La sortie (ajoutez d’abord TEAMS à la ligne d’importation) :
payment | Hi there, I sent my barbecue grill back a while ago and the return wasreturns | Hi there, I bought the garden hose reel about four months ago and it wwarranty | Hi, I tried the discount code for my compost bin order LK-308764 but tdelivery | Did you get my pressure washer return?account | Hi, I'm really anxious about this. I sent my barbecue grill back lastUn dévidoir acheté il y a quatre mois relève de la garantie, pas des retours. Un code de réduction relève du paiement, pas de la garantie. « Avez-vous reçu mon retour? » relève des retours, pas de la livraison. Le script mélange les billets avec x_train = x_train[mix], mais pas les étiquettes. Correction : ajoutez y_train = y_train[mix] à la ligne suivante pour que chaque étiquette se déplace avec son billet. La sortie après les deux corrections :
epoch 1: train loss 0.536 acc 96.4% | valid loss 0.559 acc 97.7% | gradient norm 0.601epoch 2: train loss 0.209 acc 97.4% | valid loss 0.212 acc 98.2% | gradient norm 0.812epoch 3: train loss 0.136 acc 98.0% | valid loss 0.139 acc 98.5% | gradient norm 0.202epoch 4: train loss 0.100 acc 98.4% | valid loss 0.110 acc 98.5% | gradient norm 0.170epoch 5: train loss 0.078 acc 98.7% | valid loss 0.095 acc 98.2% | gradient norm 0.108epoch 6: train loss 0.061 acc 98.8% | valid loss 0.082 acc 98.3% | gradient norm 0.391epoch 7: train loss 0.048 acc 99.2% | valid loss 0.076 acc 98.8% | gradient norm 0.437epoch 8: train loss 0.040 acc 99.3% | valid loss 0.083 acc 98.0% | gradient norm 0.085Les deux pertes diminuent ensemble jusqu’à environ 0,08. L’exactitude de validation atteint 97,7 % après une époque et 98,0 % après huit, et les gradients restent entre 0,09 et 0,81. La perte de validation est la plus faible à l’époque 7; l’arrêt précoce conserverait donc l’époque 7 (98,8 %).
La fiche de diagnostic :
| Bogue 1 | Bogue 2 | |
|---|---|---|
| Symptôme | Perte de 77,5, exactitude égale à la proportion d’une seule équipe | L’exactitude d’entraînement augmente, celle de validation reste près de 20 % |
| Preuves | Norme du gradient de 8,8, puis de 0,000 dès l’époque 3 | La perte de validation passe de 1,587 à 1,914; les étiquettes affichées correspondent à d’autres billets |
| Cause | LEARNING_RATE = 10 pour Adam |
Billets mélangés avec mix, mais pas les étiquettes |
| Correction | LEARNING_RATE = 0.001 |
y_train = y_train[mix] |
| Preuves après correction | Normes des gradients de 0,36 à 1,5; la perte d’entraînement diminue | Perte de validation de 0,559 à 0,076; 98,0 % après 8 époques |
Le script corrigé est fixed_train.py. Vous avez peut-être trouvé les bogues dans l’ordre inverse; c’est correct si vos preuves appuient chaque correction.
Vérification des connaissances du module
Section intitulée « Vérification des connaissances du module »Vérifiez votre compréhension
Nommez les quatre vérifications de la liste de débogage, dans l'ordre. Dites pourquoi la vérification sur un tout petit lot précède celle des gradients.
Afficher la réponse
- Examinez les exemples et les étiquettes. 2. Vérifiez les entrées (formes, plages, fuite de données). 3. Faites surapprendre un tout petit lot. 4. Vérifiez les gradients. L’ordre va des vérifications simples qui trouvent les bogues courants aux vérifications plus techniques. La vérification sur un tout petit lot demande une courte exécution. Elle teste à la fois le réseau, la perte, l’optimiseur et l’association des étiquettes. Si le réseau ne peut pas apprendre 20 billets, le bogue est dans le code. Les détails des gradients comptent seulement après cela.
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 : l'exactitude d'entraînement d'un routeur passe de 30 % à 85 % en 10 époques, mais son exactitude de validation reste entre 18 % et 22 %. Est-ce du surapprentissage? Que vérifiez-vous en premier?
Afficher la réponse
Ce n’est probablement pas du surapprentissage ordinaire : un réseau en surapprentissage obtient d’abord un bon résultat de validation, qui se dégrade ensuite. Ici, la validation reste au niveau du hasard (20 % pour cinq équipes) dès le départ, tandis que l’exactitude d’entraînement augmente. Cela signifie que le réseau mémorise des étiquettes qui ne correspondent pas aux entrées. Commencez par la vérification 1 : affichez quelques billets d’entraînement avec leurs étiquettes. Les étiquettes sont incorrectes ou décalées, par exemple après un mélange des entrées sans les étiquettes.
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
Vérification finale : Tomás a corrigé broken_train.py en changeant seulement le taux d'apprentissage de 10 à 0,001. Il dit que tout est corrigé, car la perte diminue maintenant. Utilisez des preuves tirées des courbes ou des gradients pour dire s'il a raison et quelle correction reste nécessaire.
Afficher la réponse
Il a corrigé un bogue, et les gradients le montrent. Avec un taux d’apprentissage de 10, la norme du gradient restait à 0,000 dès l’époque 3. Maintenant, elle se situe entre 0,36 et 1,5 : le réseau apprend donc. Mais la baisse de la perte d’entraînement ne suffit pas. La courbe de validation montre le deuxième bogue. L’exactitude de validation reste près de 20 % (20,7 % à l’époque 8), et la perte de validation passe de 1,587 à 1,914. Pendant ce temps, l’exactitude d’entraînement monte à 73,6 %. C’est le profil d’étiquettes qui ne correspondent pas à leurs billets. Les étiquettes doivent se déplacer avec les billets : y_train = y_train[mix]. Après cette correction, la perte de validation descend à 0,076 et l’exactitude atteint 98,0 %. C’est la preuve que la correction a fonctionné.
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
- Quand une exécution échoue sans erreur apparente, vérifiez dans l’ordre : exemples et étiquettes, entrées, tout petit lot, gradients. Ajustez les paramètres seulement après avoir réussi les quatre vérifications.
- Évaluez une étiquette seulement avec un modèle qui ne l’a pas apprise. Le réseau entraîné sur toutes les lignes a trouvé 2 des 70 étiquettes incorrectes. Les prédictions hors pli (out-of-fold, sur des données exclues de l’entraînement) en ont placé 49 parmi les 50 cas suspects.
- Une exactitude d’entraînement qui augmente alors que la validation reste au niveau du hasard indique des étiquettes sans sens : mélangées, décalées ou incorrectes.
- Un vocabulaire construit avec des billets de validation constitue une fuite de données; aucune courbe ne la montre. Vérifiez donc comment chaque entrée a été construite.
- Si le réseau ne peut pas apprendre 20 billets par cœur, le bogue est dans le code.
- Des gradients qui explosent (une perte de 33 689 avec un taux d’apprentissage de 10) demandent un taux d’apprentissage plus faible. Des gradients qui disparaissent (1,45 × 10⁻⁸ dans la première couche d’un empilement profond de couches sigmoïdes) demandent ReLU et les protections du module 6.
Ce module, en bref : vous pouvez maintenant lire les courbes d’entraînement et choisir un taux d’apprentissage et un optimiseur. Vous pouvez limiter le surapprentissage et trouver la cause d’une exécution ratée à l’aide de preuves.
Réinitialisation. Pour recommencer l’exercice, obtenez une nouvelle copie de broken_train.py. Exécutez de nouveau la cellule qui l’écrit dans le carnet, ou téléchargez-le de nouveau depuis le dépôt des exercices. Si ticketnet.py ou un fichier de données a changé par erreur, supprimez le dossier ticket-router. Exécutez ensuite de nouveau la cellule de configuration du carnet. Sur votre ordinateur, téléchargez-les de nouveau comme dans Installer PyTorch. Dans le carnet, utilisez Runtime › Restart session (exécution › redémarrer la session) dans Colab ou Run › Restart & clear cell outputs (exécuter › redémarrer et effacer les sorties des cellules) dans Kaggle avant d’exécuter de nouveau toutes les cellules.
Ensuite, le module 5. Le sac de mots ne voit pas l’ordre des mots. Le prochain module donne donc à chaque mot un sens appris, puis permet aux mots de s’observer entre eux.
Leçon suivante : Jetons et plongements