Skip to content

Repository files navigation

AML Feature Engineering — Détection de blanchiment : ML vs rule-based

Projet de feature engineering et machine learning appliqué à la détection de transactions de blanchiment d'argent (LCB-FT). Comparaison rigoureuse d'un système ML calibré vs un système rules-based traditionnel sur le dataset SAML-D.

Contexte métier

Les institutions bancaires et MSB (Money Service Business) sont tenues par les régulations LCB-FT (Tracfin en France, FATF / GAFI à l'international, ACPR pour le contrôle) de détecter et signaler les transactions suspectes. Les systèmes de détection traditionnels reposent sur des règles métier (montant > seuil, pays sanctionné, payment type à risque). Ces règles ont des limites bien connues :

  • Beaucoup de faux positifs : volume d'alertes souvent ingérable par les équipes compliance
  • Pas de couverture des typologies subtiles (smurfing, layering, fan-out)
  • Seuils statiques difficiles à calibrer face à l'évolution des typologies

Ce projet quantifie le gain d'un système ML vs un rules-based de référence, sur des critères opérationnels compliance (recall, volume d'alertes hebdomadaire, défendabilité métier).

Dataset

SAML-D — Synthetic Anti-Money Laundering Dataset (Oztas et al., Bournemouth University, 2023). Dataset complet : 9.5 M transactions, 28 typologies (11 normales + 17 suspectes), 0.1039 % laundering rate. Échantillon utilisé dans ce projet : 800 000 transactions, 18 pays, ~0.1 % laundering rate, période oct. 2022 - août 2023. Caractère synthétique mentionné explicitement dans toutes les communications du projet (caveat méthodologique).

Citation : B. Oztas, D. Cetinkaya, F. Adedoyin, M. Budka, H. Dogan, G. Aksu, « Enhancing Anti-Money Laundering: Development of a Synthetic Transaction Monitoring Dataset », 2023 IEEE ICEBE, Sydney, doi : 10.1109/ICEBE59045.2023.00028. Repo officiel : github.com/BOztasUK/Anti_Money_Laundering_Transaction_Data_SAML-D.

Spécificité du dataset : SAML-D incorpore 15 structures de réseaux graphiques représentant des flux de transactions complexes (fan-in / fan-out / layering / smurfing chains). C'est ce qui motive l'inclusion de features graphes dans ce projet : PageRank + degrés (NetworkX, cellule 13) et embeddings autoencoder per-compte (cellule 46) — sans exploitation explicite de cette structure graphique, le signal des typologies layering / fan-out passe inaperçu d'un classifieur tabulaire standard.

Split temporel 80 / 20 : train sur 255 jours (oct. 2022 - juin 2023), test sur 65 jours (juin - août 2023). Train sous-divisé en train_inner (204 jours) + val held-out (51 jours) pour calibrer le seuil compliance sans toucher au test. Anti-leakage strict.

Architecture

Feature engineering (18 features de base)

  • Fréquence : sender_tx_count, receiver_tx_count (all-time sur train)
  • Listes pays à risque : UNION 4 sources officielles + liste interne empirique + déclaration auteurs dataset = 50 pays uniques (juin 2026)
    • ONU Security Council sanctions (14 pays)
    • OFAC sanctions programs incluant Ethiopia EO 14046 (8 ajouts)
    • GAFI / FATF black + grey list (plénière 13 février 2026 : 3 + 22)
    • UE Règlement délégué 2016/1675 amendé par 2025/1184, 2026/46, 2026/83
      • liste interne enrichie empiriquement sur historique SAML-D (5 pays : Albanie, Inde, Italie, Pays-Bas, Suisse)
      • 4 régions déclarées hauts risques par les auteurs du dataset SAML-D (Mexique, Turquie, Maroc, UAE — citées dans le README officiel BOztasUK). Ces 4 pays ne passent pas le seuil empirique 3× sur train_inner (taux 1.8-2.3×), mais constituent un signal métier documenté à conserver par traçabilité des sources.

Finding méthodologique : l'ajout d'OZTAS_HIGH_RISK à la règle R2 du rule-based ne modifie pas les performances (89 907 alertes, 60.0 % recall, 126 TP — identiques avant/après). Les transactions Mexico/Turkey/Morocco/UAE sont déjà captées par R1 (Amount > 10 k), R4/R5 (hyperactif), R6 (smurfing) ou R7 (cold start). Le rule-based est saturé : ajouter des pays redondants ne change rien quand les règles existantes captent déjà le signal. On garde quand même OZTAS_HIGH_RISK pour la défendabilité métier (toutes les sources documentées sont intégrées), pas pour un gain de performance.

  • Payment types à risque : Cash Deposit / Cash Withdrawal / Cross-border + observations internes
  • Smurfing score : nb de senders distincts en petits montants par receiver
  • Fan-out score : nb de receivers distincts en petits montants par sender
  • Graph features (NetworkX) : PageRank pondéré, in / out degree (anti-leakage : graphe construit sur train uniquement)

Modèle ML final

  • LightGBM tuné (max_depth=5, learning_rate=0.1, min_child_samples=10) + calibration sigmoid (Platt scaling, cv=3)
  • Account Autoencoder (AE) : MLP PyTorch per-compte (425k comptes) → 8-dim latent → 16 features (8 sender + 8 receiver)
  • Total : 34 features
  • Méthodologie tuning : TimeSeriesSplit + optimisation Average Precision
  • Split 3 segments (anti-bias méthodologique) : train_inner (64 %) / val held-out (16 %) / test (20 %). Le seuil compliance est calibré sur val held-out puis appliqué tel quel sur test — pas de seuil choisi sur le test set (défendable face ACPR / Tracfin).
  • Sélection winner sur CV mean_ap (pas sur le test → anti-peeking strict)
  • Explicabilité : SHAP KernelExplainer sur le modèle calibré final

Baseline rules-based (7 règles)

Tous les seuils sont des constantes business — indépendants du train, audit-friendly devant ACPR / Tracfin :

Règle Condition Justification
R1 Amount > 10 000 Seuil type Tracfin (vigilance renforcée)
R2 Amount > 1 000 ET pays ∈ liste UNION (41 pays) Sanctions internationales + liste interne
R3 Amount > 1 000 ET payment_type ∈ liste UNION Doctrine AML + observations internes
R4 sender_tx_count > 30 (all-time) Hyperactivité chronique sender
R5 receiver_tx_count > 30 (all-time) Symétrique R4
R6 receiver_smurfing_score > 5 5+ senders distincts en petits montants = typologie smurfing
R7 Amount > 1 000 ET sender_tx_count <= 3 Cold start : compte sender peu connu → vigilance KYC renforcée

Combinaison OR : alerte si une condition est vérifiée.

Note : deux règles velocity glissante (burst 24h, velocity 28j) ont été testées puis retirées car SAML-D ne simule pas ces patterns (0 vrai positif). À réactiver en production sur données MSB réelles. Voir docs/backlog-after-simplification.md.

Résultats

Test set : 160 000 transactions, 210 laundering, 9.3 semaines.

Lecture en une phrase : le rule-based plafonne à 60 % de recall — quoi qu'on fasse avec les 7 règles métier, on rate 40 % des cas suspects. Le ML calibré (seuil figé sur validation held-out) atteint 86.7 % de recall sur test avec 2.2 × moins d'alertes que le rule-based — c'est l'apport central du projet.

Système Alertes / sem Recall Précision
Rule-based (7 règles) 9 682 60.0 % 0.14 %
ML à volume égal 9 682 86.7 % 0.20 %
ML à recall égal (60.0 %) 18 60.0 %
ML cible compliance (seuil figé val) 4 465 86.7 %

Lecture compliance :

  1. À volume d'alertes équivalent (~9 680 / sem), le ML détecte 1.4 × plus de cas suspects que le rule-based (86.7 % vs 60.0 %).
  2. Pour atteindre le recall plafond du rule-based (60.0 %), le ML n'a besoin que de 18 alertes / semaine au lieu de 9 682 — soit 538 × moins de volume.
  3. Le rule-based ne peut pas dépasser 60.0 % de recall. Le ML calibré atteint 86.7 % de recall (seuil compliance figé sur 51 jours de validation held-out, recall mesuré sur test) avec 4 465 alertes / semaine — soit +27 points de recall à volume 2.2 × moindre. La cible était 80 % en val : le test dépasse légèrement (86.7 %) car distribution proche mais pas identique, sans biais de seuil choisi sur test.

Comparaison avec le baseline officiel des auteurs du dataset

Le notebook starter publié par Oztas et al. (XGBClassifier sans tuning, sans calibration, sans gestion du déséquilibre, AUC seule comme métrique) atteint AUC test 0.812. À leur point opérationnel cible (TPR 90 %), leur FPR est de 58 % — soit ~580 000 alertes / sem sur 800 k transactions, industriellement inutilisable.

Mon pipeline LGBM+AE calibré, à volume comparable (4 465 alertes / sem, FPR ≈ 26 %), atteint 86.7 % de recall — méthodologiquement défendable face ACPR / Tracfin grâce au seuil figé sur validation held-out. Le starter officiel sert ici de point de comparaison citable, pas de strawman : c'est la baseline publiée par les auteurs eux-mêmes.

Reproductibilité

Option 1 : environnement local

# Pré-requis : Python 3.11+, dataset SAML-D dans ../SAML-D_sample_800k.csv
pip install -r requirements.txt
cp .env.example .env  # puis renseigner les variables
jupyter nbconvert --to notebook --execute --inplace feature_engineering_aml.ipynb

Option 2 : sandbox Docker (recommandé pour production)

# Build de l'image (aucune donnée embarquée)
docker build -t aml-feature-engineering .

# Run avec volume mount sur le dataset local (jamais dans l'image)
docker run --rm \
    -v /local/path/SAML-D_sample_800k.csv:/data/SAML-D_sample_800k.csv:ro \
    -v $(pwd)/logs:/app/logs \
    --env-file .env \
    aml-feature-engineering

Temps d'exécution complet : ~15-30 min sur 800k transactions (CPU, sans GPU requis).

Anonymisation préalable (recommandé avant tout partage / collaboration) :

export ANONYMIZATION_SALT=$(python -c "import secrets; print(secrets.token_hex(32))")
python scripts/anonymize.py SAML-D_sample_800k.csv SAML-D_anonymized.csv

Limitations explicites

  • Dataset synthétique : les ratios ML / rule-based sont probablement gonflés vs un environnement bancaire réel. SAML-D ne simule pas tous les patterns réels (notamment pas les bursts velocity 24h ni les velocity mensuelles).
  • Pas de features KYC — limitation intrinsèque au dataset : SAML-D ne contient aucune feature KYC (les 12 features sont purement transactionnelles, confirmé par Oztas et al. 2023 section IV). Variables KYC absentes : âge du compte, profession, source des fonds, statut PEP (Personne Politiquement Exposée), customer risk rating, niveau de Customer Due Diligence (CDD), beneficial owner. En production réelle, un système LCB-FT bancaire intègre obligatoirement ces variables (voir guides Tracfin et GAFI 40 recommandations) — c'est l'étape A.1 à intégrer après validation sur SAML-D.
  • Pas d'historique multi-année : 8.5 mois de train, fenêtre limitée pour calibrer la velocity long terme.

Roadmap & implémentation

A. Passage à l'échelle — ⏳ À venir

  • Validation croisée sur un nouvel échantillon (800k lignes distinctes, période différente) pour tester la généralisation
  • Entraînement complet sur 9M lignes avec un hold-out temporel
  • Nécessite le dataset complet SAML-D 9M (à télécharger).

B. Résolution du Cold Start — ✅ Implémenté

  • Features is_new_account_sender / is_new_account_receiver (seuil ≤ 3 tx en historique train) — cellule 7 du notebook
  • Règle compliance R7 : Amount > 1 000 ET sender peu connu → alerte vigilance KYC renforcée
  • Impact mesuré : recall rule-based passe de 33.3 % à 60.0 % (+27 points)

C. Interprétabilité & conformité (XAI) — ✅ Implémenté

  • SHAP par alerte (cellule 53) : pour chaque alerte ML, top 5 features explicatives avec valeur, contribution SHAP et direction (pousse vers ALERTE / NORMAL)
  • 3 cas types analysés : vrai positif, faux positif, borderline
  • Répond à l'article 22 RGPD (droit à l'explication d'une décision automatisée) et aux exigences ACPR / Tracfin d'auditabilité des modèles

D. Segmentation par typologies — ✅ Implémenté

  • Stage 4.3 (cellule 63) : recall mesuré par typologie LCB-FT (17 catégories SAML-D) au seuil compliance (figé sur val held-out)
  • Identification des zones aveugles du ML (Behavioural_Change : 0 % recall — nécessite KYC dynamique) et des forces (Smurfing, Cash_Withdrawal, Layered_Fan_Out : 100 % recall)
  • Permet à l'analyste compliance de prioriser l'investigation

E. Compliance by design (sécurité des données) — ✅ Implémenté

  • Anonymisation / pseudonymisation : scripts/anonymize.py (SHA-256 + salt secret) sur les colonnes Sender_account / Receiver_account — conforme RGPD art. 4(5) et 32
  • Environnement sandbox Docker : Dockerfile avec utilisateur non-root, dépendances pinnées, aucune donnée dans l'image (volume mount au runtime)
  • Audit dépendances Python : GitHub Actions (.github/workflows/audit.yml) lance safety (CVE) + bandit (code) sur push / PR / hebdomadaire
  • Gestion stricte des secrets : .env exclu du git via .gitignore, template .env.example fourni
  • Traçabilité : variables LOG_LEVEL / LOG_DIR pour exportation structurée des journaux d'inférence

F. Tests de robustesse — rééchantillonnage (finding méthodologique) — ✅ Documenté

Question testée : SMOTE ou RandomOversampler peuvent-ils battre class_weight='balanced' sur le pipeline final ?

Méthodologie anti-leakage stricte : imblearn.Pipeline(SMOTE, LGBM) wrappé dans CalibratedClassifierCV(method='sigmoid', cv=3) — SMOTE appliqué uniquement sur le train de chaque split interne, calibration apprise sur le held-out non resamplé, seuils interprétables comme probabilité calibrée sur la vraie distribution 0.1%.

Résultat à deux niveaux :

Niveau Métrique baseline class_weight SMOTE Δ
LGBM seul (18 features) AP test 0.290 0.330 +14 %
LGBM + AE (34 features) AP test 0.6055 0.5097 −16 %

Lecture : SMOTE apporte un gain réel sur LGBM seul mais devient contre-productif une fois les 16 embeddings autoencoder ajoutés. Hypothèse : l'interpolation linéaire de SMOTE est incompatible avec l'embedding non-linéaire de l'autoencoder (positifs synthétiques produits hors-manifold dans l'espace 34-dim). Le seuil calibré SMOTE chute à 0.002 vs 0.063 baseline, confirmant l'écrasement des probabilités.

Décision : class_weight='balanced' conservé sur le pipeline final LGBM+AE. SMOTE documenté comme finding négatif méthodologique — voir docs/backlog-after-simplification.md et cellules 47 + 56 du notebook.

Leçon générale : l'efficacité de SMOTE dépend fortement de la nature des features. Sur des features riches et non-linéaires (embeddings, graphe), l'interpolation linéaire devient contre-productive. Évidence à mentionner si interrogé sur la gestion du déséquilibre.

G. Features temporelles (Hour, DayOfWeek, amount_log, amount_zscore) — testées et revertées — ✅ Documenté

Question testée : ajouter des features temporelles (heure, jour de semaine) et behavioral (log et z-score d'Amount) au-dessus des 18 features de base améliore-t-il le pipeline ?

Résultat mesuré (Lever 1 Phase A) :

Feature ajoutée AP test Vol@R80
hour_of_day + day_of_week + amount_log + amount_zscore_sender + amount_zscore_receiver −26 % +4 % (régression)

Cause : SAML-D ne génère pas de patterns horaires réalistes. Diagnostic empirique : taux suspect quasi-plat par tranche horaire (0.088 % – 0.108 %, vs moyenne globale 0.095 %). Les patterns de transactions nocturnes / weekend, bien documentés en production AML (guides Tracfin / GAFI), n'existent pas dans le générateur synthétique d'Oztas.

Décision : features temporelles retirées du pipeline final (cf. spec docs/specs/2026-05-07-feature-engineering-design.md section 3.4). Voir aussi backlog Lever 1 Phase A.

Comparaison vs baseline Oztas : le starter officiel extrait Hour, Date_Year, Date_Month, Date_Day sans mesurer l'impact (AUC 0.812 obtenu avec ces features, sans tuning ni comparaison). Mon pipeline a mesuré leur effet et constaté une régression — décision documentée, pas intuitive.

Leçon générale : les features temporelles classiques (heure, jour de semaine) sont sensibles à la qualité du générateur de données synthétiques. À réactiver en production sur données MSB réelles où les patterns nocturnes / weekend sont documentés.

H. Architecture hybride R-AND-ML (intersection) — testée et non adoptée — ✅ Documenté

Hypothèse testée : alerter seulement si rule-based ET ML d'accord (intersection) — supposément "deux signaux concordants" = haute confiance + réduction des faux positifs.

Résultat mesuré (cellules 58-59 du notebook) :

Architecture Alertes / sem TP / sem Précision Recall
Rule-based seul (7 règles) 9 682 13.6 0.14 % 60.0 %
ML seul (cible compliance) 4 465 19.6 0.44 % 86.7 %
R-AND-ML (meilleur seuil 0.5) 1 171 4.2 0.36 % 18.6 %

Diagnostic : l'intersection R-AND-ML est pire que le ML seul — elle rate 79 % des cas que le ML détecterait (4.2 TP/sem vs 19.6 TP/sem) pour quasi-aucun gain de précision (0.36 % vs 0.44 %).

Cause : le rule-based et le ML captent des typologies LCB-FT disjointes, pas redondantes :

  • Rule-based capte : violations de seuils métier (montant, pays sanctions, hyperactif, smurfing brut)
  • ML capte : patterns statistiques subtils (layering, behavioral change, fan-out fin, embeddings autoencoder)
  • Intersection : 111 cas sur 210 (52.9 %) au mieux — le rule-based rate 71 cas que le ML voit

Décision : R-AND-ML non adopté. Le ML seul (cible compliance) reste le winner. L'hypothèse "deux systèmes d'accord = signal renforcé" supposait une redondance qui n'existe pas sur SAML-D.

Alternative à explorer en production : architecture cascade L1→L2 (rule-based en filtre L1 audit-friendly, ML en re-scoring L2 pour priorisation) plutôt qu'intersection brutale. Cette piste n'est pas implémentée dans ce projet — laissée en roadmap.

Leçon générale : ne jamais présupposer la redondance entre deux systèmes de détection. Mesurer empiriquement leur recouvrement avant de combiner.

Stack technique

Python · pandas · scikit-learn · LightGBM · XGBoost · imbalanced-learn · PyTorch · NetworkX · SHAP · matplotlib · seaborn

Structure du dépôt

.
├── feature_engineering_aml.ipynb       # Notebook principal (67 cellules)
├── requirements.txt                    # Dépendances Python pinnées
├── Dockerfile                          # Sandbox reproductible (non-root, sans données)
├── .dockerignore                       # Exclut données et secrets de l'image
├── .env.example                        # Template variables d'environnement (à copier en .env)
├── .gitignore                          # Exclut *.csv, .env, logs, etc.
├── scripts/
│   └── anonymize.py                    # Pseudonymisation SHA-256 + salt (RGPD)
├── .github/
│   └── workflows/
│       └── audit.yml                   # CI safety (CVE) + bandit (code) hebdomadaire
├── docs/
│   ├── backlog-after-simplification.md # Suivi des décisions techniques
│   ├── specs/                          # Specs de design historiques
│   └── plans/                          # Plans d'exécution historiques
└── README.md

About

Detection de blanchiment d'argent (AML/LCB-FT) : ML calibre vs rule-based, XAI compliance (SHAP), audit-friendly

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages