Module L35 · Partie K · Ingénierie de l’IA
Maintenir un modèle : le monde change, le modèle non.
Un modèle déployé est une photographie du passé. Les données dérivent (nouveaux capteurs, nouveaux clients, nouvelles formulations), les relations changent (dérive de concept), les dépendances évoluent (API, bibliothèques, modèles amont), et personne ne remarque tant que quelqu’un ne mesure pas. Ce chapitre couvre la détection de dérive (tests statistiques, distances entre distributions, monitoring sans étiquettes), le monitoring de qualité (étiquettes différées, échantillonnage, LLM-juge en production), le ré-entraînement (quand, comment, avec quelles données), le versionnage, les retours utilisateurs, la gestion des incidents et les coûts — avec la théorie et les articles.
Durée : 3 séances · Prérequis : L11, L32, L33, L34. Objectifs : implémenter des détecteurs de dérive (KS, PSI, KL/JS, MMD, classifieur adversarial) ; concevoir un tableau de bord de production ; définir une politique de ré-entraînement ; gérer les versions et les retours arrière ; rédiger un post-mortem.
Ce que vous saurez faire à la fin
- Distinguer dérive de covariables, de concept, de prior, et les détecter avec ou sans étiquettes.
- Fixer des seuils d’alerte fondés sur des tests et un taux de fausses alertes contrôlé.
- Décider entre ré-entraîner, réajuster un seuil, recalibrer ou revenir en arrière.
- Outiller : versions de modèle/données/prompt, déploiement progressif, journalisation, boucle de retours.
Fiche de cours · Définitions
Définitions
Fiche de cours · Formules
Formules à connaître
Fiche de cours · Théorèmes et démonstrations
Démonstrations à savoir refaire
01 / Détecter
Détecteurs de dérive implémentés : KS, PSI, JS, Wasserstein, MMD, classifieur
01 / Détecter
Seuils, fausses alertes, fenêtres : régler un détecteur comme un test
02 / Surveiller la qualité
Qualité sans étiquettes, avec étiquettes différées, et par pondération d’importance
03 / Agir
Politique de ré-entraînement : quand, sur quoi, comment valider — et les alternatives
| Signal | Diagnostic probable | Action | Coût / risque |
|---|---|---|---|
| Dérive de prédiction (plus de classe A), entrées stables | Bug amont (schéma, unité, encodage), changement de prétraitement | Vérifier le pipeline avant tout ré-entraînement ; rollback si régression | Faible ; le ré-entraînement « guérirait » le symptôme en apprenant le bug |
| Dérive de covariables, qualité stable sur l’échantillon annoté | Nouveaux cas couverts | Surveiller ; enrichir le jeu de test avec les nouveaux cas | Aucun |
| Dérive de covariables + baisse de qualité | Régions nouvelles mal couvertes | Ré-entraîner avec données récentes (fenêtre glissante ou pondérée) ; augmentation | Modéré ; valider par tranche |
| Dérive de prior (proportion des classes) | Saisonnalité, campagne | Réajuster le seuil de décision ou les a priori (correction bayésienne) sans ré-entraîner | Faible |
| Calibration dégradée, ordre correct | Confiances décalées | Recalibrer (température, Platt) sur l’échantillon récent | Faible |
| Dérive de concept confirmée par étiquettes | La règle du monde a changé | Ré-entraîner sur données récentes étiquetées ; revoir les variables ; parfois refaire le modèle | Élevé ; risque d’oubli des cas anciens (garder un mélange) |
| Qualité stable, coût/latence en hausse | Volume, longueur des entrées | Optimisation infra (L33), distillation, cache | Modéré |
| Changement de version d’un modèle amont (API, embeddings) | Rupture silencieuse | Banc d’essai automatique à chaque version ; ré-indexer si les embeddings changent (RAG) | Variable ; figer les versions |
04 / Outiller
Versionnage, déploiement progressif, retour arrière, journalisation
| Pratique | Contenu | Outils | Règle |
|---|---|---|---|
| Version de modèle | Hash du code (commit), des données (DVC), des hyperparamètres, des poids, du prompt, de l’environnement (lockfile), du tokeniseur | MLflow/W&B + registre, DVC, Git, conteneur | Toute prédiction en production est traçable à une version complète |
| Journalisation des prédictions | Entrée (ou son hash/plongement), sortie, confiance, version, latence, horodatage, identifiant de session ; échantillon des entrées brutes (avec consentement/anonymisation) | Base événementielle, stockage objet, Langfuse/Arize pour LLM | Sans journal, pas de post-mortem ni de ré-entraînement |
| Déploiement shadow | Le candidat prédit en parallèle sans agir ; on compare hors ligne | Routage applicatif | Avant tout canary sur un modèle critique |
| Canary / A/B | 1 → 5 → 25 → 100 % ; métriques métier et techniques comparées avec IC (L32) | Feature flags, service mesh, KServe | Critère d’arrêt automatique (p95, erreurs, qualité proxy) |
| Retour arrière | Version précédente prête, données compatibles, prompts et index associés | Registre + déploiement immuable | Testé régulièrement (comme une sauvegarde) |
| Tests de non-régression | Banc d’essai (L32) exécuté en CI à chaque changement de modèle, prompt, index, dépendance | CI/CD, pytest | Une régression bloque le déploiement |
| Runbooks et alertes | Pour chaque alerte : quoi vérifier, qui appeler, comment revenir en arrière | Grafana/Prometheus, PagerDuty | Une alerte sans runbook est du bruit |
| Boucle de retours | Bouton « réponse utile ? », corrections des opérateurs, escalades → file d’annotation → jeu de test enrichi | Outil d’annotation (Label Studio, Argilla) | Fermer la boucle : chaque incident devient un cas de test |
05 / Incidents
Post-mortem d’un incident IA : modèle et exemple
POST-MORTEM #23 — Assistant support : réponses erronées sur les procédures de sécurité (2026-08-12 → 08-19)
Impact : 7 jours ; ~340 réponses citant une version obsolète de la procédure P-17 (révisée le 08-11) ; 2 escalades ; 0 accident.
Détection : plainte d'un technicien (08-19). Le monitoring n'a rien signalé : entrées et sorties stables, fidélité 0,92 (le modèle citait
fidèlement… un document périmé), pas d'étiquettes sur ces questions dans l'échantillon hebdomadaire (0 sur 200).
Chronologie : 08-11 nouvelle P-17 publiée dans la GED ; l'ingestion nocturne a échoué (format PDF/A non géré) ; l'erreur est allée dans un
journal non surveillé ; l'index a gardé l'ancienne version ; 08-19 plainte ; 08-19 14:00 ré-indexation manuelle ; 08-20 correctif du parseur.
Cause racine : pipeline d'ingestion sans alerte sur échec ni contrôle de fraîcheur ; métadonnée « version du document » absente du contexte.
Facteurs contributifs : la fidélité (L26) mesure la cohérence avec le contexte, pas l'actualité du contexte ; l'échantillon annoté
hebdomadaire ne stratifie pas par document ; aucune règle « documents de sécurité = priorité ».
Ce qui a bien marché : rollback inutile (correction par ré-indexation en 1 h) ; les citations ont permis de retrouver les 340 réponses.
Actions : (1) alerte sur échec d'ingestion + contrôle de fraîcheur (âge max de l'index par collection) — fait ; (2) métadonnée de version
et date dans chaque extrait, et consigne de privilégier la plus récente — fait ; (3) stratifier l'échantillon annoté par criticité — en cours ;
(4) test de non-régression : « question sur P-17 → cite la version du jour » — fait ; (5) rappel aux utilisateurs des 340 réponses — fait.
Sans blâme : le parseur a été écrit pour les formats vus en 2025 ; le problème est l'absence de filet, pas l'erreur.Un post-mortem se lit en 3 minutes et produit des actions vérifiables. Les incidents IA typiques : données périmées, changement silencieux amont, dérive de concept non étiquetée, seuil non réajusté, boucle de retour (le modèle apprend ses propres sorties), injection, coût qui dérive.
06 / Articles
Les articles à lire
| Article | Contribution | À retenir |
|---|---|---|
| Sculley et al., Hidden Technical Debt in Machine Learning Systems, NeurIPS 2015 | Dettes : enchevêtrement, boucles de retour, dépendances de données, glue code | « Changer quoi que ce soit change tout » (CACE) |
| Gama et al., A Survey on Concept Drift Adaptation, ACM CSUR 2014 | Taxonomie des dérives et des détecteurs | Soudaine, graduelle, incrémentale, récurrente |
| Rabanser, Günnemann & Lipton, Failing Loudly: An Empirical Study of Methods for Detecting Dataset Shift, NeurIPS 2019 — 1810.11953 | Comparaison des détecteurs ; réduction de dimension + tests | Le classifieur adversarial et les tests sur les sorties du modèle marchent bien |
| Lipton, Wang & Smola, Detecting and Correcting for Label Shift with Black Box Predictors, ICML 2018 — 1802.03916 | Dérive de prior : estimation et correction via la matrice de confusion | Correction sans ré-entraînement |
| Saerens, Latinne & Decaestecker, Adjusting the Outputs of a Classifier to New a Priori Probabilities, Neural Computation 2002 | EM pour estimer la prévalence | Le code de la partie 03 |
| Sugiyama et al., Covariate Shift Adaptation by Importance Weighted Cross Validation, JMLR 2007 | Pondération d’importance | Théorème 1 et sa variance |
| Garg et al., Leveraging Unlabeled Data to Predict Out-of-Distribution Performance (ATC), ICLR 2022 — 2201.04234 | Estimer l’exactitude sans étiquettes par un seuil de confiance | Valide sous covariables, pas sous concept (Théorème 2) |
| Breck et al., The ML Test Score: A Rubric for ML Production Readiness, IEEE Big Data 2017 | 28 tests pour données, modèle, infra, monitoring | Une liste de contrôle concrète |
| Shankar et al., Operationalizing Machine Learning: An Interview Study, 2022 — 2209.09125 | Ce que font vraiment les ingénieurs MLOps | Vitesse, validation, versionnage ; la plupart des incidents viennent des données |
| Chen et al., How Is ChatGPT’s Behavior Changing over Time?, 2023 — 2307.09009 | Dérive des API de LLM entre versions | Banc d’essai à chaque version amont |
| Google SRE Book (chap. incidents, post-mortems) ; Allspaw, Blameless PostMortems | Culture de l’incident | Sans blâme, avec actions |
TP guidé
TP — Un modèle en production simulée pendant « 12 semaines » (5 h)
- Système. Un classifieur (L13/L31) ou votre RAG, avec journalisation des prédictions (SQLite ou Parquet : entrée, sortie, confiance, version, horodatage).
- Simulateur de production. Générer 12 semaines de trafic avec des dérives programmées : semaine 4 décalage de covariables, semaine 7 changement de prior, semaine 9 dérive de concept, semaine 11 bug amont (colonne en mauvaise unité). Étiquettes disponibles pour 200 cas aléatoires par semaine.
- Détecteurs. Implémenter (ou utiliser Evidently/Alibi Detect) KS/PSI par variable, AUC adversarial, dérive des prédictions, qualité sur l’échantillon, calibration. Tableau de bord hebdomadaire ; seuils réglés sur les 3 premières semaines (fausses alertes ≤ 1/mois).
- Politique. Écrire la règle de décision (tableau 03) ; l’appliquer chaque semaine : rien / recalibrer / réajuster le seuil / ré-entraîner (fenêtre glissante de 6 semaines) / rollback ; valider chaque changement par le banc d’essai et un canary simulé (50 % du trafic).
- Bilan. Quelles dérives ont été détectées, quand, avec quel délai ? Laquelle ne l’a pas été (concept sans étiquettes ?) ; coût total (annotations, ré-entraînements) ; un post-mortem pour le bug amont.
- Livrable. Dépôt (simulateur, détecteurs, tableau de bord), journal des décisions hebdomadaires, post-mortem, et une page « ce que je surveillerais en vrai ».
Exercices
Exercices auto-corrigés
Exercice 1 — PSI et ses propriétés
Implémentez psi(ref, cur, bins) avec des bins définis par les quantiles de la référence (bornes ±∞), et vérifiez : PSI ≈ 0 pour deux échantillons de la même loi ; PSI croît avec le décalage ; PSI est invariant par transformation monotone croissante (appliquée aux deux) ; PSI(ref, cur) ≠ PSI(cur, ref) en général mais les deux sont ≥ 0.
Correction
def psi(ref, cur, bins=10):
edges = np.quantile(ref, np.linspace(0, 1, bins + 1)); edges[0], edges[-1] = -np.inf, np.inf
pr = np.histogram(ref, edges)[0] / len(ref) + 1e-6; pc = np.histogram(cur, edges)[0] / len(cur) + 1e-6
return float(np.sum((pc - pr) * np.log(pc / pr)))Exercice 2 — Pondération d’importance (Théorème 1)
Écrivez poids_importance(Xref, Xcur) qui estime w(x) = pcur(x)/pref(x) par un classifieur logistique réf/cour (avec correction des effectifs), puis exactitude_ponderee(pred_ok_ref, w). Vérifiez sur une dérive de covariables 1D que l’estimation approche la vraie exactitude courante à ±0,03.
Correction
def poids_importance(Xref, Xcur, iters=500, lr=0.5):
Z = np.vstack([Xref, Xcur]); t = np.r_[np.zeros(len(Xref)), np.ones(len(Xcur))]; mu, sd = Z.mean(0), Z.std(0); Zs = (Z - mu) / sd
w = np.zeros(Z.shape[1]); b = 0.0
for _ in range(iters):
p = 1 / (1 + np.exp(-(Zs @ w + b))); w -= lr * Zs.T @ (p - t) / len(t); b -= lr * (p - t).mean()
p_ref = 1 / (1 + np.exp(-(((Xref - mu) / sd) @ w + b)))
return p_ref / (1 - p_ref) * len(Xref) / len(Xcur)
def exactitude_ponderee(ok_ref, w): return float(np.sum(w * ok_ref) / np.sum(w))Exercices
Exercices auto-corrigés (suite)
Exercice 3 — Correction de dérive de prior et estimation EM
Implémentez corriger_prior(p, pi_ref, pi_cur) et estimer_prior(p, pi_ref, iters) (EM de Saerens : itérer π ← moyenne des probabilités corrigées). Vérifiez que la correction est l’identité si π ne change pas, qu’elle augmente les probabilités quand π augmente, et que l’EM retrouve une prévalence de 0,25 à ±0,02 à partir de scores calibrés pour π_ref = 0,5.
Correction
def corriger_prior(p, pi_ref, pi_cur):
a = p * pi_cur / pi_ref; b = (1 - p) * (1 - pi_cur) / (1 - pi_ref); return a / (a + b)
def estimer_prior(p, pi_ref, iters=100):
pi = pi_ref
for _ in range(iters): pi = corriger_prior(p, pi_ref, pi).mean()
return float(pi)Exercice 4 — Politique d’alerte à taux de fausses alertes contrôlé
Écrivez seuil_effet(ref, n_cur, alpha_jour, m_vars, essais, rng) : par simulation sans dérive (rééchantillonner la référence), le seuil D de KS tel que la probabilité qu’au moins une des m_vars variables dépasse D en un jour soit ≈ alpha_jour. Puis puissance(ref, n_cur, D, decalage, essais, rng) : fraction de détections pour un décalage de moyenne donné.
Correction
def seuil_effet(ref, n_cur, alpha_jour=0.05, m_vars=10, essais=200, rng=None):
rng = rng or np.random.default_rng(0)
maxs = [max(ks_stat(ref, rng.choice(ref, n_cur)) for _ in range(m_vars)) for _ in range(essais)]
return float(np.quantile(maxs, 1 - alpha_jour))
def puissance(ref, n_cur, D, decalage, essais=100, rng=None):
rng = rng or np.random.default_rng(0)
return float(np.mean([ks_stat(ref, rng.choice(ref, n_cur) + decalage) > D for _ in range(essais)]))Fiche de cours · Exercices corrigés
Exercices corrigés (rédaction)
Vérification
Le monitoring de dérive des entrées et des prédictions est vert depuis six mois. Peut-on conclure que la qualité du modèle est stable ?
Deux questions supplémentaires
1. Pourquoi ne pas alerter sur la p-valeur d’un test de KS à grande échelle ? Elle tend vers 0 pour tout écart non nul ; alerter sur une taille d’effet calibrée sur l’impact (Théorème 3).
2. Que faire face à une dérive de prior seule ? Réajuster le seuil ou corriger les probabilités a posteriori, sans ré-entraîner.
Référence
Les mots à retenir
| Mot | Définition |
|---|---|
| Dérive de covariables / concept / prior | p(x) / p(y | x) / p(y) change. |
| KS, PSI, JS, W₁, MMD, AUC adversarial | Détecteurs 1D et multidimensionnels ; alerter sur la taille d’effet. |
| Étiquettes différées | Organiser leur arrivée : échantillon annoté, retours, juge. |
| Pondération d’importance | Corriger le risque sous dérive de covariables. |
| Correction de prior | Réajuster les probabilités sans ré-entraîner. |
| Shadow / canary / rollback | Déploiement progressif et retour arrière. |
| Boucle de retour | Le modèle façonne ses données ; exploration ou IPW. |
| Post-mortem | Chronologie, cause racine, détection, actions ; sans blâme. |
Suite
Tout commence et finit par les données.
Chapitre suivant : créer un jeu de données from scratch — collecte, annotation, qualité, nettoyage, déduplication, fuites, biais, documentation, licences, et les mathématiques de l’annotation.