Вы подписаны на Машинное обучение доступно
Отлично! завершите оплату для полного доступа к Машинное обучение доступно
Снова приветствуем Вас! Вы успешно авторизовались
Успех! Аккаунт активирован, и Вы имеете полный доступ к контенту.
Выброс (Outlier)

Выброс (Outlier)

in

Выброс – это наблюдение, удаленное от других в выборке. Другими словами, это Наблюдение (Observation), которое расходится с общей закономерностью Выборки (Sample).

Выбросы обозначены голубым цветом

Стоит различать это определение с Несбалансированным датасетом (Imbalanced Dataset). Хоть в определениях и есть некоторые сходства, однако несбалансированный набор данных с точки зрения Машинного обучения (ML) – это меньший размер выборки одного класса в сравнении с другим.

Источники выбросов

Появление таких наблюдений может быть вызвано:

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

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

Разновидности выбросов

Выделяют 3 типа выбросов:

  • Глобальные: наблюдение далеко выходит за пределы всего набора данных. Пример: в классе все ученики – сверстники, но попадается запись об учащемся в возрасте 500 лет.
  • Условные: наблюдения считаются аномальными с учетом контекста. Пример: экономические показатели резко страны падают из-за мирового экономического кризиса, и на какое-то время нормой становятся более низкие показатели.
  • Коллективные: набор наблюдений, близких друг к другу и имеющих близкие аномальные значения. Подмножество точек считается аномальным, если эти значения как совокупность значительно отклоняются от всего набора данных, но значения отдельных точек данных сами по себе не являются аномальными ни в контекстном, ни в глобальном смысле:
Стоимость акции $MOMO

Почему так важно идентифицировать выбросы?

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

Визуальные методы обнаружения выбросов

Выбросы легко обнаружить с помощью следующих графиков:

  • Гистограмма (Histogram)

Математические методы обнаружения выбросов

Наряду с визуальными методами мы также можем использовать некоторые математические функции:

  • Стандартизованная оценка (Z-Score), которая характеризует меру наблюдаемого значения. В большинстве случаев используется пороговое значение: если значение Z-оценки больше или меньше 3 или -3 соответственно, эта точка данных будет классифицирована как выброс.
  • Межквартильный размах (Interquartile Range), разница между верхним и нижним квартилями.

Это далеко не полный список методов для поиска выбросов.

Выбросы и библиотека Scikit-learn

Выбросы можно найти с помощью Scikit-learn. Начнем с импорта необходимых библиотек:

import numpy as np
import pandas as pd

import matplotlib
import matplotlib.pyplot as plt

import seaborn as sns

import sklearn
from sklearn import datasets

import scipy
from scipy import stats

Затем мы загрузим "Бостонский датасет" о ценах на недвижимость:

# Используем встроенную функцию load_boston
boston = datasets.load_boston(return_X_y = False)

# Преобразуем данные в датафрейм
boston_df = pd.DataFrame(boston.data)

# Присвоим названиям столбцов заранее подготовленный список feature_names
boston_df.columns = boston.feature_names
boston_df.head()

Мы будем работать со следующим Датафреймом (DataFrame):

Названия признаков имеют следующие значения:

Отобразим ящик с усами для одного из признаков – расстояния от бостонских центров занятости:

sns.boxplot(x = boston_df['DIS'])

Теперь – точечную диаграмму:

fig, ax = plt.subplots(figsize = (16, 8))
ax.scatter(boston_df['INDUS'], boston_df['TAX'])
ax.set_xlabel('INDUS')
ax.set_ylabel('TAX')
plt.show()

Обратимся к математическим методам обнаружения выбросов и начнем со Стандартизированной оценки:

# Получим абсолютное значение (модуль) чисел признаков
z = np.abs(stats.zscore(boston_df))
print(z)

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

[[0.41978194 0.28482986 1.2879095  ... 1.45900038 0.44105193 1.0755623 ]
 [0.41733926 0.48772236 0.59338101 ... 0.30309415 0.44105193 0.49243937]
 [0.41734159 0.48772236 0.59338101 ... 0.30309415 0.39642699 1.2087274 ]
 ...
 [0.41344658 0.48772236 0.11573841 ... 1.17646583 0.44105193 0.98304761]
 [0.40776407 0.48772236 0.11573841 ... 1.17646583 0.4032249  0.86530163]
 [0.41500016 0.48772236 0.11573841 ... 1.17646583 0.44105193 0.66905833]]

Сузим область поиска и отсечем нормальные значения:

# Отсечем значения, лежашие в пределах трех стандартных отклонений
threshold = 3
print(np.where(z > 3))

Список значительно сузился:

(array([ 55,  56,  57, 102, 141, 142, 152, 154, 155, 160, 162, 163, 199,
       200, 201, 202, 203, 204, 208, 209, 210, 211, 212, 216, 218, 219,
       220, 221, 222, 225, 234, 236, 256, 257, 262, 269, 273, 274, 276,
       277, 282, 283, 283, 284, 347, 351, 352, 353, 353, 354, 355, 356,
       357, 358, 363, 364, 364, 365, 367, 369, 370, 372, 373, 374, 374,
       380, 398, 404, 405, 406, 410, 410, 411, 412, 412, 414, 414, 415,
       416, 418, 418, 419, 423, 424, 425, 426, 427, 427, 429, 431, 436,
       437, 438, 445, 450, 454, 455, 456, 457, 466]), array([ 1,  1,  1, 11, 12,  3,  3,  3,  3,  3,  3,  3,  1,  1,  1,  1,  1,
        1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  5,  3,  3,  1,  5,
        5,  3,  3,  3,  3,  3,  3,  1,  3,  1,  1,  7,  7,  1,  7,  7,  7,
        3,  3,  3,  3,  3,  5,  5,  5,  3,  3,  3, 12,  5, 12,  0,  0,  0,
        0,  5,  0, 11, 11, 11, 12,  0, 12, 11, 11,  0, 11, 11, 11, 11, 11,
       11,  0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11]))

Удалим из датасета значения, чей Z-критерий меньше 3:

clean_boston_df = boston_df
clean_boston_df = clean_boston_df[z < 3].all(axis = 1)
display(clean_boston_df.shape)

Размер датасета слегка изменился:

(6478,)

Рассмотрим еще один способ – межквартильный размах:

# Первая квантиль
Q1 = boston_df.quantile(0.25)

# Третья квантиль
Q3 = boston_df.quantile(0.75)

# Межквантильное расстояние
IQR = Q3 - Q1
print(IQR)

Применив метод quantile() к датасету, мы получили список межквартильных размахов для каждого признака датасета:

CRIM         3.595038
ZN          12.500000
INDUS       12.910000
CHAS         0.000000
NOX          0.175000
RM           0.738000
AGE         49.050000
DIS          3.088250
RAD         20.000000
TAX        387.000000
PTRATIO      2.800000
B           20.847500
LSTAT       10.005000
dtype: float64

Очистим набор данных с помощью специального условия:

# Это причудливое выражение – прекрасный способ отфильтровать значения, выходящие за пределы 
# трех межквартильных размахов (по полторы с каждой стороны), причем с обеих сторон. 
# Мы намеренно отыскиваем значения, выходящие за эти пределы и инвертируем их тильдой ('~'),
# тем самым выделяя нормальные значения в отдельный временный список.
clean_iqr_boston_df = boston_df[~((boston_df < (Q1 - 1.5 * IQR)) | (boston_df > (Q3 + 1.5 * IQR))).any(axis = 1)]
clean_iqr_boston_df.shape
(274, 13)

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

Попробуйте наши курсы по Машинному обучению на Udemy.

Фото: @woblack