Поиск аномалий(Outlier Detection) является важной темой в машинном обучении. Алгоритмы такого типа актуальны и используются повсеместно: Кибербез, Банковские системы, предобработка данных, медицина, анализ логов, контроль качества и это лишь малая часть всего списка.Сегодня мы с вами познакомимся с двумя такими алгоритмами, сравним их и посмотрим результаты нашей работы.В нашем исследовании оценивать алгоритмы мы будем по метрикам Recall(реальная доля тех, кого правильно пометили как аномалию), Precision(Показывает долю истинно положительных результатов среди всех, которые модель пометила как положительные)

Я взял достаточно сложный и объемный датасет с Kaggle.
Он предоставляет данные денежных операций, наша задача-выявить аномалии(потенциальные мошеннические операции).

1. IQR: Начинаем с базы

IQR (Межквартильный размах) — это быстрый и эффективный способ найти отклонения, не требующий обучения модели в привычном понимании этого слова.Дело в том, что IQR является лишь статистическим методом анализа, который основан на "границах нормального".Для этого мы строим некий барьер в пространстве, все, чт�� дальше этого барьера считается аномалией Если рассматривать математически, то мы находим Q_2-медиану наших значений, Q_1-точка, ниже которой 25% данных, Q_3-точка, ниже которой 75% данныхIQR = Q3 - Q1 Тогда: Нижняя граница: Q1 - 1.5 \times IQR

Верхняя граница: Q3 + 1.5 \times IQR Теперь реализуем это c использованием Pandas:

import pandas as pd
import numpy as np

def detect_outliers_iqr(data: pd.Series):
    q1 = data.quantile(0.25)
    q3 = data.quantile(0.75)

    iqr = q3 - q1

    lower_bound = q1 - 1.5 * iqr
    upper_bound = q3 + 1.5 * iqr
    outliers = data[(data < lower_bound) | (data > upper_bound)]
    
    return outliers, lower_bound, upper_bound
```

Теперь попробуем запустить его на наших данных:

Скрытый текст

Name: Amount, Length: 31904, dtype: float64,

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

.Более того, давайте посмотрим результаты работы алгоритма в процентном соотношении: 8.93% от всех данных выделены как аномальные! Казалось бы, почему так много, но это интуитивно понятно, ведь если мы ставим алгоритму фиксированные рамки, которые просто отрезают часть данных, не основываясь ни на чем, кроме того, что они находятся на значительном расстоянии от центра, мы должны быть готовы к такому результату.Оценим наш алгоритм по метрикам: recall: 0.185 precision: 0.003 Получается, среди всех найденных моделью аномалий, лишь 0.3 процента действительно являются ими Но, мы смогли найти 18.5% от общей доли аномалий!

Такой разрыв между метриками наглядно иллюстрирует слабость одномерного статистического подхода


2. Теперь перейдем к более сложным методам, одним из которых является Isolation Forest.Его готовая версия находится в библиотеке scikit-learn:

from sklearn.ensemble import IsolationForest
from sklearn.model_selection import train_test_split
from sklearn.metrics import precision_score, recall_score
import pandas as pd


model = IsolationForest()

X = df.iloc[:, :-1]
Y = df.iloc[:, -1]
x_train, x_val, y_train, y_val = train_test_split(X, Y, test_size=0.2, random_state=42)

model.fit(x_train)
predict = model.predict(x_val)
predict[predict == 1] = 0
predict[predict == -1] = 1

Инициализировали модель, начинаем обучение. Алгоритм работает достаточно быстро даже на большом количестве данных,через 3 секунды все уже готово. Теперь время посмотреть на полученные метрики:recall: 0.82 precision: 0.039 Мы видим значительный прогресс, по сравнению со статистическим методом! Алгоритм нашел 82% от всех аномалий! НО, всего 3.9 процента от предсказанных моделью аномалий на самом деле являются аномалия��и Результат достаточно неплохой

def get_path_length(X, tree, current_height):
    if tree.is_leaf:
        return current_height + c(len(X))
    if X[tree.feature] < tree.split_value:
        return get_path_length(X, tree.left, current_height + 1)
    else:
        return get_path_length(X, tree.right, current_height + 1)

Здесь мы видим, как работает Isolation Forest изнутри :Если мы дошли до конечного листа, то возвращаем длину + поправку, иначе-идем дальше, причем чем меньше число мы возвращаем, тем больше вероятность того, что данный лист-аномалия. Если значение признака меньше, чем порог, то рекурсивно идем налево, иначе-направо.

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

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


Время подвести итог нашего эксперимента, мы опробовали два метода: статистический(IQR) и метод машинного обучения, основанный на ансамбле деревьев(Isolation Forest). Мы видим колоссальное преимущество ml-метода перед статистическим, но почему?Самая очевидная причина состоит в том, что статистический метод работает над одним признаком, чего крайне недостаточно, в контексте нашей задачи, если мы выбираем признаком Amount, то наша потенциально мошенническая операция могла скрыться среди,допустим 10 операций подряд на мелкие суммы(в то время как одна большая была бы распознана как аномалия). И получается, что если пользователь потратил денег в несколько раз больше, чем обычно, то это аномальная операция.Но давайте обратимся к метрикам, рассмотрим precision для IsolationForest, мы можем сказать, что из 1000, помеченных нами как мошеннические, операций всего 39 на самом деле являются ими. И на самом деле это не такой плохой результат, как могло показаться сначала, ведь в данной задаче лучше посчитать лишнюю операцию "подозрительной", чем упустить мошенника.

Репозиторий с кодом

En версия