Pourquoi ce projet existe
La plupart des projets de détection de fraude qu’on trouve en ligne s’arrêtent au notebook. On entraîne un modèle, on affiche un score AUC-ROC, on trace peut-être une matrice de confusion, et c’est terminé. Mais dans le monde réel, un modèle qui reste dans un Jupyter notebook ne détecte rien.
Je voulais aller plus loin : entraîner un modèle, le brancher sur un pipeline de micro-batching, et le regarder scorer des transactions au fil de l’eau, avec du routage, de la tolérance aux pannes et du monitoring intégrés. Pas parce que le dataset l’exigeait (c’est un CSV Kaggle), mais parce que c’est après le notebook que les vrais défis techniques commencent.
C’était aussi l’occasion de monter un vrai pipeline de micro-batching from scratch. J’avais déjà utilisé Spark en mode batch, mais jamais câblé de bout en bout avec Kafka, PostgreSQL et Prometheus dans un seul stack Docker Compose. Le cas d’usage fraude m’a fourni une charge de travail concrète et réaliste pour le mettre à l’épreuve.
En bref : le modèle ML est un moyen, pas une fin. Le vrai projet, c’est le pipeline autour.
Le volet ML
Le dataset utilisé est l’IEEE-CIS Fraud Detection de Kaggle : 590 000 transactions par carte bancaire, 434 features, et un taux de fraude de 3,5 %. Le déséquilibre de classes est important : la grande majorité des transactions sont légitimes, ce qui rend l’accuracy trompeuse. Je me suis donc appuyé sur l’AUC-ROC et les courbes precision-recall.
L’analyse exploratoire a révélé plusieurs choses. Les V-features (V1–V339) sont anonymisées et beaucoup affichent plus de 70 % de valeurs nulles. Les données d’identité ne couvrent que 24 % des transactions : impossible de s’appuyer en production sur un champ absent les trois quarts du temps. En revanche, l’heure de la journée et les incohérences de domaine email se sont révélées être de bons indicateurs de fraude, d’où la création de ces features.
La sélection de features repose sur une approche hybride :
- • Information Mutuelle pour capturer les dépendances individuelles feature-cible
- • Feature Importance via XGBoost pour capturer les effets d’interaction
- • Fusion des top 50 de chaque méthode, complétée par 5 features de domaine forcées (TransactionAmt, ProductCD, card4, card6, hour)
- • Résultat : 76 features retenues sur les 434 d’origine
J’ai comparé LightGBM et XGBoost via une validation croisée stratifiée à 5 folds. Les deux étaient au coude à coude en CV, mais LightGBM l’a emporté sur le holdout temporel, tout en étant plus rapide à entraîner et moins gourmand en mémoire : un avantage non négligeable quand le scoring tourne dans des executors Spark.
| Métrique | Valeur |
|---|---|
| AUC-ROC (holdout) | 0,8779 |
| PR-AUC (holdout) | 0,4661 |
| Architecture | LightGBM : 300 arbres, max_depth=6 |
| Pondération des classes | scale_pos_weight=27,46 (pénalise la fraude manquée 27×) |
Un choix délibéré : le holdout est temporel (premiers 80 % des jours pour l’entraînement, derniers 20 % pour le test), et non un split aléatoire. Les patterns de fraude évoluent dans le temps : un split aléatoire revient à faire fuiter le futur dans le passé et gonfle artificiellement les scores.
Trois niveaux, pas binaire
La plupart des systèmes anti-fraude raisonnent en binaire : approuver ou bloquer. C’est trop brutal. Un faux positif bloque un client légitime et coûte sa valeur à vie. Un faux négatif laisse passer la fraude et entraîne des remboursements. Il faut un entre-deux.
D’où l’utilisation de deux seuils qui découpent le trafic en trois niveaux :
| Niveau | Plage de score | Trafic | Comportement |
|---|---|---|---|
| Approuvé | < 0,2 | 61,1 % | Auto-approuvé, 88,4 % de la fraude détectée avant ce point |
| En attente | 0,2 – 0,8 | 35,2 % | Mis en file pour revue humaine manuelle |
| Rejeté | ≥ 0,8 | 3,6 % | Auto-refusé, précision de 44,7 % |
Ces seuils ne sont pas choisis au doigt mouillé mais découlent d’un modèle de coût :
- • Manquer une fraude coûte 3× le montant de la transaction (remboursements + pénalités)
- • Bloquer un client légitime coûte 5× sa valeur à vie (risque de churn)
- • La revue manuelle coûte 5 $ par transaction
- • Les seuils 0,2/0,8 économisent ~400 K$ par rapport à une baseline naïve 0,3/0,7 à 15 % de churn
“88,4 % de la fraude est détectée avant même qu’une transaction n’atteigne le niveau d’approbation automatique. Les 11,6 % restants atterrissent en attente de revue, où un humain peut trancher.”
Architecture en un coup d’œil
Tout tourne dans un seul Docker Compose : 12 services, zéro dépendance cloud :
| Composant | Technologie | Pourquoi |
|---|---|---|
| File de messages | Kafka 4.0 (KRaft) | Replay durable, distribué, sans Zookeeper |
| Streaming | Spark 4.0 Structured Streaming | Micro-batching tolérant aux pannes, intégration Kafka native |
| Modèle ML | LightGBM | Rapide, peu gourmand en mémoire, égale XGBoost en précision |
| Base de données | PostgreSQL 18 | ACID, driver JDBC intégré à Spark |
| Pool de connexions | PgBouncer | Pooling en mode transaction entre Spark et PostgreSQL |
| Monitoring | Prometheus + Grafana | Standard industriel, time-series natif, gratuit |
| Sérialisation | JSON | Pas d’évolution de schéma nécessaire pour le replay du dataset |

Scoring en micro-batch
Le job Spark lit depuis Kafka, score chaque micro-batch avec LightGBM, et route les résultats vers trois tables PostgreSQL. Toutes les 5 secondes, un nouveau batch arrive.
Le flux à l’intérieur de chaque micro-batch :
- Lecture du JSON depuis Kafka (sélection des 76 champs nécessaires sur 434)
- Feature engineering : calcul de l’heure depuis TransactionDT, calcul de email_mismatch
- Label encoding : application des mêmes mappings de catégories que l’entraînement
- Imputation par médiane : remplissage des nulls avec les médianes calculées à l’entraînement
- Scoring distribué : mapInPandas envoie le prétraitement et predict_proba() aux workers Spark via le modèle broadcast
- Routage par seuil et écriture avec upsert (INSERT ON CONFLICT DO NOTHING) via PgBouncer vers transactions_approved, transactions_pending_review ou transactions_rejected
- Mise à jour des métriques Prometheus (compteurs, histogramme de latence, gauge du taux de fraude)
Le choix architectural clé ici est foreachBatch plutôt que le traitement continu, et ce n’est pas anodin :
Pourquoi foreachBatch ?
- • Écritures multi-sorties : trois tables PostgreSQL différentes par batch, impossible avec un seul sink continu
- • ML distribué : mapInPandas distribue le scoring LightGBM aux workers, avec predict_proba vectorisé sur des tableaux numpy par partition
- • Intégration Prometheus : mise à jour des métriques après chaque batch, pas après chaque ligne
- • Checkpointing : les offsets Kafka sont suivis automatiquement, le redémarrage reprend au dernier batch sans perte de données

Observabilité
Un pipeline de scoring sans monitoring, c’est naviguer à l’aveugle. Il faut détecter la dérive du modèle, le retard Kafka ou les pics de latence avant que ça ne devienne un problème visible.
| Métrique | Type | Mesure |
|---|---|---|
| transactions_scored_total | Counter | Total de transactions traitées |
| approved/pending/rejected_total | Counters | Distribution du routage |
| fraud_rate | Gauge | Taux de rejet (moyenne glissante sur 10 batchs) |
| null_feature_rate | Gauge | Qualité des données (% moyen de nulls par batch) |
| batch_size | Gauge | Transactions par micro-batch |
| scoring_duration_seconds | Histogram | Percentiles de latence (p50/p95/p99) |
Le dashboard Grafana comporte quatre lignes :
- • Santé de l’infrastructure : nombre de brokers, cibles Prometheus, partitions du topic, total scoré
- • Débit Kafka : taux de production par partition, retard du consumer group dans le temps
- • Scoring ML : gauge du taux de fraude (code couleur), taille des batchs, latence du scoring
- • Performance : percentiles de latence dans le temps (p50/p95/p99)

Trois règles d’alerte détectent les problèmes en amont :
- • Pic de retard consommateur : lag > 100 pendant 30s → Warning (le scorer est peut-être arrêté)
- • Anomalie du taux de fraude : taux de rejet > 5 % pendant 1min → Critical (dérive du modèle ou données corrompues)
- • Latence de scoring élevée : p95 > 5s pendant 1min → Warning (santé des executors dégradée)
Lire le détail technique
Le code complet est documenté dans le repo : notebooks ML, pipeline Spark, producteur Kafka, stack Docker Compose, dashboards Grafana et règles d’alerte.
BIENTÔT DISPONIBLE
