Monitoring de l'entraînement
Mis à jour le 28 juillet 2026
Pourquoi monitorer l’entraînement
Un job de fine-tuning lancé puis oublié vous laisse devant un fait accompli : au bout d’une heure, vous découvrez un modèle sans savoir ce qui s’est passé entre-temps. En observant les métriques pendant que l’entraînement tourne, vous détectez les problèmes tôt, vous décidez d’ajuster les hyperparamètres avant d’avoir consommé tout le budget, et surtout vous comprenez pourquoi tel run a mieux marché que tel autre. Le monitoring transforme une suite d’essais en démarche d’ingénierie.
Les deux métriques à lire
La training loss mesure à quel point le modèle se trompe sur les données d’entraînement, celles-là mêmes qu’il est en train d’ingérer. Elle doit diminuer au fil des étapes (steps) : c’est le signe que l’apprentissage a lieu. Une loss qui stagne ou remonte indique un problème, généralement un learning rate mal calibré ou un corpus trop hétérogène.
La validation loss mesure la performance sur des données que le modèle n’a jamais vues pendant l’entraînement — le fichier que vous avez mis de côté au moment du split. C’est la métrique la plus fiable pour évaluer la qualité réelle de votre modèle, parce qu’elle est la seule que le modèle ne peut pas améliorer en mémorisant. C’est leur évolution conjointe qui porte le diagnostic.
| Training loss | Validation loss | Diagnostic |
|---|---|---|
| Descend | Descend | Tout va bien, continuez |
| Descend | Remonte | Surapprentissage — réduisez les epochs |
| Stagne | Stagne | Sous-apprentissage — augmentez le learning rate |
| Oscille | Oscille | Instabilité — réduisez le learning rate |
La deuxième ligne est celle qui coûte le plus cher en production : une training loss qui continue de descendre pendant que la validation loss remonte décrit un modèle qui apprend vos exemples par cœur. Sur le papier, tout semble s’améliorer ; en réalité, chaque step supplémentaire dégrade sa capacité à répondre à une question qu’il n’a jamais vue.
Suivre les événements via l’API
L’API ne pousse pas de notifications : c’est à vous d’interroger le job à intervalle régulier. La boucle ci-dessous récupère les nouveaux événements, les affiche dans l’ordre chronologique en évitant de réafficher ceux déjà vus, et rend la main dès que le job atteint un état terminal.
from openai import OpenAI
import time
client = OpenAI()
def monitorer_job(job_id: str):
"""Monitore un job en temps réel."""
dernier_event = None
while True:
job = client.fine_tuning.jobs.retrieve(job_id)
# Récupérer les nouveaux événements
events = client.fine_tuning.jobs.list_events(
fine_tuning_job_id=job_id,
limit=50
)
for event in reversed(events.data):
if dernier_event and event.id == dernier_event:
continue
print(f"[{event.created_at}] {event.message}")
if events.data:
dernier_event = events.data[0].id
if job.status in ("succeeded", "failed", "cancelled"):
print(f"\nJob terminé : {job.status}")
if job.status == "succeeded":
print(f"Modèle : {job.fine_tuned_model}")
return job
time.sleep(30)
# Utilisation
job = monitorer_job("ftjob-xxxxxxxxxxxxxxxx")
Les événements ne se contentent pas de messages textuels : ils transportent aussi les valeurs de loss à chaque step. Les extraire vous permet de tracer une courbe plutôt que de lire des lignes de journal, et c’est sur une courbe que l’on repère une remontée de validation loss en un coup d’œil.
def extraire_metriques(job_id: str) -> dict:
"""Extrait les métriques de loss d'un job terminé."""
events = client.fine_tuning.jobs.list_events(
fine_tuning_job_id=job_id,
limit=200
)
training_losses = []
validation_losses = []
for event in events.data:
data = event.data
if data and "train_loss" in str(data):
training_losses.append(data)
if data and "valid_loss" in str(data):
validation_losses.append(data)
return {
"training": training_losses,
"validation": validation_losses
}
Checkpoints et résultats intermédiaires
OpenAI sauvegarde des checkpoints pendant l’entraînement. Si le job échoue ou si vous l’annulez, vous pouvez parfois récupérer un checkpoint intermédiaire utilisable — autrement dit, un run interrompu n’est pas systématiquement un run perdu. C’est aussi un recours précieux en cas de surapprentissage tardif : le checkpoint pris avant la remontée de la validation loss peut être meilleur que le modèle final.
# Lister les checkpoints d'un job
checkpoints = client.fine_tuning.jobs.checkpoints.list(
fine_tuning_job_id=job.id
)
for cp in checkpoints.data:
print(f"Step {cp.step_number}")
print(f" Modèle : {cp.fine_tuned_model_checkpoint}")
if cp.metrics:
print(f" Training loss : {cp.metrics.train_loss:.4f}")
if hasattr(cp.metrics, "valid_loss"):
print(f" Validation loss : {cp.metrics.valid_loss:.4f}")
Savoir arrêter
Trois signaux justifient d’interrompre un entraînement en cours. Le premier est une validation loss qui remonte depuis plusieurs steps, et non depuis un seul : une valeur isolée relève du bruit, une tendance sur cinq points relève du surapprentissage. Le deuxième est une training loss proche de zéro, qui signifie que le modèle reproduit vos exemples à l’identique et n’a plus rien à apprendre d’eux. Le troisième est plus prosaïque : vos tests ne s’améliorent plus d’un checkpoint à l’autre, et chaque step supplémentaire ne fait qu’ajouter à la facture.
# Si vous détectez un problème, annulez le job
client.fine_tuning.jobs.cancel(job.id)
print("Job annulé. Ajustez vos données ou hyperparamètres et relancez.")
Un rapport de synthèse par run
Terminez chaque entraînement en produisant une trace exploitable. La fonction suivante rassemble en une sortie l’identifiant du job, le modèle de base, les epochs utilisés, le nombre de tokens entraînés et les métriques du dernier checkpoint. Collez ces rapports dans votre carnet de runs : dans un mois, ils vous diront pourquoi vous avez retenu cette version-là plutôt qu’une autre.
def rapport_job(job_id: str):
"""Génère un rapport de synthèse du job."""
job = client.fine_tuning.jobs.retrieve(job_id)
print("=" * 50)
print(f"Job : {job.id}")
print(f"Modèle de base : {job.model}")
print(f"Statut : {job.status}")
print(f"Epochs : {job.hyperparameters.n_epochs}")
if job.status == "succeeded":
print(f"Modèle fine-tuné : {job.fine_tuned_model}")
print(f"Tokens entraînés : {job.trained_tokens}")
if job.status == "failed":
print(f"Erreur : {job.error}")
# Checkpoints
checkpoints = client.fine_tuning.jobs.checkpoints.list(
fine_tuning_job_id=job_id
)
if checkpoints.data:
dernier = checkpoints.data[0]
print(f"Dernier checkpoint : step {dernier.step_number}")
if dernier.metrics:
print(f" Train loss : {dernier.metrics.train_loss:.4f}")
print("=" * 50)
rapport_job("ftjob-xxxxxxxxxxxxxxxx")
Points clés à retenir
- Surveillez toujours la validation loss — c’est votre indicateur de qualité réel
- Si la validation loss remonte, vous surapprenez : réduisez les epochs
- Les checkpoints permettent de récupérer un état intermédiaire
- Automatisez le monitoring pour vos jobs longs
- Documentez les métriques de chaque run pour comparer vos itérations