自行实现的Lasso与scikit-learn的lasso_path求解路径差异原因是什么?
二者结果差异的核心原因
- L1正则次梯度实现错误:你当前写的
abs(self.B)/self.B * self.alpha逻辑存在两个致命问题:一是当权重B_j为0时会出现除以0的错误,二是完全无法匹配L1正则的次梯度定义,自然不可能得到和sklearn一致的稀疏解(比如你测试集中的b_2=0的结果)。此外你加了if j:的判断,第一次迭代完全不加入正则项的梯度,也会导致权重更新逻辑偏离优化目标。 - 特征预处理未对齐sklearn默认逻辑:sklearn的
lasso_path默认会对输入特征做标准化处理,同时自动拟合截距项。如果你的输入数据没有做标准化,两者的alpha参数的实际作用尺度完全不同,结果自然差异巨大。 - 求解器实现逻辑差异:sklearn的Lasso求解默认使用坐标下降法或者LARS算法,专门针对L1正则的稀疏特性做了优化,数值稳定性远高于你实现的朴素梯度下降。你的朴素梯度下降如果学习率设置不合理、迭代次数不足,很可能根本没有收敛到最优解。
需要补充/修改的代码内容
首先要把错误的次梯度更新替换为软阈值操作,这是Lasso得到稀疏解的核心逻辑;其次要补充特征标准化、截距项拟合的逻辑,对齐sklearn的默认处理;最后调整迭代逻辑保证收敛,修改后的完整代码如下:
import numpy as np class custom_lasso(): def __init__(self, lr=0.001, max_iter=10000, lamb=1.0, fit_intercept=True, standardize=True): self.eta = lr self.max_iter = max_iter self.alpha = lamb self.fit_intercept = fit_intercept self.standardize = standardize def _preprocess_data(self, X, Y): # 拟合截距的前置处理:对X、Y去均值 if self.fit_intercept: self.y_mean = np.mean(Y) Y = Y - self.y_mean self.x_mean = np.mean(X, axis=0) X = X - self.x_mean # 特征标准化 if self.standardize: self.x_std = np.std(X, axis=0) X = X / self.x_std return X, Y def _soft_threshold(self, x, lambda_): # L1正则对应的软阈值函数,实现系数稀疏化 return np.sign(x) * np.maximum(np.abs(x) - lambda_, 0) def fit(self, X, Y): X, Y = self._preprocess_data(X, Y) self.n, self.p = X.shape self.B = np.zeros(self.p) for i in range(self.max_iter): # 计算平方误差项的梯度 dB = X.T @ (X @ self.B - Y) # 梯度下降更新权重 self.B -= self.eta * dB # 软阈值操作处理L1正则 self.B = self._soft_threshold(self.B, self.eta * self.alpha) # 将系数还原回原始数据尺度 if self.standardize: self.B = self.B / self.x_std if self.fit_intercept: self.intercept_ = self.y_mean - self.x_mean @ self.B else: self.intercept_ = 0 return self
内容的提问来源于stack exchange,提问作者Yuki.F
相关产品推荐
相关产品推荐

