Aller au contenu principal

Module 1 — L'écart entraînement-service et ses conséquences

Le fil rouge de ce cours est un scoreur de fraude déployé chez une banque. Il reçoit une transaction par carte, doit décider en moins de 50 ms si elle est licite, et il utilise sept variables : le nombre de transactions du porteur sur les dernières 1 h, 24 h et 7 j, le montant moyen sur ces mêmes fenêtres, et la distance en kilomètres au lieu de la dernière opération. À l'entraînement, ces variables ont été calculées par un notebook pandas sur un an d'historique. En production, elles sont recalculées par un microservice Java à chaque paiement. Le modèle est le même ; les variables ne le sont pas.

Deux implémentations d'une même variable

Prenons la plus simple : « nombre de transactions du porteur sur les dernières 24 h ». Le notebook d'entraînement écrit une agrégation groupby sur toute la table historique, alignée sur l'horodatage de l'événement à prédire. Le microservice de production, lui, interroge une base opérationnelle et compte les lignes des dernières 24 h. Ces deux calculs ont l'air d'implémenter la même définition — ils ne l'implémentent pas.

Voici cinq différences que nous avons observées dans une équipe réelle, sur exactement cette variable :

  1. Fuseau horaire. Le notebook travaille en UTC ; le microservice utilise l'heure locale du serveur, sur laquelle un opérateur a activé l'heure d'été. Deux fois par an, le décompte se décale d'une heure.
  2. Inclusion de la transaction courante. À l'entraînement, la transaction qu'on cherche à prédire n'est pas comptée (elle est postérieure à la fenêtre). En production, elle l'est, parce que le microservice lit après insertion.
  3. Statut des transactions. L'historique contient les paiements acceptés ; la base opérationnelle contient aussi les tentatives et les pré-autorisations. Le décompte n'est donc pas homogène.
  4. Tolérance aux retards. Une transaction arrive avec un délai réseau moyen de 3 s. À l'entraînement, elle est datée par le processeur de paiement ; en production, elle est parfois datée par la passerelle. Sur des fenêtres courtes, l'écart change le comptage.
  5. Traitement des valeurs manquantes. Un porteur sans transaction reçoit 0 à l'entraînement (grâce à fillna(0)), et null en production (défaut de la base). Le modèle interprète différemment ces deux entrées.

Individuellement, chacune de ces différences est invisible : le microservice répond, la variable a une valeur, la prédiction sort. Aucune erreur, aucune alerte. Empilées, elles ont fait tomber le rappel du modèle de 78 % à 61 %, ce qui, à volume constant, s'est traduit par un million de dollars de fraude non détectée par trimestre avant qu'un auditeur ne remarque l'écart.

Une dérive silencieuse

Le problème n'est pas seulement l'écart : c'est qu'il ne déclenche aucune alarme. Un modèle qui tombe en erreur (NaN, exception, timeout) réveille des astreintes. Un modèle qui prédit un peu moins bien continue de tourner. Les tableaux de bord affichent la latence, le débit, le taux d'erreurs HTTP — tous verts — pendant que la précision réelle dérive de plusieurs points par mois.

Le nom canonique de cette famille de bogues est training-serving skew, l'écart entraînement-service. Il existe sous quatre formes, à connaître :

  • Écart de code : deux implémentations distinctes du même calcul (le cas ci-dessus).
  • Écart de données : entraîné sur un extrait daté, servi sur un flux dont la distribution a bougé.
  • Écart de temps : la variable est calculée à un instant qui n'est pas celui de la prédiction (fenêtre non alignée, décalage d'horodatage).
  • Écart de rétroaction : la variable en production dépend d'une action que le modèle lui-même a déclenchée, boucle absente à l'entraînement.

Ce cours traite les trois premiers ; le quatrième relève du cours de MLOps (cours 20) sur les boucles causales.

Ce qu'un magasin de variables promet

Un magasin de variables (feature store) est une infrastructure qui garantit qu'entraînement et production consomment la même variable. La promesse tient en trois points :

  • Une définition unique de chaque variable, écrite une seule fois, exécutée deux fois — au moment de l'entraînement sur l'historique, au moment de la décision sur les données récentes.
  • Un magasin hors ligne pour l'entraînement, un magasin en ligne pour la production, alimentés par le même code.
  • Des jointures point-en-temps qui reproduisent, sur l'historique, l'état exact des variables tel qu'il aurait été observé au moment de chaque événement — la garantie qu'on n'entraîne pas sur le futur.

Un magasin n'est donc pas une base de données de plus. C'est un contrat entre l'équipe qui entraîne et l'équipe qui sert, matérialisé par une définition versionnée et deux backends synchronisés.

En résumé

  • L'écart entraînement-service naît de deux implémentations d'une même variable ; individuellement invisibles, ces différences s'additionnent.
  • Il est silencieux : latence et taux d'erreur restent verts pendant que la performance dérive.
  • Il existe sous quatre formes ; code, données, temps et rétroaction ; les trois premières relèvent du magasin.
  • Un magasin de variables promet une définition unique, exécutée à l'identique en historique et en direct, jointes proprement dans le temps.

Module suivant : l'anatomie interne d'un magasin — registre, entités, vues, sources et backends — pour comprendre où s'inscrit chaque garantie.