Перейти к основному контенту

Data Science в банковской сфере: выявление мошенничества

Узнайте, как реализуется data science в банковском секторе, на примере одного из самых распространённых кейсов — выявления мошенничества.
Обновлено 31 авг. 2026 г.  · 11 мин читать

Изучить с помощью AI

ChatGPTClaudePerplexity

Иллюстрация на тему финтеха

Банковская сфера — одна из немногих, где исторически накапливается много структурированных данных, и одна из первых, где начали применять технологии data science.

Как используется data science в банках? Сегодня данные стали самым ценным активом в этой области. Data science — необходимое условие, чтобы банки не отставали от конкурентов, привлекали больше клиентов, повышали лояльность текущих, принимали более эффективные решения на основе данных, усиливали бизнес, повышали операционную эффективность, улучшали существующие сервисы и продукты и выводили новые, укрепляли безопасность и, как результат всех этих действий, увеличивали доход. Неудивительно, что большая часть спроса на специалистов по data science исходит из банковского сектора.

Data science позволяет банковской отрасли успешно решать множество задач, включая: 

  • анализ инвестиционных рисков
  • прогнозирование пожизненной ценности клиента (CLV)
  • сегментацию клиентов
  • прогнозирование оттока клиентов
  • персонифицированный маркетинг
  • анализ мнений клиентов
  • виртуальных помощников и чат-ботов

Ниже мы подробнее рассмотрим один из самых распространённых кейсов использования data science в банках.

Кейс data science в банкинге: выявление мошенничества 

Мошеннические действия — сложная задача не только для банков, но и для многих других сфер: государственного сектора, страхования, продаж, здравоохранения. Любой бизнес с большим числом онлайн-транзакций сталкивается со значительным риском мошенничества. Финансовые преступления принимают разные формы: мошеннические операции по кредитным картам, поддельные банковские чеки, уклонение от налогов, отмывание денег, кибератаки, кража аккаунтов клиентов, синтетические личности, фиктивные заявки и разнообразные схемы обмана.

Выявление мошенничества — это набор проактивных мер, направленных на идентификацию и предотвращение мошеннических действий и финансовых потерь. Основные аналитические методы делятся на две группы:

  • Статистические: расчёт статистических параметров, регрессия, распределения вероятностей, сопоставление данных
  • Искусственный интеллект (AI): интеллектуальный анализ данных, машинное обучение, глубокое обучение

Машинное обучение — ключевая опора для выявления мошенничества. Его инструментарий предлагает два подхода:

  • Обучение с учителем: метод k ближайших соседей, логистическая регрессия, опорные векторы, дерево решений, случайный лес, анализ временных рядов, нейронные сети и др.
  • Обучение без учителя: кластерный анализ, анализ связей, самоорганизующиеся карты, метод главных компонент, распознавание аномалий и др.

Универсального и надёжного алгоритма машинного обучения для выявления мошенничества не существует. В реальных кейсах обычно тестируют несколько техник или их комбинации, оценивают точность предсказаний модели и выбирают оптимальный подход.

Главная сложность систем выявления мошенничества — быстро адаптироваться к постоянно меняющимся паттернам и тактикам мошенников и оперативно раскрывать новые, всё более изощрённые схемы. Случаи мошенничества всегда в меньшинстве и хорошо скрыты среди реальных транзакций.

Подготовка набора данных

Рассмотрим реализацию выявления мошенничества по операциям с кредитными картами с помощью Python. Мы будем работать с набором данных creditcard_data — модифицированным образцом с Kaggle по теме Credit Card Fraud Detection. Исходные данные представляют операции по кредитным картам европейских держателей за два дня в сентябре 2013 года.

Импортируем данные и быстро на них посмотрим:

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 

Набор данных содержит следующие переменные:

  • Числовые переменные V1–V28 — это главные компоненты, полученные в результате преобразования PCA. По соображениям конфиденциальности информация об исходных признаках не предоставлена.
  • Переменная Amount — сумма транзакции.
  • Переменная Class показывает, была ли транзакция мошеннической (1) или нет (0).

По своей природе случаи мошенничества, к счастью, составляют крайне малую долю в списке транзакций. Однако алгоритмы машинного обучения обычно работают лучше, когда классы в наборе данных представлены более-менее равномерно.  Иначе алгоритму просто нечему учиться. Эта проблема называется несбалансированностью классов.

Подсчёт доли мошенничества в наборе данных

Подсчитаем процент мошеннических транзакций от общего числа операций в наборе данных:

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

и построим график соотношения меток «мошенничество/не мошенничество»:

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 для ребалансировки данных 

Мы видим, что доля мошеннических операций очень мала — это и есть проблема несбалансированности классов. Чтобы её исправить, можно «перебалансировать» данные с помощью метода синтетического дополнения миноритарного класса (SMOTE). В отличие от случайного увеличения выборки, SMOTE работает чуть тоньше: он не делает точные копии наблюдений, а использует характеристики ближайших соседей случаев мошенничества для создания новых синтетических образцов, похожих на существующие наблюдения в миноритарном классе. Применим SMOTE к нашим данным по кредитным картам:

from imblearn.over_sampling import SMOTE

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

График SMOTE

Как видно, SMOTE сразу даёт больше наблюдений миноритарного класса. Чтобы лучше увидеть эффект, сравним результаты с исходными данными:

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

Таким образом, SMOTE полностью сбалансировал наши данные: размер миноритарного класса сравнялся с мажоритарным.

К практическому применению SMOTE мы ещё вернёмся, а пока вернёмся к исходным данным и попробуем обнаружить случаи мошенничества. Следуя «олдскульному» подходу, нужно задать правила для отлова мошенничества. Например, необычное местоположение транзакций или подозрительно частые операции. Идея — определить пороговые значения на основе типовых статистик, часто средних значений наблюдений, и применять эти пороги к признакам для выявления мошенничества.

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

В нашем случае применим условия: V1 < -3 и V3 < -5. Затем для оценки эффективности сравним помеченные как мошеннические операции с фактическими:

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

Применение логистической регрессии 

Мы обнаружили 22 из 50 случаев мошенничества, но не смогли найти остальные 28, при этом получили 16 ложных срабатываний. Посмотрим, смогут ли методы машинного обучения улучшить эти результаты.

Реализуем простой алгоритм бинарной классификации — логистическую регрессию — для выявления мошеннических операций и визуализируем результат в матрице ошибок:

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

Важно отметить, что в этой матрице ошибок меньше наблюдений, поскольку мы используем только тестовую выборку — то есть 30% всего набора данных.

Мы обнаружили более высокий процент мошеннических случаев: 90% (9 из 10) против 44% (22 из 50) ранее. Также получено значительно меньше ложных срабатываний — это улучшение.

Вернёмся к проблеме несбалансированности классов и посмотрим, можно ли ещё повысить качество предсказаний, объединив логистическую регрессию с ресемплированием SMOTE. Чтобы сделать это эффективно и за один проход, определим конвейер (pipeline) и запустим его на данных:

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

В нашем случае SMOTE не дал улучшений: мы по-прежнему нашли 90% мошеннических операций, а число ложных срабатываний немного выросло. Дело в том, что ресемплирование не всегда улучшает результат. Когда случаи мошенничества сильно «размазаны» по данным, их ближайшие соседи могут не быть мошенническими случаями, и SMOTE может вносить смещение.

Выводы 

В качестве дальнейших шагов для повышения точности логистической регрессии можно настроить параметры алгоритма. Вместо простого деления набора данных на две части стоит рассмотреть k-fold кросс-валидацию. Наконец, можно попробовать другие алгоритмы машинного обучения (например, дерево решений или случайный лес) и проверить, дадут ли они лучшие результаты. 

Если вы хотите глубже изучить теоретические и технические аспекты реализации моделей выявления мошенничества, ознакомьтесь с материалами курса Fraud Detection in Python.

Темы
Data Science
Машинное обучение