我去年做一个点击率预估项目,特征做到几百维,样本却只有几万条。训练损失一路往下掉,掉到0.2出头,我当时还挺高兴,结果验证集的AUC不但没涨,反而比模型训练中期的表现还差。我把模型权重翻出来一看,发现不少特征的权重绝对值已经到了几十甚至上百——用户在这个特征上稍微波动一下,预估值就直接反向。那次之后我才真正理解,L1和L2正则化不是教科书里一个“背下来就行”的名词,而是真实工程里救场用的东西。
这篇文章想聊透一件事:神经网络里的L1和L2正则化,到底有什么用,什么时候用哪个,为什么L1能产生稀疏权重而L2不能。我不会只堆公式,会结合自己实际调模型时看到的现象来讲,适合已经会用框架但还没完全搞清楚原理的人,也适合被“L1稀疏、L2平滑”这种口诀困扰了很久的读者。
1. 过拟合才是正则化真正在解决的问题
1.1 训练损失降得很漂亮,验证损失却开始抬头:这就是信号
很多刚接触模型的同学会有一个误区,觉得训练集上loss越低,模型越好。实际上模型在训练集上的任务只有一个:记住训练样本里的规律。问题是它记的规律里,有多少是数据里真实存在的,有多少只是样本的偶然噪声。
过拟合最典型的表现就是:训练损失一路下降,验证集loss降到某个最低点后开始回升。那个拐点就是模型从“学习规律”切换到“背答案”的位置。背答案的特征在权重上体现得很明显——大量权重变得特别大,模型对输入的每个微小变化都极其敏感。
我记得自己第一次看权重分布图的时候,最直观的感受是:过拟合模型的权重尾巴特别长,有一堆偏离主体分布很远的极端值。后来我用L2正则化把整个权重分布往中间收了一下,验证AUC立刻回升了几个点。所以理解正则化,第一件事就是理解它解决的核心问题:过拟合导致的泛化能力下降。
1.2 偏差和方差的权衡:正则化是在“加一点偏差”换“降很多方差”
机器学习里有一个经典的偏差-方差分解:总误差 = 偏差² + 方差 + 噪声。
偏差衡量的是模型对真实规律的拟合能力不足,方差衡量的是模型对不同训练样本集的敏感程度。欠拟合的时候偏差大,过拟合的时候方差大。正则化做的事情,本质上是往偏差那边推一点点,换取方差的大幅下降。只要方差的下降幅度大于偏差的上升幅度,整体泛化误差就是降低的。
用大白话说:一个模型权重全都特别大,就像一个人背题背得太死,题目稍微换个说法就不会了。正则化相当于强制他去总结更一般、更简洁的规律,哪怕这个规律在训练集上稍微损失一点点精度,但在新题目上会稳定得多。
我去复盘过实际项目的损失曲线,正则化加上之后,训练loss通常会比不加的时候高一些,但验证集表现明显更稳。很多人看训练loss升了一点就觉得是坏事,其实只要验证指标在变好,这个“牺牲”就是完全值得的。这背后的机制,就是偏差-方差权衡在工作。
2. L2正则化:把过大的权重按比例拉回来
2.1 加了λΣw²之后,梯度更新变成了什么样
L2正则化的做法,是在原始损失函数后面加一项:
L = L₀ + λ∑wᵢ²
其中L₀是原来的数据损失,λ是人手设定的正则化系数。这个式子看起来简单,但它的梯度行为很值得细看。对损失函数求权重wᵢ的梯度:
∂L/∂wᵢ = ∂L₀/∂wᵢ + 2λwᵢ
做了梯度下降之后,更新公式变成:
wᵢ ← wᵢ - η(∂L₀/∂wᵢ + 2λwᵢ) = (1 - 2ηλ)wᵢ - η∂L₀/∂wᵢ
注意最后那个形式:无论原始损失带来的梯度是什么,每次更新都在强制给权重乘以一个(1 - 2ηλ)的系数。只要学习率η和λ的乘积不太大,这个系数就是个略小于1的数,意味着每一轮权重都会被“等比缩小”一点。这就是L2正则化别名“权重衰减”的由来。
我见过很多人只在框架里传一个weight_decay参数,根本不看背后的更新公式,这样一来调参的时候就很盲目。理解了这层关系,你就知道weight_decay越大,权重每一轮被压缩得越狠,模型能维持的权重上限就越低。
2.2 为什么L2基本不会把权重压成精确的0
很多人以为L2会让权重变小,既然变小,那不就应该慢慢变成0吗?实际训练中你会看到,L2正则化后的权重确实集中在0附近,但很少出现精确的0。
原因在于L2对权重的“拉力”是2λw,它是和权重本身成正比的。当权重w已经非常接近0的时候,这个惩罚项的梯度也会变得非常小,数据损失只要稍微想把权重拉回一点,就能和正则项形成平衡。最终权重稳定在一个很小的非零值上。
这个特性用一个类比特别好理解:L2像是给每个权重绑了一根弹簧,弹簧的另一端固定在0点。权重离0越远,弹簧拉力越大;离0越近,弹簧拉力越弱。所以它的结果是让权重回到0附近,但不会精确停在0上——除非权重本来就等于0。
这个特性决定了L2的效果是“整体收缩”而不是“选择性删除”。它能有效控制权重范数,让模型决策边界更平滑,但不会帮你自动做特征筛选。如果你指望模型通过L2帮你去掉某些特征,那会失望——几乎所有特征都会残留一个小权重。
2.3 从几何上看L2对解空间的约束
还有一个理解L2的角度,是在权重空间里看解的空间。L2约束等价于在权重空间里加了一个半径为√C的球形约束,目标是在球的范围内找到使损失最小的点。
这带来两个好处。一个是在约束空间里优化,函数的“地貌”通常更干净,不容易陷入某些极端的局部最小值。另一个是约束本身限制了权重向量能去的方向,让模型无法为了让单个特征完全控制输出而把权重推到极端。
我记得之前看一个实验,同样是二分类问题,不加正则化的模型决策边界为了拟合几个边缘样本,弯出了很奇怪的形状;加了L2之后,决策边界平滑了很多,几个边缘异常的样本被牺牲掉了,但整体分类边界明显更符合数据分布。这种视觉上的差异,就是泛化能力差异的直观体现。
3. L1正则化:常数推力造就稀疏权重
3.1 L1的梯度不是按比例而是恒定的
L1正则化是在损失函数后加|w|的求和:
L = L₀ + λ∑|wᵢ|
求导之后得到的是符号函数sgn(wᵢ),也就是说,无论权重绝对值是100还是0.01,惩罚项的梯度都是±λ,一个恒定值。这就是L1和L2最核心的区别:
- L2的惩罚梯度是2λw,权重越小拉力越小。
- L1的惩罚梯度是λ,权重再小拉力也不变。
这个区别直接决定了它们行为方式的不同。L1对每个权重施加的是一个恒定推力的“摩擦”,而L2是一个随着距离增大的“弹簧力”。
因为L1的梯度不随权重缩小而减弱,所以一个小权重如果数据损失给它的“回拉梯度”小于λ,它就会被一直推向0。这就是L1能产生稀疏解的直观原因。
3.2 一个简单的数值例子看L1和L2的差异
假设某个权重当前值是0.1,数据损失对它的梯度是0.01,学习率η=0.1,正则化系数λ=0.05,分别看L1和L2。
L2的更新量 = η(0.01 + 2×0.05×0.1) = 0.1×0.02 = 0.002,权重更新后为0.098。正则项贡献只有一半左右,权重缓慢下降。
L1的更新量 = η(0.01 + 0.05) = 0.1×0.06 = 0.006,其中正则贡献是0.005,权重更新后变为0.094。下降速度更快,而且哪怕权重已经变成0.001,L1的推力仍然有0.005,远远大于L2在那个位置的0.00001。
这个数值例子能清楚看到,L1对接近0的权重依然保持强力的“清除”趋势,而L2在权重接近0后会进入一个几乎不施加影响的区域。所以实际训练L1正则化的模型,你会看到一批权重被压到0附近后直接被压过0,在0的两侧小幅震荡或者直接卡住。
严格来说,用普通的梯度下降训练神经网络,权重极少被更新成二进制的精确0。真正拿到精确零解通常需要坐标下降、近端梯度这类专门算法,或者训练完成后做阈值截断。但从效果上说,一大批权重降到0.001以下时,删除这些特征模型的表现几乎不变,这和“真正的0”已经没有本质区别。
3.3 稀疏化带来的三个附加好处
如果说L2的目标是“让所有权重都别太大”,那L1的目标就是“让无关的权重直接滚蛋”。稀疏化一旦发生,会带来几个实际好处。
第一个好处是特征选择。在高维特征场景里,很多特征本来就是噪声,L1自动帮你把这些特征对应的权重清成0,相当于模型自己告诉你哪些特征值得保留。我做一个风控模型的时候,几千维的特征被L1撸完之后,活跃特征只剩两三百个,剩下那些全是在数据里偶然跟目标相关的噪声。
第二个好处是模型压缩和推理加速。稀疏权重意味着大量特征可以根本不参与乘法计算,用稀疏张量存储能省内存,推理时间也能缩短。在移动端模型或者在线推理场景里,这往往是L1最大的价值。
第三个好处是可解释性。精简单纯的模型更容易向非技术同事解释。我和业务团队开会的时候,给他们展示“我们几百个特征里只有二十几个真正在起作用”,比拿着一堆权重接近0的特征表讲半小时有效得多。
3.4 贝叶斯视角:L1对应拉普拉斯先验,L2对应高斯先验
如果接触过贝叶斯方法,正则化其实可以理解为在权重上施加先验分布。L2等价于假设权重服从均值为0的高斯先验,L1等价于假设权重服从拉普拉斯先验。
高斯先验的特点是大部分概率集中在0附近,但拖尾相对较厚,允许少数权重比较大;拉普拉斯先验在0处的概率密度更高,而且从0到两侧的衰减是线性的,这种分布天然更倾向于让权重精确落在0上。
这个视角提供了一个额外的决策依据:如果你大致知道权重应该比较密集地分布在0附近,但允许个别值比较大,选L2更合理;如果你怀疑大部分特征根本就没用,只有小部分特征有信号,选L1更合理。
4. 实际项目里L1和L2怎么选:从数据特征到模型结构
4.1 高维稀疏数据更吃L1,稠密连续特征用L2更稳
现在回到工程选择。我的经验里,L1和L2的选择首先看数据长什么样。
在CTR预估、推荐召回、文本分类这类场景,特征是典型的“高维稀疏”,比如几千个one-hot类别特征,或者词袋模型生成的词汇特征。这类数据里绝大多数字段对目标没有贡献,用L1可以直接产生一个非常干净的特征集,训练出来的模型体积小、线上速度快,效果也不会差。
反过来,如果你处理的是稠密的连续特征,比如房价预测里的面积、楼层、周边配套评分,这些特征之间通常有相关性,而且每个特征多多少少都带一点信息。这时候用L1可能会把一些弱但真实存在的信号给清掉,L2把所有特征权重整体收缩一下,模型会更稳。
我自己有一个印象比较深刻的案例。一个销量预测任务里,特征维度不过几十个,但相关性很高。我用L1加进去,确实把不少权重压成了0,但交叉验证结果反而比不加正则还差。换L2之后,整体权重被平均地压缩,验证集表现立刻变好。这就是“数据形态决定正则化选择”的典型例子。
4.2 一个L1和L2的直观对比表
| 维度 | L1正则化 | L2正则化 |
|---|---|---|
| 惩罚形式 | λ∑|wᵢ| | λ∑wᵢ² |
| 惩罚梯度 | ±λ,恒定 | 2λwᵢ,与权重成正比 |
| 是否产生稀疏解 | 容易产生,大量权重趋近0 | 几乎不产生精确0 |
| 特征选择能力 | 自带特征筛选 | 不筛选,只收缩 |
| 适合特征形态 | 高维稀疏、大部分特征无用 | 稠密连续、特征间相关性高 |
| 对权重极端值的影响 | 强拉回0 | 越远拉得越狠 |
| 优化平滑度 | 0点不可导,后期容易震荡 | 处处可导,优化更平稳 |
4.3 两个都想要的时候:Elastic Net的折中方案
实际项目里经常出现一种情况:特征维度高,很多无用特征,但剩下那部分特征之间又有强相关性。这种情况下,只选L1可能随机淘汰掉一组相关特征中的一个,只留下另一个;只选L2呢,权重整体收缩但特征还是在,模型体积降不下来。
Elastic Net就是用来解决这个问题的:同时把L1和L2加进损失函数。
L = L₀ + λ₁∑|wᵢ| + λ₂∑wᵢ²
在scikit-learn里,它的参数是alpha和l1_ratio。alpha控制总惩罚强度,l1_ratio控制L1在总惩罚里的占比。如果l1_ratio=1就是纯Lasso,等于0是纯岭回归,中间值则是“先选特征、再稳定权重”。
你可以这样理解它:L1负责决定哪些特征留下,L2负责让留下的特征权重不用太极端。两个正则化各干各的一份活,配合起来效果通常比单独用任何一个都好。
在深度神经网络里也一样,我见过不少人会在前面几层用L2,在最后一两层的某些特征稀疏化目标上用L1。这种混合用法的难点在于λ₁和λ₂要分别调,但方向上是对的:层次不同,对权重的诉求不同。
4.4 深度网络里的“L2”大多数是weight decay,和损失函数里的L2微妙不同
到了神经网络这块,事情有个细节特别容易踩坑。很多框架里的weight_decay参数,虽然常被等同于L2正则化,但严格来说它们并不完全是一回事。
在标准的L2正则化里,惩罚项的梯度2λw是加在整个梯度上的,然后参与整个优化器的动量、自适应学习率计算。而weight decay的实现是:在参数更新完成后,直接把权重乘一个小于1的衰减因子。在普通SGD下,二者数学上等价,这也是大家混着叫的原因。
但换成Adam这类自适应学习率优化器,情况就变了。Adam会把梯度按历史二阶矩做归一化,如果L2惩罚梯度也参与了归一化,实际产生的“惩罚”可能小得可怜。Loshchilov和Hutter在论文里专门讨论过这个问题,并提出解耦权重衰减(AdamW),把权重衰减从梯度里剥出来单独做,训练效果和稳定性都比普通Adam+L2好。
我实际在训练BERT或者CNN分类模型时,几乎不用自定义的L2损失,而是直接用AdamW的weight_decay。这样既避免了L2在自适应优化器里被“稀释”的坑,又享受着权重衰减带来的训练稳定性提升。
4.5 图神经网络、CNN里的正则化也逃不开这两个基本思想
再往深一层说,无论你用的网络是CNN、RNN还是图神经网络,L1和L2的底层作用机理都是一样的。CNN里大量的卷积核参数如果不加约束,学到的特征响应会越来越极端,L2能让feature map的数值分布更健康。图神经网络里节点特征经过多层聚合后可能产生尺度爆炸,L2对权重矩阵的约束同样能抑制这种爆炸。
这些年物理信息神经网络、大模型这些概念特别火,很多人以为正则化是不是已经被Attention、归一化之类的机制替代了。其实没有。归一化解决的是网络内部数值分布不稳定的问题,正则化解决的是模型对训练集过度拟合的问题,二者解决的不是一个层面的事。很多大模型在预训练阶段照样用了weight decay,只不过λ给得很小。
5. 几个常被忽略的细节:尺度、实现和调参顺序
5.1 损失函数有没有除以N,直接决定λ该给多大
这是最容易被忽略却最影响调参效率的细节。深度学习框架里,最终的loss通常是mini-batch里所有样本损失的均值,也就是除以了batch size N。但L1和L2惩罚项加的时候,很多初学者直接写一个裸的∑|w|或∑w²,根本没有考虑这个求和会不会比数据损失大几个数量级。
如果惩罚项尺度远大于数据损失,梯度更新会被正则项完全主导,模型直接学不动,loss卡在一个奇怪的位置。所以正规做法是把正则项也除以样本数量N,或者把λ设得足够小去补偿。
我自己的习惯是:如果框架里有weight_decay这种参数就直接用,框架内部已经处理好了尺度问题。如果自己往loss里加L1/L2,一定先打印出来看一下惩罚项和数据损失的量级对比,确认两者在同一数量级再开始调λ。这个习惯帮我排掉过无数个“loss不下降”的入门坑。
5.2 先调学习率还是先调正则系数:我的建议顺序
调参顺序这件事,我踩过不少次坑。最忌讳的是同时动学习率和正则化系数,一旦效果变差,你根本不知道是谁导致了退化。
我的做法是先固定一个不算离谱的正则化系数,比如weight_decay=1e-4,把学习率调好,让模型能在训练集上正常收敛。正常收敛的意思是训练loss稳定下降,验证loss出现过拟合拐点。然后我再开始扫正则化系数:1e-5、1e-4、1e-3、1e-2这样一个对数网格扫描,观察验证集指标的变化。
在深度网络里,weight_decay常见范围是1e-5到1e-2;在线性模型或sklearn的Lasso/Ridge里,C值的范围建议用小规模网格更残酷地扫描。经常出现的情况是:λ太小,过拟合依旧;λ太大,训练loss下不去。中间那一段会出现验证指标先升后降的“甜区”,找到甜区之后我才会回头微调学习率。
5.3 把L1加进深度学习训练后,我遇到过的几个问题
L1在传统线性模型里是很好用的,但在深度神经网络里用起来麻烦比L2多。
第一个问题是训练早期稀疏化过猛。神经网络的初始权重本来就小,如果λ给得偏大,训练初期一堆权重直接被杀到0,后面想恢复也恢复不过来,相当于模型一开始就失去了表达能力。我的应对方法是给L1正则系数做一个warmup,前几百步用很小的λ,等模型先把主要特征结构建立起来,再把λ拉到位;或者用余弦退火,让λ逐步增大。这两种方式都比一上来就给满λ稳定得多。
第二个问题是和BatchNorm的相互影响。BatchNorm本身就有一定的正则化效果,因为它让每层输入分布归一化,对梯度噪声有一定的容忍度。加了BatchNorm之后,网络对weight_decay的敏感度会降低,过大的weight_decay甚至可能影响收敛。我看不少卷积网络的config里,用了BatchNorm之后weight_decay都给得比较小。
第三个问题是特征尺度。L1惩罚的是权重绝对值,如果某一个特征的数据本身数值特别大,为了让预测合理,模型给它的权重天然就会比较小,L1惩罚在数值上会显得更“便宜”。也就是说,L1对特征尺度非常敏感。你在做L1之前,一定要把特征做标准化或者归一化,否则L1选出来的特征可能不是真正有用的,而是量级小的。这一点在线性模型中就容易踩,在神经网络里特征经过多层变换后就更隐蔽。
5.4 正则化和batch size的关系
还有一个大家讨论相对少的点:batch size其实也会影响正则化强度的选择。
小batch size训练时,每个batch的梯度噪声大,这种噪声本身就有正则化效果,能让模型跳出一些尖锐的局部最小点。相应地,你可能不需要特别大的正则化系数。大batch size训练时,梯度更平滑更“确定”,模型很容易收敛到尖锐的极小值,泛化能力差,这时候通常需要更强的正则化来补偿。
我试过用同一个模型,batch size从32调到256,如果保持weight_decay不变,验证集表现确实会下降;把weight_decay调大一点之后,差距缩小很多。这算是大模型训练里一个比较实际的调参策略:batch size扩大之后,正则化强度也要相应跟上。
5.5 我现在的默认做法
写到最后,分享一下我目前的默认做法。绝大多数神经网络任务,我直接用AdamW优化器,weight_decay设1e-4起步,根据模型深度和batch size调整,最多到1e-2。如果特征是明显的高维稀疏,并且我有特征筛选需求,我会优先试ElasticNet这种同时带L1和L2的方案,用交叉验证定alpha和l1_ratio,而不是纯L1。如果只是训练一个普通的全连接网络做回归或分类,L2/weight decay基本够用,不需要强行上L1。
L1和L2没有绝对的好坏,它们是从两个不同角度约束模型的工具。L2让你的权重别太嚣张,L1让你的权重学会舍弃。理解它们的区别,不只是为了应付面试,更是为了让模型在真实数据上多一分稳定、少一分意外。这些经验都是我在项目里一轮一轮调出来的,希望对你有用。