Aller au contenu

~/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.

Les slides

Le fil rouge, pas à pas : le dépôt du score de défaut, en captures d'écran

Référence. Machine Learning, Centrale Méditerranée, option DDEFI, 2026-2027.

Ouvrir les slides (mot de passe donné par l'enseignant)

Le deck lui-même est chiffré pour les élèves du cours ; cette page le résume pour tous.

La page du cours · L'attention et le transformer, pas à pas

Plan du deck

  1. 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.
  2. Les commandes. make reproduce reconstruit à l'identique données, coupe, modèles et métriques ; make test vérifie treize propriétés en quelques secondes.
  3. Le notebook guide. Sept étapes, une par séance, chacune close par une liste de choses à ne pas faire.
  4. 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.
  5. Travailler en équipe. Branches courtes, demandes de fusion relues, une CI qui reproduit deux fois, un commit par idée, une interface mince.
  6. 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.

Tableau 1 : Où se trouve quoi dans le dépôt.
EmplacementRô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.yamlGraine, coûts d'erreur, seuil, colonnes exclues
app/app.pyUne interface minimale : un client entre, une décision sort
Documents à la racineDECISIONS.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.

Retour au sommaire.

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.

Tableau 2 : Pli de validation, graine fixée, tel qu'affiché par make reproduce (coûts : 600 € par bon client refusé, 4 500 € par défaut accepté).
ModèleAUCLog-lossBrierCoût par dossier
Taux de base0,50,52840,1723467,3 €
Logistique calibrée0,72320,46660,146468,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 pp 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.

H(p)=−pln⁡p−(1−p)ln⁡(1−p),H(0.2212)≈0.5284 natsH(p) = -p\ln p - (1-p)\ln(1-p), \qquad H(0.2212) \approx 0.5284 \text{ nats}
(1)

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é à 10−1210^{-12} 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.

Retour au sommaire.

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 :

t∗=cFPcFP+cFN=600600+4500≈0.1176t^{*} = \frac{c_{\mathrm{FP}}}{c_{\mathrm{FP}} + c_{\mathrm{FN}}} = \frac{600}{600 + 4500} \approx 0.1176
(2)

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.

Retour au sommaire.

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.

Tableau 3 : Coût attendu par dossier sur le pli de validation, un bon client refusé coûtant 600 € et un défaut accepté 4 500 €.
DécisionCoût par dossier
Refuser si le dernier retard de paiement ≥ 1 mois541 €
Accepter tous les demandeurs995 €
Refuser tous les demandeurs467 €
Logistique calibrée au seuil t∗=0.1176t^{*} = 0.1176469 €

À 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.

Tableau 4 : Le tableau de sensibilité du deck : coût par dossier selon le coût d'un défaut accepté.
Coût d'un défautRè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

  1. Écart par dossier. 133−112=21133 - 112 = 21 euros en faveur de la logistique, soit 21/133≈16%21 / 133 \approx 16\% de moins que la règle.
  2. Par an. Sur 6 000 dossiers, environ 21×6000=126 00021 \times 6000 = 126\,000 euros ; le deck, parti de coûts non arrondis, annonce environ 130 000 euros.
  3. 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.

Retour au sommaire.

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 [0,1][0, 1].
  • 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.

Retour au sommaire.

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

  1. Partir de main. Se placer sur main, tirer les changements, créer une branche courte nommée d'après une seule tâche.
  2. Un commit par idée. Des messages au présent qui disent quoi et pourquoi, pour que git log se lise comme le journal du projet ; jamais de données, de modèles, de secrets ni de sorties de notebook dans un commit.
  3. 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.
  4. Fusionner avec un commit de fusion. Pas de squash, pour que l'historique garde la trace de chaque branche.
  5. Étiqueter le rendu. Le dernier commit poussé avant l'échéance fait foi ; une étiquette comme jalon-1 le 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.

Retour au sommaire.

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.

Tableau 5 : Les trois jalons et les fichiers qui les prouvent.
JalonCe 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 à 10−610^{-6} 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.
  • SEX n'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.

Retour au sommaire.

À retenir

  1. Versionner le code, la configuration et les empreintes ; régénérer données, plis et modèles d'une seule commande.
  2. 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.
  3. 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.
  4. 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.
  5. É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.
  6. 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.

  1. IntroductionHivers de l'IA, emploi et méthode
  2. OutilsKaggle, GitHub, Python : les outils
  3. Séance 1Cadrage de projet et moindres carrés
  4. Séance 2Vraisemblance et régression logistique
  5. Séance 3Descente de gradient, ridge et lasso
  6. Séance 4Seuil de décision et ML reproductible
  7. DevoirUn modèle de référence défendable
  8. Séance 5Noyaux, SVM et arbres de décision
  9. Séance 6Forêts, boosting, comparaison honnête
  10. Séance 7SHAP, équité, ACP et partitionnement
  11. DevoirUn modèle champion documenté
  12. Séance 8Réseaux de neurones, dérive, prédiction conforme
  13. DevoirUn projet de machine learning livré
  14. Séance 9Soutenir un projet de machine learning
  15. RéférenceProjet de score de défaut de crédit
  16. RéférenceRendements boursiers : XGBoost et sentiment
  17. RéférenceLe code Python de chaque modèle
  18. RéférenceDeep learning, l'essentiel
  19. RéférenceDonnées ouvertes et projets par secteur
  20. RéférenceManuel de l'expert data et IA
  21. RéférenceData engineering, SRE et cloud
  22. RéférencePanorama des modèles de machine learning

Toutes les fiches, en liste de lecture

L'attention et le transformer, pas à pas

À 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 : .