Sklearn 和 StatsModels 给出了非常不同的逻辑回归答案

Posted

技术标签:

【中文标题】Sklearn 和 StatsModels 给出了非常不同的逻辑回归答案【英文标题】:Sklearn and StatsModels give very different logistic regression answers 【发布时间】:2021-06-24 02:29:44 【问题描述】:

我正在对布尔值 0/1 数据集进行逻辑回归(预测某个年龄给你的薪水超过一定数额的概率),我使用 sklearn 和 StatsModels 得到了非常不同的结果,其中 sklearn 非常 错误

我已将 sklearn 惩罚设置为 None 并将截距项设置为 false 以使函数更类似于 StatsModels,但我看不出如何让 sklearn 给出合理的答案。

灰线是 0 或 1 处的原始数据点,我只是在绘图上将 1 缩小到 0.1 以使其可见。

变量:

# X and Y
X = df.age.values.reshape(-1,1)
X_poly = PolynomialFeatures(degree=4).fit_transform(X)
y_bool = np.array(df.wage.values > 250, dtype = "int")

# Generate a sequence of ages
age_grid = np.arange(X.min(), X.max()).reshape(-1,1)
age_grid_poly =  PolynomialFeatures(degree=4).fit_transform(age_grid)

代码如下:

# sklearn Model
clf = LogisticRegression(penalty = None, fit_intercept = False,max_iter = 300).fit(X=X_poly, y=y_bool)
preds = clf.predict_proba(age_grid_poly)

# Plot
fig, ax = plt.subplots(figsize=(8,6))
ax.scatter(X ,y_bool/10, s=30, c='grey', marker='|', alpha=0.7)
plt.plot(age_grid, preds[:,1], color = 'r', alpha = 1)
plt.xlabel('Age')
plt.ylabel('Wage')
plt.show()

sklearn result

# StatsModels
log_reg = sm.Logit(y_bool, X_poly).fit()
preds = log_reg.predict(age_grid_poly)
# Plot
fig, ax = plt.subplots(figsize=(8,6))
ax.scatter(X ,y_bool/10, s=30, c='grey', marker='|', alpha=0.7)
plt.plot(age_grid, preds, color = 'r', alpha = 1)
plt.xlabel('Age')
plt.ylabel('Wage')
plt.show()

StatsModels result

【问题讨论】:

【参考方案1】:

一旦我没有数据集或 scikit-learn 和 statsmodels 的特定版本,我就无法准确重现结果。但是,我认为您无法成功删除代码中的正则化参数。文档说明您应该传递字符串 'none',而不是常量 None

请参考sklearn.linear_model.LogisticRegression 文档:

penalty'l1', 'l2', 'elasticnet', 'none', default='l2' 用于 指定在处罚中使用的规范。 “newton-cg”、“sag”和 “lbfgs”求解器仅支持 l2 惩罚。 'elasticnet' 只是 由“saga”求解器支持。如果“无”(不支持 liblinear 求解器),不应用正则化。

我认为通过调查系数而不是使用绘图更容易理解差异。 您可以直接使用属性coef_ 对 scikit-learn 模型和 params 对 statsmodels 模型进行调查。

从逻辑上讲,如果没有正确禁用正则化参数,您应该期望 scikit-learn 模型中的系数较低。

【讨论】:

我已经尝试了两个惩罚 = 'none' 和一个非常大的 C 值,但我仍然得到相同的情节。尽管对于 sklearn,系数看起来确实很可疑 - statsmodel 的最大系数约为 200,而 sklearn 的系数为 0.02(sklearn 版本 0.24.0,statsmodels 版本 0.12.1,数据集 here,如果您有兴趣) 这可能是由每个实现用来处理多项式特征生成的非线性的假设引起的。我尝试仅在年龄为 IV 时对这两个模型进行试验,并在下面设置所有其他情况,结果是相似的系数:LogisticRegression(solver = 'lbfgs', pain = 'none', fit_intercept = False, max_iter = 300).fit(X = X, y = y_bool) Logit(y_bool, X).fit(method = 'lbfgs', maxiter=300, full_output=0, disp=0)【参考方案2】:

这似乎是因为 sklearn 的实现非常依赖于规模(并且多项式项非常大)。通过首先缩放数据,我得到了质量上相同的结果。

# sklearn Model
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

clf = Pipeline([
    ('scale', StandardScaler()),
    ('lr', LogisticRegression(penalty='none', fit_intercept=True, max_iter=1000)),
]).fit(X=X_poly, y=y_bool)
preds = clf.predict_proba(age_grid_poly)

# Plot
fig, ax = plt.subplots(figsize=(8,6))
ax.scatter(X ,y_bool/10, s=30, c='grey', marker='|', alpha=0.7)
plt.plot(age_grid, preds[:,1], color = 'r', alpha = 1)
plt.xlabel('Age')
plt.ylabel('Wage')
plt.show()

请注意,在这种情况下我们需要设置fit_intercept=True,因为StandardScaler 会终止来自PolynomialFeatures 的常量列(使其全为零)。

【讨论】:

以上是关于Sklearn 和 StatsModels 给出了非常不同的逻辑回归答案的主要内容,如果未能解决你的问题,请参考以下文章

`statsmodels` 和 `sklearn` 中的 Logit 估计器

sklearn 和 statsmodels 的逻辑回归结果不匹配

Python使用sklearn和statsmodels构建多元线性回归模型(Multiple Linear Regression)并解读

sklearn/statsmodels 奇异协方差矩阵下普通最小二乘的结果

为啥当 fit_intercept=False 时 Sklearn R-squared 与 statsmodels 不同?

使用 ywunbiased 时,statsmodels.tsa.stattools 中的 PACF 函数给出大于 1 的数字?