- Cas d’usage data science dans la banque : détecter la fraude
- Préparer le jeu de données
- Calculer la fraude dans le jeu de données
- Rééquilibrer les données avec SMOTE
- Appliquer la régression logistique
- Conclusion
La banque fait partie de ces secteurs qui ont historiquement collecté beaucoup de données structurées, et a été l’un des premiers à appliquer des technologies de data science.
Comment la data science est-elle utilisée dans la banque ? Aujourd’hui, la donnée est devenue l’actif le plus précieux dans ce domaine. La data science est indispensable pour que les banques restent au niveau de leurs concurrents, attirent de nouveaux clients, fidélisent les clients existants, prennent des décisions plus efficaces basées sur les données, développent leur activité, améliorent l’efficacité opérationnelle, optimisent l’offre de services/produits et en lancent de nouveaux, renforcent la sécurité et, par conséquent, génèrent davantage de revenus. Sans surprise, la majorité des offres d’emploi en data science proviennent du secteur bancaire.
La data science permet au secteur bancaire de réussir de nombreuses tâches, notamment :
- analyse des risques d’investissement
- prédiction de la valeur vie client (CLV)
- segmentation client
- prédiction du taux d’attrition
- marketing personnalisé
- analyse du sentiment client
- assistants virtuels et chatbots
Ci-dessous, nous allons examiner de plus près l’un des cas d’usage les plus courants de la data science en banque.
Cas d’usage data science dans la banque : détecter la fraude
Les activités frauduleuses constituent un problème majeur non seulement dans la banque, mais aussi dans de nombreux autres domaines comme l’administration publique, l’assurance, le secteur public, la vente et la santé. Toute entreprise qui traite un grand nombre de transactions en ligne s’expose à un risque de fraude important. Les infractions financières prennent diverses formes : transactions par carte bancaire frauduleuses, chèques falsifiés, évasion fiscale, blanchiment d’argent, cyberattaques, vol de comptes clients, identités synthétiques, fausses demandes et arnaques.
La détection de la fraude regroupe un ensemble de mesures proactives destinées à identifier et prévenir les activités frauduleuses et les pertes financières. Ses principales techniques d’analyse se répartissent en deux familles :
- Statistiques : calcul de paramètres statistiques, régression, lois de probabilité, appariement de données
- Intelligence artificielle (IA) : fouille de données, apprentissage automatique, apprentissage profond
L’apprentissage automatique est un pilier essentiel de la détection de la fraude. Sa boîte à outils propose deux approches :
- Méthodes supervisées : k plus proches voisins, régression logistique, machines à vecteurs de support, arbre de décision, forêt aléatoire, analyse de séries temporelles, réseaux de neurones, etc.
- Méthodes non supervisées : analyse par grappes, analyse de liens, cartes auto-organisées, analyse en composantes principales, détection d’anomalies, etc.
Il n’existe pas d’algorithme d’apprentissage automatique universel et infaillible pour la détection de la fraude. En pratique, plusieurs techniques ou leurs combinaisons sont généralement testées, on calcule la précision prédictive du modèle, puis on retient l’approche optimale.
Le principal défi des systèmes de détection de la fraude est de s’adapter rapidement à l’évolution constante des schémas de fraude et des tactiques des fraudeurs, et de dévoiler sans délai des montages toujours plus élaborés. Les cas de fraude sont toujours minoritaires et bien dissimulés parmi les transactions légitimes.
Préparer le jeu de données
Explorons une implémentation d’apprentissage automatique pour détecter la fraude aux cartes bancaires avec le langage Python. Nous allons travailler sur le jeu de données creditcard_data, un échantillon modifié d’un dataset Kaggle sur la détection de la fraude par carte bancaire. Les données d’origine représentent des transactions effectuées avec des cartes détenues par des titulaires européens sur deux jours en septembre 2013.
Importons les données et jetons-y un rapide coup d’œil :
import pandas as pd
creditcard_data = pd.read_csv('creditcard_data.csv', index_col=0)
print(creditcard_data.info())
print('\n')
pd.options.display.max_columns = len(creditcard_data)
print(creditcard_data.head(3))
<class 'pandas.core.frame.DataFrame'>
Int64Index: 5050 entries, 0 to 5049
Data columns (total 30 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 V1 5050 non-null float64
1 V2 5050 non-null float64
2 V3 5050 non-null float64
3 V4 5050 non-null float64
4 V5 5050 non-null float64
5 V6 5050 non-null float64
6 V7 5050 non-null float64
7 V8 5050 non-null float64
8 V9 5050 non-null float64
9 V10 5050 non-null float64
10 V11 5050 non-null float64
11 V12 5050 non-null float64
12 V13 5050 non-null float64
13 V14 5050 non-null float64
14 V15 5050 non-null float64
15 V16 5050 non-null float64
16 V17 5050 non-null float64
17 V18 5050 non-null float64
18 V19 5050 non-null float64
19 V20 5050 non-null float64
20 V21 5050 non-null float64
21 V22 5050 non-null float64
22 V23 5050 non-null float64
23 V24 5050 non-null float64
24 V25 5050 non-null float64
25 V26 5050 non-null float64
26 V27 5050 non-null float64
27 V28 5050 non-null float64
28 Amount 5050 non-null float64
29 Class 5050 non-null int64
dtypes: float64(29), int64(1)
memory usage: 1.2 MB
V1 V2 V3 V4 V5 V6 V7 \
0 1.725265 -1.337256 -1.012687 -0.361656 -1.431611 -1.098681 -0.842274
1 0.683254 -1.681875 0.533349 -0.326064 -1.455603 0.101832 -0.520590
2 1.067973 -0.656667 1.029738 0.253899 -1.172715 0.073232 -0.745771
V8 V9 V10 V11 V12 V13 V14 \
0 -0.026594 -0.032409 0.215113 1.618952 -0.654046 -1.442665 -1.546538
1 0.114036 -0.601760 0.444011 1.521570 0.499202 -0.127849 -0.237253
2 0.249803 1.383057 -0.483771 -0.782780 0.005242 -1.273288 -0.269260
V15 V16 V17 V18 V19 V20 V21 \
0 -0.230008 1.785539 1.419793 0.071666 0.233031 0.275911 0.414524
1 -0.752351 0.667190 0.724785 -1.736615 0.702088 0.638186 0.116898
2 0.091287 -0.347973 0.495328 -0.925949 0.099138 -0.083859 -0.189315
V22 V23 V24 V25 V26 V27 V28 \
0 0.793434 0.028887 0.419421 -0.367529 -0.155634 -0.015768 0.010790
1 -0.304605 -0.125547 0.244848 0.069163 -0.460712 -0.017068 0.063542
2 -0.426743 0.079539 0.129692 0.002778 0.970498 -0.035056 0.017313
Amount Class
0 189.00 0
1 315.17 0
2 59.98 0
Le jeu de données contient les variables suivantes :
- Des variables codées numériquement V1 à V28, qui sont les composantes principales issues d’une ACP. Pour des raisons de confidentialité, aucune information sur les variables d’origine n’a été fournie.
- La variable Amount représente le montant de la transaction.
- La variable Class indique si la transaction est frauduleuse (1) ou non (0).
Par nature, les occurrences de fraude sont heureusement extrêmement minoritaires dans toute liste de transactions. Cependant, les algorithmes d’apprentissage automatique fonctionnent généralement mieux lorsque les différentes classes du jeu de données sont plus ou moins également représentées. Dans le cas contraire, il y a peu de données dont apprendre. Ce problème est appelé déséquilibre des classes.
Calculer la fraude dans le jeu de données
Calculons le pourcentage de transactions frauduleuses par rapport au nombre total de transactions de notre jeu de données :
round(creditcard_data['Class'].value_counts()*100/len(creditcard_data)).convert_dtypes()
0 99
1 1
Name: Class, dtype: Int64
et traçons un graphique pour visualiser les points de données fraude vs non-fraude :
import matplotlib.pyplot as plt
import numpy as np
def prep_data(df):
X = df.iloc[:, 1:28]
X = np.array(X).astype(float)
y = df.iloc[:, 29]
y = np.array(y).astype(float)
return X, y
def plot_data(X, y):
plt.scatter(X[y==0, 0], X[y==0, 1], label='Class #0', alpha=0.5, linewidth=0.15)
plt.scatter(X[y==1, 0], X[y==1, 1], label='Class #1', alpha=0.5, linewidth=0.15, c='r')
plt.legend()
return plt.show()
X, y = prep_data(creditcard_data)
plot_data(X, y)

Rééquilibrer les données avec SMOTE
Nous pouvons confirmer que la proportion de transactions frauduleuses est très faible et que nous sommes face à un problème de déséquilibre des classes. Pour y remédier, nous pouvons rééquilibrer nos données à l’aide de la technique d’oversampling de la minorité synthétique (SMOTE). Contrairement au suréchantillonnage aléatoire, SMOTE est un peu plus sophistiqué car il ne crée pas de copies exactes des observations. Il s’appuie plutôt sur les caractéristiques des plus proches voisins des cas de fraude pour générer de nouveaux échantillons synthétiques, assez proches des observations existantes de la classe minoritaire. Appliquons SMOTE à nos données de carte bancaire :
from imblearn.over_sampling import SMOTE
method = SMOTE()
X_resampled, y_resampled = method.fit_resample(X, y)
plot_data(X_resampled, y_resampled)

Comme on le voit, SMOTE nous fournit soudainement davantage d’observations de la classe minoritaire. Pour mieux apprécier les résultats, comparons-les aux données d’origine :
def compare_plot(X, y, X_resampled, y_resampled, method):
f, (ax1, ax2) = plt.subplots(1, 2)
c0 = ax1.scatter(X[y==0, 0], X[y==0, 1], label='Class #0',alpha=0.5)
c1 = ax1.scatter(X[y==1, 0], X[y==1, 1], label='Class #1',alpha=0.5, c='r')
ax1.set_title('Original set')
ax2.scatter(X_resampled[y_resampled==0, 0], X_resampled[y_resampled==0, 1], label='Class #0', alpha=.5)
ax2.scatter(X_resampled[y_resampled==1, 0], X_resampled[y_resampled==1, 1], label='Class #1', alpha=.5,c='r')
ax2.set_title(method)
plt.figlegend((c0, c1), ('Class #0', 'Class #1'), loc='lower center', ncol=2, labelspacing=0.)
plt.tight_layout(pad=3)
return plt.show()
print(f'Original set:\n'
f'{pd.value_counts(pd.Series(y))}\n\n'
f'SMOTE:\n'
f'{pd.value_counts(pd.Series(y_resampled))}\n')
compare_plot(X, y, X_resampled, y_resampled, method='SMOTE')
Original set:
0.0 5000
1.0 50
dtype: int64
SMOTE:
0.0 5000
1.0 5000
dtype: int64

Ainsi, la méthode SMOTE a complètement équilibré nos données, et la classe minoritaire est désormais de même taille que la classe majoritaire.
Nous reviendrons bientôt à l’application pratique de SMOTE, mais pour l’instant, revenons aux données d’origine et tentons de détecter les cas de fraude. En suivant une approche "à l’ancienne", il faut créer des règles pour repérer la fraude. Ces règles peuvent concerner, par exemple, des lieux inhabituels de transaction ou une fréquence suspecte des opérations. L’idée est de définir des seuils basés sur des statistiques usuelles, souvent sur les moyennes des observations, puis d’appliquer ces seuils à nos variables pour détecter la fraude.
print(creditcard_data.groupby('Class').mean().round(3)[['V1', 'V3']])
V1 V3
Class
0 0.035 0.037
1 -4.985 -7.294
Dans notre cas, appliquons les conditions suivantes : V1 < -3 et V3 < -5. Puis, pour évaluer les performances de cette approche, comparons les fraudes signalées aux fraudes réelles :
creditcard_data['flag_as_fraud'] = np.where(np.logical_and(creditcard_data['V1']<-3, creditcard_data['V3']<-5), 1, 0)
print(pd.crosstab(creditcard_data['Class'], creditcard_data['flag_as_fraud'], rownames=['Actual Fraud'], colnames=['Flagged Fraud']))
Flagged Fraud 0 1
Actual Fraud
0 4984 16
1 28 22
Appliquer la régression logistique
Nous avons détecté 22 cas de fraude sur 50, mais nous en manquons 28, et nous comptons 16 faux positifs. Voyons si les techniques d’apprentissage automatique peuvent faire mieux.
Nous allons maintenant implémenter un algorithme de classification par régression logistique simple sur nos données de carte bancaire pour identifier les occurrences frauduleuses, puis visualiser les résultats dans une matrice de confusion :
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
lr = LogisticRegression()
lr.fit(X_train, y_train)
predictions = lr.predict(X_test)
print(pd.crosstab(y_test, predictions, rownames=['Actual Fraud'], colnames=['Flagged Fraud']))
Flagged Fraud 0.0 1.0
Actual Fraud
0.0 1504 1
1.0 1 9
Il est important de noter que nous avons ici moins d’observations dans la matrice de confusion, car nous utilisons uniquement l’ensemble de test pour calculer les résultats du modèle, c’est-à-dire seulement 30 % de l’ensemble du jeu de données.
Nous avons intercepté une plus grande part des fraudes : 90 % (9 sur 10), contre 44 % précédemment (22 sur 50). Nous avons également nettement moins de faux positifs qu’auparavant : c’est un progrès.
Revenons maintenant au problème de déséquilibre des classes évoqué plus haut et voyons si nous pouvons encore améliorer la prédiction en combinant la régression logistique avec la méthode de rééchantillonnage SMOTE. Pour le faire efficacement en une seule passe, définissons un pipeline et exécutons-le sur nos données :
from imblearn.pipeline import Pipeline
# Définir la méthode de rééchantillonnage et le modèle de ML à utiliser dans le pipeline
resampling = SMOTE()
lr = LogisticRegression()
pipeline = Pipeline([('SMOTE', resampling), ('Logistic Regression', lr)])
pipeline.fit(X_train, y_train)
predictions = pipeline.predict(X_test)
print(pd.crosstab(y_test, predictions, rownames=['Actual Fraud'], colnames=['Flagged Fraud']))
Flagged Fraud 0.0 1.0
Actual Fraud
0.0 1496 9
1.0 1 9
Comme on le voit, dans notre cas SMOTE n’apporte pas d’amélioration : nous détectons toujours 90 % des fraudes et, en outre, nous avons légèrement plus de faux positifs. L’explication est que le rééchantillonnage ne conduit pas nécessairement à de meilleurs résultats dans tous les cas. Lorsque les cas de fraude sont très dispersés dans les données, leurs plus proches voisins ne sont pas forcément eux-mêmes des cas de fraude, ce qui peut introduire un biais avec SMOTE.
Conclusion
Pour aller plus loin et augmenter la précision du modèle de régression logistique, nous pouvons ajuster certains paramètres de l’algorithme. La validation croisée en k plis peut également être envisagée au lieu de scinder simplement le jeu de données en deux parties. Enfin, nous pouvons tester d’autres algorithmes d’apprentissage automatique (par exemple, arbre de décision ou forêt aléatoire) et voir s’ils donnent de meilleurs résultats.
Si vous souhaitez approfondir les aspects théoriques et techniques de l’implémentation de modèles de détection de fraude, consultez le cours Fraud Detection in Python.

