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 :
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 :
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 :
Le choix architectural clé ici est foreachBatch plutôt que le traitement continu, et ce n’est pas anodin :
Pourquoi foreachBatch ?

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 :

Trois règles d’alerte détectent les problèmes en amont :
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.
https://github.com/scottsantinho/streaming-fraud-detection
