ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

正则化项:解决过拟合的利器,L1/L2范数选型与实战调参指南

正则化项:解决过拟合的利器,L1/L2范数选型与实战调参指南

1. 从一个“考满分却不会做题”的模型说起

如果你训练过一个稍微复杂点的模型,大概率见过这种场面:训练集上的准确率一路飙到99%,损失曲线平滑得像教科书插图,可一换到验证集或者真实数据上,表现立刻塌方,准确率掉到七八十甚至更低。这种“训练集学霸、测试集学渣”的现象,就是过拟合。而解决它最经典、最常用、也最容易被低估的手段,就是给损失函数加一个正则化项。

这篇文章我想聊的就是这个看似简单、实则门道很多的“正则化项”。它是什么、为什么能起作用、L1和L2到底怎么选、参数λ怎么调、实际写代码时有哪些坑,我都会结合自己踩过的经验讲清楚。不管你是刚入门机器学习的新手,还是已经调过几个模型但总被过拟合困扰的从业者,这篇内容都能直接拿去参考复现。核心关键词就三个:过拟合、正则化项、L1/L2范数,我会围绕它们把背后的逻辑和实操细节全部展开。

先说结论性的认知:正则化项本质上是在损失函数里加了一个“惩罚项”,用来约束模型参数的大小或数量,让模型别把训练数据里的噪声也当成规律学进去。它不改变模型结构,却能在几乎不增加推理成本的前提下显著提升泛化能力。这也是为什么它被称为“过拟合解决利器”——便宜、有效、通用。

2. 正则化项到底在惩罚什么

2.1 过拟合的根源:模型太“自由”了

要理解正则化,得先搞清楚过拟合是怎么来的。假设你有一个多项式回归模型,数据本身是二次关系,但你用了十次多项式去拟合。模型有足够的自由度去穿过每一个训练点,包括那些由噪声造成的抖动。结果就是训练误差极低,但模型学到的是一条剧烈震荡的曲线,换一批数据就完全失效。

从参数角度看,过拟合往往伴随着参数绝对值变得很大。因为模型为了让输出精确匹配每个样本,不得不把某些权重调得非常大,用剧烈的函数变化去“迁就”噪声点。所以一个直观的思路就是:如果我能限制参数不要太大,模型就会更平滑,泛化就更好。这正是正则化项的核心动机。

2.2 正则化项的数学形式与直觉

标准的损失函数可以写成:

J(w) = 原始损失(如交叉熵、MSE) + λ * 正则化项(w)

其中λ是超参数,控制惩罚力度。正则化项常见的有两种:

  • L2正则(Ridge):正则项为(1/2) * ||w||²,即所有参数平方和的一半。
  • L1正则(Lasso):正则项为||w||₁,即所有参数绝对值之和。

为什么L2能让参数变小?因为对损失求导时,L2的梯度是λ * w,参数越大,被拉回原点的力就越大,最终所有参数都被“压缩”到接近零但不为零。而L1的梯度是λ * sign(w),它不管参数大小,只要不为零就施加固定方向的力,所以很多参数会被直接压到恰好为零,产生稀疏解。

提示:L2是“均匀缩小”,L1是“择优淘汰”。这个区别决定了它们在不同场景下的选择。

2.3 从贝叶斯视角看正则化

换个角度理解会更通透。L2正则等价于假设参数服从高斯先验,L1正则等价于假设参数服从拉普拉斯先验。也就是说,正则化其实是在做最大后验估计(MAP),而不是纯粹的最大似然估计。你注入的“先验知识”就是:参数不应该太大,或者大部分参数应该是零。这个视角解释了为什么正则化能防过拟合——它把先验信息融入了学习过程,相当于给模型加了约束。

3. L1与L2的选型逻辑与实操对比

3.1 什么时候用L2,什么时候用L1

这是被问得最多的问题。我的经验总结成一句话:特征都有用、只想防止过拟合,用L2;特征很多、怀疑大部分没用、想要特征选择,用L1。

L2会让所有参数变小但不为零,模型会保留所有特征的信息,只是削弱它们的权重。适合特征本身都有一定贡献、样本量不算太少的场景。L1会把不重要的特征权重直接压成零,天然具备特征选择能力,适合高维稀疏数据,比如文本分类里几万个词特征,真正有用的可能就几百个。

实际项目中还有一个折中方案:Elastic Net,即L1和L2的加权组合。它既保留稀疏性,又能在相关特征之间做分组选择,稳定性比纯L1更好。当特征之间存在强相关时,纯L1会随机保留其中一个,而Elastic Net倾向于一起保留或一起剔除。

对比维度L1正则L2正则
正则项形式绝对值之和平方和的一半
解的特性稀疏,部分参数为零稠密,参数整体缩小
主要用途特征选择、降维防过拟合、提升稳定性
对异常值较鲁棒对异常值敏感
优化难度不可导点,需特殊处理处处可导,易优化
典型场景高维稀疏特征一般回归与神经网络

3.2 参数λ的调节:从大到小的搜索策略

λ是正则化的“音量旋钮”。λ太大,模型被压得太狠,训练集和验证集都表现差,这叫欠拟合;λ太小,等于没加,过拟合照旧。所以调λ的本质是找欠拟合和过拟合之间的平衡点。

我的实操习惯是对数尺度搜索:先取[0.001, 0.01, 0.1, 1, 10]跑一轮,看验证集表现落在哪个区间,再在那个区间附近细化。比如发现0.01最好,就再试[0.003, 0.01, 0.03]。不要用线性步长去搜,因为λ的影响是指数级的。

注意:调λ时一定要用验证集或交叉验证,绝对不能用测试集。测试集只能用一次,用来报告最终结果。

3.3 一个容易忽略的细节:偏置项不参与正则化

这是新手常犯的错误。正则化应该只作用于权重w,不应该惩罚偏置项b。因为偏置只控制输出的整体偏移,不影响函数的形状和复杂度,惩罚它没有防过拟合的意义,反而可能让模型欠拟合。在框架里通常通过参数控制,比如PyTorch优化器里把权重和偏置分成两组,只对权重组设置weight_decay。

4. 在代码里落地正则化项

4.1 传统机器学习中的实现

以线性回归为例,用scikit-learn可以直接调用带正则化的类:

from sklearn.linear_model import Ridge, Lasso, ElasticNet # L2正则 ridge = Ridge(alpha=1.0) ridge.fit(X_train, y_train) # L1正则 lasso = Lasso(alpha=0.1) lasso.fit(X_train, y_train) # Elastic Net enet = ElasticNet(alpha=0.1, l1_ratio=0.5) enet.fit(X_train, y_train)

这里的alpha就是λ。注意sklearn里Ridge的损失函数是MSE + alpha * ||w||²,和有些教材里带1/2系数的写法略有差异,调参时以实际文档为准,别死记公式。

4.2 神经网络中的权重衰减

在深度学习框架里,L2正则通常以**权重衰减(weight decay)**的形式实现。以PyTorch为例:

import torch.optim as optim # 只对权重做衰减,偏置和BN参数不衰减 optimizer = optim.SGD([ {'params': model.weight_params, 'weight_decay': 1e-4}, {'params': model.bias_params, 'weight_decay': 0.0} ], lr=0.01, momentum=0.9)

这里有个关键点必须说清楚:在自适应优化器(如Adam)中,weight decay和真正的L2正则并不等价。标准Adam的weight decay会被自适应学习率缩放,导致大梯度的参数衰减反而小。后来提出的AdamW把权重衰减从梯度更新中解耦出来,才是真正意义上的L2正则。如果你用Adam且在意正则效果,优先用AdamW。

# 推荐:AdamW,解耦权重衰减 optimizer = optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-2)

4.3 手动实现正则化项

如果你想彻底搞懂原理,可以手动在损失里加正则项:

def compute_loss(model, X, y, lam=1e-4, reg_type='l2'): y_pred = model(X) base_loss = ((y_pred - y) ** 2).mean() if reg_type == 'l2': reg = sum((p ** 2).sum() for p in model.weight_params) elif reg_type == 'l1': reg = sum(p.abs().sum() for p in model.weight_params) return base_loss + lam * reg

手动实现的好处是你能清楚看到正则项对总损失的贡献比例。我习惯在训练时打印base_loss和reg_loss两个值,如果正则项占比过大(比如超过总损失的30%),说明λ可能设大了。

5. 常见问题与排查技巧实录

5.1 加了正则化反而更差?

这是最常见的困惑。原因通常有三个:一是λ设得太大,模型被压成欠拟合;二是数据本身噪声不大、模型也不复杂,正则化属于画蛇添足;三是正则化加错了地方,比如把偏置也惩罚了。排查顺序建议:先把λ调小一个数量级看效果,再检查是否误伤了偏置项,最后评估模型复杂度是否真的需要正则化。

5.2 L1正则训练不收敛怎么办

L1在零点不可导,直接用梯度下降会在零点附近震荡。解决方案有两个:一是使用近端梯度下降(Proximal Gradient),在每步更新后做软阈值操作;二是用次梯度,但收敛慢。实际中更推荐直接用支持L1的求解器,比如sklearn的Lasso用的是坐标下降,比手写梯度下降稳定得多。

5.3 训练损失和验证损失怎么看

判断正则化是否生效,最直接的方法是画两条损失曲线。健康的状态是:训练损失略低于验证损失,两者都在下降且逐渐靠近。如果训练损失持续下降但验证损失开始上升,说明正则化力度不够;如果两条曲线都很高且几乎重合,说明正则化过头了。这个判断方法比单看准确率更灵敏。

问题现象可能原因解决方向
训练验证都差λ过大,欠拟合减小λ
训练好验证差λ过小,过拟合增大λ或换L1
L1不收敛零点不可导换坐标下降/近端梯度
加了正则更差数据简单或误伤偏置去掉正则或排除偏置
Adam下正则无效weight decay被缩放改用AdamW

5.4 独家避坑经验

第一个坑:不要在BatchNorm的缩放参数上加正则。BN的γ参数本来就是为了调整分布,惩罚它会影响归一化效果。第二个坑:正则化系数和数据量有关,数据越多,过拟合风险越小,λ应该相应调小。第三个坑:L1的稀疏性在小模型上不明显,参数量少的时候L1和L2差别不大,别指望L1一定给你选出漂亮的特征子集。

6. 正则化之外,还能怎么配合

正则化项不是孤军奋战。实际项目里我通常把它和Dropout、早停、数据增强组合使用。Dropout可以看作一种隐式的模型集成,早停则是在验证损失回升时及时刹车,数据增强从源头增加样本多样性。这几招配合起来,比单纯调大λ效果更稳。

另外,正则化的思想还延伸出了很多变体,比如谱归一化用于GAN的判别器,正交正则化用于RNN,标签平滑本质上也是一种正则化。理解了“惩罚复杂度”这个核心,你就能举一反三,看到新方法时快速判断它是不是正则化家族的一员。

我个人在实际操作中的体会是:正则化项的价值不在于它多复杂,而在于它用极低的成本换来了泛化能力的提升。但千万别把它当成万能药,λ调不对、位置加错了,反而会帮倒忙。每次加正则化,都老老实实画损失曲线、做交叉验证,比盲目相信默认参数靠谱得多。最后分享一个小技巧:如果你不确定该用L1还是L2,先用L2跑一版基线,再试Elastic Net,多数情况下后者能给你惊喜。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进