我正在研究不同情况下的线性回归示例,比较使用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
初始化线性回归,并进行标准化和不进行标准化。总的来说,我发现结果令人困惑。
这是我设置所有内容的方式:
# 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)))
这是结果数据框:
我有三个我无法调和的问题:
normalize=False
无效。我可以理解预测值和R ^ 2值相同,但是我的特征具有不同的数值范围,因此我不确定为什么归一化根本没有效果。当您考虑使用StandardScaler
会显着改变系数时,这会令人困惑。 Normalizer
的模型会导致彼此之间如此根本不同的系数值,尤其是当使用LinearRegression(normalize=True)
的模型完全没有变化时。 如果您要查看每个文档,它们看起来很相似,甚至不相同。
摘自sklearn.linear_model.LinearRegression()上的文档:
标准化:布尔值,可选,默认为False
当fit_intercept设置为False时,将忽略此参数。如果为True,则将在回归之前通过减去均值并除以l2-范数来对回归变量X进行归一化。
与此同时,sklearn.preprocessing.Normalizer
states that it normalizes to the l2 norm by default上的文档。
我没有看到这两个选项之间的区别,也没有看到为什么一个选项的系数值与另一个选项会有如此根本的差异。
StandardScaler
的模型的结果与我一致,但我不明白为什么使用StandardScaler
并设置set_intercept=False
的模型的性能如此差。 摘自Linear Regression module上的文档:
fit_intercept:布尔值,可选,默认为True
是否计算此模型的截距。如果设置为False,否
截距将用于计算(例如,预计数据已经是
居中)。
StandardScaler
使您的数据居中,所以我不明白为什么将fit_intercept=False
与数据一起使用会产生不一致的结果。
答案 0 :(得分:8)
Sklearn
在根据归一化的输入数据计算出系数后,对幕后系数进行了归一化。 Reference 已经完成了这种去规格化处理,以便任何测试数据,我们都可以直接应用协效应。并通过标准化测试数据获得预测。
因此,设置normalize=True
确实会影响系数,但无论如何它们不会影响最佳拟合线。
Normalizer
对每个样本进行归一化(意味着逐行)。您会看到参考代码here。将样本分别归一化为单位范数。
而normalize=True
对每个列/特征进行归一化。 Reference
示例以了解规范化对数据的不同维度的影响。让我们将二维x1和x2设为目标变量。目标变量值在图中用颜色编码。
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()
您会看到图1,2和4中数据的最佳拟合线是相同的;表示R2_得分不会由于列/功能规范化或标准化数据而改变。就是这样,最终会带来不同的协同效应。值。
注意:fig3
的最佳拟合线会有所不同。
对于没有按比例缩放目标变量(均值= 0)的问题,预期截距为零的prediction会表现不好。您可以在每行中看到22.532的差异,这表示输出的影响。
答案 1 :(得分:4)
我假设您对前两个模型的意思是reg1
和reg2
。让我们知道是否是这种情况。
如果您对数据进行归一化,则线性回归具有相同的预测能力。因此,使用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)
这证实了这两种方法都能正确捕获[x1,x2]和y之间的真实信号,即分别捕获2和1。
Normalizer
不是您所期望的。它将每行标准化。因此,结果将发生巨大变化,并可能破坏要素和您要避免的目标之间的关系,除非是特定情况(例如TF-IDF)。
要了解如何进行操作,请假设上面的示例,但考虑与x3
不相关的其他功能y
。使用Normalizer
导致x1
被x3
的值修改,从而降低了与y
的关系。
系数之间的差异在于,当您在拟合之前进行标准化时,系数将相对于标准化特征,与我在答案第一部分中提到的系数相同。可以使用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 = a1*[(x1 - mu1)/sigma1] + a2*[(x2 - mu2)/sigma2] + a0
恢复y和x之间的关系,可以将其简化为y = (a1/sigma1)*x1 + (a2/sigma2)*x2 + (a0 - a1*mu1/sigma1 - a2*mu2/sigma2)
。
reg4.coef_ / scaler.scale_
在以上表示法中表示[a1/sigma1, a2/sigma2]
,这正是normalize=True
为确保系数相同所做的工作。
标准化特征为零均值,但目标变量不一定是零。因此,不拟合截距将导致模型忽略目标的均值。在我一直使用的示例中,y = 3 + ...
中的“ 3”未拟合,这自然降低了模型的预测能力。 :)
答案 2 :(得分:1)
关于带有fit_intercept = 0和标准化数据的不一致结果的最后一个问题(3)尚未得到完全回答。
OP可能期望StandardScaler标准化X和y,这将使截距必定为0(Gurobi license向下的1/3)。
但是StandardScaler忽略y。参见proof。
api提供了一种解决方案。这种方法对于因变量的非线性变换(例如y的对数变换)也很有用(但请考虑TransformedTargetRegressor)。
以下是解决OP问题#3的示例:
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.pipeline import make_pipeline
from sklearn.datasets import make_regression
from sklearn.compose import TransformedTargetRegressor
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
import numpy as np
# define a custom transformer
class stdY(BaseEstimator,TransformerMixin):
def __init__(self):
pass
def fit(self,Y):
self.std_err_=np.std(Y)
self.mean_=np.mean(Y)
return self
def transform(self,Y):
return (Y-self.mean_)/self.std_err_
def inverse_transform(self,Y):
return Y*self.std_err_+self.mean_
# standardize X and no intercept pipeline
no_int_pipe=make_pipeline(StandardScaler(),LinearRegression(fit_intercept=0)) # only standardizing X, so not expecting a great fit by itself.
# standardize y pipeline
std_lin_reg=TransformedTargetRegressor(regressor=no_int_pipe, transformer=stdY()) # transforms y, estimates the model, then reverses the transformation for evaluating loss.
#after returning to re-read my answer, there's an even easier solution, use StandardScaler as the transfromer:
std_lin_reg_easy=TransformedTargetRegressor(regressor=no_int_pipe, transformer=StandardScaler())
# generate some simple data
X, y, w = make_regression(n_samples=100,
n_features=3, # x variables generated and returned
n_informative=3, # x variables included in the actual model of y
effective_rank=3, # make less than n_informative for multicollinearity
coef=True,
noise=0.1,
random_state=0,
bias=10)
std_lin_reg.fit(X,y)
print('custom transformer on y and no intercept r2_score: ',std_lin_reg.score(X,y))
std_lin_reg_easy.fit(X,y)
print('standard scaler on y and no intercept r2_score: ',std_lin_reg_easy.score(X,y))
no_int_pipe.fit(X,y)
print('\nonly standard scalar and no intercept r2_score: ',no_int_pipe.score(X,y))
返回
custom transformer on y and no intercept r2_score: 0.9999343800041816
standard scaler on y and no intercept r2_score: 0.9999343800041816
only standard scalar and no intercept r2_score: 0.3319175799267782