- Användningsfall för data science i bank: upptäcka bedrägerier
- Förbereda datasetet
- Beräkna bedrägeri i datasetet
- Använda SMOTE för att återbalansera data
- Tillämpa logistisk regression
- Slutsats
Bank är ett av de lyckligt lottade områdena där det historiskt har samlats in mycket strukturerad data, och också ett av de första där data science-tekniker tillämpades.
Hur används data science i bank? Idag har data blivit den mest värdefulla tillgången inom detta område. Data science är ett nödvändigt krav för banker för att hålla jämna steg med sina konkurrenter, attrahera fler kunder, öka lojaliteten hos befintliga kunder, fatta mer effektiva datadrivna beslut, stärka verksamheten, höja den operativa effektiviteten, förbättra befintliga tjänster/produkter och introducera nya, förstärka säkerheten och, som ett resultat av allt detta, öka intäkterna. Det är inte förvånande att majoriteten av all efterfrågan på data science-jobb kommer från banksektorn.
Data science gör det möjligt för banksektorn att framgångsrikt utföra många uppgifter, bland annat:
- analys av investeringsrisk
- prognos av kundens livstidsvärde
- kundsegmentering
- prognos av kundbortfall
- personanpassad marknadsföring
- analys av kundsentiment
- virtuella assistenter och chattbotar
Nedan tittar vi närmare på ett av de vanligaste användningsfallen för data science i bank.
Användningsfall för data science i bank: upptäcka bedrägerier
Bedrägliga aktiviteter är ett svårt problem inte bara inom bank utan även inom många andra områden, som offentlig förvaltning, försäkring, offentlig sektor, försäljning och sjukvård. Alla verksamheter som hanterar ett stort antal onlinetransaktioner löper betydande bedrägeririsk. Finansiella brott kan ta olika former, inklusive bedrägliga kreditkortstransaktioner, förfalskade bankcheckar, skatteflykt, penningtvätt, cyberattacker, kapning av kundkonton, syntetiska identiteter, falska ansökningar och bedrägerier.
Bedrägeriupptäckt är en uppsättning proaktiva åtgärder för att identifiera och förhindra bedrägliga aktiviteter och finansiella förluster. Dess viktigaste analytiska tekniker kan delas in i två grupper:
- Statistiska: beräkning av statistiska parametrar, regression, sannolikhetsfördelningar, datamatchning
- Artificiell intelligens (AI): datautvinning, maskininlärning, djupinlärning
Maskininlärning är en grundpelare för bedrägeriupptäckt. Verktygslådan erbjuder två angreppssätt:
- Övervakade metoder: k-närmaste grannar, logistisk regression, supportvektormaskiner, beslutsträd, random forest, tidsserieanalys, neurala nätverk etc.
- Oövervakade metoder: klusteranalys, länkanalys, självorganiserande kartor, huvudkomponentanalys, avvikelseigenkänning etc.
Det finns ingen universell och helt tillförlitlig algoritm för maskininlärning vid bedrägeriupptäckt. I stället testas vanligtvis flera tekniker eller kombinationer av dem i verkliga användningsfall inom data science, modellernas prediktiva noggrannhet beräknas och det optimala angreppssättet väljs.
Den största utmaningen för system för bedrägeriupptäckt är att snabbt anpassa sig till ständigt föränderliga bedrägarmönster och bedragarnas taktiker samt snabbt avslöja nya och allt mer sofistikerade upplägg. Bedrägerifall är alltid i minoritet och är väl dolda bland de verkliga transaktionerna.
Förbereda datasetet
Låt oss utforska en implementering av maskininlärning för upptäckt av kreditkortsbedrägerier med programmeringsspråket Python. Vi arbetar med datasetet creditcard_data, som är ett modifierat urval från ett Kaggle-dataset om Credit Card Fraud Detection. De ursprungliga uppgifterna representerar transaktioner som gjorts med kreditkort ägda av europeiska kortinnehavare under två dagar i september 2013.
Låt oss importera datan och titta snabbt på den:
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
Datasetet innehåller följande variabler:
- Numeriskt kodade variabler V1 till V28 som är huvudkomponenterna från en PCA-transformering. På grund av sekretessfrågor gavs ingen bakgrundsinformation om de ursprungliga variablerna.
- Variabeln Amount representerar transaktionsbeloppet.
- Variabeln Class visar om transaktionen var ett bedrägeri (1) eller inte (0).
Till sin natur är bedrägerihändelser tack och lov en extrem minoritet i alla transaktionslistor. Maskininlärningsalgoritmer fungerar dock oftast bäst när de olika klasserna i datasetet är mer eller mindre lika representerade. Annars finns det för lite data att lära av. Detta problem kallas klassobalans.
Beräkna bedrägeri i datasetet
Låt oss beräkna andelen bedrägliga transaktioner av det totala antalet transaktioner i vårt dataset:
round(creditcard_data['Class'].value_counts()*100/len(creditcard_data)).convert_dtypes()
0 99
1 1
Name: Class, dtype: Int64
och skapa ett diagram för att visualisera datapunkterna för bedrägeri respektive icke-bedrägeri:
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)

Använda SMOTE för att återbalansera data
Vi kan nu bekräfta att andelen bedrägliga transaktioner är mycket låg och att vi har ett fall av klassobalans. För att åtgärda detta kan vi återbalansera våra data med hjälp av den syntetiska minoritetsöversamplingstekniken (SMOTE). Till skillnad från slumpmässig översampling är SMOTE något mer sofistikerat eftersom det inte bara skapar exakta kopior av observationer. I stället använder det egenskaper hos närmaste grannar till bedrägerifall för att skapa nya, syntetiska prover som liknar de befintliga observationerna i minoritetsklassen. Låt oss tillämpa SMOTE på våra kreditkortsdata:
from imblearn.over_sampling import SMOTE
method = SMOTE()
X_resampled, y_resampled = method.fit_resample(X, y)
plot_data(X_resampled, y_resampled)

Som vi ser ger SMOTE oss plötsligt fler observationer av minoritetsklassen. För att se resultaten av detta angreppssätt ännu tydligare jämför vi dem med originaldatan:
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

Följaktligen har SMOTE-metoden balanserat våra data helt, och minoritetsklassen är nu lika stor som majoritetsklassen.
Vi återkommer snart till den praktiska tillämpningen av SMOTE-metoden, men låt oss nu gå tillbaka till originaldatan och försöka upptäcka bedrägerifallen. Enligt det "+old-school+"-sättet måste vi skapa vissa regler för att fånga bedrägeri. Sådana regler kan till exempel röra ovanliga platser för transaktioner eller misstänkt frekventa transaktioner. Tanken är att definiera tröskelvärden baserat på vanlig statistik, ofta på medelvärden av observationer, och använda dessa trösklar på våra variabler för att upptäcka bedrägeri.
print(creditcard_data.groupby('Class').mean().round(3)[['V1', 'V3']])
V1 V3
Class
0 0.035 0.037
1 -4.985 -7.294
I vårt specifika fall tillämpar vi följande villkor: V1 < -3 och V3 < -5. För att sedan uppskatta hur väl detta angreppssätt fungerar jämför vi de flaggade bedrägerifallen med de faktiska:
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
Tillämpa logistisk regression
Vi upptäckte 22 av 50 bedrägerifall, men missade de övriga 28, och fick 16 falska positiva. Låt oss se om användning av maskininlärning kan slå de resultaten.
Nu implementerar vi en enkel klassificeringsalgoritm med logistisk regression på våra kreditkortsdata för att identifiera bedrägliga händelser och visualiserar sedan resultaten i en konfusionsmatris:
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
Det är viktigt att notera att vi här har färre observationer i konfusionsmatrisen eftersom vi bara använder testmängden för att beräkna modellens resultat, dvs. endast 30% av hela datasetet.
Vi fångade en högre andel bedrägerifall: 90% (9 av 10), jämfört med det tidigare resultatet 44% (22 av 50). Vi fick också betydligt färre falska positiva än tidigare, så det är en förbättring.
Låt oss nu återvända till problemet med klassobalans som diskuterades tidigare och undersöka om vi kan förbättra prediktionsresultaten ytterligare genom att kombinera den logistiska regressionsmodellen med SMOTE-återsampling. För att göra det effektivt och i ett enda steg behöver vi definiera en pipeline och köra den på våra 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
Som vi ser gav SMOTE i vårt fall inga förbättringar: vi fångade fortfarande 90% av bedrägerifallen och dessutom har vi något fler falska positiva. Förklaringen är att återsampling inte nödvändigtvis leder till bättre resultat i alla fall. När bedrägerifallen är mycket utspridda i datan är deras närmaste grannar inte nödvändigtvis också bedrägerifall, så användning av SMOTE kan introducera en bias.
Slutsats
Som en möjlig väg framåt, för att öka noggrannheten i den logistiska regressionsmodellen, kan vi finjustera vissa av algoritmens parametrar. K-faldig korsvalidering kan också övervägas i stället för att bara dela datasetet i två delar. Slutligen kan vi prova andra maskininlärningsalgoritmer (t.ex. beslutsträd eller random forest) och se om de ger bättre resultat.
Om du vill lära dig mer om de teoretiska och tekniska aspekterna av implementering av modeller för bedrägeriupptäckt kan du utforska materialet i kursen Fraud Detection in Python.
