statsmodels 和 sklearn ridge 回归之间的不匹配

Mismatch between statsmodels and sklearn ridge regression

我正在探索岭回归。在比较 statsmodelssklearn 时,我发现这两个库导致岭回归的输出不同。下面是一个简单的区别例子

import numpy as np
import pandas as pd 
import statsmodels.api as sm
from sklearn.linear_model import Lasso, Ridge

np.random.seed(142131)

n = 500
d = pd.DataFrame()
d['A'] = np.random.normal(size=n)
d['B'] = d['A'] + np.random.normal(scale=0.25, size=n)
d['C'] = np.random.normal(size=n)
d['D'] = np.random.normal(size=n)
d['intercept'] = 1
d['Y'] = 5 - 2*d['A'] + 1*d['D'] + np.random.normal(size=n)

y = np.asarray(d['Y'])
X = np.asarray(d[['intercept', 'A', 'B', 'C', 'D']])

首先,使用sklearnRidge

ridge = Ridge(alpha=1, fit_intercept=True)
ridge.fit(X=np.asarray(d[['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']]), y=y)
ridge.intercept_, ridge.coef_

输出 4.99721, -2.00968, 0.03363, -0.02145, 1.02895].

接下来,statsmodelsOLS.fit_regularized

penalty = np.array([0, 1., 1., 1., 1.])
ols = sm.OLS(y, X).fit_regularized(L1_wt=0., alpha=penalty)
ols.params

输出 [5.01623, -0.69164, -0.63901, 0.00156, 0.55158]。但是,由于它们都在实施岭回归,我希望它们是相同的。

请注意,这些都不会惩罚截距项(已经将其检查为可能的潜在差异)。我也不认为这是我的错误。具体来说,我发现这两种实现都为 LASSO 提供了相同的输出。下面是对之前数据的演示

# sklearn LASSO
lasso = Lasso(alpha=0.5, fit_intercept=True)
lasso.fit(X=np.asarray(d[['A', 'B', 'C', 'D']]), y=y)
lasso.intercept_, lasso.coef_

# statsmodels LASSO
penalty = np.array([0, 0.5, 0.5, 0.5, 0.5])
ols = sm.OLS(y, X).fit_regularized(L1_wt=1., alpha=penalty)
ols.params

两者都输出[5.01465, -1.51832, 0., 0., 0.57799].

所以我的问题是,为什么岭回归的估计系数在 sklearnstatsmodels 中的实现不同?

在进一步挖掘之后,我发现了它们为何不同的答案。不同之处在于 sklearnRidge 将惩罚项缩放为 alpha / n,其中 n 是观测值的数量。 statsmodels 不应用调整参数的这种缩放。如果 re-scale 对 statsmodels.

的惩罚,则可以让山脊实现匹配

使用我发布的示例,您将如何在两者之间进行输出匹配:

# sklearn 
# NOTE: there is no difference from above
ridge = Ridge(alpha=1, fit_intercept=True)
ridge.fit(X=np.asarray(d[['A', 'B', 'C', 'D']]), y=y)
ridge.intercept_, ridge.coef_

# statsmodels
# NOTE: going to re-scale the penalties based on n observations
n = X.shape[0]
penalty = np.array([0, 1., 1., 1., 1.]) / n  # scaling penalties
ols = sm.OLS(y, X).fit_regularized(L1_wt=0., alpha=penalty)
ols.params

现在都输出[ 4.99721, -2.00968, 0.03363, -0.02145, 1.02895].

我发布这个,所以如果其他人发现他们处于我的情况,他们可以更容易地找到答案(因为我之前没有看到任何关于这种差异的讨论)。我不确定 re-scaling 的基本原理。令我感到奇怪的是,Ridge re-scale 是调整参数,但 Lasso 不是。看起来像是需要注意的重要行为。阅读 Ridge and LASSOsklearn 文档,我没有看到讨论的 Ridge 在 re-scaling 行为上的差异。