~/teaching · machine learning · référence
Un projet de score de défaut de crédit, pas à pas : le dépôt reproductible bâti sur les données UCI Default of Credit Card Clients
Un dépôt, deux commandes, treize tests, et la question de tout comité : pourquoi un modèle ?
Résumé
Ce résumé porte sur un deck de référence du cours de Machine Learning de Centrale Méditerranée (option DDEFI, 2026-2027). Il parcourt, capture après capture, le projet fil rouge que chaque équipe reçoit en début de cours : un score de défaut de crédit construit sur le jeu de données UCI Default of Credit Card Clients (Yeh et Lien, 2009, CC BY 4.0), livré sous forme de dépôt complet avec son script de données, sa coupe figée, deux modèles de départ, ses tests, son intégration continue, ses notebooks guides et ses gabarits de documents.
La note sert au-delà du cours à qui veut voir à quoi ressemble un petit dépôt de machine learning sérieux : quoi versionner et quoi régénérer, ce que vérifie un test en apprentissage automatique, pourquoi le seuil de décision vit dans un fichier de configuration, comment montrer qu'un modèle bat la règle qu'une banque applique déjà, et quelles propriétés de robustesse écrire avant de regarder les chiffres.
Elle se lit avec la séance sur le seuil de décision et le dépôt reproductible, la note outils sur Git, GitHub et Python, et les trois notes de jalons : modèle de départ, champion et livraison.
Plan du deck
- Le dépôt et la façon dont il arrive. La carte des fichiers, l'historique dans l'ordre du cours, et la première demande de fusion que chaque équipe relit et fusionne.
- Les commandes.
make reproducereconstruit à l'identique données, coupe, modèles et métriques ;make testvérifie treize propriétés en quelques secondes. - Le notebook guide. Sept étapes, une par séance, chacune close par une liste de choses à ne pas faire.
- Pourquoi un modèle, et tient-il ?. Le modèle face aux règles métier en euros, la dépendance aux coûts, six tests de robustesse et une simulation de dérive.
- Travailler en équipe. Branches courtes, demandes de fusion relues, une CI qui reproduit deux fois, un commit par idée, une interface mince.
- Ce qui reste à faire. Trois jalons, un modèle lourd volontairement vide, et cinq règles du dépôt avec leurs gardiens.
Le projet et le dépôt
Il s'agit d'estimer, pour chacun de 30 000 titulaires de carte, la probabilité de faire défaut le mois suivant, afin d'aider un analyste crédit à décider ; le dépôt rend cette estimation reconstructible par n'importe qui.
Les données sont la table UCI Default of Credit Card Clients : 30 000 clients d'une banque taïwanaise en 2005, avec leur plafond de crédit, des champs démographiques, six mois de statut de remboursement, de montants facturés et payés, et une cible binaire, le défaut du mois suivant. Le fichier brut n'est jamais versionné : un script le télécharge et compare son empreinte SHA-256 à celle du dépôt, si bien qu'un changement silencieux de la source arrête la chaîne.
La coupe apprentissage / validation / test (train / validation / test split) est faite une fois, stratifiée, 60 % pour l'apprentissage, 20 % pour la validation, 20 % pour le test, avec une graine aléatoire (random seed) fixe, et l'empreinte de chaque pli est conservée aussi. Deux modèles viennent avec le dépôt : le taux de base (base rate), qui prédit la même probabilité pour tous, et une régression logistique dans un Pipeline scikit-learn, calibrée sur le pli de validation. Le modèle lourd est laissé aux équipes.
| Emplacement | Rôle |
|---|---|
src/credit/ | Six modules importables (données, coupe, variables, modèle, évaluation, comparaison) : le seul endroit où vit la logique |
batch/ | Les scripts de préparation, d'entraînement, de prédiction et d'évaluation ; ils appellent src/credit, ils ne le recopient jamais |
notebooks/ | Un guide, une exploration, et un notebook sur le pourquoi du modèle et sa robustesse |
tests/ | Treize tests de propriété, lancés en local et dans la CI |
config.yaml | Graine, coûts d'erreur, seuil, colonnes exclues |
app/app.py | Une interface minimale : un client entre, une décision sort |
| Documents à la racine | DECISIONS.md (journal des décisions) et les gabarits de fiche de modèle, fiche de données, étude d'ablation, plan de surveillance et note au client |
La règle : rien de régénérable n'est versionné, ni données brutes, ni plis, ni modèle entraîné. Le .gitignore les refuse et les commandes les reconstruisent. L'historique des commits suit le cours, du squelette et du cadrage jusqu'aux tests, aux notebooks, aux gabarits de jalons et à l'interface, en passant par les données, la coupe et les modèles : le lire, c'est lire le projet. Chaque équipe reçoit l'ensemble sous forme de demande de fusion (pull request) dans son propre dépôt : un membre relit les changements, un autre fusionne, puis tout le monde lance les deux commandes.
Deux commandes : make reproduce et make test
Si la même commande ne donne pas deux fois les mêmes chiffres, c'est que les données, le code, la graine ou l'environnement ont bougé, et il faut savoir lequel.
make reproduce # téléchargement et empreinte, coupe, entraînement, métriques make test # treize tests de propriété, quelques secondes make compare # jalon 2 seulement, lit le pli de test une fois make app # un client, une décision
make reproduce enchaîne quatre cibles. L'étape des données s'arrête si l'empreinte a changé, car on ne modifie pas les données sans une entrée au journal des décisions. L'étape de coupe écrit les plis et leurs empreintes, qu'un test fige ensuite. L'étape d'entraînement ajuste les deux modèles fournis. L'étape des métriques mesure sur le seul pli de validation et écrit un metrics.json. Le pli de test n'est pas lu ici : il ne s'ouvre qu'une fois, avec make compare, au deuxième jalon.
| Modèle | AUC | Log-loss | Brier | Coût par dossier |
|---|---|---|---|---|
| Taux de base | 0,5 | 0,5284 | 0,1723 | 467,3 € |
| Logistique calibrée | 0,7232 | 0,4666 | 0,146 | 468,6 € |
Le taux de défaut vaut 22,12 % dans chaque pli, par construction de la coupe stratifiée. La log-loss du modèle naïf n'a rien d'arbitraire : prédire le taux de base pour tous donne exactement l'entropie de la cible, le plancher sous lequel tout modèle utile doit descendre. On la lit avec le score de Brier (Brier score), l'écart quadratique moyen entre probabilité et issue.
make test vérifie treize propriétés en quelques secondes. Un test de propriété (property-based test) en apprentissage automatique ne dit pas « l'AUC est bonne » : il énonce ce qui doit tenir quoi qu'il arrive. Le contrat de schéma (schema contract), avec colonnes, nombre de lignes, bornes, cible 0/1 et valeurs manquantes ; une coupe dont les empreintes ne changent jamais et où aucun client n'apparaît dans deux plis ; un modèle qui atteint une AUC de 1 sur des données séparables, faute de quoi le code d'entraînement est cassé ; des prédictions inchangées quand on permute les lignes ; un modèle rechargé égal au modèle entraîné à près ; un modèle naïf égal au taux de base ; une logistique qui le bat. S'y ajoutent six propriétés de robustesse, décrites plus bas.
Le notebook guide : le seuil est une décision, la fuite est l'ennemie
Le notebook importe le code du dépôt au lieu de le recopier, et il montre que le seuil vient des coûts, pas du modèle.
Le notebook guide compte sept sections, une par séance : cadrage, coupe figée, modèle naïf puis logistique, seuil, puis les trois jalons. Sa première cellule importe src/credit. Un notebook qui colle sa propre copie du code finit vite par présenter autre chose que ce qui tourne. Chaque section se termine par une courte liste d'interdits, qui forment ensemble un catalogue de la fuite de données (data leakage) : calculer des corrélations sur tout le jeu avant la coupe, recouper avec une autre graine, standardiser avant la coupe, régler les hyperparamètres sur le pli de validation, jeter un œil au pli de test.
Deux garde-fous se voient à l'étape logistique. Une assertion vérifie que la colonne SEX ne figure pas parmi les variables : c'est un principe, pas un choix de performance. Et la standardisation vit dans le Pipeline, si bien que moyennes et variances sont apprises sur le seul pli d'apprentissage, ce qui supprime la fuite la plus fréquente du cours.
Avec des probabilités calibrées, le seuil qui minimise le coût attendu (expected cost) d'une décision refuser/accepter ne dépend que des deux coûts d'erreur :
Sur le pli de validation, le minimum empirique de la courbe de coût tombe à 0,045, loin de 0,1176, et la courbe est presque plate entre les deux. C'est le diagnostic honnête : le modèle de référence (baseline) trie mal les faibles risques. Le dépôt garde le seuil théorique, défendable, l'écrit dans config.yaml et le justifie dans le journal des décisions. Un seuil retouché à la main dans un script batch ou dans l'interface serait une décision que personne n'a prise.
Pourquoi un modèle ? Face aux règles métier, en euros
Avant tout apprentissage automatique, une banque refuse déjà les clients en retard de paiement : le modèle doit battre cette règle en argent, pas seulement en AUC.
| Décision | Coût par dossier |
|---|---|
| Refuser si le dernier retard de paiement ≥ 1 mois | 541 € |
| Accepter tous les demandeurs | 995 € |
| Refuser tous les demandeurs | 467 € |
| Logistique calibrée au seuil | 469 € |
À ces coûts, le modèle de départ ne fait pas mieux que le bon sens le plus rustique : refuser presque tout le monde coûte à peu près autant. Avec 22 % de défauts et un défaut 7,5 fois plus cher qu'un client perdu, une AUC de 0,72 ne sépare pas assez bien les faibles risques. C'est un fait sur ces coûts et ce modèle, pas un verdict sur l'apprentissage automatique, et c'est le point de départ du deuxième jalon.
| Coût d'un défaut | Règle (retard ≥ 1) | Logistique | Économie |
|---|---|---|---|
| 600 € | 133 € | 112 € | 21 € |
| 1 500 € | 227 € | 226 € | 1 € |
| 3 000 € | 384 € | 374 € | 10 € |
| 4 500 € | 541 € | 460 € | 81 € |
Exemple chiffré : quand les deux erreurs coûtent autant
- Écart par dossier. euros en faveur de la logistique, soit de moins que la règle.
- Par an. Sur 6 000 dossiers, environ euros ; le deck, parti de coûts non arrondis, annonce environ 130 000 euros.
- Avec son incertitude. L'AUC de validation, 0,723, a un intervalle bootstrap à 95 % qui s'étend de 0,706 jusqu'à 0,740 : un concurrent à 0,735 ne serait pas établi comme meilleur.
La leçon pour une note au client : les coûts sont des hypothèses fournies par le métier, et la valeur d'un modèle bouge avec eux. L'argument se montre sur plusieurs niveaux de coût, avec son intervalle.
Six tests de robustesse, écrits avant de regarder
Un modèle robuste est un modèle pour lequel on a écrit à l'avance ce qu'il doit faire quand les données bougent, et transformé chaque énoncé en test automatique.
- Intervalle. L'AUC bootstrap est nettement éloignée de 0,5 et son intervalle fait moins de 0,06 de large.
- Bruit. Une perturbation de 1 % des montants ne déplace aucune probabilité prédite de plus de 2 points.
- Monotonie. Un mois de retard de paiement en plus ne fait jamais baisser le risque prédit.
- Entrées absurdes. Un plafond d'un milliard ou un âge de 200 ans donnent encore une sortie dans .
- Stabilité. Cinq rééchantillonnages du pli d'apprentissage donnent des AUC à moins de 0,01 l'une de l'autre.
- Tranches. Aucun groupe de situation familiale ne descend sous une AUC de 0,65.
Chaque propriété est à la fois un test de tests/test_robustness.py et une cellule de notebook qui l'explique. Les tests disent franchement leurs limites : pour un client au plafond d'un milliard, le modèle sort une probabilité de 0,0, qui respecte les bornes sans être une réponse sensée. En production, le contrat de schéma doit rejeter une telle ligne avant que le modèle ne la voie.
La dernière cellule simule une dérive (drift) : tous les plafonds doublent, et le risque moyen prédit baisse de 1,4 point. Personne ne peut dire si c'est juste, puisque le modèle n'a jamais vu de tels clients. C'est exactement pourquoi on surveille les entrées après la mise en service, sujet de la séance sur la dérive et la prédiction conforme.
Travailler en équipe : branches, demandes de fusion, CI, interface
La branche principale est la version qui marche ; chaque changement passe par une branche courte, une demande de fusion relue par quelqu'un d'autre et une CI qui reproduit les chiffres.
Le cycle que décrit le guide des branches
- Partir de main. Se placer sur
main, tirer les changements, créer une branche courte nommée d'après une seule tâche. - Un commit par idée. Des messages au présent qui disent quoi et pourquoi, pour que
git logse lise comme le journal du projet ; jamais de données, de modèles, de secrets ni de sorties de notebook dans un commit. - Demande de fusion. Trois lignes : ce qu'elle change, comment le vérifier, ce qui reste ; relue par quelqu'un d'autre que son auteur.
- Fusionner avec un commit de fusion. Pas de squash, pour que l'historique garde la trace de chaque branche.
- Étiqueter le rendu. Le dernier commit poussé avant l'échéance fait foi ; une étiquette comme
jalon-1le marque.
Le fichier d'intégration continue (continuous integration, CI) installe les versions épinglées, lance make reproduce et make test, puis recalcule les métriques et compare les deux metrics.json. Si une seule ligne diffère, la vérification est rouge et la demande de fusion ne passe pas. C'est exactement le critère d'entrée du premier jalon (milestone) : une autre équipe clone votre dépôt et retrouve vos chiffres.
L'interface, app/app.py, est une page Streamlit d'une soixantaine de lignes : des champs pour les variables, un bouton, la probabilité et la décision. Elle charge le même Pipeline que le batch et lit le seuil dans config.yaml, sans aucune logique métier propre. Un calcul modifié dans la seule interface divergerait en silence du batch de nuit, qui fait la même chose sur tout un fichier CSV.
Ce qui reste : trois jalons, un modèle lourd, cinq règles
Le dépôt est un point de départ ; trois jalons le complètent, et le projet final reprend la même structure sur un autre sujet.
| Jalon | Ce qui doit exister dans le dépôt |
|---|---|
| 1. Modèle de départ défendable (novembre) | Une CI verte sur la machine d'une autre équipe ; une étude d'ablation (ablation study) pré-enregistrée dans une issue, puis calculée ; dix entrées au journal des décisions ; des notebooks MCO et logistique écrits à la main qui retrouvent scikit-learn à près |
| 2. Champion documenté (février) | Le modèle lourd écrit ; la règle du champion au journal avant un unique make compare ; une fiche de modèle (model card) avec un audit d'équité sur la situation familiale ; un test apparié corrigé |
| 3. Projet livré (mars) | La même structure sur le sujet de l'équipe : plan de surveillance, note au client, rapport, diapositives, interface, fiche individuelle, puis la soutenance |
Le modèle lourd est vide à dessein : la fonction de src/credit/model.py liste ce qu'il faut faire (gradient boosting dans un pipeline à graine fixe, hyperparamètres par validation croisée sur le pli d'apprentissage, même calibration que le modèle de départ, règle du champion écrite d'abord, test apparié corrigé de Nadeau et Bengio) et lève une erreur tant qu'elle n'est pas écrite. make compare refuse aussi de tourner tant que le journal des décisions ne contient pas la règle du champion. Écrire un boosting prend quelques secondes avec un assistant ; expliquer chaque hyperparamètre et montrer l'intervalle qui sépare le champion de la logistique, voilà le vrai travail.
- Les notebooks importent
src/credit; ils ne le recopient pas. - Tout ce qui s'apprend s'apprend sur le pli d'apprentissage, jamais sur le jeu entier.
- Le pli de test se lit une seule fois, par
make compare, après l'écriture de la règle du champion. SEXn'entre jamais dans le modèle ; la situation familiale est permise, avec un audit d'équité.- Le code peut être écrit avec un assistant d'IA, mais chaque membre doit savoir en expliquer chaque ligne à l'oral.
Quatre des cinq règles ont un gardien automatique : la fonction de variables refuse les colonnes interdites, le Pipeline porte le prétraitement, make compare vérifie le journal, les tests figent la coupe. La cinquième se vérifie en personne.
À retenir
- Versionner le code, la configuration et les empreintes ; régénérer données, plis et modèles d'une seule commande.
- Un test en apprentissage automatique vérifie une propriété qui doit toujours tenir, comme une coupe figée ou une AUC parfaite sur des données séparables, pas un bon score.
- Le seuil de décision découle des coûts d'erreur et vit dans la configuration ; avec ces coûts, il vaut 600/5 100, environ 0,118.
- Un modèle doit battre la règle déjà en place, en euros et sur plusieurs niveaux de coût, avec un intervalle ; ici la logistique n'y parvient que modestement.
- Écrire les propriétés de robustesse avant de regarder les chiffres, et reconnaître ce qu'elles ne tranchent pas, comme le comportement sous une dérive jamais vue.
- Des branches courtes, des demandes de fusion relues et une CI qui reproduit deux fois gardent la branche principale digne de confiance.
Glossaire
- coupe apprentissage / validation / test (train / validation / test split)
- Les trois plis disjoints : un pour ajuster, un pour calibrer et choisir le seuil, un lu une seule fois pour la comparaison finale.
- graine aléatoire (random seed)
- Le nombre fixé qui rend la coupe et l'entraînement répétables.
- fuite de données (data leakage)
- Tout usage, pendant l'ajustement, d'une information venant de la validation, du test ou du futur.
- taux de base (base rate)
- La part de défauts dans les données, 22,12 % ici ; la prédiction du modèle naïf.
- score de Brier (Brier score)
- L'écart quadratique moyen entre probabilité prédite et issue ; plus bas, c'est mieux.
- coût attendu (expected cost)
- Le coût moyen par dossier d'une règle de décision, chaque erreur pondérée par son prix.
- test de propriété (property-based test)
- Un test qui vérifie une propriété censée tenir quelles que soient les données, plutôt qu'un score particulier.
- contrat de schéma (schema contract)
- Les colonnes, types et bornes déclarés que toute ligne d'entrée doit respecter.
- intégration continue (continuous integration, CI)
- La reproduction et les tests automatiques du dépôt à chaque push et à chaque demande de fusion.
- demande de fusion (pull request)
- Un changement proposé sur une branche, relu par quelqu'un d'autre avant d'être fusionné dans main.
- jalon (milestone)
- Un rendu daté, identifié par le dernier commit poussé avant l'échéance et en général une étiquette.
- fiche de modèle (model card)
- Un document qui dit à quoi sert un modèle, comment il a été évalué, et ses limites.
- étude d'ablation (ablation study)
- Retirer un groupe de variables, de façon pré-enregistrée, pour mesurer ce qu'il apporte vraiment.
- modèle de référence (baseline)
- Le premier modèle sérieux, ici la logistique calibrée, que tout modèle plus lourd doit battre de façon établie.
Les autres fiches du cours
Un résumé public par deck, dans l'ordre du cours.
Précédent : Soutenir un projet de machine learningSuivant : Rendements boursiers : XGBoost et sentiment
- IntroductionHivers de l'IA, emploi et méthode
- OutilsKaggle, GitHub, Python : les outils
- Séance 1Cadrage de projet et moindres carrés
- Séance 2Vraisemblance et régression logistique
- Séance 3Descente de gradient, ridge et lasso
- Séance 4Seuil de décision et ML reproductible
- DevoirUn modèle de référence défendable
- Séance 5Noyaux, SVM et arbres de décision
- Séance 6Forêts, boosting, comparaison honnête
- Séance 7SHAP, équité, ACP et partitionnement
- DevoirUn modèle champion documenté
- Séance 8Réseaux de neurones, dérive, prédiction conforme
- DevoirUn projet de machine learning livré
- Séance 9Soutenir un projet de machine learning
- RéférenceProjet de score de défaut de crédit
- RéférenceRendements boursiers : XGBoost et sentiment
- RéférenceLe code Python de chaque modèle
- RéférenceDeep learning, l'essentiel
- RéférenceDonnées ouvertes et projets par secteur
- RéférenceManuel de l'expert data et IA
- RéférenceData engineering, SRE et cloud
- RéférencePanorama des modèles de machine learning
À propos de ces fiches
Écrites par l'enseignant à partir des slides du deck, comme un résumé public : les idées clés, les formules et un exemple calculé. Les exercices, leurs corrigés, les contrôles et la notation restent dans le deck, pour les élèves du cours.
Révision : .