Détection de Fraude par Machine Learning
Détection de Fraude par Machine Learning
ABDOULAYE TANGARA
Student in MSc in Quantitative and Computable Economics
Contacts
ï LinkedIn
§ Mon GitHub
Mon Portfolio
# abdoulayetangara722@[Link]
"To get something you never had, you’ve got to do something you
never did. Get your mind right"
©Tangara Abdoulaye
Contexte :
Il y a quelques années, un de mes cousins a été victime d’une fraude bancaire. Un matin, il
consulte son relevé de compte et découvre que plusieurs virements ont été effectués à son insu
durant la nuit. Des sommes modestes, transférées vers des comptes inconnus, mais cumulées,
elles représentaient presque tout son solde.
Il avait pourtant activé les alertes SMS et n’avait cliqué sur aucun lien suspect. Après enquête,
la banque conclut à un piratage silencieux, utilisant des transactions éclatées et discrètes pour
contourner les systèmes de détection.
Cet événement a été un déclic. Il m’a fait prendre conscience que la fraude moderne ne se
manifeste plus toujours par des montants exorbitants ou des comportements flagrants. Elle
devient sournoise, évolutive, algorithmique.
Ce projet est donc né de cette prise de conscience : construire un modèle prédictif basé sur l’IA,
capable de repérer les signaux faibles que l’humain ne perçoit pas, et d’alerter avant qu’il ne soit
trop tard.
Problématique :
Comment exploiter la puissance du Machine Learning pour identifier des schémas frauduleux
complexes, tout en minimisant les faux positifs qui perturbent l’expérience client ?
Objectif :
Ce projet vise à développer un modèle prédictif capable de détecter les transactions frauduleuses
en temps réel, en s’appuyant sur une analyse approfondie des données transactionnelles (mon-
tants, soldes, types d’opérations). Grâce à des techniques avancées de feature engineering et à
l’algorithme XGBoost, le système parvient à une précision de 99 %, tout en maintenant un taux
de rappel élevé pour ne pas laisser passer les fraudes.
Enjeux :
• Réduire les pertes financières liées aux fraudes.
• Améliorer la confiance des clients dans les transactions digitales.
• Automatiser la détection pour une réponse plus rapide que les méthodes manuelles.
©Tangara Abdoulaye
1
Importation des modules nécessaires
import shap
import warnings
[Link]('ignore')
[Link].chained_assignment = None
Gestion du temps
2
def gantt_diagram (df, title=None, xaxis_title=None, yaxis_title=None):
"""
"""
3
1 M2044282225 0.0 0.0 0 0
2 C553264065 0.0 0.0 1 0
3 C38997010 21182.0 0.0 1 0
4 M1230701703 0.0 0.0 0 0
Le nombre de caractéristiques : 11
1. Analyse Univarée
def analyser_quanti(self):
print(f"\nStatistique descriptive des variables quantitatives :
,→\n{[Link][self.var_quanti].describe()}\n")
else:
[Link](f"{col} est normalement distribuée (p = {np.
,→round(pvalue, 2)})")
4
[Link]()
def analyser_quali(self):
print(f"\nStatistique descriptive des variables qualitatives :\n{self.
,→df[self.var_quali].describe(include='all')}\n")
def lancer_analyse(self):
print("\nDÉBUT DE L'ANALYSE UNIVARIÉE\n")
if self.var_quanti:
self.analyser_quanti()
if self.var_quali:
self.analyser_quali()
print("\n FIN DE L'ANALYSE UNIVARIÉE\n")
newbalanceDest
count 6.362620e+06
mean 1.224996e+06
5
std 3.674129e+06
min 0.000000e+00
25% 0.000000e+00
50% 2.146614e+05
75% 1.111909e+06
max 3.561793e+08
6
7
Analyse de la normalité (histogrammes + Shapiro) :
8
9
Statistique descriptive des variables qualitatives :
10
max NaN 1.000000e+00 1.000000e+00
11
où 0 signifie pas de fraude et 1 signifie l’existence de fraude.
12
Interprétation de l’analyse univariée
Variables Quantitatives
les caractéristiques quantitatives telles que (amount, oldbalanceOrg etc.) montrent une distri-
bution fortement asymétrique avec des valeurs extrêmes (visibles dans les boxplots étirés et les
histogrammes non-normaux). Cela suggère la nécessité de transformations (log, scaling) pour la
modélisation. Par ailleurs, les tests Shapiro-Wilks (p=0.0) rejettent l’hypothèse de normalité
pour toutes les variables, justifiant l’utilisation de méthodes robustes comme XGBoost.
Variables Qualitatives
L’analyse sur les types de transactions (représentés par la variable ’type’) montre que les
CASH_OUT (35.2%) et les PAYMENT (22.0%) dominent. Les fraudes sont souvent liées
à des types spécifiques (à vérifier en bivariée). Nous pouvons également repérer un déséquilibre
très important des classes de la variable cible ’isFraud’ avec seulement 0.1% de fraude, ce qui
nécessite des techniques comme le rééchantillonnage ou des métriques focalisées sur la classe
13
minoritaire (recall, F1-score).
2. Analyse Bivariée
if len(valid_vars) < 2:
raise ValueError("Moins de 2 variables numériques valides␣
,→trouvées dans le DataFrame")
df_clean = [Link][valid_vars].dropna()
for i in range(len(valid_vars)):
for j in range(i + 1, len(valid_vars)):
x_var, y_var = valid_vars[i], valid_vars[j]
[Link](figsize=(8, 6))
[Link](data=df_clean, x=x_var, y=y_var,
scatter_kws={'alpha': 0.5},
line_kws={'color': 'red'})
[Link](f"Relation entre {x_var} et {y_var}", pad=20)
plt.tight_layout()
[Link]()
print("-" * 50)
corr_matrix = df_clean.corr(method='spearman')
mask = [Link](np.ones_like(corr_matrix, dtype=bool))
[Link](figsize=(10, 8))
14
[Link](corr_matrix, mask=mask, annot=True, fmt=".2f",␣
cmap="coolwarm", square=True)
,→
def BivariateQual(self):
for i in self.qual_vars:
for j in self.qual_vars:
if i != j:
contingency_table = [Link]([Link][i], [Link][j])
print(f"\n## Test du Chi2 entre {i} et {j} ##")
chi2, pvalue, dof, expected = stats.
,→chi2_contingency(contingency_table)
else:
print(f" Aucune association entre {i} et {j}, p =␣
,→{pvalue:.4f}")
def BivariateQuantQual(self):
quant_vars = [var for var in self.quant_vars if var in [Link].
,→columns and [Link].is_numeric_dtype([Link][var])]
[Link](figsize=(10, 6))
[Link](x=qual_var, y=quant_var, data=df_clean)
[Link](x=qual_var, y=quant_var, data=df_clean,
15
color='black', alpha=0.3, jitter=True)
[Link](f"{quant_var} par {qual_var} (p = {pvalue:.
4f})", pad=20)
,→
[Link](rotation=45)
plt.tight_layout()
[Link]()
except ValueError as e:
print(f"\nErreur avec {quant_var} et {qual_var} : {e}")
def lancer_analyse(self):
print("\n--- Analyse bivariée quantitative ---")
[Link](self.quant_vars)
[Link]()
16
Test Kruskal-Wallis entre amount et isFraud :
H = 8273.37, p = 0.0000 → Association significative
17
Test Kruskal-Wallis entre oldbalanceOrg et isFraud :
H = 9892.17, p = 0.0000 → Association significative
18
H = 4999.38, p = 0.0000 → Association significative
19
Test Kruskal-Wallis entre newbalanceDest et type :
H = 4170791.50, p = 0.0000 → Association significative
20
Interprétation de l’analyse univariée
(NB : Compte tenu de la taille de la base de donnée et de la performance des outils utilisés pour
ce projet, certain resultat de cette section ne sont pas affiché)
Variables
Matrice de Spearman: Aucune corrélation forte (>0.5) entre les variables quantitatives,
sauf entre oldbalanceOrg/newbalanceOrig (0.99) et oldbalanceDest/newbalanceDest
(0.97), suggérant des redondances à traiter (PCA ou suppression).
Relations non linéaires: Les regplots montrent des nuages de points dispersés, indiquant
que des modèles linéaires seraient peu adaptés → choix de XGBoost justifié par sa capacité à
capturer des relations complexes.
Fraude vs Montant: Les transactions frauduleuses ont des montants médians plus élevés
(boxplot amount par isFraud), mais avec une grande variance. XGBoost peut gérer cette
hétérogénéité via ses splits optimaux.
Type de transaction: Les TRANSFER et CASH_OUT montrent des distributions de soldes
différentes pour les fraudes (p=0.0000), confirmant leur importance comme features.
Association type/isFraud: Lien significatif (p=0.0000), validant l’inclusion de type dans le
modèle.
4.1. Analyse Multivariée avant le Feature Engineering
• Encodage
• Normalisation
• Définition du target et des features
• Instanciation de PCA
21
[7]: # Suppression des variables inutiles
data_1 = [Link]()
[Link](columns=["isFlaggedFraud","nameOrig","nameDest"], inplace=True)
,→data_0[['amount','oldbalanceOrg', 'newbalanceOrig','oldbalanceDest',␣
,→'newbalanceDest']].mean())/data_0[['amount','oldbalanceOrg',␣
,→'newbalanceOrig','oldbalanceDest', 'newbalanceDest']].std()
data_vf = data_0.copy()
def pca(self):
}
)
22
# Impact de variables initiales sur les composantes
vecteur_propre = [Link](
pca.components_.T,
columns=["PCA_" + str(x+1) for x in range([Link][1])],
index = [Link].to_list()
)
vecteur_propre_sorted = vecteur_propre.abs().max(axis=1).
,→sort_values(ascending=False).index
vecteur_propre = vecteur_propre.loc[vecteur_propre_sorted]
[Link]().add_artist(circle)
23
# Afficher la figure
[Link]()
def lancer_analyse(self):
"""
Cela permet de lancer l'analyse
"""
print("\n --Analyse Multivariée -- \n")
cp, vp5 = [Link]()
print("-- Les Composants Principaux --")
print(f"\n {cp} \n")
--Analyse Multivariée --
24
-- Les Composants Principaux --
25
type_PAYMENT 0.000016 -0.108373 -0.017541 -0.035981 -0.672788 -0.466415
type_TRANSFER 0.000013 0.033807 -0.038587 0.095204 -0.039269 0.103640
type_CASH_IN 0.000012 0.077308 0.142813 -0.002966 -0.020406 0.755084
type_DEBIT 0.000002 -0.000387 -0.001019 -0.002575 -0.000706 0.007656
type_CASH_OUT -0.000043 -0.002356 -0.085667 -0.053682 0.733169 -0.399966
oldbalanceOrg -0.000071 0.305094 0.621940 0.030464 0.016009 -0.158605
newbalanceOrig -0.000072 0.305032 0.623160 0.024146 0.009264 -0.106115
data_1["DiffSoldeOrig"] = data_1["DiffSoldeOrig"].apply(lambda x: 1 if x != 0␣
,→else 0) # | Si 0 → anomalie possible. |
26
## | Montant transféré par rapport au solde initial du client. Un % élevé est␣
,→suspect. |
data_1["PourcentageMontantOrig"] = data_1["amount"] /␣
,→(data_1["oldbalanceOrg"] + 1)
27
Statistique descriptive des variables qualitatives :
DiffSoldeOrig DiffSoldeDest isSameAccount activiteAnormale
count 6362620 6362620 6362620 6362620
unique 2 2 1 3
top 1 1 0 2
freq 5413997 4736674 6362620 3910108
28
29
30
## Test du Chi2 entre type et isFraud ##
Association entre type et isFraud, p = 0.0000
31
Test Kruskal-Wallis entre amount et isFraud :
H = 8273.37, p = 0.0000 → Association significative
32
Test Kruskal-Wallis entre oldbalanceOrg et isFraud :
H = 9892.17, p = 0.0000 → Association significative
33
Test Kruskal-Wallis entre newbalanceOrig et isFraud :
H = 4999.38, p = 0.0000 → Association significative
34
Test Kruskal-Wallis entre oldbalanceDest et isFraud :
H = 1869.46, p = 0.0000 → Association significative
35
Test Kruskal-Wallis entre newbalanceDest et isFraud :
H = 170.84, p = 0.0000 → Association significative
Interprétations
Ces nouvelles variables comme : DiffSoldeOrig et DiffSoldeDest (qui vérifient si l’argent est vrai-
ment retiré du compte client. Si 0 ,→ anomalie possible.), détectent des anomalies fréquentes,
notamment dans des cas de fraude (ex. : argent "disparaît" ou "apparaît"). La variable is-
36
SameAccount (qui permet de détecter si le compte de départ est égal au compte d’arrivée) = 0
pour tous : donc pas discriminant ici.
activiteAnormale → valeur 2 indique une double anomalie (origine + destination), très utile.
Ces variables renforcent le signal de comportements soupçonnés frauduleux.
4.2. Analyse Multivariée après le Feature Engineering
• Encodage
• Normalisation
• Définition du target et des features
• Instanciation de PCA
df = [Link]([data_1,df_demmies], axis=1)
[Link](columns=["type"], inplace = True)
,→'newbalanceDest','step_day']] - df[['amount','oldbalanceOrg',␣
,→'newbalanceOrig','oldbalanceDest', 'newbalanceDest',
'step_day']].mean())/df[['amount','oldbalanceOrg',␣
,→'newbalanceOrig','oldbalanceDest', 'newbalanceDest','step_day']].std()
df_vf = [Link]()
--Analyse Multivariée --
37
-- Les Composants Principaux --
38
amount 1.607082e-06 4.219399e-02 6.515435e-02
newbalanceDest 8.605063e-07 2.231253e-01 6.430570e-01
oldbalanceDest 6.054529e-07 2.463096e-01 6.727538e-01
type_TRANSFER 1.374247e-07 -8.152279e-03 2.428221e-02
DiffSoldeOrig 4.076303e-08 4.004814e-02 1.749633e-02
step_day 2.695479e-08 -4.222665e-03 5.271563e-02
type_CASH_OUT 9.167774e-09 -6.401319e-02 8.339555e-02
isSameAccount 0.000000e+00 0.000000e+00 -4.336809e-19
type_DEBIT -1.756655e-09 -1.142765e-03 1.383769e-03
activiteAnormale -2.757388e-08 9.262916e-02 -4.443263e-02
type_CASH_IN -5.863893e-08 1.602958e-01 -4.004766e-02
DiffSoldeDest -6.833692e-08 5.258101e-02 -6.192896e-02
oldbalanceOrg -7.893704e-08 6.463769e-01 -2.310380e-01
newbalanceOrig -7.981877e-08 6.477419e-01 -2.311900e-01
type_PAYMENT -8.619693e-08 -8.698759e-02 -6.901387e-02
39
PCA_10 PCA_11 PCA_12 \
PourcentageMontantOrig 8.334211e-08 -4.122009e-09 1.967199e-08
amount -1.032256e-01 -1.383476e-02 -1.431641e-01
newbalanceDest -3.789028e-03 1.073283e-01 7.147940e-01
oldbalanceDest -1.683341e-02 -1.014861e-01 -6.659168e-01
type_TRANSFER 6.410180e-01 -2.370497e-01 2.529606e-02
DiffSoldeOrig -3.426591e-01 1.947270e-02 1.728121e-03
step_day 1.549176e-02 5.442266e-04 2.523842e-03
type_CASH_OUT 8.025424e-02 -2.137766e-01 1.007356e-02
isSameAccount 2.220446e-16 -7.910339e-16 9.992007e-16
type_DEBIT 1.559426e-02 8.840285e-01 -1.320504e-01
activiteAnormale 7.418193e-02 1.616893e-02 -6.649165e-03
type_CASH_IN -3.372730e-01 -2.223210e-01 8.083643e-02
DiffSoldeDest 4.168410e-01 -3.303769e-03 -8.377287e-03
oldbalanceOrg 3.316732e-02 5.911184e-03 -3.930155e-03
newbalanceOrig 4.001879e-03 -2.474776e-03 4.990598e-03
type_PAYMENT -3.995935e-01 -2.108812e-01 1.584434e-02
40
Instancier le modèle
[11]: # Définition du train et test
features = df_vf.drop(columns="isFraud")
target = df_vf["isFraud"]
X_train,X_test, y_train, y_test = train_test_split(features,target,␣
,→train_size=0.3, random_state=42)
# Prediction du modèle
y_pred = model_xgb.predict(X_test)
41
Pipeline du Modèle
],remainder='passthrough')
# Inplementation du pipeline
pipe_xgboost = Pipeline([
('prepros', preprocessing),
('model',XGBClassifier(
n_estimators = 100,
n_jobs = -1,
learning_rate = 0.1,
max_depth = 6,
subsample = 0.8,
enable_categorical = True))
])
# Entraitement final
pipe_xgboost.fit(X_train, y_train)
[12]: Pipeline(steps=[('prepros',
ColumnTransformer(remainder='passthrough',
transformers=[('cat',
OneHotEncoder(handle_unknown='ignore'),
['type']),
('norm', StandardScaler(),
['amount', 'oldbalanceOrg',
'newbalanceOrig',
'oldbalanceDest',
'newbalanceDest',
'step_day'])])),
('model',
XGBClassifier(base_score=None, booster=None, callbacks=None,
colsample_bylevel=None, col...
feature_types=None, gamma=None,␣
,→grow_policy=None,
importance_type=None,
interaction_constraints=None, learning_rate=0.
,→1,
max_bin=None, max_cat_threshold=None,
42
max_cat_to_onehot=None, max_delta_step=None,
max_depth=6, max_leaves=None,
min_child_weight=None, missing=nan,
monotone_constraints=None, multi_strategy=None,
n_estimators=100, n_jobs=-1,
num_parallel_tree=None, random_state=None,
...))])
La matrice de confusion
0 4.44803e+06 36
1 29 5742
Interprétation
Malgré le ratio 1:1000, le modèle capture 99% des fraudes sans sacrifier la précision.
L’importance de chaque variables dans le resultat de la prediction du modèle
43
shap_values = explainer(X_transformed)
[Link](shap_values[0])
Interprétation
Les variables telles que type_CASH_IN et type_DEBIT (Feature 11/13) dominent, aligné avec
les résultats ACP.
type_TRANSFER/type_CASH_OUT contribuent significativement (confirmant l’analyse bi-
variée).
Le waterfall plot montre comment chaque feature influence la prédiction pour un
cas individuel, utile pour l’audit
Perspectives
Bien que le modèle prédictif basé sur le machine learning ait montré d’excellentes performances
pour identifier les transactions suspectes individuellement, une prochaine étape incontournable
sera d’aller au-delà des observations isolées, en analysant les relations entre les entités
: comptes, bénéficiaires, transactions récurrentes.
Pour cela, la théorie des graphes s’impose comme une approche stratégique. En représentant
les transactions comme des arêtes reliant des nœuds (comptes émetteurs et récepteurs), il devient
possible de :
44
• Identifier des comportements collaboratifs suspects (fraude en réseau),
• Détecter des comptes pivot ou transitaires,
• Reconnaître des motifs cycliques ou en étoile, typiques du blanchiment d’argent.
Ainsi, l’intégration d’un moteur d’analyse graphes (avec des outils comme ‘networkx‘, ‘Neo4j‘,
ou ‘GraphSAGE‘) permettrait d’optimiser le système de détection actuel et d’augmenter
encore sa robustesse face à des scénarios de fraude complexes.
45