- Caz de utilizare data science în banking: detectarea fraudelor
- Pregătirea setului de date
- Calcularea fraudei în setul de date
- Folosirea SMOTE pentru reechilibrarea datelor
- Aplicarea regresiei logistice
- Concluzie
Bankingul este unul dintre acele domenii norocoase în care, istoric, s-au colectat multe date structurate și, de asemenea, printre primele în care au fost aplicate tehnologiile de data science.
Cum este folosită data science în banking? În prezent, datele au devenit cel mai valoros activ în această sferă. Data science este o cerință necesară pentru ca băncile să țină pasul cu rivalii, să atragă mai mulți clienți, să crească loialitatea celor existenți, să ia decizii mai eficiente bazate pe date, să-și potențeze afacerea, să-și îmbunătățească eficiența operațională, să îmbunătățească serviciile/produsele existente și să introducă unele noi, să consolideze securitatea și, ca rezultat al tuturor acestor acțiuni, să obțină venituri mai mari. Nu e de mirare că majoritatea cererii de joburi în data science vine din banking.
Data science le permite băncilor să îndeplinească cu succes numeroase sarcini, inclusiv:
- analiza riscului investițional
- predicția valorii pe viață a clientului (CLV)
- segmentarea clienților
- predicția ratei de churn
- marketing personalizat
- analiza sentimentului clienților
- asistenți virtuali și chatboți
Mai jos, vom analiza mai îndeaproape unul dintre cele mai comune cazuri de utilizare a data science în banking.
Caz de utilizare data science în banking: detectarea fraudelor
Activitățile frauduloase reprezintă o problemă dificilă nu doar în banking, ci și în multe alte domenii, precum guvern, asigurări, sector public, vânzări și sănătate. Orice afacere care se confruntă cu un număr mare de tranzacții online are un risc semnificativ de fraudă. Infracțiunile financiare pot lua diverse forme, inclusiv tranzacții frauduloase cu cardul de credit, cecuri bancare falsificate, evaziune fiscală, spălare de bani, atacuri cibernetice, furt de conturi de client, identități sintetice, aplicații false și înșelătorii.
Detectarea fraudelor este un set de măsuri proactive întreprinse pentru a identifica și preveni activitățile frauduloase și pierderile financiare. Principalele sale tehnici analitice pot fi împărțite în două grupuri:
- Statistice: calculul parametrilor statistici, regresie, distribuții de probabilitate, potrivire de date
- Inteligență artificială (AI): data mining, machine learning, deep learning
Machine learning reprezintă un pilon esențial pentru detectarea fraudelor. Instrumentele sale oferă două abordări:
- Metode supravegheate: cei mai apropiați k vecini, regresie logistică, mașini cu vectori de susținere, arbore de decizie, pădure aleatoare, analiză de serii temporale, rețele neuronale etc.
- Metode nesupravegheate: analiză de clustere, analiză de legături, hărți autoorganizatoare, analiză a componentelor principale, recunoașterea anomaliilor etc.
Nu există un algoritm universal și fiabil de machine learning pentru detectarea fraudelor. În schimb, în cazurile reale de utilizare în data science, se testează de obicei mai multe tehnici sau combinații ale acestora, se calculează acuratețea predictivă a modelului și se selectează abordarea optimă.
Provocarea principală pentru sistemele de detectare a fraudelor este să se adapteze rapid la modelele de fraudă și tacticile fraudatorilor, aflate în continuă schimbare, și să descopere prompt scheme noi și tot mai elaborate. Cazurile de fraudă sunt întotdeauna o minoritate și sunt bine ascunse printre tranzacțiile reale.
Pregătirea setului de date
Hai să explorăm o implementare de machine learning pentru detectarea fraudelor pe cardul de credit folosind limbajul de programare Python. Vom lucra pe setul de date creditcard_data, care este un eșantion modificat dintr-un set de date Kaggle despre Detectarea fraudelor cu cardul de credit. Datele originale reprezintă tranzacțiile efectuate pe carduri deținute de titulari europeni pe parcursul a două zile din septembrie 2013.
Hai să importăm datele și să aruncăm o privire rapidă:
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
Setul de date conține următoarele variabile:
- Variabile codificate numeric V1 până la V28, care sunt componentele principale obținute dintr-o transformare PCA. Din motive de confidențialitate, nu au fost furnizate informații de fundal despre caracteristicile originale.
- Variabila Amount reprezintă suma tranzacției.
- Variabila Class arată dacă tranzacția a fost fraudă (1) sau nu (0).
Prin natura lor, aparițiile de fraudă sunt, din fericire, o minoritate extremă în orice listă de tranzacții. Totuși, algoritmii de machine learning funcționează de obicei cel mai bine când clasele diferite din setul de date sunt mai mult sau mai puțin reprezentate în mod egal. Altfel, există puține date din care să învețe. Această problemă se numește dezechilibru de clasă.
Calcularea fraudei în setul de date
Hai să calculăm procentul tranzacțiilor frauduloase din numărul total de tranzacții din setul nostru de date:
round(creditcard_data['Class'].value_counts()*100/len(creditcard_data)).convert_dtypes()
0 99
1 1
Name: Class, dtype: Int64
și să creăm un grafic pentru a vizualiza punctele de date fraudă vs. non-fraudă:
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)

Folosirea SMOTE pentru reechilibrarea datelor
Putem confirma acum că raportul tranzacțiilor frauduloase este foarte scăzut și că avem un caz de dezechilibru de clasă. Pentru a-l remedia, ne putem reechilibra datele folosind tehnica de supra-eșantionare a minorității sintetice (SMOTE). Spre deosebire de supra-eșantionarea aleatorie, SMOTE este puțin mai sofisticată, deoarece nu creează doar copii exacte ale observațiilor. În schimb, folosește caracteristicile celor mai apropiați vecini ai cazurilor de fraudă pentru a crea eșantioane sintetice noi, destul de similare cu observațiile existente din clasa minoritară. Hai să aplicăm SMOTE pe datele noastre de card de credit:
from imblearn.over_sampling import SMOTE
method = SMOTE()
X_resampled, y_resampled = method.fit_resample(X, y)
plot_data(X_resampled, y_resampled)

După cum putem vedea, folosirea SMOTE ne oferă brusc mai multe observații ale clasei minoritare. Pentru a vedea și mai bine rezultatele acestei abordări, le vom compara cu datele originale:
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

Prin urmare, metoda SMOTE ne-a echilibrat complet datele, iar clasa minoritară este acum egală ca mărime cu clasa majoritară.
Vom reveni în curând la aplicarea practică a metodei SMOTE, dar, deocamdată, să ne întoarcem la datele originale și să încercăm să detectăm cazurile de fraudă. Urmând o abordare „old-school”, trebuie să creăm câteva reguli pentru a prinde frauda. Astfel de reguli pot viza, de exemplu, locații neobișnuite ale tranzacțiilor sau tranzacții suspect de frecvente. Ideea este să definim valori prag bazate pe statistici comune, adesea pe valorile medii ale observațiilor, și să folosim aceste praguri pe caracteristicile noastre pentru a detecta frauda.
print(creditcard_data.groupby('Class').mean().round(3)[['V1', 'V3']])
V1 V3
Class
0 0.035 0.037
1 -4.985 -7.294
În cazul nostru particular, hai să aplicăm următoarele condiții: V1 < -3 și V3 < -5. Apoi, pentru a estima performanța acestei abordări, vom compara cazurile de fraudă semnalate cu cele reale:
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
Aplicarea regresiei logistice
Am detectat 22 din 50 de cazuri de fraudă, dar nu putem detecta celelalte 28 și am obținut 16 alarme false. Să vedem dacă folosirea tehnicilor de machine learning poate depăși aceste rezultate.
Vom implementa acum un algoritm simplu de clasificare prin regresie logistică pe datele noastre de card de credit pentru a identifica aparițiile frauduloase și apoi vom vizualiza rezultatele într-o matrice de confuzie:
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
Este important de menționat că aici avem mai puține observații de privit în matricea de confuzie pentru că folosim doar setul de test pentru a calcula rezultatele modelului, adică doar 30% din întregul set de date.
Am prins un procent mai mare de cazuri de fraudă: 90% (9 din 10), comparativ cu rezultatul anterior de 44% (22 din 50). Am avut, de asemenea, mult mai puține alarme false decât înainte, deci este o îmbunătățire.
Hai să revenim acum la problema dezechilibrului de clasă discutată mai devreme și să explorăm dacă putem îmbunătăți și mai mult rezultatele de predicție combinând modelul de regresie logistică cu metoda de re-eșantionare SMOTE. Pentru a face asta eficient și dintr-o singură mișcare, trebuie să definim un pipeline și să-l rulăm pe datele noastre:
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
După cum putem vedea, în cazul nostru SMOTE nu a adus nicio îmbunătățire: tot am prins 90% din aparițiile de fraudă și, în plus, avem un număr ușor mai mare de alarme false. Explicația aici este că re-eșantionarea nu duce neapărat la rezultate mai bune în toate cazurile. Când cazurile de fraudă sunt foarte împrăștiate în date, cei mai apropiați vecini ai lor nu sunt neapărat tot cazuri de fraudă, astfel că folosirea SMOTE poate introduce un bias.
Concluzie
Ca posibil pas înainte, pentru a crește acuratețea modelului de regresie logistică, putem regla anumiți parametri ai algoritmului. Se poate lua în considerare și validarea încrucișată k-fold în loc să împărțim setul de date doar în două părți. În final, putem încerca și alți algoritmi de machine learning (de exemplu, arbore de decizie sau pădure aleatoare) și să vedem dacă oferă rezultate mai bune.
Dacă vrei să afli mai multe despre aspectele teoretice și tehnice ale implementării unui model de detectare a fraudelor, poți explora materialele cursului Fraud Detection in Python.
