Как уже упоминалось, мы будем применять два алгоритма обнаружения аномалий; фактор локального выброса и алгоритм изолированного леса. Прежде чем обсуждать эти алгоритмы, важно подчеркнуть, что представляет собой выброс/аномалию. Любая точка данных/наблюдение, которое значительно отличается от других наблюдений, называется аномалией/выбросом. Таким образом, обнаружение аномалий стало очень важным в практических условиях с реальными данными. Методы обнаружения аномалий находят свое применение в различных областях, таких как обнаружение мошеннических банковских транзакций, обнаружение сетевых вторжений, внезапный рост/падение продаж, изменение поведения клиентов и т. д.
Коэффициент локальных выбросов (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,)
Обнаружение неконтролируемых выбросов
Теперь, когда мы обработали наши данные и кратко проанализировали их, мы можем приступить к развертыванию наших алгоритмов машинного обучения. Как уже говорилось, мы будем использовать следующие методы:
- Коэффициент локальных выбросов (LOF)
- Показатель аномалии каждого образца называется Фактор локального выброса. Он измеряет локальное отклонение плотности данного образца по отношению к его соседям. Он является локальным в том смысле, что оценка аномалии зависит от того, насколько изолирован объект по отношению к окружающей среде.
- Алгоритм изолированного леса
- 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
После запуска наших алгоритмов мы видим существенное улучшение результатов при использовании алгоритма изолированного леса по сравнению с фактором локального выброса.