Module 8 — Suivi de l'entraînement et arrêt au bon moment
Un affinage qui tourne pendant trois heures sans qu'on le regarde produit trop souvent un modèle inutilisable. Ce module explique quoi observer, comment lire ce qu'on observe, et quand arrêter — trois questions dont la réponse n'est jamais « quand la perte est basse ».
Deux courbes à afficher côte à côte
Le tableau de bord minimal d'un affinage tient en deux courbes tracées sur les mêmes axes : la perte d'entraînement et la perte de validation, en fonction du numéro de pas ou du numéro d'époque.
La perte d'entraînement décroît normalement de façon presque monotone. C'est ce que la descente de gradient est faite pour produire. Si elle stagne au tout début, c'est un problème d'échauffement ; si elle stagne au milieu, c'est un plafond de capacité ; si elle diverge, c'est le taux d'apprentissage.
La perte de validation est la seule qui compte vraiment. Elle mesure ce que le modèle sait sur des exemples qu'il n'a jamais vus, et c'est elle qui prédit la qualité en production. On la mesure toutes les 50 à 200 mises à jour, sur un sous-ensemble stable de la validation — 200 à 500 exemples suffisent, l'échantillon complet peut être trop lourd.
Trois motifs typiques :
Sain — les deux courbes descendent, l'écart entre elles reste faible. Le modèle apprend et généralise.
Surapprentissage — l'entraînement continue à baisser, la validation atteint un minimum puis remonte. Le modèle mémorise des cas particuliers. C'est le motif le plus fréquent sur un petit jeu comme nos deux mille paires. La bonne réaction : arrêter au minimum de la validation, pas au minimum de l'entraînement.
Blocage — les deux courbes stagnent après quelques centaines de pas. Deux hypothèses : capacité insuffisante (augmenter ) ou taux d'apprentissage trop bas (multiplier par trois et relancer).
Une vraie bonne surveillance : générer
Une perte de validation basse ne garantit pas une génération correcte. La perte mesure la vraisemblance jeton par jeton conditionnellement à la référence humaine ; elle passe à côté de dérives que seule la génération complète révèle — répétitions, format cassé, hallucinations sur les noms propres.
La contre-mesure est de générer à chaque évaluation deux ou trois exemples issus de la validation, et de les enregistrer.
from transformers import TrainerCallback
class GenererEchantillons(TrainerCallback):
def __init__(self, tokeniseur, exemples, n=3):
self.tokeniseur = tokeniseur
self.exemples = exemples[:n]
def on_evaluate(self, args, state, control, model=None, **kwargs):
model.eval()
for i, ex in enumerate(self.exemples):
entrees = self.tokeniseur.apply_chat_template(
ex["messages"][:-1], return_tensors="pt", add_generation_prompt=True
).to(model.device)
sortie = model.generate(entrees, max_new_tokens=400, do_sample=False)
texte = self.tokeniseur.decode(sortie[0], skip_special_tokens=True)
print(f"\n--- epoque {state.epoch:.2f} exemple {i} ---\n{texte[-800:]}")
model.train()
Trois exemples fixes, générés à chaque évaluation, forment un journal visuel de la trajectoire du modèle. On y voit apparaître les traces de format cassé bien avant qu'elles n'affectent la perte de validation, on y détecte les répétitions, et l'on peut arrêter dès qu'un régime clairement dégradé s'installe même si les chiffres ne le disent pas encore.
Points de contrôle et arrêt anticipé
Un point de contrôle (checkpoint) est une sauvegarde intermédiaire des poids et de l'état de l'optimiseur. La configuration typique du module 7 en écrit tous les 50 pas et garde les trois derniers pour économiser l'espace.
L'arrêt anticipé (early stopping) surveille la perte de validation et arrête l'entraînement dès qu'elle n'a plus baissé depuis un nombre fixé d'évaluations, dit patience.
from transformers import EarlyStoppingCallback
arret = EarlyStoppingCallback(
early_stopping_patience=3, # nb d'evaluations sans amelioration
early_stopping_threshold=0.001, # amelioration minimale a prendre en compte
)
Combiné à load_best_model_at_end=True dans les TrainingArguments, on récupère automatiquement les poids du meilleur point de contrôle — pas ceux du dernier, qui sont souvent moins bons quand un surapprentissage a démarré.
Détecter la divergence numérique
Le pire scénario d'entraînement n'est pas le surapprentissage, c'est la divergence : à un pas donné, la perte devient NaN ou Inf, et tout ce qui suit est corrompu. Trois causes reviennent presque toujours.
D'abord, un taux d'apprentissage trop élevé — la cause de loin la plus fréquente. Le signal apparaît en général dans les premiers 5 à 10 % des pas : la perte monte au lieu de descendre, puis explose. Diviser par trois et relancer.
Ensuite, un écrêtage de gradient absent ou trop laxiste. Une seule pointe de gradient sur un exemple pathologique peut suffire à casser les poids. max_grad_norm=1.0 du module 7 est presque toujours la bonne valeur.
Enfin, un exemple mal formé dans le jeu — chaîne vide, entr ée qui remplit toute la fenêtre au point qu'il ne reste aucun jeton pour la réponse. Filtrer les longueurs extrêmes à la préparation évite cette classe de bugs.
Le journal minimum à conserver
Le tableau de bord ci-dessus peut être perdu à la fin de l'entraînement si rien n'est sauvegardé. Le journal minimum à écrire dans le dossier de sortie :
- les deux courbes (perte d'entraînement, perte de validation) —
tensorboardouwandbs'en chargent ; - la configuration complète utilisée — un simple
training_args.jsonsauvegardé automatiquement ; - les exemples générés aux évaluations — utile pour comparer deux essais ;
- la liste des points de contrôle avec leur perte de validation — pour retrouver le meilleur si l'arrêt anticipé n'a pas trouvé le vrai minimum.
Trois heures d'entraînement sans journal, c'est trois heures qu'il faudra refaire à la moindre question sur ce qui s'est passé.
Un modèle avec une perte de validation légèrement supérieure mais qui génère un format parfaitement propre est presque toujours préférable à un modèle avec une perte plus basse mais des répétitions occasionnelles. La perte n'est qu'un proxy de ce que veut l'utilisateur, souvent excellent mais parfois trompeur. C'est pour cela que les échantillons générés sont un contre-pouvoir indispensable — et c'est aussi pour cela que le module 10 exige une évaluation humaine finale.
En résumé
- Deux courbes côte à côte : la perte d'entraînement décroît normalement, la perte de validation guide l'arrêt.
- Surapprentissage typique sur petit jeu : la validation atteint un minimum puis remonte, alors que l'entraînement continue à baisser.
- Générer deux ou trois exemples fixes à chaque évaluation révèle des dérives (format, répétitions) que la perte ne voit pas.
- Points de contrôle réguliers, arrêt anticipé avec patience,
load_best_model_at_end=Truepour récupérer le vrai meilleur. - Divergence numérique : presque toujours un taux d'apprentissage trop élevé ; diviser par trois et relancer.
Module suivant : fusionner les adaptateurs et exporter le modèle pour Ollama et llama.cpp.