CI/CD, validation des données & gouvernance — plus personne ne livre à la main. À partir d'aujourd'hui, c'est la machine qui vérifie.
Le fil rouge · où on en est
Tout est en boîte, tout est tracé
Bloc 4 : le système vit dans une image Docker. Bloc 5 : chaque expérience laisse une trace, le registry sait quel modèle est champion. Mais regardez qui appuie sur les boutons : vous. Chaque livraison dépend encore de la discipline humaine.
Mission du bloc 6 : remplacer la discipline par une chaîne — tests, données, modèle, image : tout se vérifie tout seul.
GitHub Actionstestsgouvernance
L'objectif du bloc
À la fin du bloc, un commit suffit — et si un feu est rouge, rien ne part
Tests, validation des données, contrôle du modèle, build de l'image : tout se déclenche seul à chaque push. La qualité n'est plus une promesse — c'est un mécanisme.
Bloc 6 · 6 h · théo 1h10 / prat 4h50
Quatre chantiers, une chaîne
2h00
CI/CD pour le MLGitHub Actions : workflow, tests, build & push d'image — la colonne vertébrale de la journée.
théo 0h30 + prat 1h30
1h30
Validation automatique des donnéesSchema drift, qualité : des tests unitaires pour la data, branchés dans la CI.
pratique
1h20
Tests de régression MLLe gate sur la PR-AUC : aucun modèle qui régresse ne passe.
pratique
1h10
GouvernanceModel card, audit trail, IA responsable — ce que la banque exigera.
théo 0h40 + prat 0h30
Acte 1
Le dernier maillon humain
Vendredi, 17 h 40
« C'est juste un petit fix. Je copie le fichier sur le serveur, trente secondes, et je file. »
Dernières paroles célèbres — tous les systèmes cassés un week-end ont commencé par cette phrase.
L'autopsie · lundi 9 h 02
Le week-end ruiné
Le « petit fix » embarquait un modèle jamais réévalué et une dépendance mise à jour en douce. L'API est tombée samedi à 3 h. Personne n'a rien vu avant lundi — c'est la douleur 2 du bloc 1, en version vécue.
Le problème n'est pas la personne. C'est qu'un humain fatigué était le dernier contrôle qualité.
# samedi 03:12 — première erreur 500 # lundi 09:02 — on découvre. Zéro test, zéro trace.
Une livraison à la main : rapide le vendredi, ruineuse le lundi.
Pourquoi le ML aggrave le cas
Trois choses peuvent casser, pas une
En logiciel classique, on teste le code. En ML, le code peut être parfait et le système mauvais quand même.
Le code
Un bug dans l'API, une dépendance cassée. Les tests classiques le voient : pytest, lint — le métier du logiciel depuis vingt ans.
Les données
Une colonne renommée en amont, des valeurs aberrantes. Aucun test de code ne le voit — il faut des tests de données.
Le modèle
Une PR-AUC qui régresse en silence. Ni le code ni les données ne crient — il faut un gate de performance.
Une chaîne qualité ML vérifie les trois, à chaque commit. C'est le programme de la journée.
Le mot qui fâche, défini simplement
CI/CD, sans le jargon
À chaque changement, une machine neutre rejoue toutes les vérifications — et si tout est vert, elle livre.
CI, intégration continue : tester chaque commit, automatiquement. CD, livraison continue : construire et pousser l'artefact prêt à déployer. Le contraire exact du scp de vendredi soir.
CI = vérifierCD = livrerà chaque commit, sans exception
Acte 2 · théo 0h30
Le convoyeur : GitHub Actions
Feynman · 3 minutes
Une recette déclenchée par un événement
Un workflow, c'est une fiche recette posée dans le dépôt : « quand ceci arrive (un push), fais cela (installe, teste, construis) ». GitHub lit la fiche et l'exécute, sur une machine à lui, sans qu'on demande.
Vous l'écrivez une fois. Il l'exécute à chaque commit, pour toujours — jamais fatigué, jamais un vendredi soir.
# le modèle mental complet
quand : quelqu'un pousse du code
faire : 1. récupérer le code
2. lancer les tests
3. construire l'image un pas échoue → tout s'arrête, commit rouge
Le YAML n'est que cette fiche, en syntaxe stricte.
L'anatomie d'un workflow
La fiche recette, en vrai
on: push # l'événement déclencheur jobs:
quality: # un job = une machine fraîche
runs-on: ubuntu-latest steps:# les pas, dans l'ordre
- uses: actions/checkout@v4
- run: pip install -r requirements.txt
- run: pytest # rouge ici = tout s'arrête
Trois mots à retenir : on (quand), jobs (où), steps (quoi). Le fichier vit dans .github/workflows/ — versionné avec le code, relu en PR comme du code.
Notre chaîne, étape par étape
Lint → tests → build → push
Étape 1 · lint
Le style et les erreurs bêtes
Quelques secondes pour attraper l'import oublié et la variable morte. Échouer vite, échouer tôt.
Étape 2 · tests
pytest rejoue tout
Les tests de l'API du bloc 3, exécutés sur une machine neutre. Le code qui casse ne va pas plus loin.
Étape 3 · build
L'image Docker du bloc 4
Construite par le robot, plus jamais sur un laptop. La même recette, à chaque fois, pour tout le monde.
Étape 4 · push
Au registry, versionnée
L'image part sur ghcr.io, taggée par commit : prête à déployer, traçable jusqu'à la ligne de code.
Où ça tourne
Une machine neuve à chaque exécution
Chaque run démarre sur une VM vierge — rien d'installé, aucun souvenir du run précédent. C'est une contrainte volontaire, et c'est elle qui fait la valeur du verdict.
Votre laptop
« Ça marche chez moi »
Un environnement unique, pollué par des mois d'installations
Si ça passe ici, ça ne prouve rien pour ailleurs
Le runner
Machine jetable, identique pour tous
Oblige à tout déclarer — le requirements.txt du bloc 1 devient vital
Si ça passe ici, ça passera partout
C'est l'argument de Docker au bloc 4, appliqué aux vérifications : la reproductibilité par la machine neutre.
La règle de sécurité
Jamais de mot de passe dans le code
Pour pousser l'image au registry, le workflow doit s'authentifier. Le réflexe fatal : coller le token dans le YAML — un fichier public, versionné, cloné partout. Les scanners de dépôts trouvent un credential commité en quelques minutes.
Un secret commité n'est plus un secret. Révoquer, régénérer, ranger au coffre — dans cet ordre.
# le dépôt ne contient jamais le token
- run: docker login ghcr.io \
-u ${{ github.actor }} \
-p ${{ secrets.GITHUB_TOKEN }} # ← le coffre GitHub
Settings → Secrets : chiffrés, masqués dans les logs, jamais dans Git.
Acte 3
Valider les données
L'histoire vraie de trop
Un mardi, l'équipe data amont renomme une colonne. Personne ne prévient. Le modèle continue de répondre — des chiffres faux, avec assurance.
Aucun crash, aucune alerte. Trois semaines de scores absurdes avant que quelqu'un ne remarque.
Le crime silencieux
Ça ne crashe pas. C'est pire
Un code cassé s'arrête et crie. Des données cassées ne crient jamais : le pipeline tourne, l'API répond, les dashboards sont verts — et chaque prédiction est fausse. Le ML échoue en silence.
En ML, le bug le plus cher n'est pas celui qui plante — c'est celui qui continue de répondre.
# hier — le CSV attendu
Time, V1 … V28, Amount, Class
# aujourd'hui — livré en amont, sans prévenir
Time, V1 … V28, Montant_EUR, Class
# le modèle ne crashe pas : il prédit n'importe quoi
Deux façons pour la donnée de trahir — et donc deux familles de vérifications à écrire.
Schema drift
La structure change
Colonne renommée, disparue, type qui passe de nombre à texte
Le contrat d'interface est rompu — souvent par une équipe amont qui l'ignore
Qualité
Les valeurs dérivent
Valeurs manquantes qui explosent, montants négatifs, bornes violées
La structure est intacte — le contenu est pourri
Les deux se testent. Et comme toujours dans ce cours : ce qui se teste s'automatise.
L'idée clé
Des tests unitaires pour la data
Une assertion sur les données dit : « voilà ce que j'attends — si ce n'est pas ça, refuse. »
« Amount existe, numérique, ≥ 0 » · « Class ∈ {0, 1} » · « moins de 1 % de valeurs manquantes ». Exactement le réflexe du test unitaire — sauf que le sujet du test est le CSV, pas la fonction.
attendu vs reçurefus explicitemessage qui dit quoi et où
Ce qu'on vérifie, concrètement
Quatre familles d'assertions
Famille 1
Le schéma
Les bonnes colonnes, les bons types, rien en plus, rien en moins. La première ligne de défense — celle qui aurait sauvé le mardi silencieux.
Famille 2
Les bornes
Amount ≥ 0, Class dans {0, 1}, dates plausibles. Tout ce qui a un domaine de validité se vérifie.
Famille 3
Les manquants
Un taux de null qui explose, c'est un capteur qui meurt en amont. On fixe un seuil, et on le tient.
Famille 4
Les distributions
La part de fraudes passe de 0,17 % à 3 % ? Le schéma est intact — mais quelque chose a changé le monde. Avant-goût du drift, bloc 7.
L'automatisation
La suite tourne à chaque changement de données
Écrire les assertions ne suffit pas : il faut qu'elles tournent sans qu'on y pense. Leur place : un job dans la CI.
jobs:
data-validation:
steps:
- run: python -m src.data.validate data/creditcard.csv # schéma OK · bornes OK · manquants 0.0 % · classes OK # une assertion échoue → exit 1 → commit rouge
Un CSV qui change en silence, désormais, fait du bruit : le commit vire au rouge et cite l'assertion violée. Le mardi silencieux ne peut plus arriver.
Acte 4
Le gate PR-AUC
Le principe
Aucun modèle qui régresse ne passe
Le code est testé, les données sont testées. Reste le troisième suspect : le modèle. Le gate compare chaque candidat au champion en place — moins bon, il ne part pas. Pas de débat, pas d'exception de vendredi soir.
Les deux acteurs
Le champion et le challenger
Le champion
Le modèle en production, béni par le registry MLflow du bloc 5. Sa PR-AUC sur le jeu de référence fixe la barre.
0,84 PR-AUC — la référence à battre
Le challenger
Le modèle produit par votre commit : nouvelle feature, nouveau réglage. À lui de prouver qu'il vaut mieux — ou au moins pas moins.
? c'est le gate qui tranche
Le gate rend le duel systématique : chaque commit qui touche au modèle rejoue ce match, avec les mêmes règles.
Comment ça marche
Entraîner, comparer, trancher
1 · Entraîner en CI
Sur un échantillon du dataset : quelques minutes, pas des heures. Le but n'est pas le meilleur modèle possible — c'est un verdict comparable.
2 · Comparer
Même split, même graine, même métrique : la PR-AUC du challenger face à celle du champion, à conditions strictement égales.
3 · Trancher
Sous la barre : exit 1, commit rouge, rien ne part. Au-dessus : le pipeline continue vers le build. La machine décide, personne ne négocie.
Trois étapes, un script : src/eval/gate.py. Vous l'écrivez cet après-midi.
La subtilité qui change tout
Le bruit n'est pas une régression
Deux entraînements identiques ne donnent jamais exactement la même PR-AUC : échantillonnage, initialisation… ça fluctue de quelques millièmes. Un gate à égalité stricte bloquerait des commits innocents — et l'équipe le débrancherait en une semaine.
Un gate trop strict finit débranché. Et un gate débranché ne protège plus personne.
La règle : PR-AUC ≥ champion − ε. Assez pour le bruit, pas pour une régression.
La boucle se boucle
Le contrat qualité, enfin signé
Au bloc 2, on promettait : « la PR-AUC deviendra une barrière automatique dans la CI ». C'est fait.
La métrique choisie le lundi — la PR-AUC, parce que classe rare — devient un contrat exécutable : personne, ni vous, ni un collègue pressé, ni un futur stagiaire, ne peut livrer un détecteur qui détecte moins bien. La machine tient la promesse à votre place.
Bloquer une transaction, c'est bloquer une personne — parfois au restaurant, parfois à la pharmacie. Un régulateur bancaire ne demande pas si votre modèle est bon : il demande de prouver comment il décide, sur quelles données, avec quelles limites. La gouvernance, c'est cette preuve — écrite avant qu'on la réclame.
Outil 1 · la carte d'identité
La model card
Une page, format standard, qui accompagne le modèle partout : sur quelles données il a appris, ses métriques et là où elles s'effondrent, ce pour quoi il est prévu — et ce pour quoi il ne l'est pas.
Si le modèle était un médicament, la model card serait sa notice.
# model_card.md — détecteur de fraude v3
Données : transactions UE, 2 jours, 284 807 lignes
Métriques : PR-AUC 0.84 · rappel 0.78 @ seuil 0.31
Limites : fraudes < 1 € quasi invisibles
Usage prévu : aide à la décision — pas de blocage auto
Une page honnête vaut mieux que dix slides flatteurs.
Outil 2 · la chaîne de preuve
Qui a déployé quoi, quand
La question du régulateur — et celle de l'incident de 3 h du matin — est toujours la même : ce jour-là, quel modèle décidait, et d'où venait-il ?
Git
Qui a changé quelle ligne, quand, validé par qui. L'histoire du code.
MLflow
Quel run a produit le modèle : données, paramètres, métriques. L'histoire du modèle.
La CI
Quels tests ont validé la livraison, quel build, poussé où. L'histoire de la livraison.
Trois outils déjà en place : la gouvernance ne coûte presque rien quand le MLOps est propre. L'audit trail, c'est votre pipeline qui se souvient.
IA responsable · les questions à se poser
Un détecteur juste, pas seulement bon
Question 1
Qui bloque-t-on à tort ?
L'analyse d'erreurs du bloc 2, relue en éthique : si les faux positifs touchent toujours le même profil de clients, la PR-AUC globale le cache. Il faut mesurer par segment.
Question 2
Sait-on expliquer une décision ?
« Pourquoi ma carte a-t-elle été bloquée ? » mérite mieux que « le score était haut ». Explicabilité minimale : les indices qui ont pesé le plus.
Question 3
Un humain peut-il reprendre la main ?
Notre choix d'usage : le modèle alerte, un analyste décide. Ce garde-fou se documente — dans la model card, justement.
Question 4
Saurait-on prouver tout ça ?
Mesures de biais, explications, garde-fous : sans trace écrite, ils n'existent pas aux yeux d'un auditeur.
Le cadre qui se met en place
L'Europe encadre l'IA — la banque est en première ligne
L'Union européenne a adopté un règlement sur l'IA, avec des obligations graduées selon le risque — et les usages financiers touchant les personnes sont parmi les plus surveillés.
Le détail juridique évolue et dépasse ce cours. Ce qui est stable : documentation, traçabilité, supervision humaine et gestion des risques seront exigées. Bonne nouvelle — model card, audit trail et gate forment exactement ce socle. Faire du MLOps propre, c'est prendre de l'avance sur la conformité.
Démo live · à vous de jouer
Un commit, six feux
●
lint
●
tests
●
data check
●
gate PR-AUC
●
build
●
push
en attente d'un commit…
Avant le lab · l'essentiel
Cinq réflexes à emporter
Réflexe 1
Plus de livraison à la main
Tout part d'un commit, tout passe par la chaîne. Le scp de vendredi soir est mort.
Réflexe 2
Trois suspects, trois tests
Code (pytest), données (assertions), modèle (gate PR-AUC). En ML, tester le code ne suffit jamais.
Réflexe 3
Feu rouge = on s'arrête
Un gate qu'on contourne « juste cette fois » ne protège plus personne. La tolérance ε est là pour le bruit, pas pour les exceptions.
Réflexe 4
Les secrets au coffre
Jamais de credentials dans le code. Settings → Secrets, et rien d'autre.
Et le cinquième : documenter avant qu'on le demande — model card et audit trail se construisent pendant, pas après.
Acte 6 · lab
À vous : 4h50 au clavier
Mission 1 / 4 · 1h30 · pratique
Monter la chaîne : tests, build, push
Créer .github/workflows/ci.yml : lint + pytest à chaque push, puis build de l'image Docker du bloc 4 et push vers ghcr.io — authentifié par un secret, jamais en clair. Puis cassez un test exprès, et regardez le commit virer au rouge.
Livrable : un push → badge vert sur le dépôt, image fraîche dans le registry.
GitHub Actionspytestdocker buildghcr.iosecrets
Binômes : un qui écrit le YAML, un qui lit les logs du runner — on échange à la mi-temps.
Mission 2 / 4 · 1h30 · pratique
Écrire les tests que la data mérite
Dans src/data/validate.py : les quatre familles d'assertions — schéma, bornes, manquants, distribution des classes — sur creditcard.csv. Puis un job data-validation dans la CI. Test final : renommez une colonne et vérifiez que la chaîne crie.
Livrable : le CSV sabordé fait échouer la CI, avec un message qui dit quoi et où.
pandasassertionsschéma attenduexit 1
Le mardi silencieux de l'acte 3 doit devenir techniquement impossible sur votre dépôt.
Mission 3 / 4 · 1h20 · pratique
Poser le gate PR-AUC
Dans src/eval/gate.py : entraîner sur un échantillon en CI, calculer la PR-AUC, la comparer au champion — le seuil de référence issu du bloc 5 — avec une tolérance ε. Sous la barre : exit 1. Sabotez ensuite le modèle (supprimez une feature) et regardez le gate bloquer.
Livrable : un modèle dégradé ne peut plus atteindre le registry. Le contrat du bloc 2 est signé.
Réglez ε avec soin : relancez deux fois le même entraînement et mesurez le bruit vous-mêmes.
Mission 4 / 4 · 0h30 · pratique
La model card du détecteur
Dans docs/model_card.md : données d'entraînement, métriques — y compris les limites, les fraudes qu'il rate —, usage prévu, supervision humaine. Une page, honnête. Relecture croisée entre binômes : la carte du voisin dit-elle la vérité ?
Livrable : une model card commitée — le premier document que vous montrerez à un auditeur.
model_card.mdlimites documentéesusage prévu
Le test de la carte : quelqu'un qui n'a pas suivi le projet comprend-il ce que le modèle sait — et ne sait pas — faire ?
Definition of done
La chaîne est montée quand…
Preuve 1
Le badge est vert
Un push déclenche lint, tests, validation data, gate, build, push — sans aucune intervention humaine.
Preuve 2
Le rouge bloque vraiment
Colonne renommée ou modèle dégradé : la CI refuse, avec un message exploitable en trente secondes.
Preuve 3
L'image est au registry
Taggée par commit, traçable, produite par le robot — plus jamais par un laptop.
Preuve 4
La model card existe
Une page honnête, commitée à côté du code, relue par un autre binôme.
C'est cette chaîne qui alimentera Kubernetes demain — elle devient le seul chemin vers la prod.
Le fil rouge · fin du bloc 6
La qualité est automatique
Regardez la carte : le convoyeur s'allume. Chaque changement — code, données, modèle — passe par la chaîne avant de toucher la boîte. Plus personne ne livre à la main, et le système ne dépend plus de la discipline de qui que ce soit.
Mais tout ça tourne encore sur une seule machine. Reste à faire tourner le système en vraie prod, à l'échelle : des réplicas, de la charge, des pannes.
commit → prod ✓gate armé ✓l'échelle… pas encore
La suite
Le gros morceau : Kubernetes
Bloc 7 — 12 h : réplicas, load balancing, monitoring, drift. Votre chaîne qualité livre des images — Kubernetes va les faire tourner, à l'échelle, sans dormir.