Ga naar hoofdinhoud

Data science in de bankensector: fraudedetectie

Leer hoe data science wordt toegepast in de bankensector aan de hand van een van de meest voorkomende use-cases: fraudedetectie.
Bijgewerkt 31 aug 2026  · 11 min lezen

Verkennen met AI

ChatGPTClaudePerplexity

Illustratie van fintech-concept

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 fraude­patronen 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)

Frauduleuze transacties

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)

SMOTE-grafiek

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

SMOTE-methode-grafiek

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.

Onderwerpen
Data Science
Machine Learning