自定义SGD实现的LogisticRegression系数与sklearn原生结果不一致的问题及参数调优咨询
自定义SGD版Logistic Regression与sklearn结果不一致的问题解决
问题1:修改自定义实现以匹配sklearn结果
你的自定义代码和sklearn结果差异的核心原因是损失函数与梯度更新公式不匹配,再加上SGD本身的收敛特性和sklearn使用的优化器不同,具体修改点如下:
- 修正梯度更新公式
sklearn的LogisticRegression(penalty="none")使用交叉熵损失(对数似然损失),而你的参考代码里的梯度更新是针对均方误差(MSE)损失设计的。交叉熵损失对参数的梯度计算不需要乘以sigmoid的导数yhat*(1-yhat),所以需要去掉这部分:
原代码的梯度更新:
coef[0] = coef[0] + l_rate * error * yhat * (1.0 - yhat) for i in range(len(row)-1): coef[i + 1] = coef[i + 1] + l_rate * error * yhat * (1.0 - yhat) * row[i]
修改为:
coef[0] = coef[0] + l_rate * error for i in range(len(row)-1): coef[i + 1] = coef[i + 1] + l_rate * error * row[i]
这里error = row[-1] - yhat,刚好对应交叉熵损失梯度的反向更新方向。
- 调整学习率与训练轮数
sklearn默认用**L-BFGS(拟牛顿法)**优化,收敛速度远快于随机梯度下降(SGD)。你的初始设置l_rate=0.3太大(SGD对学习率极敏感),n_epoch=100太少,导致还没收敛到最优值。建议改成:
l_rate = 0.01 n_epoch = 10000
修改后运行,得到的系数会和sklearn的结果非常接近。
- 可选:特征标准化(加速收敛)
SGD对特征尺度很敏感,虽然sklearn的penalty="none"不做标准化,但如果你对输入特征做标准化(比如减去均值、除以标准差),SGD会更快收敛到稳定的系数,也能避免因特征尺度差异导致的梯度震荡。
问题2:合理设置初始系数、学习率与训练轮数
初始系数
- 最简单且有效的方式是初始化为全0,就像你原代码那样。也可以用小范围的随机数(比如从
N(0, 0.01)的正态分布采样),但逻辑回归中全0初始化不会带来对称问题,足够好用。
学习率
- 初始尝试值:从
0.001、0.01开始测试,避免一开始用过大的学习率(比如0.3)导致参数震荡、无法收敛。 - 观察调整:如果训练过程中损失值上下波动,说明学习率太大,需要减小;如果损失下降极慢,说明学习率太小,可以适当增大。
- 进阶优化:使用学习率衰减,比如每轮训练后将学习率乘以一个衰减因子(如0.99),让前期快速接近最优值,后期精细调整参数。
训练轮数
- 设置足够大的初始值:比如10000轮,同时加入早停机制:如果训练过程中损失值连续多轮不再下降(或者变化小于某个阈值,比如1e-6),就提前停止训练,避免无效计算和过拟合。
- 无验证集时的判断:观察训练集的损失变化,当损失趋于稳定(不再明显下降)时,就可以停止训练。
内容的提问来源于stack exchange,提问作者user16386186
相关产品推荐
相关产品推荐

