- Кейс data science в банкинге: выявление мошенничества
- Подготовка набора данных
- Подсчёт доли мошенничества в наборе данных
- Использование SMOTE для ребалансировки данных
- Применение логистической регрессии
- Выводы
Банковская сфера — одна из немногих, где исторически накапливается много структурированных данных, и одна из первых, где начали применять технологии 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 сразу даёт больше наблюдений миноритарного класса. Чтобы лучше увидеть эффект, сравним результаты с исходными данными:
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 мы ещё вернёмся, а пока вернёмся к исходным данным и попробуем обнаружить случаи мошенничества. Следуя «олдскульному» подходу, нужно задать правила для отлова мошенничества. Например, необычное местоположение транзакций или подозрительно частые операции. Идея — определить пороговые значения на основе типовых статистик, часто средних значений наблюдений, и применять эти пороги к признакам для выявления мошенничества.
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.
