
Pourquoi votre modèle ML fonctionne en formation mais échoue en production
j’ai travaillé sur des systèmes de détection de fraude en temps réel et des modèles de recommandation pour des sociétés de produits qui semblaient excellentes lors du développement. Les mesures hors ligne étaient solides. Courbes ASC étaient stables sur toutes les fenêtres de validation. Les graphiques d’importance des fonctionnalités racontaient une histoire claire et intuitive. Nous avons expédié en toute confiance.
Quelques semaines plus tard, nos mesures ont commencé à dériver.
Les taux de clics sur les recommandations ont commencé à baisser. Les modèles de fraude se sont comportés de manière incohérente pendant les heures de pointe. Certaines décisions semblaient trop confiantes, d’autres étrangement aveugles. Les modèles eux-mêmes ne s’étaient pas dégradés. Il n’y a eu aucune panne de données soudaine ni rupture de pipeline. Ce qui a échoué, c’est notre compréhension du comportement du système une fois qu’il a rencontré le temps, la latence et la vérité retardée dans le monde réel.
Cet article concerne ces échecs. Les problèmes discrets et peu glamour qui n’apparaissent que lorsque les systèmes d’apprentissage automatique entrent en collision avec la réalité. Pas de choix d’optimiseur ou de dernière architecture. Des problèmes qui n’apparaissent pas dans les cahiers, mais qui font surface à 3 heures du matin sur les tableaux de bord.
Mon message est simple : la plupart des échecs de ML en production sont des problèmes de données et de temps, et non des problèmes de modélisation. Si vous ne concevez pas explicitement la manière dont les informations arrivent, mûrissent et changent, le système formulera discrètement ces hypothèses à votre place.
Voyage dans le temps : une fuite d’hypothèse
Le voyage dans le temps est l’échec de ML de production le plus courant que j’ai vu, et aussi le moins discuté en termes concrets. Tout le monde hoche la tête lorsque vous évoquez une fuite. Très peu d’équipes peuvent indiquer exactement la rangée où cela s’est produit.
Permettez-moi de le rendre explicite.
Imaginez un ensemble de données sur la fraude avec deux tables :
- transactions: quand le paiement a eu lieu

- rétrofacturations: quand le résultat de la fraude a été signalé

La fonctionnalité que nous voulons est user_chargeback_count_last_30_days.
Le travail par lots s’exécute en fin de journée, juste avant minuit, et calcule le nombre de rétrofacturations pour les 30 derniers jours. Pour l’utilisateur U123, le décompte est de 1. À minuit, c’est factuellement exact.

Regardez maintenant l’ensemble de données de formation final joint.
Les transactions du matin à 9h10 et 11h45 comportent déjà un nombre de rétrofacturation de 1. Au moment où ces paiements ont été effectués, la rétrofacturation n’avait pas encore été signalée. Mais les données de formation ne le savent pas. Le temps a été aplati.
C’est là que le modèle triche.

Du point de vue du modèle, les transactions qui semblent risquées s’accompagnent déjà de signaux de fraude confirmés. Le rappel hors ligne s’améliore considérablement. Rien ne semble faux à ce stade.
Mais en production, le modèle est censé ne jamais voir l’avenir.
Une fois déployées, ces premières transactions n’ont pas encore de compte de rétrofacturation. Le signal disparaît et les performances s’effondrent.
Ce n’est pas une erreur de modélisation. C’est une fuite d’hypothèse.
L’hypothèse cachée est qu’une fonctionnalité de lot quotidien est valable pour tous les événements de cette journée. Ce n’est pas. Une caractéristique n’est valide que si elle aurait pu exister au moment exact où la prédiction a été faite.
Chaque fonctionnalité doit répondre à une question :
« Cette valeur aurait-elle pu exister au moment exact où la prédiction a été faite ?
Si la réponse n’est pas un oui certain, la fonctionnalité n’est pas valide.
Des fonctionnalités par défaut qui deviennent des signaux
Après le voyage dans le temps, c’est une raison d’échec très courante que j’ai constatée dans les systèmes de production. Contrairement aux fuites, celle-ci ne dépend pas de l’avenir. Cela repose sur le silence.
La plupart des ingénieurs considèrent les valeurs manquantes comme un problème d’hygiène. Remplissez-les avec la moyenne, la médiane ou une autre technique d’imputation, puis continuez.
Ces défauts semblent inoffensifs. Quelque chose d’assez sûr pour que le modèle puisse continuer à fonctionner.
Cette hypothèse s’avère coûteuse.
Dans les systèmes réels, manquer est rarement synonyme de hasard. Manquant signifie souvent nouveau, inconnu, pas encore observé ou pas encore fiable. Lorsque nous réduisons tout cela en une seule valeur par défaut, le modèle ne voit aucune lacune. Il voit un modèle.
Permettez-moi de rendre cela concret.
J’ai rencontré ce problème pour la première fois dans un système de fraude en temps réel où nous utilisions une fonctionnalité appelée avg_transaction_amount_last_7_days. Pour les utilisateurs actifs, cette valeur s’est bien comportée. Pour les utilisateurs nouveaux ou inactifs, le pipeline de fonctionnalités a renvoyé une valeur par défaut de zéro.

Pour illustrer comment la valeur par défaut est devenue un indicateur puissant du statut de l’utilisateur, j’ai calculé le taux de fraude observé regroupé par la valeur de la fonctionnalité :
data.groupby("avg_txn_amount_last_7_days")["is_fraud"].mean()
Comme indiqué, les utilisateurs avec une valeur de zéro présentent un taux de fraude nettement inférieur, non pas parce que zéro dépense est intrinsèquement sûre, mais parce qu’elle code implicitement « un utilisateur nouveau ou inactif ».
Tous les utilisateurs avec un montant de transaction moyen de zéro ne sont pas des fraudeurs. Non pas parce que zéro est intrinsèquement sûr, mais parce que ces utilisateurs sont nouveaux/inactifs. Le modèle n’apprend pas que « de faibles dépenses sont sûres ». Il apprend que « l’histoire manquante signifie la sécurité ».
Le défaut est devenu un signal.
Pendant l’entraînement, cela semble bon à mesure que la précision s’améliore. Puis le trafic de production change.
Un service en aval commence à expirer pendant les heures de pointe. Du coup, les utilisateurs actifs perdent temporairement leurs fonctionnalités d’historique. Leur avg_transaction_amount_last_7_days retourne à zéro. Le modèle les considère avec confiance comme étant à faible risque.
Les équipes expérimentées gèrent cela différemment. Ils séparent l’absence de la valeur et suivent explicitement la disponibilité des fonctionnalités. Plus important encore, ils ne permettent jamais au silence de se faire passer pour une information.
Changement de population sans changement de distribution
Ce mode de défaillance m’a pris beaucoup plus de temps à reconnaître, principalement parce que toutes les alarmes habituelles sont restées silencieuses.
Lorsque les gens parlent de dérive des données, ils font généralement référence à un changement de distribution. Les histogrammes de caractéristiques se déplacent. Les percentiles changent. Tests du SK éclairer les tableaux de bord. Tout le monde comprend quoi faire ensuite. Examiner les données en amont, recycler, recalibrer.
Changement de population sans changement de distribution est différent. Ici, les distributions de fonctionnalités restent stables. Les statistiques récapitulatives bougent à peine. Les tableaux de bord de surveillance semblent rassurants. Et pourtant, le comportement des modèles se dégrade régulièrement.
J’ai rencontré ce problème pour la première fois dans un système de gestion des risques liés aux paiements à grande échelle qui fonctionnait sur plusieurs segments d’utilisateurs. Le modèle utilisait des fonctionnalités au niveau des transactions telles que le montant, l’heure de la journée, les signaux de l’appareil, les compteurs de vitesse et les codes de catégorie de commerçant. Toutes ces fonctionnalités ont été fortement surveillées. Leurs distributions ont à peine changé d’un mois à l’autre.
Pourtant, les taux de fraude ont commencé à augmenter dans une tranche de trafic très spécifique. Ce qui a changé, ce ne sont pas les données. C’était qui représentaient les données.
Au fil du temps, le produit s’est étendu à de nouvelles cohortes d’utilisateurs. De nouvelles zones géographiques avec des habitudes de paiement différentes. De nouvelles catégories de commerçants avec des modèles de transactions inconnus. Campagnes promotionnelles qui ont attiré des utilisateurs qui se comportaient différemment mais qui se situaient toujours dans les mêmes fourchettes numériques. Du point de vue de la distribution, rien ne semblait inhabituel. Mais la population sous-jacente avait changé.
Le modèle avait été formé principalement sur des utilisateurs matures ayant de longs antécédents comportementaux. À mesure que la base d’utilisateurs grandissait, une plus grande fraction du trafic provenait d’utilisateurs plus récents dont le comportement semblait statistiquement similaire mais sémantiquement différent. Un montant de transaction de 2 000 signifiait quelque chose de très différent pour un utilisateur de longue date que pour quelqu’un le premier jour. Le modèle ne le savait pas, parce que nous ne lui avions pas appris à s’en soucier.

Voir cette figure ci-dessus. Cela montre pourquoi ce mode de défaillance est difficile à détecter en pratique. Les deux premiers graphiques montrent le montant des transactions et les distributions de vitesse à court terme pour les utilisateurs matures et nouveaux. Du point de vue de la surveillance, ces caractéristiques semblent stables avec le chevauchement. Si c’était le seul signal disponible, la plupart des équipes concluraient que le pipeline de données et les entrées du modèle restent sains.
La troisième intrigue révèle le vrai problème. Même si la répartition des fonctionnalités est presque identique, le taux de fraude diffère considérablement selon les populations. Le modèle applique les mêmes limites de décision aux deux groupes car les entrées semblent familières, mais le risque sous-jacent n’est pas le même. Ce qui a changé, ce ne sont pas les données elles-mêmes, mais ce qu’elles représentent.
À mesure que la composition du trafic change en raison de la croissance ou de l’expansion, ces hypothèses cessent d’être valables, même si les données continuent de paraître statistiquement normales. Sans modélisation explicite du contexte démographique ou évaluation des performances des cohortes, ces échecs restent invisibles jusqu’à ce que les indicateurs commerciaux commencent à se dégrader.
Avant de partir
Aucun des échecs décrits dans cet article n’a été causé par de mauvais modèles.
Les architectures étaient raisonnables. Les fonctionnalités ont été soigneusement conçues. Ce qui a échoué, c’est le système autour du modèle, en particulier les hypothèses que nous avions faites sur le temps, les absences et les personnes représentées par les données.
Le temps n’est pas un index statique. Les étiquettes arrivent en retard. Les caractéristiques mûrissent de manière inégale. Les limites des lots correspondent rarement aux moments de décision. Lorsque nous ignorons cela, les modèles apprennent à partir d’informations qu’ils ne reverront plus jamais.
S’il y a une chose à retenir, c’est la suivante : des mesures hors ligne solides ne sont pas une preuve d’exactitude. Ils prouvent que le modèle correspond aux hypothèses que vous lui avez données. Le véritable travail de l’apprentissage automatique commence lorsque ces hypothèses correspondent à la réalité.
Concevoir pour ce moment.
Références et lectures complémentaires
[1] Courbes ROC et AUC (cours intensif de Google Machine Learning)
https://developers.google.com/machine-learning/crash-course/classification/roc-and-auc
[2] Test de Kolmogorov-Smirnov (Wikipédia)
https://en.wikipedia.org/wiki/Kolmogorov%E2%80%93Smirnov_test[3] Changements et surveillance de la distribution des données (puce Huyen)
https://huyenchip.com/2022/02/07/data-distribution-shifts-and-monitoring.html



