Suivi des expériences : le carnet de laboratoire du ML

Trois jours plus tard, personne ne sait quel réglage a donné 0,93. Ce qu’il faut enregistrer pour que deux essais soient comparables, et quand MLflow devient utile.

7 min de lecturemachine-learningmlopsmlflowdata-science

Vous essayez trois modèles dans l’après-midi :

EssaiModèleRéglageF1
1forêt aléatoire100 arbres0,84
2forêt aléatoire300 arbres0,89
3XGBoosttaux d’apprentissage 0,050,93

Trois jours plus tard, quatre questions restent sans réponse : quel modèle était le meilleur, avec quels réglages exactement, sur quelles données, et mesuré comment ? Le suivi des expériences répond à ces quatre questions sans que vous ayez à y penser.

C’est le carnet de laboratoire du travail sur les modèles : ce qui a été tenté, dans quelles conditions, et ce que ça a donné.

Le vrai problème n’est pas la mémoire

On croit tenir un carnet pour se souvenir. En réalité, on le tient pour pouvoir comparer.

Reprenez le tableau ci-dessus et posez une seule question : les trois lignes sont-elles comparables ? Si l’essai 3 a tourné sur un découpage différent, ou sur les données après correction d’un bug de préparation, ou avec une définition de F1 moyennée autrement, alors le classement est une fiction. Le tableau paraît informatif et il induit en erreur — ce qui est pire que pas de tableau du tout.

Un suivi utile ne se contente donc pas d’enregistrer le score. Il enregistre de quoi prouver que deux lignes sont comparables.

Ce qu’il faut enregistrer

Le score et les hyperparamètres vont de soi. Voici les cinq que presque personne ne consigne, et dont l’absence rend une expérience irreproductible.

Le commit du code. Sans lui, « le modèle du 12 août » ne désigne rien : le code de préparation a changé depuis. C’est la ligne la moins coûteuse à écrire et celle qui sert le plus souvent.

La version des données. Pas « la table clients », mais une référence immuable : un instantané, une empreinte du fichier, ou au minimum les bornes de la période extraite. Deux entraînements sur « la table clients » à deux semaines d’écart ne portent pas sur les mêmes données.

La graine aléatoire. Sans elle, un écart entre deux essais peut n’être que du hasard, et vous ne pourrez pas le vérifier après coup.

Les versions des bibliothèques. Un changement de version mineure d’une bibliothèque d’arbres peut déplacer un score de plusieurs dixièmes de point. Le fichier de dépendances verrouillé suffit.

Le protocole d’évaluation. Quel découpage, combien de plis, quelle définition exacte de la métrique. C’est ce qui autorise la comparaison, et c’est précisément ce qu’on oublie parce qu’on croit que « c’est toujours le même ».

Et une sixième, qui change la façon de lire le tableau : la dispersion. Un F1 de 0,89 accompagné d’un écart-type de 0,03 entre les plis n’est pas distinguable d’un F1 de 0,91. Consigner le score moyen sans sa dispersion produit des classements où l’on choisit du bruit — le même piège que dans la comparaison entre un modèle candidat et celui en production.

Commencer sans outil

Vous n’avez pas besoin d’une plateforme le premier jour. Un fichier en ajout seul capte déjà l’essentiel :

python
import csv
import subprocess
from datetime import datetime, timezone

CHAMPS = [
    'date',
    'commit',
    'modele',
    'hyperparametres',
    'donnees',
    'protocole',
    'f1',
    'f1_ecart_type',
    'duree_s',
    'intention',
]


def journaliser(essai):
    essai['date'] = datetime.now(timezone.utc).isoformat()
    essai['commit'] = subprocess.run(
        ['git', 'rev-parse', '--short', 'HEAD'],
        capture_output=True,
        text=True,
        check=True,
    ).stdout.strip()

    with open('experiences.csv', 'a', newline='', encoding='utf-8') as fichier:
        csv.DictWriter(fichier, fieldnames=CHAMPS).writerow(essai)

Une quinzaine de lignes, et l’essentiel du bénéfice. Le dire n’est pas une provocation : la valeur du suivi vient de la discipline de consigner, pas de la sophistication de l’outil. Une équipe qui remplit consciencieusement un fichier va plus loin qu’une équipe équipée d’une plateforme qu’elle ne renseigne pas.

Ce que ce fichier ne fera pas : stocker les artefacts de modèles, afficher des courbes, comparer visuellement quarante essais, servir à plusieurs personnes en même temps. Quand ces besoins apparaissent, l’outil se justifie.

MLflow, en trois lignes utiles

python
import mlflow

mlflow.set_experiment('detection-fraude')

with mlflow.start_run(run_name='foret-300-arbres'):
    mlflow.log_params({'n_estimators': 300, 'max_depth': 10})
    mlflow.log_metrics({'f1': 0.89, 'f1_ecart_type': 0.012})
    mlflow.set_tag('intention', '300 arbres valent-ils mieux que 100 ?')
    mlflow.log_artifact('rapport_classification.txt')

Puis l’interface :

bash
mlflow ui

Le meilleur point de départ est en fait plus court, parce qu’il ne demande de ne rien oublier :

python
mlflow.sklearn.autolog()

Cette ligne enregistre d’elle-même les hyperparamètres, les métriques d’entraînement et le modèle lui-même à chaque appel de fit. Des équivalents existent pour les principales bibliothèques. Commencer par l’enregistrement automatique, puis ajouter à la main ce qui manque — la version des données, l’intention — donne le meilleur rapport entre effort et couverture.

L’opération complète sur un script existant, ligne par ligne et avec les pièges d’API, est déroulée dans instrumenter un script d’entraînement avec MLflow.

Comment suivre une recherche d’hyperparamètres sans noyer le journal ?

Avec des exécutions imbriquées : une exécution parente pour la recherche, une exécution enfant par configuration essayée.

python
with mlflow.start_run(run_name='recherche-aleatoire-60-essais'):
    for configuration in configurations:
        with mlflow.start_run(nested=True):
            mlflow.log_params(configuration)
            mlflow.log_metric('f1', evaluer(configuration))

Sans cette hiérarchie, soixante exécutions plates viennent se mêler aux essais réfléchis, et le journal devient illisible en une après-midi. L’exécution parente porte le résultat qui compte : la configuration retenue et le score obtenu.

Ce qu’il ne faut pas y mettre

Les données elles-mêmes. Un jeu de données attaché à chaque exécution fait exploser le stockage et ne prouve rien de plus qu’une empreinte. On enregistre une référence, pas une copie.

Des identifiants. L’adresse du serveur de suivi et ses accès n’ont pas leur place dans le code. Dans un cluster, ils passent par un Secret, et l’application les lit dans son environnement.

Tout, à chaque fois. Enregistrer l’artefact complet du modèle à chacun des soixante essais d’une recherche remplit un disque pour rien. Les artefacts lourds se justifient pour les essais qu’on garde.

La discipline qui rend le journal utile

Un suivi mal tenu se reconnaît à ses noms d’exécution : run_2026_08_12_143201, quatre-vingts fois. Techniquement complet, pratiquement inutilisable — parce que retrouver « l’essai où j’avais retiré la variable de région » demande d’ouvrir chaque entrée.

Trois habitudes changent tout :

Nommer par ce qui varie. foret-300-arbres, sans-variable-region, donnees-2026-t2. Le nom doit permettre de retrouver l’essai sans l’ouvrir.

Consigner l’intention. Une expérience répond à une question ; l’écrire dans une étiquette transforme un journal en raisonnement. Six mois plus tard, c’est cette phrase qui a de la valeur, pas le score.

Marquer le modèle de référence. Un essai étiqueté comme référence donne l’échelle de tous les autres. Sans lui, on ne sait pas si 0,93 est un exploit ou une déception.

Ce que ça débloque ensuite

Un journal d’expériences bien tenu n’est pas qu’un confort personnel. Il rend possibles trois choses qui n’ont rien d’évident autrement.

Répondre à « pourquoi ce modèle ? » — la question d’un auditeur, d’un client ou d’un collègue six mois plus tard. Sans journal, la réponse honnête est qu’on ne sait plus.

Détecter les fausses améliorations. En relisant vingt essais avec leur dispersion, on voit combien de « gains » tenaient dans le bruit. C’est une leçon d’humilité utile et un temps considérable épargné.

Passer du carnet au registre. Quand un essai devient le modèle servi en production, il change de statut : il lui faut un nom, une version, et un moyen de savoir lequel tourne actuellement. C’est le rôle du registre de modèles, et c’est la frontière entre le suivi des expériences et la mise en production — traitée dans MLOps et MLflow.

Ce qu’il faut retenir

Le suivi des expériences est le carnet de laboratoire : ce qui a été testé, avec quels réglages, pour quel résultat.

Mais sa vraie fonction est la comparabilité. Un score sans son protocole, sa version de données et sa dispersion n’est pas une mesure, c’est une anecdote. Enregistrez de quoi prouver que deux lignes se comparent, et le tableau devient un outil de décision plutôt qu’un classement rassurant.

Pour la suite : ce que vous réglez et ce que le modèle apprend, puisque ce sont ces réglages que le journal consigne, et quand réentraîner, qui est la question suivante une fois le modèle en service.

Continuer sur le même sujet