Machine Learning

Почему One-hot encoding недопустим в линейных моделях

Время прочтения: 3 мин.

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

Dj — множество возможных значений для j – ого признака.

  1. Бинарные признаки: Dj = {0;1}
  2. Числовые признаки: Dj = R
  3. Категорийные признаки: Dj = {u1, … , un}, где n — количество категорий

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

На просторах интернета почти всегда предлагают применять к категориальным признакам One-hot encoding, пример применения метода приведен в таблице ниже.

Перед тем как говорит о том, чем плох данный метод в линейных моделях вспомним базовые предположения МНК.

  1. Линейность отклика: y=Xw + ε
  2. Случайность выборки: наблюдения (xj, yj)
  3. Полнота ранга X: ни один из признаков не является линейной комбинацией других признаков
  4. Случайность ошибок: E(x)=0

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

Теперь вспомним, что при построении линейной регрессии в матрицу объектов-признаков мы добавляем столбец из единиц, с помощью которого мы будем учитывать константный признак. Однако после применения One-hot encoding этот столбец будет полностью линейно выразѝм с помощью новых признаков. То есть мы утратим несмещенность и состоятельность оценок коэффициентов, а их дисперсия будет бесконечной.  Это не очень удобно при построении доверительных интервалов: это будут доверительные интервалы бесконечной ширины, гипотезы тоже не проверить. Способ избежать этого можно использовать dummy-кодирование, кодировать n-1 переменную, как в таблицу ниже.

Перейдем к практическому примеру, воспользуемся данными из прошлой статьи (ссылка), прологарифмируем totsp, kitsp и целевую переменную price, удалим kitsp. Наша таблица имеет следующий вид.

Применим One-hot encoding к признаку code.

import numpy as np
import pandas as pd

from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer

ohe = ColumnTransformer([("One hot", OneHotEncoder(sparse=False),[5])], remainder="passthrough")
X = ohe.fit_transform(X.drop(columns=['log_price']))
y = df['log_price']

На выходе мы получаем numpy матрицу, у которой первые восемь столбцов соответствуют нашим новым признакам. Обучим модель.

reg = LinearRegression().fit(X, y)
print(reg.intercept_, reg.coef_, reg.score(X, y), sep='\n')

Обратим внимание на значения коэффициентов, уж больно они велики, хоть и качество приемлемое. Попробуем использовать dummy-кодирование.

df['code_1'] = np.where(df['code'] == 1, 1, 0)
df['code_2'] = np.where(df['code'] == 2, 1, 0)
df['code_3'] = np.where(df['code'] == 3, 1, 0)
df['code_4'] = np.where(df['code'] == 4, 1, 0)
df['code_5'] = np.where(df['code'] == 5, 1, 0)
df['code_6'] = np.where(df['code'] == 6, 1, 0)
df['code_7'] = np.where(df['code'] == 7, 1, 0)

X = df.drop(columns=['log_price'])
y = df['log_price']
X.head()
reg = LinearRegression().fit(X, y)
print(reg.intercept_, reg.coef_, reg.score(X, y), sep='\n')

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

Советуем почитать