- Data science-use-case in de bankensector: fraude opsporen
- De dataset voorbereiden
- Fraude in de dataset berekenen
- SMOTE gebruiken om de data te herverdelen
- Logistische regressie toepassen
- Conclusie
Bankieren is een van die gelukkige domeinen waar historisch gezien veel gestructureerde data wordt verzameld, en ook een van de eerste waar data science-technologieën zijn toegepast.
Hoe wordt data science gebruikt in de bankensector? Tegenwoordig is data het meest waardevolle bezit in deze sector. Data science is een noodzakelijke voorwaarde voor banken om hun concurrenten bij te houden, meer klanten aan te trekken, de loyaliteit van bestaande klanten te vergroten, efficiëntere datagedreven beslissingen te nemen, hun business te versterken, de operationele efficiëntie te verbeteren, bestaande diensten/producten te verbeteren en nieuwe te introduceren, de beveiliging te versterken en als resultaat van al deze acties meer omzet te behalen. Het is niet verrassend dat het merendeel van de vraag naar data science-banen uit de bankensector komt.
Met data science kan de bankensector tal van taken succesvol uitvoeren, waaronder:
- analyse van beleggingsrisico
- voorspelling van customer lifetime value
- klantsegmentatie
- voorspelling van klantverloop
- gepersonaliseerde marketing
- sentimentanalyse van klanten
- virtuele assistenten en chatbots
Hieronder kijken we van dichtbij naar een van de meest voorkomende data science-use-cases in banken.
Data science-use-case in de bankensector: fraude opsporen
Frauduleuze activiteiten vormen een uitdagend probleem, niet alleen in de bankwereld maar ook in veel andere sectoren, zoals overheid, verzekeringen, publieke sector, verkoop en gezondheidszorg. Elk bedrijf dat te maken heeft met veel online transacties loopt een aanzienlijk frauderisico. Financiële misdrijven kunnen verschillende vormen aannemen, waaronder frauduleuze creditcardtransacties, vervalste bankcheques, belastingontduiking, witwassen, cyberaanvallen, diefstal van klantaccounts, synthetische identiteiten, valse aanvragen en oplichtingspraktijken.
Fraudedetectie is een reeks proactieve maatregelen om frauduleuze activiteiten en financiële verliezen te identificeren en te voorkomen. De belangrijkste analysetechnieken zijn onder te verdelen in twee groepen:
- Statistisch: berekening van statistische parameters, regressie, kansverdelingen, datamatching
- Kunstmatige intelligentie (AI): datamining, machine learning, deep learning
Machine learning is een essentieel fundament voor fraudedetectie. De toolkit biedt twee benaderingen:
- Bewaakte methoden: k-nearest neighbors, logistische regressie, supportvector-machines, beslisboom, random forest, tijdreeksanalyse, neurale netwerken, enz.
- Onbewaakte methoden: clusteranalyse, linkanalyse, self-organizing maps, principalecomponentenanalyse, anomaliedetectie, enz.
Er bestaat geen universeel en betrouwbaar machine-learningalgoritme voor fraudedetectie. In plaats daarvan worden in real-world data science-use-cases meestal meerdere technieken of combinaties daarvan getest, wordt de voorspellende nauwkeurigheid van het model berekend en wordt de optimale aanpak gekozen.
De grootste uitdaging voor fraudedetectiesystemen is om zich snel aan te passen aan constant veranderende fraudepatronen en tactieken van fraudeurs, en om nieuwe en steeds geavanceerdere schema's snel te ontmaskeren. Fraudegevallen zijn altijd in de minderheid en goed verborgen tussen de echte transacties.
De dataset voorbereiden
Laten we een machine-learningimplementatie van creditcardfraudedetectie verkennen met de programmeertaal Python. We werken met de dataset creditcard_data, een aangepaste sample van een Kaggle-dataset over Credit Card Fraud Detection. De oorspronkelijke data betreft transacties met creditcards van Europese kaarthouders in twee dagen in september 2013.
Importeer de data en neem er snel een kijkje in:
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
De dataset bevat de volgende variabelen:
- Numeriek gecodeerde variabelen V1 tot en met V28, de hoofdbestanddelen die zijn verkregen uit een PCA-transformatie. Vanwege vertrouwelijkheid is geen achtergrondinformatie over de oorspronkelijke kenmerken verstrekt.
- De variabele Amount geeft het transactiebedrag weer.
- De variabele Class toont of de transactie fraude was (1) of niet (0).
Fraude komt van nature gelukkig extreem weinig voor in een lijst met transacties. Machine-learningalgoritmen werken echter meestal het best wanneer de verschillende klassen in de dataset min of meer gelijk vertegenwoordigd zijn. Anders is er weinig data om van te leren. Dit probleem heet class imbalance (klasse-onbalans).
Fraude in de dataset berekenen
Laten we het percentage frauduleuze transacties ten opzichte van het totale aantal transacties in onze dataset berekenen:
round(creditcard_data['Class'].value_counts()*100/len(creditcard_data)).convert_dtypes()
0 99
1 1
Name: Class, dtype: Int64
en een plot maken om de verhouding tussen fraude en geen fraude te visualiseren:
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)

SMOTE gebruiken om de data te herverdelen
We kunnen nu bevestigen dat het aandeel frauduleuze transacties erg laag is en dat we te maken hebben met klasse-onbalans. Om dit op te lossen, kunnen we onze data herverdelen met de synthetic minority oversampling technique (SMOTE). In tegenstelling tot willekeurig oversamplen is SMOTE iets geavanceerder, omdat het niet simpelweg exacte kopieën van observaties maakt. In plaats daarvan gebruikt het de kenmerken van de dichtstbijzijnde buren van fraudegevallen om nieuwe, synthetische samples te creëren die sterk lijken op de bestaande observaties in de minderheidsklasse. Laten we SMOTE toepassen op onze creditcarddata:
from imblearn.over_sampling import SMOTE
method = SMOTE()
X_resampled, y_resampled = method.fit_resample(X, y)
plot_data(X_resampled, y_resampled)

Zoals we zien, levert SMOTE ineens meer observaties van de minderheidsklasse op. Om dit nog beter te zien, vergelijken we de resultaten met de oorspronkelijke data:
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

De SMOTE-methode heeft onze data dus volledig in balans gebracht: de minderheidsklasse is nu even groot als de meerderheidsklasse.
We komen straks terug op de praktische toepassing van de SMOTE-methode, maar keren nu eerst terug naar de oorspronkelijke data en proberen de fraudegevallen te detecteren. Op de "old-school" manier moeten we regels opstellen om fraude te vangen. Zulke regels kunnen bijvoorbeeld gaan over ongewone locaties van transacties of verdacht frequente transacties. Het idee is om drempelwaarden te definiëren op basis van gangbare statistieken, vaak op de gemiddelde waarden van observaties, en die drempels op onze features te gebruiken om fraude te detecteren.
print(creditcard_data.groupby('Class').mean().round(3)[['V1', 'V3']])
V1 V3
Class
0 0.035 0.037
1 -4.985 -7.294
In ons specifieke geval passen we de volgende voorwaarden toe: V1 < -3 en V3 < -5. Om de prestaties van deze aanpak te schatten, vergelijken we de gemarkeerde fraudegevallen met de daadwerkelijke:
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
Logistische regressie toepassen
We hebben 22 van de 50 fraudegevallen gedetecteerd, maar de andere 28 niet, en we hebben 16 false positives. Laten we kijken of machine-learningtechnieken dit resultaat kunnen verbeteren.
We gaan nu een eenvoudig classificatie-algoritme met logistische regressie implementeren op onze creditcarddata om frauduleuze gebeurtenissen te identificeren en de resultaten vervolgens te visualiseren in een confusion matrix:
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
Belangrijk om op te merken: we hebben hier minder observaties in de confusion matrix, omdat we alleen de testset gebruiken om de modelresultaten op te berekenen, dus slechts 30% van de hele dataset.
We hebben een hoger percentage fraudegevallen gevangen: 90% (9 van de 10), vergeleken met het vorige resultaat van 44% (22 van de 50). We hebben ook veel minder false positives dan voorheen, dus dat is een verbetering.
Laten we nu terugkomen op het eerder besproken probleem van klasse-onbalans en onderzoeken of we de voorspellingen nog verder kunnen verbeteren door het logistische-regressiemodel te combineren met de SMOTE-resamplingmethode. Om dit efficiënt en in één keer te doen, definiëren we een pipeline en draaien die op onze data:
from imblearn.pipeline import Pipeline
# Defining which resampling method and which ML model to use in the 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
Zoals we zien, heeft SMOTE in ons geval geen verbetering gebracht: we vangen nog steeds 90% van de fraudegevallen en bovendien hebben we iets meer false positives. De verklaring is dat resampling niet per se in alle gevallen tot betere resultaten leidt. Wanneer de fraudegevallen erg verspreid in de data liggen, zijn hun naaste buren niet per se ook fraudegevallen, waardoor SMOTE een bias kan introduceren.
Conclusie
Als mogelijke vervolgstap om de nauwkeurigheid van het logistische-regressiemodel te verhogen, kunnen we enkele parameters van het algoritme afstellen. K-fold cross-validatie kan ook worden overwogen in plaats van de dataset simpelweg in twee delen te splitsen. Tot slot kunnen we andere machine-learningalgoritmen proberen (bijv. beslisboom of random forest) en kijken of die betere resultaten geven.
Als je meer wilt leren over de theoretische en technische aspecten van het implementeren van een fraudedetectiemodel, bekijk dan het cursusmateriaal van Fraud Detection in Python.
