Обнаружение мошенничества с помощью машинного обучения

ML для обнаружения аномалий и важность кривой прецизионного вызова

Обработка несбалансированного набора данных для обнаружения вероятности мошенничества

Https://sarit-maitra.medium.com/membership

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

Здесь у нас есть набор данных о банковских платежах, как показано ниже. Целевая переменная, которая показывает, является ли транзакция мошеннической (1) или действительной (0).

Этот набор данных содержит информацию о растущем риске цифрового финансового мошенничества, подчеркивая сложность получения таких данных. Техническая проблема, которую он представляет для прогнозирования мошенничества, заключается в крайне несбалансированном распределении между положительными и отрицательными классами в 6 миллионах строк данных. Чтобы лучше понять набор данных о финансовых транзакциях, давайте посчитаем количество различных значений по признакам.

Данные несбалансированы, как мы можем видеть на графике ниже и из outlier_fraction.

ax = (df['fraud'].value_counts()*100.0 /len(df)).plot(kind='bar', stacked = True, rot = 0)
ax.yaxis.set_major_formatter(mtick.PercentFormatter())
ax.set_ylabel('Frequency Percentage')
ax.set_xlabel('Class')
ax.set_title('Frequency Percentage by Class')
totals = []  
for i in ax.patches:
 totals.append(i.get_width())
total = sum(totals)  
for i in ax.patches:
 ax.text(i.get_x()+.15, i.get_height()-3.5, \
 str(round((i.get_height()/total), 1))+'%', color='black', weight = 'bold')

Предварительная обработка данных:

Есть только одно уникальное значение zipCode, поэтому мы их опустим.

print("Unique zipCodeOri values: ",df.zipcodeOri.nunique())
print("Unique zipMerchant values: ",df.zipMerchant.nunique())

# dropping zipcodeori and zipMerchant 
reducedDF = df.drop(['zipcodeOri','zipMerchant'],axis=1)
# changing object dtype to categorical for easing the transformation process
categorical = reducedDF.select_dtypes(include= ['object']).columns
for col in categorical:
reducedDF[col] = reducedDF[col].astype('category')
# categorical values to numeric values
reducedDF[categorical] = reducedDF[categorical].apply(lambda x: x.cat.codes)
reducedDF.head()

Можно заметить, что здесь избегается горячее кодирование для упрощения вычислений; однако обычно лучше превратить такие категориальные значения в фиктивные, потому что они не имеют никакого отношения к размеру (т. е. customer1 не больше, чем customer2).

Создайте матрицу признаков X и массив меток y

Давайте изменим масштаб матрицы признаков, чтобы получить среднее значение, равное нулю, и стандартное отклонение, равное единице.

featuresToScale = X.columns
sX = pp.StandardScaler(copy=True)
X.loc[:,featuresToScale] = sX.fit_transform(X[featuresToScale])

Соотношение признаков:

Подготовка модели:

Теперь, когда данные готовы, давайте подготовимся к модели. Нам нужно разделить данные на обучающий и тестовый набор, выбрать функцию стоимости.

Выберите функцию стоимости:

Нам нужно применить к алгоритму функцию стоимости, которая представляет собой частоту ошибок, перед обучением. Алгоритм попытается минимизировать эту функцию затрат, изучая обучающие примеры. Мы будем использовать потери журнала двоичной классификации, который будет вычислять перекрестную энтропию между истинными метками и прогнозами на основе модели.

Здесь n - количество наблюдений; m - количество меток класса; log - натуральный логарифм; равно 1, если наблюдение i находится в классе j, и 0 в противном случае; и - прогнозируемая вероятность того, что наблюдение i находится в классе j.

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

featuresToScale = X.columns
sX = pp.StandardScaler(copy=True)
X.loc[:,featuresToScale] = sX.fit_transform(X[featuresToScale])
# Define resampling method and split into train and test
method = SMOTE(kind='borderline1')
trainX, testX, trainY, testY = train_test_split(X,y, test_size = 0.2, random_state = 42, stratify=y)
# Apply resampling to the training data only
X_resampled, y_resampled = method.fit_sample(trainX, np.ravel(trainY))

Установка Light GBM:

model = LGBMClassifier().fit(X_resampled, y_resampled)
# Get model performance metrics
predicted = model.predict(testX)
print(classification_report(np.ravel(testY), predicted))
probabilities = model.fit(trainX,
                          np.ravel(trainY)).predict_proba(testX)
print('AUPRC = {}'.format(average_precision_score(testY, \
probabilities[:, 1])))
# Probabilities for the positive outcome only
lgb_probs = probabilities[:, 1]
# Precision-recall AUC
precision, recall, _ = precision_recall_curve(np.ravel(testY), lgb_probs)
auc_score = auc(recall, precision)
print('LightGBM PR AUC: %.3f' % auc_score)
average_precision = average_precision_score(testY, lgb_probs)
plt.figure(figsize=(10,6))
# calculate the no skill line as the proportion of the positive class
no_skill = len(y[y==1]) / len(y)
# plot the no skill precision-recall curve
plt.plot([0, 1], [no_skill, no_skill], linestyle='--', label='No Skill')
# plot the model precision-recall curve
plt.plot(recall, precision, marker='.', label='LightGBM')
plt.fill_between(recall, precision, step='post', alpha=0.3, color='k')
# axis labels
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.legend()
plt.title('Precision-Recall curve: Average Precision = {0:0.2f}'.format(average_precision))

Чувствительность, специфичность, точность, F1 и MCC:

Чувствительность и специфичность обычно считаются характеристиками матрицы неточностей.

  • Чувствительность или отзыв - это вероятность того, что обнаруженное мошенничество приведет к положительному результату теста (истинно-положительный результат).

Здесь мы видим, что формула не содержит FP и TN; таким образом, чувствительность может привести к смещенному результату, особенно для нашего варианта использования с несбалансированными классами.

Таким образом, когда классификатор сообщает о положительном результате в 8 из 10 транзакций с вероятностью мошенничества, чувствительность составляет 0,8 (80%). Он показывает, насколько хорошо наш классификатор обнаруживает мошенничество; классификатор с низкой чувствительностью не идентифицирует многие мошеннические транзакции, а классификатор с высокой чувствительностью полезен для исключения транзакции при отрицательных результатах. Итак, мы видим, что чувствительность - это дополнение к ложноотрицательному показателю (то есть ложноотрицательному показателю плюс чувствительность = 100%).

  • Специфичность - это вероятность того, что транзакции мошеннические, будут иметь отрицательный результат теста в транзакциях без мошенничества (истинно-отрицательный показатель).

Таким образом, когда классификатор сообщает об отрицательном результате в 8 из 10 транзакций без мошенничества, специфичность составляет 0,8 (80%). Специфичность показывает, насколько хорошо наш классификатор правильно определяет транзакции с мошенничеством, поскольку у классификатора с высокой специфичностью низкий уровень ложных срабатываний. Классификатор с низкой специфичностью сигнализирует о том, что многие подлинные транзакции являются мошенническими. Это дополнение к количеству ложных срабатываний. Однако и здесь мы видим, что формула не содержит FN и TP; Специфика также может дать нам необъективный результат для несбалансированных классов.

  • Точность также известна как положительная прогностическая ценность.

Здесь также формула не содержит FN и TN, и аналогичным образом точность может дать искаженный результат для наших несбалансированных классов. Он дает нам процент правильно спрогнозированных мошенничеств из общего числа спрогнозированных мошенничеств.

  • Оценка F1 включает в себя как отзыв, так и точность и представляет более сбалансированное представление по сравнению с указанными выше тремя показателями, но может дать необъективный результат в сценарии, поскольку он не включает TN.

  • Коэффициент корреляции Мэтьюза (MCC) учитывает все ячейки матрицы неточностей в своей формуле. Однако мы не экспериментировали с MCC в этой работе, хотя некоторые отчеты предполагают, что MCC легко интерпретируем, а также устойчив к изменениям в цели прогнозирования.

Процент мошенничества в наших тематических исследованиях 1,2% от общего числа случаев. Обнаружение мошенничества может быть недостижимо при достижении высокой степени точности. Поэтому мы рассмотрим другие показатели производительности, в частности, чувствительность, площадь под кривой точности-отзыва (AURPC) и оценку F1.

Уровень точности представляет собой процент правильно классифицированных наблюдений из обоих классов:

Точность в процентах = (T P + T N / T N + F N + T P + F P) * 100

Чувствительность, то есть коэффициент истинной положительности (TPR) и отзыв, представляет собой долю положительных результатов, правильно классифицированных как положительные. Эти параметры критически важны, и мы рассматриваем их как показатель эффективности наряду с точностью.

Чувствительность = T P / T P + F N

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

Кривая точности-отзыва:

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

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

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

print("Classification Report for LightGBM: \n", classification_report(testY, predicted))
print("Confusion Matrix of LightGBM: \n", confusion_matrix(testY, predicted))

Учитывая, что наш набор данных о финансовых транзакциях сильно несбалансирован, использование матрицы путаницы не имеет смысла. Матрица путаницы плохо справляется с фиксацией этого неоптимального результата с учетом этой проблемы несбалансированного класса.

Подбор модели логистической регрессии:

# fit Logistic Regression model
logreg.fit(trainX, trainY)
log_pred = logreg.predict_proba(testX)
log_probs = y_pred[:, 1]
# calculate the precision-recall auc
precision, recall, _ = precision_recall_curve(testY, log_probs)
auc_score = auc(recall, precision)
print('LogReg PRAUC: %.3f' % auc_score)
log_probabilities = logreg.fit(trainX, trainY).predict_proba(testX)
avg_prec = average_precision_score(testY, log_probabilities[:, 1])
plt.figure(figsize=(10,6))
# calculate the no skill line as the proportion of the positive class
no_skill = len(y[y==1]) / len(y)
# plot the no skill precision-recall curve
plt.plot([0, 1], [no_skill, no_skill], linestyle='--', label='No Skill')
# plot the model precision-recall curve
plt.plot(recall, precision, marker='.', label='LogisticRegression')
plt.fill_between(recall, precision, step='post', alpha=0.3, color='k')
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.legend()
plt.title('Precision-Recall curve: Average Precision = {0:0.2f}'.format(avg_prec))

Ансамбль LightGBM и логистической регрессии

Составное обобщение обеспечивает механизм использования коллективной дискриминирующей способности ансамбля гетерогенных методов классификации. Это предполагает использование модели классификации верхнего уровня, которая способна учиться на прогнозах (и систематических отклонениях) моделей базового уровня для достижения большей мощности классификации. Учитывая, что XGBoost и LightGBM принадлежат к одному семейству, мы выберем модели LightGBM и логистической регрессии для ансамблевого подхода.

kfold = model_selection.KFold(n_splits=10, shuffle=True, random_state=42)
# create the sub models
estimators = []
model1 = LogisticRegression()
estimators.append(('logistic', model1))
model2 = LGBMClassifier()
estimators.append(('lgb', model2))
# create the ensemble model
ensemble = VotingClassifier(estimators)
= model_selection.cross_val_score(ensemble, trainX, trainY, cv=kfold)
print(results.mean())

Эта производительность немного лучше, чем у Logistic Regression, но далеко не у LightGBM, чем у предыдущих двух моделей. Итак, мы откажемся от этого.

Используя неконтролируемую модель LightGBM, мы достигли средней точности 0,90. Мы попробуем настроить Автоэнкодер, чтобы проверить, дает ли неконтролируемая модель лучшую точность.

Автокодировщик:

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

oversample_multiplier = 100
trainX_original = trainX.copy()
trainY_original = trainY.copy()
testX_original = testX.copy()
testY_original = testY.copy()
trainX_oversampled = trainX.copy()
trainY_oversampled = trainY.copy()
trainX_oversampled = trainX_oversampled.append( \[trainX_oversampled[trainY==1]]*oversample_multiplier, ignore_index=False)
trainY_oversampled = trainY_oversampled.append( \[trainY_oversampled[trainY==1]]*oversample_multiplier, ignore_index=False)
trainX = trainX_oversampled.copy()
trainY = trainY_oversampled.copy()
model = Sequential()
model.add(Dense(units=20, activation='linear', 
activity_regularizer=regularizers.l1(10e-5), input_dim=7,name='hidden_layer'))
model.add(Dropout(0.02))
model.add(Dense(units=1, activation='linear'))
model.compile(optimizer='adam',loss='mean_squared_error',metrics=['accuracy'])
num_epochs = 5
batch_size = 32
history = model.fit(x=trainX, y=trainY,epochs=num_epochs,
batch_size=batch_size,shuffle=True,validation_split=0.20,verbose=1)
predictions = model.predict(testX, verbose=1)
anomalyScoresAE = anomalyScores(testX, predictions)
preds, average_precision = plotResults(testY, anomalyScoresAE, True)

Средняя точность теста, основанная на кривой точности-отзыва, составляет 0,40, что является наихудшим представлением данных. Мы можем попробовать объединить контролируемую и неконтролируемую модель, чтобы создать полууправляемую модель для проверки производительности. Однако, поскольку наши существующие данные размечены, неконтролируемый алгоритм, скорее всего, даст лучшие результаты.

Порог для кривой точного отзыва:

fscore = (2 * precision * recall) / (precision + recall)
ix = argmax(fscore)
print('Best Threshold=%f, F-Score=%.3f' % (thresholds[ix], fscore[ix]))
no_skill = len(testY[testY==1]) / len(testY)
plt.plot([0,1], [no_skill,no_skill], linestyle='--', label='No Skill')
plt.plot(recall, precision, marker='.', label='LightGBM')
plt.scatter(recall[ix], precision[ix], s=(10*2)**2, marker='s', color='black', label='Best')
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.legend()
plt.show()

Сюжет можно дополнительно настроить для оптимальной работы; однако здесь оптимальная оценка F1 обозначена как пороговое значение 0,74.

Производственный трубопровод:

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

# Pipeline for New Data
# first, import new data into a data frame called ‘newData’
# Scale all the variables to a range of 0 to 1
newData.loc[:, featuresToScale] = sX.transform(newData[featuresToScale])
# third, predict using LightGBM
lgb.predict(newData, num_iteration=lgb.best_iteration)

Ключевые выводы

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

  1. Система должна быть способна обрабатывать неравномерное распределение, поскольку только небольшой процент всех транзакций является мошенническим. Чтобы преодолеть это, обучающая выборка должна быть разделена на части, распределение которых менее искажено.
  2. Система должна уметь обрабатывать шум, который является ошибкой в ​​данных. Шум в данных ограничивает точность обобщения, независимо от того, насколько обширным является обучение.
  3. Методы повторной выборки, если они используются, следует использовать осторожно; они не должны использоваться как отдельные решения, а должны сочетаться с переработкой проблемы для достижения конкретной цели.
  4. Система должна иметь возможность адаптироваться к новому виду данных. Мошенники постоянно меняют свое поведение, поэтому через некоторое время эффективность успешных методов мошенничества снижается.
  5. Хорошая метрика имеет решающее значение для проверки производительности классификатора. Общий высокий балл по неравномерному распределению не означает, что система способна выявлять все мошеннические транзакции.
  6. Система также должна учитывать затраты на обнаружение мошеннической транзакции и затраты, связанные с ее остановкой. Уровень принятия решений на вершине системы обнаружения мошенничества может помочь с фактором затрат. Классификатор и правило принятия решения должны быть установлены с учетом минимизации затрат.

Вывод:

Со временем решение необходимо будет переобучать, поскольку модели мошенничества всегда меняются. Кроме того, мы можем поэкспериментировать с другими алгоритмами машинного обучения, которые работают так же хорошо, как и повышение градиента, и можем включить их в ансамбль для повышения эффективности обнаружения мошенничества в целом. При достаточном количестве итераций часто можно найти подходящую модель машинного обучения с правильным балансом смещения и дисперсии и точности и отзыва. Более того, интерпретируемость очень важна для реальных приложений машинного обучения. Мы сохраним интерпретируемость для следующих обсуждений.

Хотя финансовое мошенничество невозможно подтвердить без проведения полного расследования, прогресс в методах обнаружения мошенничества может стать тревожным сигналом, предупреждающим заинтересованные стороны о вероятности мошенничества.

Со мной можно связаться здесь.