使用L1正则化或AdamW复现scikit-learn LASSO结果遇难题
解决方案
先从纯线性模型对齐LASSO本质
要复现sklearn的LASSO结果,先跳过全连接(带非线性激活)、LSTM这类模型,先实现纯线性回归模型——sklearn的LASSO是带L1正则的线性模型,PyTorch里直接用nn.Linear(in_features, 1),不添加任何非线性激活,确保模型结构和sklearn完全一致,避免非线性带来的固有偏差。正确实现L1正则化损失
AdamW的权重衰减是L2正则的变种,和LASSO的L1正则逻辑完全不同,必须显式计算L1惩罚项:def lasso_loss(outputs, targets, model, alpha): # 对齐sklearn的损失尺度:sklearn用(1/(2*n_samples))*MSE,PyTorch默认MSE是均值 mse_loss = torch.sum((outputs - targets)**2) / (2 * len(targets)) # 仅对权重施加L1惩罚,偏置项不惩罚(和sklearn默认行为一致) l1_penalty = alpha * sum(p.abs().sum() for name, p in model.named_parameters() if 'weight' in name) return mse_loss + l1_penalty如果sklearn设置了
fit_intercept=False,再把偏置项也加入L1惩罚。换用凸优化专用优化器
sklearn的LASSO用坐标下降或LBFGS这类针对凸问题的算法,AdamW这类自适应优化器不适合凸问题的精确求解,容易产生损失噪声:- 优先用
LBFGS:这是最接近sklearn求解逻辑的优化器,训练时需要自定义闭包计算损失和梯度:optimizer = torch.optim.LBFGS(model.parameters(), lr=1.0, max_iter=1000, tolerance_grad=1e-7, tolerance_change=1e-9) for epoch in range(10): def closure(): optimizer.zero_grad() outputs = model(X_train) loss = lasso_loss(outputs, y_train, model, alpha) loss.backward() return loss optimizer.step(closure) - 若用SGD,必须关闭动量(凸问题不需要动量),配合全量数据训练:
optimizer = torch.optim.SGD(model.parameters(), lr=1e-2, momentum=0) # 训练时用全量数据,避免小批量带来的损失波动 for epoch in range(500): optimizer.zero_grad() outputs = model(X_train) loss = lasso_loss(outputs, y_train, model, alpha) loss.backward() optimizer.step()
- 优先用
对齐数据预处理与初始化
- 数据标准化:sklearn的LASSO默认对特征做标准化(
StandardScaler),必须确保PyTorch的训练数据和sklearn用完全相同的预处理流程——对X做均值0、方差1的标准化,y的处理逻辑也要完全对齐。 - 模型初始化:sklearn的线性模型默认初始化权重为0,偏置为训练集y的均值(当
fit_intercept=True时),PyTorch里手动对齐:with torch.no_grad(): model.weight.fill_(0.0) model.bias.fill_(y_train.mean().item())
- 数据标准化:sklearn的LASSO默认对特征做标准化(
消除训练损失噪声的关键操作
- 用全量数据训练:小批量SGD会导致损失波动,直接设置
batch_size=len(X_train),每一步都用全量数据计算损失和梯度,和sklearn的全量优化逻辑一致。 - 对齐正则化强度:sklearn的
alpha和PyTorch的alpha存在尺度差异,sklearn的损失是(1/(2*n_samples))*MSE + alpha*L1,如果PyTorch用默认MSE(均值),需要把PyTorch的alpha设置为sklearn_alpha * len(X_train) / 2,确保正则化强度完全匹配。
- 用全量数据训练:小批量SGD会导致损失波动,直接设置
非线性模型逼近线性结果的技巧
如果必须用全连接或LSTM:- 全连接网络:只用一层线性层,或多层但激活函数用
nn.Identity()(恒等映射),同时限制隐层神经元数量,避免模型容量过大偏离线性特性。 - LSTM:设置
num_layers=1、hidden_size=1,强制隐藏状态初始化为0,且仅取最后一步的输出,同时大幅提高L1正则化强度,用全量数据训练,避免过拟合带来的偏差。
- 全连接网络:只用一层线性层,或多层但激活函数用
内容的提问来源于stack exchange,提问作者DrJubbs
相关产品推荐
相关产品推荐

