Посмотрите, как квантильная обработка выбросов может повысить точность модели

Основной подход машинного обучения состоит в разделении данных на наборы для обучения и тестирования. Затем алгоритмы машинного обучения обучаются на обучающем наборе (наборе данных) для обобщения от шаблона до невидимых данных, то есть тестового набора. Когда алгоритму не удается обобщить то, что он узнал из обучающего набора, на тестовый набор, это означает, что алгоритм переобучается.

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

Если все данные в тестовом наборе следуют тому же распределению, что и обучающий набор, почему происходит переобучение?

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

Верный перевод этих слов — «Сущности не должны умножаться сверх необходимого», исходя из этих слов, в сценарии переобучения это можно интерпретировать как:

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

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

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

Если у вас уже есть набор данных на вашем компьютере, вы можете загрузить данные с помощью pandas:

import pandas as pd
filename = "iris.csv"
names = ["SepalLength","SepalWidth","PetalLength","PetalWidth","Species"]
df = pd.read_csv(filename,names=names)
df.head(5)

Набор данных растений Iris стал популярным благодаря статистику и биологу Р.А. Фишером, когда он представил его в своей статье под названием «Использование множественных измерений в таксономических задачах» в качестве примера линейного дискриминантного анализа в 1936 году. Сейчас он широко используется в литературе по распознаванию образов. Этот набор данных имеет пять переменных, а именно:

  1. длина чашелистика в см
  2. ширина чашелистика в см
  3. длина лепестка в см
  4. ширина лепестка в см
  5. класс:
    — Iris Setosa
    — Iris Versicolor
    — Iris Virginica

Как указано в репозитории UCI, на самом деле в данных по радужной оболочке есть некоторые неточности:

35-й образец должен быть: 4.9,3.1,1.5,0.2 «Iris-setosa», где ошибка в четвертом признаке. Образец 38-й: 4.9,3.6,1.4,0.1 «Ирис-сетоза», где ошибки во втором и третьем признаках.

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

Мы приступаем к получению статистического описания наших данных, а также вызываем функцию pandas для подсчета количества нашего выходного класса (видов): у нас есть 3 класса по 50 членов в каждом.

Мы также можем проверить среднее значение, стандартное отклонение и квантили каждого класса:

df.describe()
class_count = df.groupby('Species').size()
class_count

У нас есть 3 класса по 50 членов в каждом, мы также можем проверить среднее значение, стандартное отклонение и квантили каждого класса.

У нас может быть визуальное описание наших данных.

import matplotlib.pyplot as plt

# Set species and colors
species = ['Iris-setosa', 'Iris-versicolor', 'Iris-virginica']
colors = ['blue', 'orange', 'red']


plt.figure(figsize=(8, 6))  

for i, sp in enumerate(species):
    subset = df[df['Species'] == sp]
    plt.scatter(subset['SepalLength'], subset['SepalWidth'], color=colors[i], label=sp)

plt.xlabel('Sepal Length')
plt.ylabel('Sepal Width')
plt.title('Scatter Plot of Iris Species')

plt.show()

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

Если длина лепестка ‹ 2,45, то Iris-setosa
Если ширина чашелистика ‹ 2,10, то Iris-versicolor
Если ширина чашелистика ‹ 2,45 и длина лепестка ‹ 4,55, то Iris-versicolor
Если чашелистик -ширина ‹ 2,95 и ширина лепестка ‹ 1,35, тогда Iris-versicolor
Если длина лепестка ‹ 2,45 и длина лепестка ‹ 4,45, то Iris-versicolor
Если длина чашелистика ›= 5,85 и длина лепестка ‹ 4,75, то ирис-лишай
Если ширина чашелистика ‹ 2,55 и длина лепестка ‹ 4,95, а ширина лепестка ‹ 1,55, то
радужка разноцветная
Если длина лепестка ›= 2,45 и длина лепестка ‹ 4,95 и ширина лепестка ‹ 1,55, то
разноцветный радужка
Если длина чашелистика ›= 6,55 и длина лепестка ‹ 5,05, то ирисовый лишай
Если ширина чашелистика ‹ 2,75 и ширина лепестка ‹ 1,65 и длина чашелистика ‹ 6,05
тогда радужка-лишай
Если длина чашелистика ›= 5,85 и длина чашелистика ‹ 5,95 и длина лепестка ‹ 4,85
тогда радужка-лишай
Если длина лепестка ›= 5,15, то Iris-virginica
Если ширина лепестка ›= 1,85, то Iris-virginica
Если ширина лепестка ›= 1,75 и ширина чашелистика ‹ 3,05, то Iris-virginica
> Если длина лепестка ›= 4,95 и ширина лепестка ‹ 1,55, то Iris-virginica

Если длина лепестка ›= 5,15, то Iris-virginica
Если ширина лепестка ›= 1,85, то Iris-virginica
Если ширина лепестка ›= 1,75 и ширина чашелистика ‹ 3,05, то Iris-virginica
> Если длина лепестка ›= 4,95 и ширина лепестка ‹ 1,55, то Iris-virginica

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

Где x — количество элементов в наборе данных.

Будьте осторожны, используя эту формулу. Когда данные являются многомерными, вы должны изменить эту формулу следующим образом:

Но прежде чем перейти к следующему шагу, давайте рассмотрим некоторые концепции распределения Гаусса и самого z-показателя.

В распределении Гаусса, также называемом нормальным распределением, около 68,3% записей находятся между:

Около 95,4% записей находятся между:

Около 99,7% записей находятся между:

В итоге:

На самом деле, согласно центральной предельной теореме, стандартное нормальное распределение с преобразованием Z-показателя возможно достичь только тогда, когда количество выборок достаточно велико; когда количество выборок недостаточно велико, мы столкнемся с распределением T-Student. Распределение T-Стьюдента похоже на нормальное распределение с более плоской колоколообразной формой в зависимости от степеней свободы; в общем, чем больше степеней свободы, тем ближе оно к нормальному распределению.

Часто говорят, что после преобразования Z-Score у нас есть среднее значение 0 и стандартное отклонение 1, но на самом деле Z-Score является случайной величиной, значение дисперсии точно неизвестно, просто оценка, которая является приближением к этим значениям.

Происходит переобучение

Мы оценим распространенный сценарий переобучения; мы будем обучать модель логистической регрессии для классификации наших данных Iris. Чтобы смоделировать сценарий переобучения, мы разделим данные на обучающие (90%) и тестовые наборы (10) без рандомизации данных; поскольку данные упорядочены, это позволит нам захватить все записи классов радужной оболочки сетчатой ​​и разноцветной радужной оболочки и только 70% класса радужной оболочки виргинской, поскольку алгоритм, обученный только на нескольких данных из этого класса, не будет работать хорошо, когда оценка данных, не видимых в этом классе. Другими словами, можно сделать вывод, что нам нужно переобучить. Наша цель — создать подход для повышения производительности модели до 100 % без рандомизации данных или подвергания модели дополнительным обучающим данным. Это возможно? Посмотрим дальше.

import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report

array = df.values
X = array[:,0:4] # data
Y = array[:,4] # label

# Standardize the data
X = (X - np.mean(X))/2*np.std(X)

# Reserve 10% to test.
split = int(X.shape[0]*0.9)

X_train = X[0:split]
Y_train = Y[0:split]

X_test = X[split:]
Y_test = Y[split:]

model = LogisticRegression(max_iter=1000)
model.fit(X_train, Y_train)
result = model.score(X_test, Y_test)

Наша модель имеет точность 93%. Это выглядит фантастически, но на самом деле есть проблема, которую нужно решить, наша модель не смогла правильно классифицировать записи класса virginica, как ожидалось, и поскольку мы решили ее решить, мы должны искать способ улучшить производительность наша модель для этого класса.

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

df.plot(kind='box')

Как видно на изображении, эти точки за пределами усов являются нашими выбросами. Мы видим, что SepalWidth имеет выбросы, к счастью, у нас есть способ их локализации, поэтому давайте проанализируем нашу проблему более статистически:
Поскольку X является переменной класса setosa, virginica или versicolor, X является выбросом, если:

Или альтернативно:

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

def outlier_search(column):
    quantile1 = df[column].quantile(0.25)
    quantile3 = df[column].quantile(0.75)
    inter_quantile = quantile3 - quantile1
    out_found = df.loc[(df[column] < (quantile1 - (1.5*inter_quantile))) | (df[column] > (quantile3 + (1.5*inter_quantile))), column] = df[column].std() 
    return column

outlier_search('SepalWidth')

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

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

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

...
...

def outlier_search(column):
    quantile1 = df[column].quantile(0.25)
    quantile3_optimized = (df[column].quantile(0.75))/2
    inter_quantile = df[column].quantile(0.75) - df[column].quantile(0.25)
    out_found = df.loc[(df[column] < (quantile1 - (1.5*inter_quantile))) | (df[column] > (quantile3_optimized + (1.5*inter_quantile))), column] = df[column].std() 
    return column

outlier_search('SepalWidth')

array = df.values
X = array[:,0:4] # data
Y = array[:,4] # test

# Standardize the data
X = (X - np.mean(X))/2*np.std(X)

# Reserve 10% to test
split = int(X.shape[0]*0.9)

X_train = X[0:split]
Y_train = Y[0:split]

X_test = X[split:]
Y_test = Y[split:]

model = LogisticRegression(max_iter=1000)
model.fit(X_train, Y_train)
result = model.score(X_test, Y_test)

Ух ты! Оценка нашей модели теперь составляет 100%! Мы достигаем своей цели, просто предлагая подход к работе с выбросами.

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

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

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

Я надеюсь, что этот пост прояснил, что это такое и как справиться с этой проблемой.

Спасибо, что прочитали сообщение!

Подписывайтесь на меня, чтобы получать больше контента, подобного этому, это очень помогает!