Certains bogues dans votre code sont très simples. Vous essayez de l'exécuter,
vous obtenez une erreur de syntaxe quelque part, Python vous indique exactement où et vous la corrigez. C'est
génial : c'est simple et satisfaisant. Parfois, cependant, les choses se bloquent et l'erreur est impossible
à comprendre. Cela se produit souvent en apprentissage automatique pour plusieurs raisons : vous travaillez avec
des structures de données volumineuses, utilisez des bibliothèques volumineuses et complexes avec de nombreuses pièces mobiles, et vous faites également
beaucoup de calcul GPU qui sont en génrénal les plus durs à corriger. Dans Keras, il y a le problème supplémentaire que vos modèles sont souvent compilés
avant l'exécution, ce qui est excellent pour les performances mais rend leur débogage très difficile.
Ce sera une vidéo sur ce qu'il faut faire lorsque vous rencontrez l'un de ces bugs cauchemardesques et avez aucune idée de comment les déboguer.
Pour vous donner une idée des choses les plus courantes de ce qui peut mal tourner et où chercher la source des bogues que
vous rencontrez, utilisons cet exemple de script. Je vais vous le montrer ici en deux parties. Tout d'abord,
nous effectuons toutes nos importations, nous chargeons un jeu de données, nous créons notre tokenizer et nous tokenisons le jeu de données.
Ensuite, nous convertissons nos jeux de données en jeux de données TensorFlow, c'est le `to_tf_dataset`, afin de pouvoir leur appliquer `fit()`,
puis nous chargeons notre modèle à partir d'un checkpoint pré-entraîné, le compilons et pouvons appliquer `fit` sur le jeu de données. Cela semble
assez simple, similaire à ce qu'on a vu dans le cours avant, mais attention ! Ce code effrayant cache de nombreux secrets sombres et mystérieux.
Que se passe-t-il lorsque nous l'exécutons ? Eh bien, ce n'est pas génial. Nous obtenons ce message d'erreur. Qu'est-ce que cela signifie? Nous avons essayé de nous entraîner
sur nos données, mais nous n'avons pas obtenu de gradient ? C'est assez déroutant. Comment pouvons-nous même commencer à déboguer
quelque chose comme ça ? Lorsque l'erreur que vous obtenez ne suggère pas immédiatement où se situe le problème,
la meilleure solution consiste souvent à parcourir les éléments dans l'ordre, en s'assurant à chaque étape
que tout semble correct. Que tout est ok à ce point. Et bien sûr, le point de départ consiste toujours à vérifier vos données.
La meilleure façon de le faire est de récupérer un batch à partir de `tf.data.Dataset` sur lequel votre modèle s'entraîne,
juste à la fin du pipeline d'entraînement. Et si votre sortie est bonne, cela garanti que votre pipeline de données fonctionne bien. Et nous pouvons faire cela comme ça, en bouclant
le jeu de données pendant une itération, puis en interrompant. Alors, qu'obtenons-nous lorsque nous inspectons ce batch ?
Nous constatons que nous n'obtenons aucun gradient car nous ne transmettons pas d'étiquettes à Keras !
Nos étiquettes se trouvent dans le batch, mais il s'agit d'une clé dans le dictionnaire d'entrée, et non d'une étiquette distincte.
Il s'agit de l'un des problèmes les plus courants que vous rencontrerez lors de l'entraînement de vos transformers
avec TensorFlow. Nos modèles peuvent tous calculer la perte en interne, mais pour utiliser cette perte pour l'entraînement,
les étiquettes doivent être transmises dans le dictionnaire d'entrée, où le modèle peut les voir.
Cette perte interne est la perte que nous utilisons lorsque nous ne spécifions pas de valeur de perte à `compile()`.
Keras, d'autre part, s'attend généralement à ce que les étiquettes soient transmises séparément du
dictionnaire d'entrée et qu'elles ne soient pas visibles par le modèle, et les calculs de perte échouent généralement si vous
ne le faites pas. Nous devons choisir l'un ou l'autre. Soit nous utilisons la perte interne du modèle et conservons
les étiquettes là où elles se trouvent, soit nous continuons à utiliser les pertes de Keras, mais nous déplaçons les étiquettes à l'endroit où Keras les
attend. Pour plus de simplicité, utilisons les pertes internes du modèle, en supprimant l'argument
de perte lors de l'appel de `compile()`. Donc que se passe-t-il si nous essayons ré-entraînons. Et bien nous recompilons en appelant `model.fit()` à nouveau.
Que se passe t'il ? Eh bien, il fonctionne cette fois... mais maintenant nous obtenons une perte de `nan`. Ce n'est pas bon.
`nan` signifique « not a number » et ce n'est pas une bonne perte à avoir en général. En fait, si nous inspectons notre modèle maintenant, nous verrons que non seulement toutes
les sorties sont `nan` , mais que toutes les poids sont également `nan` comme pour la perte. Une fois qu'un seul `nan` se glisse dans vos calculs,
il a tendance à se propager, car il se propage depuis la perte jusqu'à votre gradient
puis dans les mises à jour de poids et donc à chaque mise à jour vous avez des `nan`. Donc `nan` détruit notre modèle. Mais où s'est-il glissé en premier ?
Pour le savoir, nous devons réinitialiser le modèle et examiner les résultats du premier batch uniquement.
Et lorsque nous faisons cela, nous voyons que `nan` apparaît d'abord dans la perte, mais seulement dans certains échantillons !
Vous pouvez voir cela plus en détail dans la section du cours qui nous accompagneme, je vais très vite ici.
Mais nous constatons que si nous regardons les étiquettes, les échantillons avec une perte de `nan` ont tous une étiquette de 2 !
Cela nous donne un indice très fort. Si nous vérifions le modèle, avec `model.config.num_labels`, nous voyons que
le modèle pense qu'il n'y a que 2 étiquettes, mais si nous voyons une valeur de 2, cela signifie qu'il y a au moins
3 étiquettes, car 0 c'est aussi une étiquette ! Nous avons donc perdu `nan` parce que nous avons obtenu une étiquette impossible dans notre jeu de données
Pour résoudre ce problème, nous devons revenir en arrière et configurer le modèle pour qu'il ait le bon nombre d'étiquettes.
Nous pouvons définir `num_labels=3` lorsque nous initialisons le modèle avec `from_pretrained`. Alors maintenant, nous pensons que nos
données sont bonnes et que notre modèle est bon, donc l'entraînement devrait fonctionner. Et si nous essayons d'exécuter `model.fit()`,
nous obtenons... hmm. Nous obtenons une perte, c'est un nombre, cela diminue, mais cela ne diminue pas très rapidement. Et si nous continuons,
nous constaterons que cela se fige à une valeur assez élevée. Que se passe-t-il ? Eh bien, lorsque les choses fonctionnent pour la
plupart mais que l'entraînement est simplement lent, cela peut être un bon moment pour examiner votre optimiseur
et les hyperparamètres d'entraînement. Et c'est là que je veux mentionner l'une des sources les plus courantes
de problèmes lorsque vous travaillez avec Keras. Vous pouvez nommer des choses comme des optimiseurs avec des chaînes, Keras supporte ça et c'est très utile,
mais si vous faites cela, toutes les options sont silencieusement définies sur leurs valeurs par défaut.
Nous avons donc spécifié Adam comme optimiseur, mais au cours du processus, nous avons obtenu de manière invisible le taux d'apprentissage par défaut,
qui est de 1e-3, soit 10 à la puissance moins 3. C'est bien trop élevé pour entraîner des transformers.
Nous devrions revenir en arrière et spécifier directement le taux d'apprentissage sans utiliser une chaîne. Les bonnes valeurs se situent entre 1e-5
et 1e-4. Séparons en deux et choisissons 5e-5. Et si vous recompilez avec cela,
vous constaterez que l'entraînement fonctionne enfin. La perte diminue efficacement et converge vers une valeur basse. Encore une fois, je suis passé ici assez rapidement, et je vous
recommande de consulter les notes de cours pour voir cela plus en détail et d'expérimenter
le code vous-même pour regarder à quoi l'erreur ressemble et comment l'approcher.
J'espère que cela vous a donné un bref résumé des bugs les plus fréquents et des approches de deboguage les plus fréquentes pour les résoudre. Bonne chance et n'oubliez pas de faire des pauses si votre code vous donne du fil à retordre !