比较线性回归中StandardScaler与Normalizer的结果

Jon*_*tel 16 python machine-learning linear-regression scikit-learn

我通过不同情景下的线性回归的一些实例工作,使用比较结果NormalizerStandardScaler,结果是令人费解.

我正在使用波士顿住房数据集,并以这种方式准备:

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
Run Code Online (Sandbox Code Playgroud)

我目前正试图推断我从以下场景得到的结果:

  • 使用参数normalize=Truevs using 初始化线性回归Normalizer
  • 使用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)
Run Code Online (Sandbox Code Playgroud)

然后,我创建了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]
})
Run Code Online (Sandbox Code Playgroud)

以下是我创建数据框以比较每个模型的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)
)
Run Code Online (Sandbox Code Playgroud)

最后,这是比较每个预测的数据框:

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)))
Run Code Online (Sandbox Code Playgroud)

以下是结果数据框:

系数: 数据帧比较不同模型的系数

成绩: 数据帧比较来自不同模型的R ^ 2值

预测: 数据帧比较来自不同模型的预测值

我有三个问题我无法调和:

  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:boolean,optional,默认为True

是否计算此模型的截距.如果设置为False,则不会
在计算中使用截距(例如,预计数据已经
居中).

StandardScaler中心的数据,所以我不为什么使用它与理解fit_intercept=False产生不连贯的结果.

Ven*_*lam 8

  1. 前两个模型之间的Sklearn系数没有差异的原因是在计算归一化输入数据的共同效应之后,对场后的系数进行去标准化.参考

这种去标准化已经完成,所以任何测试数据,我们都可以直接应用co-effs.并通过规范化测试数据来获得预测.

因此,设置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()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

你可以看到图1,2和4中数据的最佳拟合线是相同的; 表示由于列/要素规范化或标准化数据,R2_得分不会更改.就是这样,它最终会产生不同的效果.值.

注意:最合适的线条fig3会有所不同.

  1. 当您设置fit_intercept = False时,会从预测中减去偏差项.意味着拦截被设置为零,否则将是目标变量的平均值.

截距为零的预测预计会对目标变量未缩放(平均值= 0)的问题表现不佳.您可以在每行中看到22.532的差异,这表示输出的影响.


Jor*_*tao 5

回答问题1

我假设您对前两个模型的意思是reg1reg2。如果情况并非如此,请告诉我们。

无论是否对数据进行标准化,线性回归都具有相同的预测能力。因此,使用normalize=True对预测没有影响。理解这一点的一种方法是看到归一化(按列)是对每一列 ( (x-a)/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)
Run Code Online (Sandbox Code Playgroud)

这证实了两种方法都正确捕获了 [x1,x2] 和 y 之间的真实信号,即分别为 2 和 1。

回答问题2

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)
Run Code Online (Sandbox Code Playgroud)

这是因为,在数学上,设置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的得分下降。

标准化特征为零均值,但目标变量不一定。因此,不拟合截距会导致模型忽略目标的平均值。在我一直使用的示例中,y = 3 + ...没有拟合“3”,这自然会降低模型的预测能力。:)