Przejdź do głównej treści

Data science w bankowości: wykrywanie nadużyć

Dowiedz się, jak data science jest wdrażane w sektorze bankowym, na przykładzie jednego z najczęstszych zastosowań: wykrywania nadużyć.
Zaktualizowano 31 sie 2026  · 11 min Czytać

Eksploruj z AI

ChatGPTClaudePerplexity

Ilustracja koncepcji fintech

Bankowość to jedna z tych dziedzin, w których historycznie gromadzi się dużo danych ustrukturyzowanych, a także jedna z pierwszych, gdzie zastosowano technologie data science.

Jak wykorzystuje się data science w bankowości? Dziś dane stały się w tej sferze najcenniejszym zasobem. Data science to niezbędny warunek, by banki dotrzymywały kroku konkurentom, pozyskiwały więcej klientów, zwiększały lojalność obecnych, podejmowały trafniejsze decyzje oparte na danych, wzmacniały biznes, podnosiły efektywność operacyjną, ulepszały istniejące usługi/produkty i wprowadzały nowe, wzmacniały bezpieczeństwo, a w rezultacie tych działań zwiększały przychody. Nic dziwnego, że większość popytu na stanowiska w data science pochodzi z sektora bankowego.

Data science pozwala sektorowi bankowemu z powodzeniem realizować wiele zadań, w tym: 

  • analizę ryzyka inwestycyjnego
  • prognozowanie wartości życiowej klienta (CLV)
  • segmentację klientów
  • prognozowanie wskaźnika odejść klientów (churn)
  • marketing spersonalizowany
  • analizę nastrojów klientów
  • wirtualnych asystentów i chatboty

Poniżej przyjrzymy się bliżej jednemu z najczęstszych zastosowań data science w bankowości.

Przykład użycia data science w bankowości: wykrywanie nadużyć 

Oszustwa stanowią trudny problem nie tylko w bankowości, ale i w wielu innych obszarach, takich jak administracja publiczna, ubezpieczenia, sektor publiczny, sprzedaż czy opieka zdrowotna. Każdy biznes obsługujący dużą liczbę transakcji online jest narażony na istotne ryzyko nadużyć. Przestępstwa finansowe przybierają różne formy: nieautoryzowane transakcje kartą kredytową, sfałszowane czeki bankowe, unikanie podatków, pranie pieniędzy, ataki cybernetyczne, kradzież kont klientów, tożsamości syntetyczne, fałszywe wnioski i oszustwa.

Wykrywanie nadużyć to zestaw działań prewencyjnych podejmowanych w celu identyfikacji i zapobiegania oszustwom oraz stratom finansowym. Główne techniki analityczne można podzielić na dwie grupy:

  • Statystyczne: obliczanie parametrów statystycznych, regresja, rozkłady prawdopodobieństwa, dopasowywanie danych
  • Sztuczna inteligencja (AI): eksploracja danych, uczenie maszynowe, głębokie uczenie

Uczenie maszynowe to kluczowy filar wykrywania nadużyć. Jego zestaw narzędzi oferuje dwa podejścia:

  • Metody nadzorowane: k-najbliższych sąsiadów, regresja logistyczna, maszyny wektorów nośnych, drzewo decyzyjne, las losowy, analiza szeregów czasowych, sieci neuronowe itd.
  • Metody nienadzorowane: analiza skupień, analiza powiązań, samoorganizujące się mapy, analiza głównych składowych, wykrywanie anomalii itd.

Nie ma uniwersalnego i niezawodnego algorytmu uczenia maszynowego do wykrywania nadużyć. W realnych projektach testuje się zwykle kilka technik lub ich kombinacje, oblicza się trafność predykcji modelu i wybiera optymalne podejście.

Główne wyzwanie dla systemów wykrywania nadużyć to szybka adaptacja do stale zmieniających się wzorców oszustw i taktyk oszustów oraz szybkie ujawnianie nowych, coraz bardziej złożonych schematów. Przypadki nadużyć zawsze stanowią mniejszość i są dobrze ukryte wśród prawdziwych transakcji.

Przygotowanie zbioru danych

Przeanalizujmy implementację wykrywania nadużyć na kartach kredytowych z wykorzystaniem Pythona. Będziemy pracować na zbiorze creditcard_data, który jest zmodyfikowaną próbką z Kaggle: Credit Card Fraud Detection. Oryginalne dane to transakcje dokonane kartami kredytowymi należącymi do europejskich posiadaczy kart w ciągu dwóch dni we wrześniu 2013 r.

Zaimportujmy dane i rzućmy na nie okiem:

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 

Zbiór zawiera następujące zmienne:

  • Numerycznie zakodowane zmienne V1–V28, które są głównymi składowymi uzyskanymi z transformacji PCA. Ze względu na poufność nie podano kontekstu dotyczącego oryginalnych cech.
  • Zmienna Amount to kwota transakcji.
  • Zmienna Class wskazuje, czy transakcja była nadużyciem (1), czy nie (0).

Z natury rzeczy przypadki nadużyć na szczęście stanowią skrajną mniejszość w każdej liście transakcji. Jednak algorytmy uczenia maszynowego zwykle działają najlepiej, gdy różne klasy w zbiorze danych są mniej więcej równie liczne.  W przeciwnym razie jest niewiele danych, z których można się uczyć. Ten problem to niezrównoważenie klas.

Obliczanie odsetka nadużyć w zbiorze

Policzmy odsetek transakcji oszukańczych w stosunku do łącznej liczby transakcji w naszym zbiorze:

round(creditcard_data['Class'].value_counts()*100/len(creditcard_data)).convert_dtypes()
0    99
1     1
Name: Class, dtype: Int64

i stwórzmy wykres wizualizujący punkty danych klasy oszustwo vs. brak oszustwa:

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)

Transakcje oszukańcze

Użycie SMOTE do zrównoważenia danych 

Widzimy teraz, że odsetek transakcji oszukańczych jest bardzo niski i mamy do czynienia z problemem niezrównoważenia klas. Aby go naprawić, możemy zrównoważyć dane przy użyciu techniki syntetycznego nadpróbkowania mniejszości (SMOTE). W przeciwieństwie do losowego nadpróbkowania, SMOTE jest nieco bardziej wyrafinowane, ponieważ nie tworzy dokładnych kopii obserwacji. Zamiast tego wykorzystuje cechy najbliższych sąsiadów przypadków nadużyć do tworzenia nowych, syntetycznych próbek podobnych do istniejących obserwacji w klasie mniejszościowej. Zastosujmy SMOTE do naszych danych kartowych:

from imblearn.over_sampling import SMOTE

method = SMOTE()
X_resampled, y_resampled = method.fit_resample(X, y)
plot_data(X_resampled, y_resampled)

Wykres SMOTE

Jak widać, użycie SMOTE nagle daje nam więcej obserwacji klasy mniejszościowej. Aby lepiej zobaczyć efekty, porównamy je z oryginalnymi danymi:

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

Wykres metody SMOTE

Metoda SMOTE całkowicie zrównoważyła nasze dane, a klasa mniejszościowa jest teraz tak liczna jak większościowa.

Wrócimy jeszcze do praktycznego zastosowania SMOTE, ale na razie wróćmy do oryginalnych danych i spróbujmy wykryć przypadki nadużyć. Idąc „starą szkołą”, musimy stworzyć pewne reguły, by je wychwycić. Takie reguły mogą dotyczyć np. nietypowych lokalizacji transakcji lub podejrzanie częstych operacji. Pomysł polega na zdefiniowaniu wartości progowych na podstawie statystyk, często średnich wartości obserwacji, i użyciu tych progów na naszych cechach do wykrywania nadużyć.

print(creditcard_data.groupby('Class').mean().round(3)[['V1', 'V3']])
        V1     V3
Class             
0      0.035  0.037
1     -4.985 -7.294

W naszym przypadku zastosujmy warunki: V1 < -3 oraz V3 < -5. Następnie, aby ocenić skuteczność tego podejścia, porównamy oznaczone przypadki nadużyć z rzeczywistymi:

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

Zastosowanie regresji logistycznej 

Wykryliśmy 22 z 50 przypadków nadużyć, ale nie potrafimy wykryć pozostałych 28, a do tego mamy 16 fałszywych trafień. Zobaczmy, czy techniki uczenia maszynowego poprawią te wyniki.

Wdrożymy teraz prosty algorytm klasyfikacji regresją logistyczną na naszych danych kartowych, by zidentyfikować nadużycia, a następnie zwizualizujemy wyniki na macierzy pomyłek:

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

Warto zauważyć, że w macierzy pomyłek mamy tu mniej obserwacji, ponieważ używamy jedynie zbioru testowego do obliczenia wyników modelu, tj. tylko 30% całego zbioru danych.

Wychwyciliśmy wyższy odsetek nadużyć: 90% (9 z 10), w porównaniu z wcześniejszym wynikiem 44% (22 z 50). Mamy też znacznie mniej fałszywych trafień niż wcześniej, więc to poprawa.

Wróćmy teraz do problemu niezrównoważenia klas i sprawdźmy, czy możemy jeszcze bardziej poprawić wyniki predykcji, łącząc model regresji logistycznej z metodą próbkowania SMOTE. Aby zrobić to sprawnie i za jednym razem, zdefiniujemy potok i uruchomimy go na naszych danych:

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

Jak widać, w naszym przypadku SMOTE nie przyniósł poprawy: nadal wychwyciliśmy 90% przypadków nadużyć, a dodatkowo mamy nieco więcej fałszywych trafień. Wyjaśnienie jest takie, że ponowne próbkowanie niekoniecznie zawsze daje lepsze wyniki. Gdy przypadki nadużyć są silnie rozproszone w danych, ich najbliżsi sąsiedzi nie muszą być również nadużyciami, więc użycie SMOTE może wprowadzać bias.

Wnioski 

Aby dalej zwiększyć trafność modelu regresji logistycznej, można dostroić część jego parametrów. Zamiast dzielić zbiór na dwie części, można też rozważyć walidację krzyżową k-krotną. Wreszcie, warto spróbować innych algorytmów uczenia maszynowego (np. drzew decyzyjnych lub lasów losowych) i sprawdzić, czy dadzą lepsze wyniki. 

Jeśli chcesz dowiedzieć się więcej o teoretycznych i technicznych aspektach implementacji modeli do wykrywania nadużyć, zajrzyj do materiałów kursu Fraud Detection in Python.

Tematy
Data Science
Uczenie maszynowe