Un modèle en production se dégrade sans lever la moindre erreur, et le drift des données n'est qu'un des signaux, souvent trompeur. Ce guide montre, sur trois scénarios reproductibles, que la dérive peut crier sans conséquence et que la qualité peut s'effondrer sans qu'aucune dérive ne soit détectée. Vous repartirez avec ce qu'il faut surveiller en plus du drift, comment travailler quand les étiquettes arrivent avec un mois de retard, et la chaîne de décision qui va du signal au retour arrière. Public : intermédiaire à avancé.
Tous les chiffres publiés ici sortent d'un script seedé, rejouable à l'identique, avec Evidently 0.7.21, scikit-learn 1.7.2 et Python 3.12.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Distinguer ce que le drift détecte de ce qu'il manque, sur des cas mesurés
- Reconnaître les deux faux signaux : dérive sans impact, effondrement sans dérive
- Surveiller au-delà des distributions : qualité des données, calibration, tranches, métrique métier
- Travailler avec des étiquettes retardées, et savoir ce que valent les substituts
- Décider : du signal au challenger, puis à la promotion ou au rollback
Pourquoi un modèle se dégrade sans prévenir
Section intitulée « Pourquoi un modèle se dégrade sans prévenir »Un modèle ML ne tombe pas en panne, il devient faux. Les prédictions restent techniquement valides, le service répond en quelques millisecondes, et la pertinence baisse. Un modèle de détection de fraude entraîné l'an dernier ignore les fraudes d'aujourd'hui, sans qu'aucune alerte d'infrastructure ne se déclenche.
Deux difficultés rendent cette surveillance particulière. La vérité terrain arrive souvent en retard : on ne sait qu'une transaction était frauduleuse qu'après la contestation du client, des semaines plus tard. Et les métriques système habituelles, latence et disponibilité, ne disent rien de la justesse des prédictions. Un modèle parfaitement disponible peut se tromper une fois sur deux.
Ce que le drift détecte, et ce qu'il manque
Section intitulée « Ce que le drift détecte, et ce qu'il manque »C'est le point que la plupart des guides passent sous silence, et il change
complètement la façon de bâtir une surveillance. Le drift des données d'entrée
n'est ni nécessaire ni suffisant pour conclure à une dégradation. Les trois
scénarios ci-dessous le montrent sur le même modèle, un RandomForestClassifier
de détection de fraude, avec quatre features et 4000 lignes par jeu.
Le modèle de référence, entraîné avec la graine 42 :
exactitude 0.696AUC 0.767importance des features nb_essais 0.340 anciennete 0.339 montant 0.162 heure 0.159Scénario 1 : la dérive crie, la qualité ne bouge pas
Section intitulée « Scénario 1 : la dérive crie, la qualité ne bouge pas »On décale la feature heure de six heures, soit deux écarts-types. C'est une
dérive massive, et Evidently la voit sans ambiguïté :
colonnes derivees : 1/4 (part 0.25) montant Wasserstein distance (normed) 0.0305 vs seuil 0.1 stable anciennete Wasserstein distance (normed) 0.01546 vs seuil 0.1 stable nb_essais Wasserstein distance (normed) 0.01076 vs seuil 0.1 stable heure Wasserstein distance (normed) 1.992 vs seuil 0.1 DERIVEexactitude : 0.675 (reference 0.696)ecart d exactitude : -0.021La distance mesurée vaut vingt fois le seuil, et l'exactitude perd deux
points. Une alerte configurée sur « une colonne a dérivé » se déclenche ici, et
réveille quelqu'un pour rien. La raison est dans le tableau d'importance :
heure pèse 0,159, et surtout elle n'intervient pas dans la règle qui génère
l'étiquette. Le modèle s'en sert à la marge, sa dérive ne le dérange pas.
Scénario 2 : aucune dérive, la qualité s'effondre
Section intitulée « Scénario 2 : aucune dérive, la qualité s'effondre »Cette fois on ne touche à aucune distribution d'entrée. On change la relation : les fraudeurs passent désormais par des comptes anciens avec peu de tentatives, l'inverse exact de ce que le modèle a appris. C'est du concept drift.
colonnes derivees : 0/4 (part 0.00) montant Wasserstein distance (normed) 0.02815 vs seuil 0.1 stable anciennete Wasserstein distance (normed) 0.01077 vs seuil 0.1 stable nb_essais Wasserstein distance (normed) 0.01693 vs seuil 0.1 stable heure Wasserstein distance (normed) 0.04219 vs seuil 0.1 stableexactitude : 0.312 (reference 0.696)AUC : 0.247 (reference 0.767)ecart d exactitude : -0.384Zéro colonne dérivée, et le modèle perd 38 points d'exactitude. L'AUC tombe à 0,247, c'est-à-dire sous 0,5 : le modèle est devenu anti-corrélé, on ferait mieux en inversant ses prédictions. Une surveillance qui ne regarde que les distributions d'entrée n'aurait rien vu, pendant que le système prenait des décisions systématiquement fausses.
Scénario 3 : les étiquettes arrivent dans trente jours
Section intitulée « Scénario 3 : les étiquettes arrivent dans trente jours »Reprenons le scénario 2, mais au jour J : la vérité terrain n'existe pas encore. Voici tout ce qu'on peut mesurer sans étiquette.
taux de positifs predit, reference : 0.493taux de positifs predit, scenario 2: 0.496ecart : +0.004
confiance moyenne, reference : 0.502confiance moyenne, scenario 2 : 0.504Le taux de positifs bouge de quatre millièmes, la confiance moyenne de deux. Autrement dit, le substitut le plus souvent recommandé, la surveillance de la distribution des prédictions, ne voit rien non plus. Ce n'est qu'à J+30, quand les contestations remontent, que l'exactitude réelle apparaît : 0,312.
Il faut en tirer la conséquence honnête : sur ce cas, aucune métrique sans étiquette ne donne l'alerte. Ce que vous pouvez faire, c'est raccourcir le délai plutôt que chercher un meilleur substitut, et c'est l'objet de la section suivante.
Détecter le drift avec Evidently, sans se tromper de métrique
Section intitulée « Détecter le drift avec Evidently, sans se tromper de métrique »Evidently compare un jeu de référence à un jeu courant et signale les écarts. Installez-le dans un environnement isolé, avec sa version épinglée :
pip install "evidently==0.7.21"import numpy as npimport pandas as pdfrom evidently import DataDefinition, Dataset, Reportfrom evidently.presets import DataDriftPreset
rng = np.random.default_rng(42) # sans graine, rien n'est reproductible
ref = pd.DataFrame({"feature": rng.normal(0.0, 1, 500)})cur = pd.DataFrame({"feature": rng.normal(0.8, 1, 500)})
schema = DataDefinition(numerical_columns=["feature"])rapport = Report([DataDriftPreset()]).run( current_data=Dataset.from_pandas(cur, data_definition=schema), reference_data=Dataset.from_pandas(ref, data_definition=schema),)rapport.save_html("drift_report.html")
for m in rapport.dict()["metrics"]: print(m["metric_name"], "->", m["value"])La métrique change à 1000 lignes, et le seuil avec
Section intitulée « La métrique change à 1000 lignes, et le seuil avec »C'est le piège qui fait qu'un tableau de bord ne dit pas la même chose en laboratoire et en production. Evidently 0.7.21 choisit sa méthode selon la taille du jeu de données, et bascule à 1000 lignes. Mesuré, à graine constante, sur la même dérive de moyenne 0 vers 0,8 :
| Lignes | Méthode retenue | Seuil | Valeur |
|---|---|---|---|
| 200 | K-S p_value | 0.05 | 2,57e-14 |
| 500 | K-S p_value | 0.05 | 1,57e-28 |
| 999 | K-S p_value | 0.05 | 6,09e-49 |
| 1000 | K-S p_value | 0.05 | 1,36e-48 |
| 1001 | Wasserstein distance (normed) | 0.1 | 0,755 |
| 4000 | Wasserstein distance (normed) | 0.1 | 0,820 |
Deux conséquences pratiques. Une p-value se compare à 0,05 et plus elle est petite, plus la dérive est franche ; une distance de Wasserstein se compare à 0,1 et plus elle est grande, plus la dérive est franche. Le sens de lecture s'inverse. Et un seuil d'alerte écrit pour un échantillon de test à 500 lignes n'a aucun sens sur un flux de production à 50 000.
Ce qu'il faut surveiller en plus du drift
Section intitulée « Ce qu'il faut surveiller en plus du drift »Le scénario 2 a montré qu'une surveillance réduite aux distributions d'entrée laisse passer le pire cas. Voici les couches à empiler, de la moins chère à la plus exigeante.
| Couche | Ce qu'elle attrape | Ce qu'elle coûte |
|---|---|---|
| Qualité des données | Valeurs manquantes, types changés, valeurs hors bornes, colonne figée | Rien, c'est le socle |
| Drift des entrées | Un nouveau segment d'utilisateurs, une saison, un capteur déréglé | Un jeu de référence à figer |
| Drift des prédictions | Le modèle qui bascule vers une classe unique | Rien de plus que les logs |
| Écart entraînement/service | La feature calculée différemment en production et à l'entraînement | Instrumenter les deux chemins |
| Qualité du modèle | Tout le reste, dont le concept drift | Des étiquettes |
| Calibration | Un modèle sûr de lui et faux, ce qui casse tout seuil métier | Des étiquettes, plus une courbe |
| Tranches | La dégradation sur un segment noyée dans la moyenne | Découper par segment |
| Métrique métier | Ce que la direction regardera de toute façon | Relier prédictions et résultats |
Deux de ces couches méritent un mot, parce qu'elles sont systématiquement oubliées.
L'écart entraînement/service est la cause d'incident la plus banale et la
plus humiliante : la feature montant_moyen_30j est calculée sur 30 jours
glissants à l'entraînement et sur le mois calendaire en production. Aucune
dérive, aucun bug visible, un modèle qui reçoit des entrées qui ne sont pas
celles sur lesquelles il a appris. Il se détecte en journalisant les features
telles que le service les calcule, puis en les comparant au jeu d'entraînement.
Les tranches répondent à une question que la moyenne masque toujours : un modèle qui passe de 0,90 à 0,88 d'exactitude globale peut avoir perdu 20 points sur les nouveaux clients et gagné sur les anciens. Surveillez au moins les segments qui ont une conséquence métier ou réglementaire distincte.
Travailler avec des étiquettes retardées
Section intitulée « Travailler avec des étiquettes retardées »Le délai d'étiquetage détermine votre stratégie de surveillance, plus que le choix de l'outil. Trois régimes, trois réponses.
| Délai | Ce sur quoi vous pilotez |
|---|---|
| Minutes à heures (clic, conversion) | La qualité du modèle directement. Le drift devient accessoire |
| Jours à semaines (résiliation, contestation) | Qualité en différé, plus les couches sans étiquette en attendant |
| Mois, ou jamais (risque de crédit à 3 ans) | Un proxy assumé, et une surveillance humaine |
Trois leviers permettent de raccourcir l'attente plutôt que de la subir.
-
Étiqueter un échantillon, tout de suite
Prélevez un petit échantillon aléatoire des prédictions du jour et faites-le trancher par un humain sous 24 h. Quelques centaines de cas suffisent à détecter un effondrement comme celui du scénario 2, bien avant les 30 jours. C'est le seul dispositif qui aurait vu cette panne.
-
Garder une fraction non traitée comme témoin
Sur les systèmes où le modèle agit (blocage, remise, priorisation), son action empêche d'observer ce qui serait arrivé sans lui. Laisser passer un petit pourcentage sans intervention donne une mesure non biaisée, au prix assumé d'un peu de risque.
-
Mesurer le délai lui-même
Suivez la distribution du délai d'étiquetage comme une métrique à part entière. Un allongement soudain signale souvent un problème de collecte, et il rend tous vos indicateurs de qualité optimistes sans prévenir.
Décider : du signal au rollback
Section intitulée « Décider : du signal au rollback »Détecter ne sert à rien sans une chaîne de décision écrite d'avance. Le réentraînement automatique dès qu'un seuil est franchi est un anti-pattern : le scénario 1 l'aurait déclenché pour rien, et un réentraînement sur des données déjà polluées propage le problème au lieu de le corriger.
-
Signal : un seuil est franchi, sur n'importe quelle couche. Ce n'est qu'une alerte, pas un diagnostic.
-
Investigation : la dégradation touche-t-elle la qualité du modèle ou seulement une distribution ? Une tranche ou tout le monde ? Le scénario 1 et le scénario 2 se séparent ici, et eux seuls décident de la suite.
-
Candidat challenger : entraîner un modèle sur des données récentes et vérifiées, sans le mettre en production. Un challenger n'est pas une correction, c'est une hypothèse.
-
Validation : comparer le challenger au modèle en place sur un jeu de test figé, tranche par tranche, et sur la métrique métier. Un gain global qui cache une perte sur un segment sensible n'est pas un gain.
-
Promotion progressive : basculer une fraction du trafic, en observant les mêmes indicateurs. Pas de bascule totale d'un coup.
-
Surveillance renforcée : garder les seuils resserrés quelques jours après la promotion, quand les régressions apparaissent.
-
Rollback : revenir à la version précédente par une procédure testée, pas improvisée. Le modèle précédent, ses features et son préprocesseur doivent rester versionnés ensemble et redéployables, sinon le retour arrière est un nouveau déploiement risqué.
Les outils de monitoring
Section intitulée « Les outils de monitoring »Aucun de ces outils ne remplace les couches décrites plus haut : ils les instrumentent. Le choix se fait sur ce que vous avez déjà en exploitation, pas sur la liste de fonctionnalités.
| Outil | Ce qu'il couvre | Ce qu'il ne couvre pas |
|---|---|---|
| Evidently | Qualité des données, drift des entrées et des prédictions, rapports HTML | La décision, et la collecte des étiquettes |
| Prometheus + Grafana | Latence, disponibilité, et toute métrique que vous exposez | Rien de spécifique au ML, tout est à construire |
| Arize AI / WhyLabs | Monitoring managé, alertes, tranches, biais | Le coût, et la sortie des données hors de votre périmètre |
| MLflow | Versionnement des modèles, ce qui rend le rollback possible | La surveillance en production |
Une combinaison pragmatique et peu coûteuse : Evidently pour les rapports de drift et de qualité des données, Prometheus et Grafana pour exposer la qualité du modèle et la métrique métier à côté des indicateurs système, et MLflow pour que la version précédente soit réellement redéployable.
FAQ : questions fréquentes
Section intitulée « FAQ : questions fréquentes »À retenir
Section intitulée « À retenir »- Le drift n'est ni nécessaire ni suffisant. Mesuré : une dérive à vingt fois le seuil coûte 2 points d'exactitude, et un effondrement de 38 points se produit avec zéro colonne dérivée.
- Le pire cas est silencieux. Dans le scénario de concept drift, l'AUC tombe à 0,247, sous le hasard, sans qu'aucune métrique sans étiquette ne bouge : le taux de positifs prédit varie de 0,004.
- Evidently change de métrique à 1000 lignes, de la p-value K-S à la distance de Wasserstein, et le sens de lecture s'inverse. Un seuil écrit pour 500 lignes n'a aucun sens en production.
- Sans graine, rien n'est reproductible. Un exemple
np.randomnon seedé ne permet ni de vérifier un chiffre publié, ni de comparer deux exécutions. - Empilez les couches : qualité des données, drift, écart entraînement /service, qualité du modèle, calibration, tranches, métrique métier.
- Raccourcissez le délai d'étiquetage plutôt que de chercher un meilleur substitut : un petit échantillon étiqueté sous 24 h voit ce qu'aucun proxy ne voit.
- Le réentraînement automatique sur seuil est un anti-pattern. La chaîne est signal, investigation, challenger, validation, promotion progressive, surveillance, rollback.
- Un rollback jamais joué n'existe pas. Testez-le hors incident, et versionnez le modèle avec ses features et son préprocesseur.