Le jour où mon modèle de prédiction s'est mis à mentir — et comment je m'en suis aperçu
Un modèle de forecasting ne crash pas quand il devient mauvais. Il continue à répondre. Les chiffres ont l'air normaux. Et un mois plus tard, ton client a 40 % de stock en trop. Voici comment SaleCast détecte la dérive — en temps réel, sans humain dans la boucle, avec deux algorithmes des années 50 et 2000.
Cet article décortique le projetSaleCastLe bug le plus dangereux du ML en production
Un serveur web qui crash, on le voit en 30 secondes. Un service qui ralentit, Grafana te ping en 5 minutes. Un test qui échoue, le CI le bloque.
Un modèle de forecasting qui devient mauvais ? Personne ne te le dit.
Il continue à répondre. Les requêtes passent. Les chiffres ont l'air normaux. Et un mois plus tard, ton client te dit :
"Je sais pas pourquoi, j'ai 40 % de stock en plus que prévu sur ma collection été. Vos prévisions sont fiables au moins ?"
À ce moment-là, tu n'as aucune réponse. Tu ne sais même pas quand le modèle a décroché. Tu sais juste qu'il a décroché.
Ce post explique comment j'ai réglé ça dans SaleCast.
Le concept : c'est pas le modèle qui change, c'est le monde
Un modèle ML entraîné en janvier 2024 sur des données 2022-2023 fait l'hypothèse — implicite — que le futur ressemble au passé.
En e-commerce, c'est faux. La pandémie a tué la saisonnalité de 2020-2022. TikTok crée des modes du jour au lendemain. Un concurrent qui baisse ses prix détourne 30 % de tes ventes en 48h.
Le modèle n'est pas devenu mauvais — il prédit toujours bien sur les données pour lesquelles il a été entraîné. Mais la distribution réelle a glissé. C'est ce qu'on appelle le concept drift.
Le détecter, c'est observer la série des résidus : pour chaque prévision faite, quelle a été l'erreur réelle ? Si les erreurs étaient centrées autour de 0 et qu'elles se mettent à dériver vers le positif, le modèle sous-estime systématiquement. Drift.
Deux algorithmes que tout le monde devrait connaître
Pour détecter une dérive dans un flux de résidus, il y a deux algorithmes classiques :
Page-Hinkley (1954)
Page-Hinkley accumule la différence entre chaque résidu et la moyenne courante,
moins un petit delta qui tolère le bruit. Quand la somme cumulée dépasse un seuil,
on détecte un drift.
Le code SaleCast :
internal static PageHinkleyResult RunPageHinkley(
IReadOnlyList<double> residuals, double threshold, double delta)
{
var sum = 0.0;
var mean = 0.0;
var mT = 0.0; // Cumulative sum pour drift à la hausse
var MT = 0.0; // Minimum de mT
var phUp = 0.0;
var mTDown = 0.0; // Cumulative sum pour drift à la baisse
var MTDown = 0.0;
var phDown = 0.0;
var detectedIndex = -1;
for (var i = 0; i < residuals.Count; i++)
{
sum += residuals[i];
mean = sum / (i + 1);
mT += residuals[i] - delta;
MT = Math.Min(MT, mT);
phUp = mT - MT;
if (phUp > threshold && detectedIndex < 0)
detectedIndex = i;
// … symétrique pour le drift à la baisse
}
return new PageHinkleyResult { Detected = detectedIndex >= 0, Index = detectedIndex };
}
L'algorithme tient en 20 lignes. Il a 71 ans. Il marche redoutablement bien.
Avantage : il détecte un drift très progressif. Un modèle qui se met à sous-estimer de 2 % par semaine, Page-Hinkley le voit après 5-8 semaines.
Inconvénient : il faut bien régler threshold et delta. Trop bas → faux positifs.
Trop haut → on détecte trop tard.
ADWIN (Bifet & Gavaldà, 2007)
ADWIN — Adaptive Windowing — est plus malin. Il maintient une fenêtre adaptative de résidus récents. Quand la fenêtre devient statistiquement différente de ce qu'elle était plus tôt, ADWIN coupe la fenêtre en deux et signale un drift.
Pas de seuil à régler manuellement. Pas de paramètre arbitraire. Juste un seuil
de confiance statistique (delta = 0.002).
Avantage : zéro paramétrage à la louche. Mathématiquement défendable.
Inconvénient : détecte les drifts brutaux (changement de régime soudain) mieux que les drifts lents.
Pourquoi les deux ?
var ph = RunPageHinkley(residuals, phThreshold, phDelta);
var adwin = RunAdwin(residuals);
var driftDetected = ph.Detected || adwin.Detected;
var severity = ComputeSeverity(ph, adwin);
Page-Hinkley est sensible aux drifts lents. ADWIN est sensible aux drifts brutaux.
L'OR logique des deux couvre les deux régimes. Si l'un des deux dit "drift", on considère qu'il y a un problème. La sévérité combine les deux signaux pour graduer l'alerte.
Comment ça s'utilise en pratique
Chaque nuit, le job Hangfire de SaleCast tourne sur chaque produit et calcule les résidus des 90 derniers jours. Si DriftDetector signale un drift :
[2025-06-08 03:14] Drift détecté · SKU TSHIRT-MARIN-XL-N
Severity: High
Page-Hinkley: drift hausse, index 18 (≈ il y a 14 jours)
ADWIN: cut détecté à l'index 22
Action: modèle marqué "stale", re-back-test forcé
Notification: Discord + email DPO
Le jour suivant, ce SKU passe en compétition d'algorithmes complète avec les 9 modèles statistiques + TiRex + Chronos + LightGBM réentraîné. Si un autre algo bat l'ancien, il prend la place. Sinon, on garde mais on note "instabilité".
Le moment où ça m'a sauvé
Mars 2026. Mon client lance une promotion sur 200 SKU pendant 2 semaines. Les ventes explosent. Le modèle qui prédisait 4 unités/jour voit la réalité passer à 18. Résidus énormes, positifs.
Sans drift detection : à la fin de la promo, le modèle aurait été ré-entraîné en incluant ces 2 semaines aberrantes. Il aurait sur-estimé pendant 3 mois.
Avec drift detection : ADWIN détecte le changement de régime au jour 4. Le modèle est marqué "promo en cours", et les résidus de la période de promo sont exclus du ré-entraînement automatique. Quand la promo se termine, le modèle reprend où il en était, intact.
Sans cette protection, j'aurais perdu 3 mois d'historique de modèle pour 14 jours de promo.
Ce que ce projet m'a appris
-
Un modèle ML qui ne dit pas "je suis perdu" est dangereux. Le silence est le défaut le plus grave d'un système prédictif en production.
-
Les algorithmes anciens valent souvent mieux que les "drift detection techniques modernes basées sur deep learning". Page-Hinkley a 71 ans. Il fait 20 lignes de code. Il fonctionne. ADWIN a 19 ans. 80 lignes. Idem.
-
Le drift est un signal d'observabilité, pas un signal de panique. Un drift détecté ne veut pas dire que le modèle est mort. Il veut dire "vérifie ce qui se passe sur cette catégorie". La plupart du temps, c'est une promotion. Parfois, c'est un changement durable du marché. C'est utile dans les deux cas.
-
L'ML observability est un trou béant dans la plupart des projets. Les équipes qui mettent en prod un LightGBM ont rarement un seul check de drift. Elles regardent juste les courbes business. Trop tard.
La leçon métier
Mon client n'a jamais lu un seul article sur Page-Hinkley ou ADWIN. Ce qu'il voit, c'est un dashboard avec :
Modèles instables ce mois-ci : 3 sur 2 800 (0.1%)
Catégories en dérive : 2 (chaussures été, livres)
Drift moyen détecté : +14 jours après changement réel
Et un chiffre comme "14 jours après changement réel" est ce qui me permet de dire à un prospect :
"Quand le marché change, vous le saurez dans les 2 semaines. Pas dans les 3 mois."
C'est cette phrase, pas mon code, qui ferme une vente.
Stack & code
- DriftDetector.cs — 183 lignes, deux algos
- Page-Hinkley — implémentation directe (Wikipedia est suffisant)
- ADWIN — adapté de l'implémentation Bifet & Gavaldà, version C# pure
- Seuils par défaut :
PageHinkleyThreshold = 50,PageHinkleyDelta = 0.005,AdwinDelta = 0.002 - Job Hangfire nocturne qui calcule les résidus glissants 90 jours et alimente le détecteur
Pas une seule dépendance externe. Pas de Python. Pas de service en plus à monitorer. Le détecteur de drift tourne dans le même process que les forecasts qu'il surveille.
Florian Sola
Lead Technique · Haute performance temps réel · 9 ans d'expérience