“Ça marche sur ma machine” est la phrase qui hante tout projet de données ou de machine learning. L’impossibilité de reproduire un résultat entre l’environnement d’un développeur et la production est une source majeure d’erreurs et de perte de temps. La reproductibilité n’est pas une option, c’est la base de la confiance. Pour l’atteindre, il faut systématiquement figer l’environnement, contrôler les sources d’aléatoire et travailler avec des jeux de données de test stables.
le problème vs la solution
Sans environnement contrôlé, les résultats sont imprévisibles. Avec une image immuable, le comportement est identique partout.
prérequis
- Un système de gestion de versions (Git) pour le code.
- L’utilisation de conteneurs (Docker) pour encapsuler les environnements.
- Des jeux de données d’exemple versionnés pour les tests.
idées clefs
La reproductibilité repose sur trois piliers fondamentaux.
pas à pas
étape 1: figer l’environnement avec une image immuable
L’environnement (système d’exploitation, librairies, dépendances) doit être identique partout. Un Dockerfile est la meilleure façon de le décrire et de le construire.
# Dockerfile
FROM python:3.9-slim
WORKDIR /app
# Copier le fichier des dépendances
COPY requirements.txt .
# Installer les versions exactes des librairies
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "src/main.py"]
Ensuite, on construit une image unique qui sera utilisée en développement, en test et en production.
# Construire l'image avec un tag unique basé sur le hash du commit
docker build -t mon-application:sha-abcdef1 .
étape 2: contrôler l’aléatoire avec des seeds
De nombreuses opérations en data science (initialisation de poids d’un modèle, division d’un jeu de données) sont aléatoires. Pour garantir que le résultat soit le même à chaque exécution, il faut fixer la “graine” (seed) du générateur de nombres aléatoires au début de chaque script.
import random
import numpy as np
import torch
# Fixer le seed pour toutes les librairies pertinentes
SEED = 42
random.seed(SEED)
np.random.seed(SEED)
torch.manual_seed(SEED)
étape 3: utiliser des jeux de données d’exemple stables
Tester un code sur une base de données de production qui change constamment rend les tests non reproductibles. La solution est de créer des “snapshots” : des extraits de données fixes et versionnés, dédiés aux tests.
# Créer une fixture de test à partir d'un échantillon de données
# Ce fichier sera commité dans Git (ou DVC si trop gros) et utilisé par la CI.
cp data/production_sample_oct2022.parquet tests/fixtures/training_sample.parquet
pièges frequents
-
Symptôme: “Ça marche sur ma machine mais pas en CI”.
- Cause: L’environnement du développeur a divergé de celui de la production (versions de librairies différentes, etc.).
- Correctif: Utiliser une image Docker unique pour le développement local et pour la CI/production.
-
Symptôme: Les métriques d’un modèle changent légèrement à chaque entraînement, même avec le même code.
- Cause: Un
seeda été oublié dans une des librairies utilisées. - Correctif: Fixer les seeds au tout début du script pour toutes les sources d’aléatoire.
- Cause: Un
-
Symptôme: Les tests passent en CI mais le code échoue en production sur un cas particulier.
- Cause: Les données de test sont trop simples et ne représentent pas la complexité des données réelles.
- Correctif: Créer des snapshots de données qui incluent les cas limites et les “edge cases” observés en production.
faq
-
Est-ce que Docker est la seule solution pour figer un environnement ? Non, mais c’est la plus standard et la plus portable. Des alternatives comme Conda ou des environnements virtuels Python avec des fichiers de dépendances (
requirements.txt) bien gérés peuvent aussi fonctionner, mais sont souvent moins robustes. -
Comment gérer les différences de matériel (cpu vs gpu) ? C’est un des cas où la reproductibilité parfaite est difficile. La solution est d’avoir des images Docker spécifiques pour chaque architecture (
-cpu,-gpu). De plus, il faut être conscient que certaines opérations mathématiques peuvent donner des résultats très légèrement différents entre CPU et GPU, ce qui peut nécessiter de définir des tolérances dans les tests.