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

Коэффициент локальных выбросов (LOF)

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

Пример алгоритма приведен здесь, а хорошая разбивка алгоритма доступна здесь.

Алгоритм изолированного леса

Это неконтролируемый метод обнаружения аномалий. Изолирующие леса (IF), подобные случайным лесам, строятся на основе деревьев решений. А поскольку здесь нет предопределенных меток, это неконтролируемая модель. Изолирующие леса были построены на основе того факта, что аномалии — это точки данных, которых «немного и разных». Здесь случайно отобранные данные обрабатываются в древовидной структуре на основе случайно выбранных признаков. Образцы, которые перемещаются глубже в дерево, с меньшей вероятностью будут аномалиями, поскольку для их изоляции требуется больше разрезов. Точно так же образцы, которые заканчиваются более короткими ветвями, указывают на аномалии, поскольку дереву было легче отделить их от других наблюдений.

Таким образом, мы начинаем с того, что получаем данные и создаем ансамбль деревьев решений. Во время скоринга точка данных проходит через все деревья, которые были обучены ранее. Теперь каждой точке данных присваивается «оценка аномалии» в зависимости от глубины дерева, необходимой для достижения этой точки. Эта оценка представляет собой совокупность глубины, полученной от каждого из Деревьев. Оценка аномалии -1 присваивается аномалиям и 1 нормальным баллам на основе предоставленного параметра загрязнения (процент аномалий, присутствующих в данных).

Основная идея здесь заключается в том, что аномальные точки данных (транзакции) очень разные, поэтому их очень легко разделить (короткие ветви).

Более подробная информация об алгоритме находится здесь.

Настраивать

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

#Modules
import sys
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import scipy

#Versions
print("Versions of modules")
print('Python: {}'.format(sys.version))
print('Numpy: {}'.format(np.__version__))
print('Pandas: {}'.format(pd.__version__))
print('Seaborn: {}'.format(sns.__version__))
print('Scipy: {}'.format(scipy.__version__))

import warnings
warnings.filterwarnings('ignore')
Versions of modules
Python: 3.9.12 (main, Apr  5 2022, 01:53:17) 
[Clang 12.0.0 ]
Numpy: 1.21.5
Pandas: 1.4.2
Seaborn: 0.11.2
Scipy: 1.7.3

Теперь давайте загрузим наш набор данных из файла .csv в виде кадра данных Pandas. И просмотрите столбцы в кадре данных.

# Load the dataset from the csv file using pandas
data = pd.read_csv('readyourdata.csv')
# Show columns or data variables
print(data.columns)
Index(['Time', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6', 'V7', 'V8', 'V9', 'V10',
       'V11', 'V12', 'V13', 'V14', 'V15', 'V16', 'V17', 'V18', 'V19', 'V20',
       'V21', 'V22', 'V23', 'V24', 'V25', 'V26', 'V27', 'V28', 'Amount',
       'Class'],
      dtype='object')

Описание данных

Набор данных размещен на Kaggle. Фактические данные были собраны и проанализированы в ходе совместной исследовательской работы Worldline и Группы машинного обучения ULB по интеллектуальному анализу больших данных и обнаружению мошенничества.

Набор данных содержит транзакции, совершенные по кредитным картам в сентябре 2013 года держателями карт из Европы. Этот набор данных представляет транзакции, которые произошли за два дня, где у нас есть 492 мошенничества из 284 807 транзакций. Набор данных сильно несбалансирован, на положительный класс (мошенничество) приходится 0,172% всех транзакций. Это распространенная проблема с мошенническими наборами данных транзакций — они крайне несбалансированы.

Он содержит только числовые входные переменные, которые являются результатом преобразования PCA. К сожалению, из соображений конфиденциальности мы не можем предоставить исходные функции и дополнительную справочную информацию о данных. Признаки V1, V2, … V28 являются основными компонентами, полученными с помощью PCA, единственные признаки, которые не были преобразованы с помощью PCA, — это «Время» и «Количество». Функция «Время» содержит секунды, прошедшие между каждой транзакцией и первой транзакцией в наборе данных. Функция «Сумма» — это сумма транзакции, эту функцию можно использовать для обучения, зависящего от стоимости, например. Функция «Класс» — это переменная ответа, которая принимает значение 1 в случае мошенничества и 0 в противном случае.

Учитывая коэффициент дисбаланса классов, мы рекомендуем измерять точность с помощью площади под кривой точности-отзыва (AUPRC). Точность матрицы путаницы не имеет значения для несбалансированной классификации

Мы начинаем с того, что просто видим форму данных.

# Print the shape of the data
print("Shape: ", data.shape)
data = data.sample(frac=0.2, random_state = 1) #get a sample of the full data now
Shape:  (284807, 31)

V1 — V28 являются результатом уменьшения размерности PCA для защиты удостоверений пользователей и конфиденциальных функций.

Исследование данных

# Plot histograms of each parameter 
data.hist(figsize = (20, 20))
plt.show()

Мы видим несколько переменных, имеющих близкое к нормальному распределение, а именно V13, V15, V24 и V26. Временная переменная оказывается бимодальной с двумя пиками.

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

# Determine number of fraud cases in dataset
Fraud = data[data['Class'] == 1]
Valid = data[data['Class'] == 0]

# Outliers
outlier_fraction = len(Fraud)/float(len(Valid))
print("Outlier Fraction: ", outlier_fraction)

print('Fraud Cases: {}'.format(len(data[data['Class'] == 1])))
print('Valid Transactions: {}'.format(len(data[data['Class'] == 0])))
Outlier Fraction:  0.0015296972254457222
Fraud Cases: 87
Valid Transactions: 56874
# Correlation matrix
corrmat = data.corr()
fig = plt.figure(figsize = (12, 9))

sns.heatmap(corrmat, vmax = .8, square = True)
plt.show()

Большинство переменных, по-видимому, практически не имеют корреляции (очень слабая корреляция) друг с другом. Заметные корреляции, которые действительно появляются:

  • V20 и количество — сильная положительная корреляция
  • V7 и количество — сильная положительная корреляция
  • V14, V10, V17 и класс — каждый имеет относительно слабую, но присутствующую корреляцию

Лучше бы цифры смотрели. Добьемся этого. Ниже приведен график тепловой карты корреляции с числами.

sns.set(rc = {'figure.figsize':(30,20)})                                 
sns.heatmap(data.corr(), annot=True, cmap = "summer")    

Мы можем определить корреляцию, используя этот график.

Давайте продолжим и получим наши данные X и Y. Наши данные X — это то, что мы будем тренировать в наших моделях и использовать для прогнозирования (предикторов). Наши данные Y — это ответ, который мы будем прогнозировать.

# Get all the columns from the dataFrame
columns = data.columns.tolist()

# Filter the columns to remove data we do not want
columns = [c for c in columns if c not in ["Class"]]

# Store the variable we'll be predicting on
target = "Class"

X = data[columns]
Y = data[target]

# Print shapes
print("X data shape: ",X.shape)
print("Y data shape: ", Y.shape)
X data shape:  (56961, 30)
Y data shape:  (56961,)

Обнаружение неконтролируемых выбросов

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

  1. Коэффициент локальных выбросов (LOF)
  • Показатель аномалии каждого образца называется Фактор локального выброса. Он измеряет локальное отклонение плотности данного образца по отношению к его соседям. Он является локальным в том смысле, что оценка аномалии зависит от того, насколько изолирован объект по отношению к окружающей среде.
  1. Алгоритм изолированного леса
  • IsolationForest «изолирует» наблюдения, случайным образом выбирая объект, а затем случайным образом выбирая значение разделения между максимальным и минимальным значениями выбранного объекта. Поскольку рекурсивное разбиение может быть представлено древовидной структурой, количество разбиений, необходимых для выделения выборки, эквивалентно длине пути от корневого узла до конечного узла. Эта длина пути, усредненная по лесу таких случайных деревьев, является мерой нормальности и нашей решающей функцией.
  • Случайное разбиение дает заметно более короткие пути для аномалий. Следовательно, когда лес случайных деревьев вместе дает более короткие пути для конкретных выборок, они, скорее всего, будут аномалиями.

Давайте загрузим нужные нам модули и определим начальное состояние.

# Modules Needed for Anomaly Detection
from sklearn.metrics import classification_report, accuracy_score
from sklearn.ensemble import IsolationForest
from sklearn.neighbors import LocalOutlierFactor

# define random states
state = 1

Давайте настроим алгоритмы и аргументы, для которых мы будем моделировать.

# define outlier detection tools to be compared
classifiers = {
    "Isolation Forest": IsolationForest(max_samples=len(X),
                                        contamination=outlier_fraction,
                                        random_state=state),
    "Local Outlier Factor": LocalOutlierFactor(
        n_neighbors=20,
        contamination=outlier_fraction)}

Теперь давайте подгоним нашу модель к данным

# Fit the model
plt.figure(figsize=(9, 7))
n_outliers = len(Fraud)

for i, (clf_name, clf) in enumerate(classifiers.items()):
    
    # fit the data and tag outliers
    if clf_name == "Local Outlier Factor":
        y_pred = clf.fit_predict(X)
        scores_pred = clf.negative_outlier_factor_
    else:
        clf.fit(X)
        scores_pred = clf.decision_function(X)
        y_pred = clf.predict(X)

    # Reshape the prediction values to 0 for valid, 1 for fraud.
    y_pred[y_pred == 1] = 0
    y_pred[y_pred == -1] = 1

    n_errors = (y_pred != Y).sum()

    # Run classification metrics
    print('{}: {}'.format(clf_name, n_errors))
    print(accuracy_score(Y, y_pred))
    print(classification_report(Y, y_pred))


Isolation Forest: 127
0.997770404311722
              precision    recall  f1-score   support

           0       1.00      1.00      1.00     56874
           1       0.27      0.28      0.27        87

    accuracy                           1.00     56961
   macro avg       0.64      0.64      0.64     56961
weighted avg       1.00      1.00      1.00     56961

Local Outlier Factor: 173
0.9969628342199048
              precision    recall  f1-score   support

           0       1.00      1.00      1.00     56874
           1       0.01      0.01      0.01        87

    accuracy                           1.00     56961
   macro avg       0.50      0.50      0.50     56961
weighted avg       1.00      1.00      1.00     56961

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

Рекомендации