Мотивация

Ниже представлена ​​очень подозрительная диаграмма:

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

Теперь, вот странные части. Во-первых, во всех категориях есть много выбросов, отмеченных темными пятнами над усами.

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

Эта странная взаимосвязь между качеством бриллиантов и их ценами ставит нас перед вопросом: являются ли эти выбросы на самом деле выбросами?

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

Это третья часть нашей серии по обнаружению выбросов. Проверьте первые два ниже:



Подробнее о многомерных выбросах

В первой части нашей серии мы сделали следующие пункты.

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

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

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

Возраст: 18 лет Рост: 178 см Вес: 65 кг Не курю: Да Заболевание: Рак легких

Если мы рассмотрим атрибуты Генри по отдельности, они кажутся обычными в контексте нашего набора данных. В нашем наборе данных много 18-летних и много людей ростом 178 см или весом 65 кг.

Также в больнице находится много некурящих 18-летних или больных раком легких. НО у нас НЕТ некурящих 18-летних с раком легких, весом 65 кг и ростом 178 см.

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

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

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

Загрузка набора данных

Давайте сначала загрузим набор данных, с которым мы будем работать. Это тот же набор данных Diamonds из предыдущего урока:

import seaborn as sns 

diamonds = sns.load_dataset("diamonds")
diamonds.head()

Мы выполняем небольшой шаг предварительной обработки, на котором мы кодируем категориальные с помощью класса OrdinalEncoder Sklearn:

import numpy as np
from sklearn.preprocessing import OrdinalEncoder

# Extract feature and target arrays
X, y = diamonds.drop("carat", axis=1), diamonds["carat"]
# Select categorical column names
cats = X.select_dtypes(exclude=np.number).columns.tolist()

# Encode categoricals
X.loc[:, cats] = OrdinalEncoder().fit_transform(X[cats])

Теперь давайте начнем! (потирают руки друг о друга)

Многомерное обнаружение выбросов с помощью PyOD

Несмотря на то, что теория может быть немного сложной, выполнение многомерного обнаружения выбросов в коде очень просто благодаря библиотеке Python Outlier Detection (PyOD). Давайте рассмотрим пример с использованием алгоритма Local Outlier Factor:

# Import LOF
from pyod.models.lof import LOF

# Initialize
lof = LOF(n_neighbors=30).fit(X)

# Extract inlier/outlier labels
labels = lof.labels_
outliers_X_lof = X[labels == 1]

После импорта оценки LOF из pyod мы инициализируем ее 30 соседями и подгоняем к X (не беспокойтесь о деталях LOF, мы поговорим об этом позже).

Затем мы обращаемся к его атрибуту labels_, который возвращает 0 (выбросы) или 1 (выбросы) для каждой строки X. Давайте посчитаем выбросы:

num_outliers = len(outliers_X_lof)
print(f"The number of outliers: {num_outliers}")
print(f"Percentage of outliers: {num_outliers / len(X):.3f}")
The number of outliers: 5394
Percentage of outliers: 0.100

Хм, у нас есть 5394 выброса, или ровно 10% набора данных. Это совпадение? Давайте попробуем ту же операцию с другим классификатором выбросов, Isolation Forest:

from pyod.models.iforest import IForest

iforest = IForest(n_estimators=500).fit(X)

labels = iforest.labels_
outliers_X_iforest = X[labels == 1]

num_outliers = len(outliers_X_iforest)
print(f"The number of outliers: {num_outliers}")
print(f"Percentage of outliers: {num_outliers / len(X):.3f}")
The number of outliers: 5394
Percentage of outliers: 0.100

Получаем те же подозрительные 10%. Это не может быть совпадением (на самом деле ничего — Угвей).

Что такое загрязнение при обнаружении выбросов?

Алгоритмы классификатора выбросов, такие как Isolation Forest или Local Outlier Factor, на самом деле не являются классификаторами. Когда они соответствуют набору данных, их внутренние алгоритмы вычисляют оценки аномалий для каждой строки в наборе данных. Вот пример с iForest:

iforest = IForest().fit(X)

iforest.decision_scores_[:10]
array([-0.03364944,  0.0287027 ,  0.07729889, -0.06363647, -0.03095093,
        0.05240712,  0.0230652 , -0.02713253,  0.06674287,  0.03475134])

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

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

По сути, классификаторы выбросов говорят нам: «Вот насколько аномальным я считаю каждый образец». На данный момент я выберу 10% наиболее аномальных образцов в качестве выбросов, но вы всегда можете изменить это с помощью моего параметра загрязнения.

По сути, contamination — это гиперпараметр всех классификаторов, который контролирует количество производимых выбросов.

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

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

Достоверность вероятности

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

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

from pyod.models.iforest import IForest

iforest = IForest(n_estimators=1000).fit(X)

probs = iforest.predict_proba(X)
probs[:5]
array([[0.6309838 , 0.3690162 ],
       [0.48315014, 0.51684986],
       [0.3044389 , 0.6955611 ],
       [0.73513599, 0.26486401],
       [0.59298752, 0.40701248]])

Эти вероятности генерируются путем нормализации массива decision_scores_ с использованием масштабирования MinMax, также называемого нормализацией.

Нормализация массива заставит его находиться в диапазоне от 0 до 1, что означает, что мы можем интерпретировать оценки аномалий как вероятности того, что образцы являются выбросами. Итак, вот как predict_proba пишется под капотом:

from sklearn.preprocessing import minmax_scale

# Create an empty array with two columns
probs = np.empty((len(X), 2))

# The second column is outlier probabilities
probs[:, 1] = minmax_scale(iforest.decision_scores_)

# The first column is inlier probabilities
probs[:, 0] = 1 - probs[:, 1]

# Check if the probs match
probs[:5] == iforest.predict_proba(X)[:5]
array([[ True,  True],
       [ True,  True],
       [ True,  True],
       [ True,  True],
       [ True,  True]])

Теперь вместо того, чтобы выбирать выбросы на основе загрязнения, мы можем выбирать их на основе достоверности вероятности. Вместо того, чтобы выбирать 10 % наиболее аномальных выборок, мы можем выбрать выборки с вероятностью выброса не менее 80 %, 90 % или n %. Вот как это будет выглядеть:

# Set a confidence threshold
threshold = 0.9

# Create a mask that returns True if probs over threshold
is_outlier = probs[:, 1] > threshold
outliers_X_probs = X[is_outlier]

# Count up the outliers
num_outliers = len(outliers_X_probs)
print(f"The number of outliers: {num_outliers}")
print(f"Percentage of outliers: {num_outliers / len(X):.4f}")
The number of outliers: 12
Percentage of outliers: 0.0002

Используя метод достоверности вероятности, мы находим только 12 выбросов вместо более 5000. И самое приятное то, что мы на 90% уверены, что эти 12 выбросов действительно являются выбросами!

Как выбрать правильный порог вероятности

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

Если у вас небольшой набор данных, вы хотите быть абсолютно уверены, что выделяемые вами выбросы на самом деле являются выбросами. Отбрасывая их, вы не хотите потерять важную информацию и шаблоны в своем небольшом наборе данных, что неизбежно повлияет на модели машинного обучения. Для таких случаев следует установить высокий порог, например 80–90%.

Если вы имеете дело с конфиденциальными данными, такими как медицинские записи, где крайне важно избегать ложных срабатываний, вы должны стремиться к более чем 90%. 95% и 99% также довольно распространены.

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

Как выбрать правильный классификатор выбросов

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

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

Однако есть некоторые алгоритмы, которые стабильно работают хорошо при правильном использовании.

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

Если вы хотите узнать больше о том, как выбрать правильный классификатор, ознакомьтесь с моим курсом по обнаружению аномалий на DataCamp, где я преподаю около 10 классификаторов выбросов, как и когда их использовать и как настраивать их гиперпараметры.

Также на этой странице документации PyOD перечислено более 40 алгоритмов. Вы можете увидеть, как они работают, взглянув на эту таблицу, где они перечисляют производительность 10 лучших классификаторов выбросов в более чем 15 наборах данных.

Заключение

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

Понравилась эта статья и, скажем прямо, ее причудливый стиль написания? Представьте себе, что у вас есть доступ к десяткам таких же, написанных блестящим, обаятельным, остроумным автором (кстати, это я :).

Всего за 4,99 $ членства вы получите доступ не только к моим историям, но и к сокровищнице знаний от лучших и самых ярких умов на Medium. А если вы воспользуетесь моей реферальной ссылкой, то получите мою сверхновую благодарность и виртуальную пятерку за поддержку моей работы.