Сравнение результатов StandardScaler и нормализатора в линейной регрессии

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

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

import numpy as np
import pandas as pd
from sklearn.datasets import load_boston
from sklearn.preprocessing import Normalizer
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression

#load the data
df = pd.DataFrame(boston.data)
df.columns = boston.feature_names
df['PRICE'] = boston.target

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

  • Инициализация линейной регрессии с параметром normalize=True против использования Normalizer
  • Инициализация линейной регрессии с параметром fit_intercept = False со стандартизацией и без fit_intercept = False.

В совокупности я нахожу результаты запутанными.

Вот как я все настраиваю:

# Prep the data
X = df.iloc[:, :-1]
y = df.iloc[:, -1:]
normal_X = Normalizer().fit_transform(X)
scaled_X = StandardScaler().fit_transform(X)

#now prepare some of the models
reg1 = LinearRegression().fit(X, y)
reg2 = LinearRegression(normalize=True).fit(X, y)
reg3 = LinearRegression().fit(normal_X, y)
reg4 = LinearRegression().fit(scaled_X, y)
reg5 = LinearRegression(fit_intercept=False).fit(scaled_X, y)

Затем я создал 3 отдельных кадра данных, чтобы сравнить R_score, значения коэффициентов и прогнозы для каждой модели.

Чтобы создать фрейм данных для сравнения значений коэффициентов для каждой модели, я сделал следующее:

#Create a dataframe of the coefficients
coef = pd.DataFrame({
    'coeff':                       reg1.coef_[0],
    'coeff_normalize_true':        reg2.coef_[0],
    'coeff_normalizer':            reg3.coef_[0],
    'coeff_scaler':                reg4.coef_[0],
    'coeff_scaler_no_int':         reg5.coef_[0]
})

Вот как я создал фрейм данных для сравнения значений R ^ 2 для каждой модели:

scores = pd.DataFrame({
    'score':                        reg1.score(X, y),
    'score_normalize_true':         reg2.score(X, y),
    'score_normalizer':             reg3.score(normal_X, y),
    'score_scaler':                 reg4.score(scaled_X, y),
    'score_scaler_no_int':          reg5.score(scaled_X, y)
    }, index=range(1)
)

Наконец, вот датафрейм, который сравнивает прогнозы от каждого:

predictions = pd.DataFrame({
    'pred':                        reg1.predict(X).ravel(),
    'pred_normalize_true':         reg2.predict(X).ravel(),
    'pred_normalizer':             reg3.predict(normal_X).ravel(),
    'pred_scaler':                 reg4.predict(scaled_X).ravel(),
    'pred_scaler_no_int':          reg5.predict(scaled_X).ravel()
}, index=range(len(y)))

Вот результирующие кадры данных:

КОЭФФИЦИЕНТЫ: Dataframe comparing the coefficients from different models

СЧЕТ: Dataframe comparing the R^2 values from different models

ПРОГНОЗЫ: Dataframe comparing the prediction values from different models

У меня есть три вопроса, которые я не могу решить:

  1. Почему нет абсолютно никакой разницы между первыми двумя моделями? Похоже, что настройка normalize=False ничего не делает. Я могу понять, что прогнозы и значения R ^ 2 одинаковы, но у моих функций разные числовые масштабы, поэтому я не уверен, почему нормализация не будет иметь никакого эффекта. Это вдвойне запутанно, если учесть, что использование StandardScaler меняет коэффициенты.
  2. Я не понимаю, почему модель, использующая Normalizer вызывает такие радикально отличающиеся значения коэффициентов от других, особенно когда модель с LinearRegression(normalize=True) не меняет вообще.

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

Из документов на sklearn.linear_model.LinearRegression():

normalize: логический, необязательный, по умолчанию False

Этот параметр игнорируется, когда для fit_intercept установлено значение False. Если True, регрессоры X будут нормализованы до регрессии путем вычитания среднего значения и деления на l2-норму.

Между тем, документы по sklearn.preprocessing.Normalizer что по умолчанию нормализуется до уровня l2.

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

  1. Результаты модели, использующей StandardScaler, мне соответствуют, но я не понимаю, почему модель, использующая StandardScaler и параметр set_intercept=False работает так плохо.

Из документов по модулю линейной регрессии:

fit_intercept: логический, необязательный, по умолчанию True

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

StandardScaler центрирует ваши данные, поэтому я не понимаю, почему использование их с fit_intercept=False приводит к несогласованным результатам.

Ответ 1

  1. Причиной отсутствия различий в коэффициентах между первыми двумя моделями является то, что Sklearn нормализацию коэффициентов за кулисами после расчета коэффициентов на основе нормализованных входных данных. Ссылка

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

Следовательно, установка normalize=True действительно влияет на коэффициенты, но в любом случае они не влияют на линию наилучшего соответствия.

  1. Normalizer выполняет нормализацию по каждому образцу (то есть по строкам). Вы видите код ссылки здесь.

Из документации:

Нормализуйте образцы индивидуально до единицы нормы.

тогда как normalize=True выполняет нормализацию для каждого столбца/объекта. Ссылка

Пример, чтобы понять влияние нормализации на разные измерения данных. Давайте возьмем два измерения x1 & x2 и y будет целевой переменной. Значение целевой переменной имеет цветовую кодировку на рисунке.

import matplotlib.pyplot as plt
from sklearn.preprocessing import Normalizer,StandardScaler
from sklearn.preprocessing.data import normalize

n=50
x1 = np.random.normal(0, 2, size=n)
x2 = np.random.normal(0, 2, size=n)
noise = np.random.normal(0, 1, size=n)
y = 5 + 0.5*x1 + 2.5*x2 + noise

fig,ax=plt.subplots(1,4,figsize=(20,6))

ax[0].scatter(x1,x2,c=y)
ax[0].set_title('raw_data',size=15)

X = np.column_stack((x1,x2))

column_normalized=normalize(X, axis=0)
ax[1].scatter(column_normalized[:,0],column_normalized[:,1],c=y)
ax[1].set_title('column_normalized data',size=15)

row_normalized=Normalizer().fit_transform(X)
ax[2].scatter(row_normalized[:,0],row_normalized[:,1],c=y)
ax[2].set_title('row_normalized data',size=15)

standardized_data=StandardScaler().fit_transform(X)
ax[3].scatter(standardized_data[:,0],standardized_data[:,1],c=y)
ax[3].set_title('standardized data',size=15)

plt.subplots_adjust(left=0.3, bottom=None, right=0.9, top=None, wspace=0.3, hspace=None)
plt.show()

enter image description here

Вы могли видеть, что линия наилучшего соответствия для данных на рис. 1,2 и 4 будет такой же; означает, что показатель R2_ не изменится из-за нормализации столбца/элемента или стандартизации данных. Только это заканчивается различными эффектами. ценности.

Примечание: линия наилучшего соответствия для fig3 будет отличаться.

  1. Когда вы устанавливаете fit_intercept = False, термин смещения вычитается из прогноза. Значение перехвата установлено равным нулю, что в противном случае было бы средним значением целевой переменной.

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

Ответ 2

Ответ на Q1

Я предполагаю, что вы имеете в виду первые две модели - это reg1 и reg2. Дайте нам знать, если это не так.

Линейная регрессия имеет такую же прогностическую силу, нормализуете вы данные или нет. Поэтому использование normalize=True не влияет на прогнозы. Один из способов понять это - увидеть, что нормализация (по столбцам) - это линейная операция над каждым из столбцов ((xa)/b), и линейные преобразования данных о линейной регрессии не влияют на оценку коэффициента, только изменяют их ценности. Обратите внимание, что это утверждение не верно для Lasso/Ridge/ElasticNet.

Итак, почему коэффициенты не отличаются? Что ж, normalize=True также учитывает, что пользователь обычно хочет получить коэффициенты на исходных объектах, а не нормализованные. Как таковой, он корректирует коэффициенты. Один из способов проверить, что это имеет смысл, - использовать более простой пример:

# two features, normal distributed with sigma=10
x1 = np.random.normal(0, 10, size=100)
x2 = np.random.normal(0, 10, size=100)

# y is related to each of them plus some noise
y = 3 + 2*x1 + 1*x2 + np.random.normal(0, 1, size=100)

X = np.array([x1, x2]).T  # X has two columns

reg1 = LinearRegression().fit(X, y)
reg2 = LinearRegression(normalize=True).fit(X, y)

# check that coefficients are the same and equal to [2,1]
np.testing.assert_allclose(reg1.coef_, reg2.coef_) 
np.testing.assert_allclose(reg1.coef_, np.array([2, 1]), rtol=0.01)

Что подтверждает, что оба метода правильно фиксируют реальный сигнал между [x1, x2] и y, а именно 2 и 1 соответственно.

Ответ на Q2

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

Чтобы увидеть, как это делается, рассмотрим пример выше, но рассмотрим другую особенность, x3, которая не связана с y. Использование Normalizer приводит к тому, что значение x1 изменяется на значение x3, уменьшая силу его связи с y.

Расхождение коэффициентов между моделями (1,2) и (4,5)

Расхождение между коэффициентами состоит в том, что когда вы стандартизируете перед подгонкой, коэффициенты будут соответствовать стандартизированным признакам, то есть коэффициентам, которые я упоминал в первой части ответа. Они могут быть сопоставлены с исходными параметрами, используя reg4.coef_/scaler.scale_:

x1 = np.random.normal(0, 10, size=100)
x2 = np.random.normal(0, 10, size=100)
y = 3 + 2*x1 + 1*x2 + np.random.normal(0, 1, size=100)
X = np.array([x1, x2]).T

reg1 = LinearRegression().fit(X, y)
reg2 = LinearRegression(normalize=True).fit(X, y)
scaler = StandardScaler()
reg4 = LinearRegression().fit(scaler.fit_transform(X), y)

np.testing.assert_allclose(reg1.coef_, reg2.coef_) 
np.testing.assert_allclose(reg1.coef_, np.array([2, 1]), rtol=0.01)

# here
coefficients = reg4.coef_ / scaler.scale_
np.testing.assert_allclose(coefficients, np.array([2, 1]), rtol=0.01)

Это связано с тем, что математически, устанавливая z = (x - mu)/sigma, модель reg4 решает y = a1*z1 + a2*z2 + a0. Мы можем восстановить связь между y и x с помощью простой алгебры: y = a1*[(x1 - mu1)/sigma1] + a2*[(x2 - mu2)/sigma2] + a0, которую можно упростить до y = (a1/sigma1)*x1 + (a2/sigma2)*x2 + (a0 - a1*mu1/sigma1 - a2*mu2/sigma2).

reg4.coef_/scaler.scale_ представляет [a1/sigma1, a2/sigma2] в вышеприведенных обозначениях, что и делает normalize=True, чтобы гарантировать, что коэффициенты одинаковы.

Расхождение баллов модели 5.

Стандартизованные функции имеют нулевое среднее значение, но целевая переменная не обязательно. Следовательно, отсутствие соответствия перехвату приводит к тому, что модель игнорирует среднее значение цели. В примере, который я использовал, "3" в y = 3 +... не подходит, что, естественно, уменьшает предсказательную силу модели. :)