1. 从一次模型过拟合的排查说起
如果你训练过一个稍微复杂点的模型,大概率见过这种场景:训练集上的损失一路降到接近零,准确率刷到99%以上,但一到验证集就拉胯,准确率卡在70%上下死活上不去。更气人的是,你把模型调小一点、数据加多一点,情况会好转,但始终达不到理想状态。这种“训练集学霸、测试集学渣”的现象,就是典型的过拟合。
解决过拟合的手段有很多:增加数据、简化模型、早停、Dropout、数据增强,还有今天要聊的主角——正则化。L1和L2正则化是深度学习里最基础、最常用、也最容易被忽视的两个技术点。说它基础,是因为几乎每个框架都把它封装成了一行参数,比如weight_decay=1e-4或者kernel_regularizer=l2(0.01);说它容易被忽视,是因为很多人调参时只是随手填一个数,从来没想过这个数背后到底在干什么,为什么L1能产生稀疏解而L2不能,为什么L2对大权重惩罚更狠,为什么有些场景下L1反而会让模型更难训练。
这篇内容我会从实际排查过拟合问题的角度出发,把L1和L2正则化的数学原理、代码实现、调参经验、踩坑记录完整地梳理一遍。不管你是刚入门的新手,还是已经调过几十个模型的老手,相信都能从中找到一些之前没注意到的细节。文章会涉及损失函数的构造、梯度推导、稀疏性来源、权重衰减与L2的等价性讨论、实际项目中的参数选择策略,以及我本人在多个模拟项目中积累的一些经验教训。
2. 正则化到底在损失函数里加了什么
2.1 损失函数的原始形态与过拟合的数学本质
先回到最根本的地方。一个模型训练的过程,本质上是在最小化一个损失函数:
L(θ) = (1/N) * Σ loss(y_i, f(x_i; θ))其中θ是模型参数,N是样本数量。模型通过梯度下降不断调整θ,让预测值f(x_i; θ)尽可能接近真实标签y_i。问题在于,当模型容量足够大时,它可以通过让某些权重变得非常大或非常极端,来“记住”训练样本的噪声和偶然特征,而不是学到真正的规律。
从数学上看,过拟合对应的是参数空间中那些“陡峭”的解——函数在某些方向上变化极其剧烈,输入稍微扰动一下,输出就天翻地覆。这种解在训练集上表现完美,但泛化能力极差。
正则化的思路非常直接:在原始损失函数后面加一项惩罚项,限制参数的大小或复杂度,让模型在拟合数据和保持参数简洁之间做一个权衡。加了正则化之后的损失函数长这样:
L_total(θ) = L_data(θ) + λ * R(θ)其中R(θ)就是正则化项,λ是正则化系数,控制惩罚的力度。L1和L2的区别,就在于R(θ)的形式不同。
2.2 L2正则化:对大权重施加平方惩罚
L2正则化项的定义是参数向量的L2范数平方:
R_L2(θ) = (1/2) * Σ θ_j^2前面的1/2是为了求导时方便消掉指数上的2,这是工程实现中的常见做法。加上L2之后,总损失对某个参数θ_j的梯度变成:
∂L_total/∂θ_j = ∂L_data/∂θ_j + λ * θ_j这意味着每次更新时,参数会额外减去一个与自身大小成正比的量:
θ_j := θ_j - η * (∂L_data/∂θ_j + λ * θ_j) = θ_j * (1 - η * λ) - η * ∂L_data/∂θ_j注意(1 - η * λ)这一项,它让参数在每次更新时都先“衰减”一点,这也是为什么L2正则化在深度学习框架里常被称为权重衰减(weight decay)。权重越大,衰减得越多;权重越小,衰减得越少。最终效果是模型倾向于让所有参数都保持在一个较小的范围内,函数变得更平滑,泛化能力更强。
这里有一个很多人没注意到的细节:L2正则化和权重衰减在标准SGD下是等价的,但在Adam等自适应优化器中并不完全等价。Adam的权重衰减实现(AdamW)和L2正则化在数学上是有区别的,这个后面会详细说。
2.3 L1正则化:绝对值惩罚与稀疏性的来源
L1正则化项是参数向量的L1范数:
R_L1(θ) = Σ |θ_j|它对参数的梯度是:
∂R_L1/∂θ_j = sign(θ_j)也就是说,不管θ_j是多大还是多小,只要它不是零,梯度就是+1或-1。更新规则变成:
θ_j := θ_j - η * ∂L_data/∂θ_j - η * λ * sign(θ_j)这个更新有一个非常关键的性质:每次更新时,参数会朝着零的方向移动一个固定的量η * λ,而不是像L2那样按比例衰减。当θ_j本身很小时,数据梯度可能比λ * sign(θ_j)还小,这时候参数就会被直接推到零,并且在后续更新中可能一直保持在零附近。这就是L1正则化产生稀疏解的根本原因——它倾向于让一部分参数精确地变成零。
稀疏性带来的好处是特征选择:如果输入特征有几百上千维,L1可以让模型自动挑出真正重要的那些,把不重要的权重压到零。这在某些场景下非常有用,比如高维稀疏特征、需要模型可解释性的任务。
但稀疏性也有代价。L1的梯度在零点不可导,工程实现上通常用次梯度(subgradient)来处理。另外,L1的惩罚力度是恒定的,不会因为权重变小而减弱,这可能导致训练后期参数在零附近震荡,收敛不如L2稳定。
3. 从梯度更新看L1和L2的行为差异
3.1 一个二维参数空间的可视化推演
为了更直观地理解L1和L2的区别,我们可以在二维参数空间里做一个思想实验。假设模型只有两个参数θ_1和θ_2,原始损失函数的等高线是一圈一圈的椭圆,最优解在椭圆中心。加上正则化项后,总损失的等高线会发生变化。
L2正则化项是θ_1^2 + θ_2^2,等高线是圆形。总损失的等高线相当于原始椭圆和圆形的叠加,最优解会被“拉”向原点方向,但不会精确落在坐标轴上。也就是说,L2会让参数变小,但通常不会让任何一个参数精确为零。
L1正则化项是|θ_1| + |θ_2|,等高线是菱形。菱形在坐标轴上有尖角,而原始损失的椭圆和菱形相切的位置,大概率会落在尖角上。尖角对应的就是某个参数为零的位置。这就是L1稀疏性的几何解释——菱形的尖角天然吸引最优解落在坐标轴上。
这个几何直觉非常重要。很多人只记住了“L1稀疏、L2平滑”这个结论,但不知道为什么。理解了菱形和圆形的区别,你就能自己推导出更多性质,比如为什么L1在特征维度很高时特别有用,为什么L2更适合处理共线性特征。
3.2 权重衰减系数的实际影响
在实际代码中,L2正则化通常通过优化器的weight_decay参数来实现。以PyTorch为例:
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, weight_decay=1e-4)这个1e-4就是λ。它的大小直接影响模型的行为。我做过一组对比实验,在一个图像分类任务上,固定学习率0.01,只改变weight_decay:
| weight_decay | 训练准确率 | 验证准确率 | 训练/验证差距 |
|---|---|---|---|
| 0 | 99.8% | 72.3% | 27.5% |
| 1e-5 | 98.5% | 78.6% | 19.9% |
| 1e-4 | 95.2% | 84.1% | 11.1% |
| 1e-3 | 88.7% | 82.5% | 6.2% |
| 1e-2 | 76.4% | 74.8% | 1.6% |
可以看到,weight_decay从0增加到1e-4时,验证准确率提升非常明显,训练和验证的差距大幅缩小。但继续增加到1e-2时,模型开始欠拟合,训练准确率掉得厉害,验证准确率也跟着下降。这说明正则化系数存在一个“甜点区”,太小起不到作用,太大又会压制模型的学习能力。
注意:这个表格里的数值是基于特定任务和模型结构的,不同任务的最优weight_decay可能相差几个数量级。图像分类常用1e-4到1e-2,NLP任务常用1e-5到1e-3,具体要看模型大小和数据量。
3.3 L1在深度学习框架中的实现差异
L1正则化在PyTorch中没有像weight_decay那样的直接参数,需要手动加到损失函数里:
def l1_regularization(model, lambda_l1): l1_loss = 0 for param in model.parameters(): l1_loss += torch.sum(torch.abs(param)) return lambda_l1 * l1_loss # 训练循环中 total_loss = data_loss + l1_regularization(model, 1e-5)这里有一个坑:L1正则化通常只加在权重上,不加在偏置(bias)上。因为偏置的数量很少,对模型复杂度的贡献很小,惩罚偏置反而可能影响模型的拟合能力。同样,BatchNorm层的缩放和平移参数通常也不加正则化。这个细节在手动实现时很容易忽略。
另外,L1的lambda通常比L2小一到两个数量级。因为L1的梯度是恒定的sign值,而L2的梯度与权重成正比。当权重很小时,L1的惩罚力度相对更大。我一般会先用L2调出一个不错的模型,然后把L2换成L1,lambda取原来的1/10到1/5,再微调。
4. 稀疏性、特征选择与L1的独特价值
4.1 为什么稀疏解在高维场景下如此重要
假设你在做一个文本分类任务,输入是词袋模型,维度可能上万。如果每个词都对应一个非零权重,模型会变得非常臃肿,推理速度慢,而且容易过拟合。L1正则化可以把大部分不重要的词权重压到零,只保留几百个真正有区分度的词。这不仅减小了模型体积,还提高了可解释性——你可以直接看哪些词被保留了,哪些被丢弃了。
在推荐系统、生物信息学、金融风控等领域,特征维度往往远大于样本量,L1的特征选择能力几乎是刚需。我做过一个模拟项目,输入特征有5000维,样本只有2000条。不加正则化时,模型在训练集上完美拟合,验证集AUC只有0.62。加上L1正则化后,模型自动选择了约300个非零特征,验证集AUC提升到0.81,而且推理速度提升了十几倍。
4.2 L1稀疏性的局限与替代方案
L1虽然能产生稀疏解,但它有几个明显的局限。第一,当特征之间存在高度相关性时,L1倾向于随机选择其中一个而丢弃其他,这会导致结果不稳定——换一组训练数据,选中的特征可能完全不同。第二,L1的优化不如L2平滑,收敛速度可能更慢,尤其是在训练后期。第三,L1在深度学习中的效果往往不如在传统线性模型中那么显著,因为深度神经网络的参数本身就存在大量冗余,稀疏性带来的收益可能被网络结构的复杂性抵消。
针对这些问题,实践中衍生出了一些替代方案。Elastic Net结合了L1和L2,既能产生稀疏解,又能处理相关特征。Group Lasso把参数分组,以组为单位进行稀疏化,适合处理结构化特征。在深度学习中,还有专门的结构化剪枝方法,通过移除整个神经元或卷积核来实现稀疏化,效果比单纯的L1更好。
4.3 一个L1特征选择的实操案例
我在一个模拟的信用评分项目中用过L1正则化做特征选择。数据有8000条样本,1200个特征,包括数值特征和类别特征的独热编码。模型是一个三层的全连接网络。
第一步,先不加正则化训练一个基线模型,验证集AUC约0.74,但训练集AUC达到0.96,过拟合严重。
第二步,加L2正则化,weight_decay从1e-5试到1e-2,最优在1e-3左右,验证集AUC提升到0.79。
第三步,换成L1正则化,lambda从1e-6试到1e-3。当lambda=1e-4时,验证集AUC达到0.80,同时第一层权重中有约65%变成了零。检查非零权重对应的特征,发现大部分是收入、负债率、查询次数等业务上合理的变量,而一些随机生成的噪声特征被正确丢弃了。
这个案例说明,L1不仅能提升泛化能力,还能提供特征重要性的参考。但要注意,L1选出的特征不一定是最优的,它只是让模型在训练数据上表现最好的同时保持稀疏。如果业务上需要强制保留某些特征,可以在L1基础上加约束,或者改用Group Lasso。
5. 权重衰减与L2等价性的深度辨析
5.1 SGD下两者为什么等价
在标准SGD中,L2正则化和权重衰减在数学上是完全等价的。前面推导过,L2正则化让梯度多了一项λ * θ_j,更新时相当于θ_j乘以(1 - η * λ)。而权重衰减的实现方式是在每次更新时直接让θ_j乘以(1 - η * λ),不经过损失函数。两者在SGD下的更新公式一模一样。
这个等价性成立的前提是:学习率η固定,且优化器是标准SGD。一旦学习率是动态调整的,或者使用了自适应优化器,等价性就可能被打破。
5.2 Adam中L2与权重衰减的分道扬镳
Adam优化器的更新规则涉及一阶矩和二阶矩的估计。如果把L2正则化加到损失函数里,梯度中的λ * θ_j会进入一阶矩和二阶矩的计算,导致自适应学习率对正则化项也起作用。结果是,对于梯度较大的参数,正则化力度会被自适应地减小;对于梯度较小的参数,正则化力度会被放大。这显然不是我们想要的效果——我们希望正则化对所有参数一视同仁。
AdamW(Adam with decoupled weight decay)把权重衰减从梯度计算中分离出来,直接在参数更新时乘以衰减系数,不经过自适应学习率的调整。这样权重衰减的行为就和SGD中一致了。实验表明,在Transformer等模型上,AdamW比带L2的Adam效果更好,训练更稳定。
提示:如果你在用Adam或AdamW,建议优先使用AdamW的weight_decay参数,而不是在损失函数里手动加L2。PyTorch中
torch.optim.AdamW已经内置了正确的权重衰减实现。
5.3 实际项目中的选择建议
根据我的经验,选择L2还是权重衰减,主要看优化器类型。用SGD或SGD with momentum时,两者等价,随便选。用Adam时,优先用AdamW的weight_decay。用RMSprop或其他自适应优化器时,需要查一下框架文档,确认weight_decay的实现方式。
另外,权重衰减对学习率的敏感度比L2更高。因为权重衰减的力度直接和学习率相乘,学习率调大时,等效的正则化力度也会变大。所以当你调整学习率时,可能需要同步调整weight_decay。我通常的做法是:先固定weight_decay,调好学习率,然后再微调weight_decay。
6. 调参实战:从1e-6到1e-2的搜索策略
6.1 正则化系数的量级选择
正则化系数λ的取值范围通常跨越好几个数量级。对于L2/权重衰减,常见的搜索范围是1e-6到1e-2。对于L1,常见范围是1e-7到1e-3。具体选哪个量级,取决于模型大小、数据量和任务难度。
一个经验法则是:模型参数越多、数据越少,需要的正则化越强。比如一个百万参数的小模型在十万条数据上训练,weight_decay=1e-5可能就够了;而一个亿级参数的大模型在同样数据上训练,可能需要1e-3甚至更大。
另一个经验法则是:先跑一个不加正则化的基线,观察训练集和验证集的差距。差距越大,需要的正则化越强。如果差距很小,说明模型本身没有严重过拟合,正则化可以轻一些。
6.2 对数均匀搜索与二分微调
我通常采用两阶段搜索。第一阶段,在对数尺度上均匀取点,比如[1e-6, 1e-5, 1e-4, 1e-3, 1e-2],每个值跑一次短训练(比如10个epoch),看验证集指标。第二阶段,在最优值附近做二分微调,比如最优是1e-4,就试[5e-5, 1e-4, 2e-4, 5e-4],进一步缩小范围。
这个方法比随机搜索更高效,因为正则化系数的影响通常是对数尺度上单调的——太小不起作用,太大欠拟合,中间有一个峰值。对数均匀搜索能快速定位峰值所在的数量级。
6.3 与学习率、批大小的交互影响
正则化系数不是孤立的,它和学习率、批大小有复杂的交互。学习率越大,权重衰减的等效力度越大(因为更新步长更大)。批大小越大,梯度估计越准确,正则化的效果也越稳定。
我遇到过一种情况:把批大小从32增加到256后,原来的weight_decay=1e-4变得太强了,模型欠拟合。原因是批大小增加后,梯度噪声减小,模型本身就更稳定,不需要那么强的正则化。把weight_decay降到1e-5后,效果恢复正常。
所以调参时不要只盯着正则化系数,要把它和学习率、批大小放在一起考虑。一个实用的策略是:先固定批大小和学习率,调正则化;然后固定正则化,微调学习率;最后再回头微调正则化。迭代两三轮,基本能找到不错的组合。
7. 那些年我踩过的正则化坑
7.1 对偏置和BatchNorm参数加了正则化
这是新手最容易犯的错误。在手动实现L1或L2时,把模型所有参数都加进了正则化项,包括偏置和BatchNorm的缩放平移参数。结果模型训练变得极不稳定,验证集指标波动很大。
原因很简单:偏置和BatchNorm参数的数量很少,对模型复杂度的贡献微乎其微,惩罚它们只会限制模型的拟合能力,不会改善泛化。正确的做法是只对权重矩阵(卷积核、全连接层的weight)加正则化。
在PyTorch中,可以通过参数分组来实现:
weight_params = [] bias_params = [] for name, param in model.named_parameters(): if 'bias' in name or 'bn' in name: bias_params.append(param) else: weight_params.append(param) optimizer = torch.optim.SGD([ {'params': weight_params, 'weight_decay': 1e-4}, {'params': bias_params, 'weight_decay': 0} ], lr=0.01)7.2 L1正则化导致训练后期loss震荡
L1的梯度是sign函数,在零点附近不连续。当参数接近零时,数据梯度和正则化梯度可能相互抵消,导致参数在零附近来回跳动,loss曲线出现震荡。这个问题在训练后期特别明显。
解决方法有几个:一是用平滑L1(smooth L1),在零点附近用二次函数代替绝对值,梯度连续;二是用次梯度方法,在零点取零梯度;三是降低L1的lambda,减小震荡幅度;四是改用Elastic Net,用L2的平滑性来稳定训练。
我一般会先用L2训练到一个不错的解,然后换成L1做微调,lambda取L2的1/10左右。这样既能获得稀疏性,又不会从头开始训练导致不稳定。
7.3 正则化系数太大导致模型欠拟合
有一次我在一个NLP任务上把weight_decay设成了1e-2,结果模型在训练集上的准确率只有60%,验证集55%,明显欠拟合。当时我以为是模型结构有问题,排查了半天才发现是正则化太强。
判断正则化是否过强的方法很简单:看训练集损失。如果训练集损失下降很慢,或者最终值远高于不加正则化时的值,说明正则化太强了。另一个信号是训练集和验证集的指标都很低,且差距很小——这不是泛化好,而是模型根本没学到东西。
注意:正则化的目标是缩小训练集和验证集的差距,而不是让两者都变低。如果两者都低,说明模型欠拟合,应该减小正则化系数或增加模型容量。
7.4 在微调预训练模型时用了过大的weight_decay
微调预训练模型时,模型已经在大规模数据上学到了很好的特征表示,只需要在小数据集上微调。这时候如果weight_decay设得太大,会破坏预训练学到的权重,导致灾难性遗忘。
我一般会在微调时用比从头训练更小的weight_decay,比如1e-5到1e-6。有些实践者甚至会对预训练层和新增层使用不同的weight_decay,预训练层用更小的值,新增层用正常值。这个策略在Transformer微调中特别常见。
8. 不同任务场景下的正则化策略选择
8.1 图像分类:L2为主,适度使用Dropout
图像分类任务中,L2/权重衰减是标配。ResNet、EfficientNet等经典模型在训练时通常使用weight_decay=1e-4到1e-5。数据增强和Dropout也是常用的正则化手段,和L2配合使用效果更好。
在图像任务中,L1用得相对较少,因为卷积核的稀疏性不一定能带来推理加速(除非用专门的稀疏计算库),而且图像特征本身是稠密的,强制稀疏可能损失信息。不过在一些需要模型压缩的场景下,L1可以用来做通道剪枝的辅助。
8.2 NLP任务:AdamW加权重衰减,注意Embedding层
NLP任务中,Transformer类模型通常用AdamW,weight_decay在0.01左右。但要注意,Embedding层和LayerNorm层的参数通常不加权重衰减,或者用很小的值。因为Embedding层参数量大但每个参数更新频率低,过强的衰减会导致词向量退化。
另外,NLP任务中L1的使用也比较少,因为词嵌入的稀疏性不如传统词袋模型那么有意义。但在一些需要特征选择的场景,比如文本分类中的关键词提取,L1仍然有用武之地。
8.3 表格数据:L1和L2都值得尝试
表格数据(结构化数据)是L1和L2都能发挥作用的场景。特征维度可能很高,样本量可能有限,过拟合风险大。我通常先试L2,如果特征维度特别高或者需要特征选择,再试L1。Elastic Net在表格数据上往往表现最好,因为它结合了两者的优点。
在表格数据上,正则化系数的搜索范围可以更宽一些,从1e-6到1e-1都值得试。因为表格数据的特征尺度差异大,正则化的效果对尺度很敏感。建议在正则化之前先做特征标准化,否则L2会过度惩罚那些本身数值就大的特征。
8.4 小样本学习:强正则化与早停结合
小样本场景下,过拟合是最大的敌人。这时候需要强正则化,weight_decay可以设到1e-3甚至1e-2,同时配合早停、Dropout、数据增强等手段。L1在小样本下也能帮助模型聚焦到少数关键特征上。
但要注意,小样本下验证集也很小,调参的可靠性下降。我通常会用交叉验证来评估正则化系数的效果,而不是只看单次验证集的结果。另外,小样本下正则化系数的微小变化可能导致结果的巨大波动,所以不要过度精细地调参,选一个数量级上合理的值就行。
9. 一些容易被忽略的实现细节
9.1 正则化项是否应该除以样本数
在PyTorch中,损失函数通常会对batch内的样本取平均。如果你手动加L1或L2正则化,要注意正则化项是否也需要除以样本数。有些实现会把正则化项除以N,有些不会。这会导致等效的lambda相差N倍。
我的建议是:保持一致性。如果你用的框架的weight_decay不除以N,那你手动加L1时也不要除。如果你自己实现训练循环,最好在正则化项上也除以N,这样lambda的含义更直观——它表示每个样本平均受到的惩罚。
9.2 混合精度训练下的正则化
混合精度训练(AMP)中,权重衰减和L1/L2正则化的行为可能发生变化。因为AMP会把部分计算转成float16,而float16的精度有限,小权重的衰减可能被舍入误差淹没。如果发现AMP下正则化效果不如预期,可以尝试把正则化相关的计算保持在float32,或者增大lambda。
9.3 分布式训练中的权重衰减
在分布式数据并行(DDP)中,每个进程独立计算梯度,然后做all-reduce平均。权重衰减是在优化器更新时应用的,每个进程独立执行,所以行为和数据并行一致。但要注意,如果用了梯度累积,权重衰减的次数会减少,等效的正则化力度会变弱。这时候可能需要增大lambda来补偿。
10. 从正则化延伸出去的一些思考
正则化本质上是在给模型加先验。L2对应的是高斯先验,假设权重服从均值为零的正态分布;L1对应的是拉普拉斯先验,假设权重服从均值为零的拉普拉斯分布。拉普拉斯分布在零点的峰值更尖,所以L1更倾向于产生零权重。
这个贝叶斯视角可以帮助我们理解更多正则化方法。比如Dropout可以看作是对权重加伯努利先验,数据增强可以看作是对输入加先验。当你理解了正则化的本质是引入先验知识,就能根据任务特点设计更合适的正则化策略,而不是盲目套用L1或L2。
我在实际项目中的体会是:正则化不是万能的,它不能替代好的数据、好的模型结构和好的训练策略。但在其他条件都差不多的情况下,选对正则化方法和系数,往往能让模型效果提升几个百分点。这几个百分点在竞赛或生产环境中,可能就是及格和优秀的差距。
最后分享一个小技巧:如果你不确定该用L1还是L2,可以先跑一个不加正则化的基线,然后同时试L2和L1,各跑三组不同lambda。对比验证集指标和训练集/验证集差距,通常半天之内就能做出选择。不要在这个问题上纠结太久,因为正则化的收益有上限,把时间花在数据质量和模型结构上,回报往往更大。