做过广告点击率预估的朋友应该都体会过这种痛:特征上百万维,模型文件动不动几百兆,线上推理一次要等十几毫秒。我之前一版逻辑回归就是这样,后来在损失函数里加了L1正则化,把惩罚系数调到合适值,一版下来超过一半的特征权重直接变成0,模型文件缩到原来的三分之一,推理耗时肉眼可见地降下来。当时组里有同事问我:“为什么加L1能把权重清零,加L2却只是把权重压小?”这个问题其实问到了机器学习里最基础、也最容易被一句“L1稀疏、L2平滑”带过去的点上。
L1正则化和L2正则化是机器学习、深度学习里最常用的两种正则化手段。这篇文章不打算停留在“L1做特征选择、L2防过拟合”这种口号层面,而是把两者背后的数学推导、几何直觉、贝叶斯解释和工程调参经验摊开来讲,尤其是会重点拆一个最近经常被问到的热词:软阈值算子为什么是L1正则化的解。搞懂这一层,你才算真正理解L1为什么能产生稀疏解。
1. 正则化到底在解决什么问题
1.1 过拟合:模型记住了不该记住的东西
不先讲清楚正则化在解决什么问题,后面说L1和L2的区别都是空中楼阁。过拟合的典型症状大家都见过:训练集准确率99%,验证集掉到82%;训练loss一路往下走,验证loss却开始反弹。很多新手第一反应是加数据、加dropout,但很少有人意识到,过拟合的本质是模型复杂度和样本量不匹配。
我习惯用一个比喻理解这件事:模型就像一个备考的学生,训练数据是模拟题。如果学生只盯着一套模拟题死记硬背,连题目里的错别字都背下来,换一套新题就抓瞎——这就是过拟合。正则化相当于给学生立一条规矩:可以答题,但每个知识点只能记有限的笔记,不能把整本书抄上去。这个“限制笔记量”的机制,落到损失函数里,就是给权重“上税”。
还有一个很容易被忽略的场景是p远大于n的问题,也就是特征数量远大于样本数量。比如基因表达数据,可能只有几百个样本,但特征有上万维;或者文本分类里的词袋模型,样本几万条,特征几十万维。这种时候模型有无穷多种方式完美拟合训练集,不加正则化几乎必然过拟合。正则化不是锦上添花,而是能不能用的问题。
1.2 正则项是如何被加进损失函数的
原始损失函数只关心一件事:预测值和真实值之间的误差,比如平方误差或者交叉熵。为了让模型不过于复杂,我们在损失后面加一个关于权重的惩罚项:
$$ J(\mathbf{w}) = L(\mathbf{w}) + \lambda \cdot \text{penalty}(\mathbf{w}) $$
这里很重要的一点是,惩罚项只针对权重,不针对偏置。原因是偏置只是一个平移项,它不会让模型变得弯曲或复杂,惩罚它没有意义。这个细节很多代码里没注意,框架默认对bias也做了weight decay,后面我会再提。
λ是正则化强度的控制旋钮:λ=0 的时候就是普通训练,模型想怎么复杂就怎么复杂;λ特别大的时候,权重被压得接近0,模型几乎变成一个常数预测器。从方差-偏差的角度看,λ增大是牺牲偏差换方差,模型变得简单但对数据的拟合能力下降。所以调正则化系数本质上是在偏差和方差之间找平衡点,不存在一个万能值,必须依据具体数据试。
2. L1和L2的数学定义与第一层直觉
2.1 数学形式的本质差异
先把两个公式摆出来。加了L1正则后,优化目标变成:
$$ J(\mathbf{w}) = L(\mathbf{w}) + \lambda \sum_{j=1}^{p} |w_j| $$
加了L2正则后,优化目标变成:
$$ J(\mathbf{w}) = L(\mathbf{w}) + \frac{\lambda}{2} \sum_{j=1}^{p} w_j^2 $$
单看公式,好像只是把绝对值换成了平方,但这一点差别导致了完全不同的行为。为了后面算梯度方便,L2通常写成 λ/2 乘以权重平方和,求导的时候那个2被消掉,梯度就变成 λw。L1的梯度就有意思了,它是 λ·sign(w),也就是不管权重是0.001还是0.1,只要符号相同,梯度贡献都是同一个固定值。
这个数学差异直接决定了两种正则化的性格。我做了一个对比表,方便快速抓重点:
| 对比项 | L1正则化 | L2正则化 |
|---|---|---|
| 惩罚形式 | |w| 的绝对值之和 | w² 的平方之和的一半 |
| 权重更新时的额外项 | 恒定向0方向拉,大小为λ | 按比例缩放权重,系数(1-ηλ) |
| 解的特点 | 大量权重精确等于0,产生稀疏解 | 权重被压小但不精确归零 |
| 对离群大权重的态度 | 惩罚力度恒定,相对温和 | 惩罚随权重大小线性增长,更严厉 |
| 典型应用 | 特征选择、模型压缩、可解释模型 | 防止过拟合、提升泛化、深度学习weight decay |
| 约束区域的几何形状 | 菱形/多面体,角在坐标轴上 | 圆形/球体,表面光滑 |
2.2 “固定人头税”和“收入累进税”的直觉
我第一次接触L1和L2时,觉得所有解释都太抽象,后来找到一个类比豁然开朗。把权重想象成收入,正则化想象成税收。L2是累进税,收入越高税率越高,所以高收入(大权重)会被狠狠砍一刀,低收入(小权重)几乎感觉不到压力,但永远不会把收入清零。L1更像固定人头税,不管你收入多少,只要你有收入就得交固定一笔钱。一个权重是0.01还是0.1,对L1来说惩罚一视同仁,都是λ。这时候一个小权重就会算账:我存在的价值(减少拟合误差)可能还不如我交的税多,干脆把自己清零拉倒。
这也是为什么L1能做到真正稀疏:对微小权重下狠手,逼它们归零;而L2对接近0的权重几乎不构成压力,权重就停在0附近的小值上,永远差一口气到0。理解了这层,后面看软阈值算子的时候会特别顺畅。
3. 为什么L1产生稀疏解?三个视角讲透
3.1 几何视角:菱形与圆的差别
第一个视角是几何的,也是最直观的。假设模型只有两个权重 w1 和 w2,原始损失函数的等高线是以无约束最优解为中心的一圈圈椭圆。加正则化相当于在权重空间里加了一个可行域:L1的可行域是菱形,L2的可行域是圆。优化过程就是让椭圆等高线向外扩张,直到和可行域边界相切,切点就是最优解。
关键差异就在边界的形状。菱形的四个尖角正好落在坐标轴上,等高线往外扩的时候,最先碰到尖角的概率远大于碰到边的概率。一旦切点落在坐标轴上,就意味着另一个权重等于0——稀疏解就这样产生了。圆不然,圆表面是光滑的,切点通常不在坐标轴上,大概率落在某个非零位置,于是两个权重都被压缩但不归零。
推广到高维空间,这个差异更明显。L1的约束区域是高维多面体,有大量尖角落在坐标轴上,维度越高,尖角数量相对表面面积的比例越大,解撞到尖角从而稀疏的概率也越高。这也是为什么在高维稀疏特征场景下L1几乎是必选——维度越高,L1的稀疏效果越显著。
3.2 梯度视角:L1的“拉回”和L2的“缩放”
几何视角解释了最优解长什么样,但没有回答训练过程中权重是怎么一步步走到0的。这个问题得从梯度更新看。
先说L2。用梯度下降更新权重,L2带来的额外梯度是λw。假设训练数据的梯度部分是g,更新公式为:
$$ w \leftarrow w - \eta (g + \lambda w) = (1 - \eta\lambda) w - \eta g $$
注意看这个 (1 - ηλ) 系数,它小于1。也就是说,即使没有数据梯度g,每一步权重也会被等比缩小一点。这个操作在深度学习的框架里有个专门的名字叫权重衰减(weight decay),它像一个永不停歇的抽水泵,持续把权重往外抽。但问题是,当w越来越小的时候,λw也越来越小,抽水力度同步变小,所以权重会无限逼近0却永远到不了0。
再看L1。L1带来的额外梯度是λ·sign(w),更新公式为:
$$ w \leftarrow w - \eta (g + \lambda \cdot \text{sign}(w)) $$
这里的关键在于,λ·sign(w) 的模长是固定的λ,不随权重变小而变小。哪怕w已经小到0.0001,它受到的向0拉力仍然是完整的λ。这种恒定拉力的结果就是:权重会被一直推到0,推过0之后就改变符号,然后又被反向拉回来,最终在0处达到平衡。所以L1的解里会出现很多“精确等于0”的权重,这不是近似值,而是硬生生推出边界的结果。
3.3 软阈值算子:为什么L1正则化的解是它
前面两个视角虽然直观,但都没有回答一个更根本的问题:L1正则化在0点不可导,传统梯度下降根本没法处理这个点,那优化算法到底是怎么处理它的?这时候就要引出近端梯度法,以及它的核心组件——软阈值算子。
先说结论:L1正则化对应的近端算子就是软阈值函数。给定一个变量z,软阈值算子的定义是:
$$ \text{soft}(z, \tau) = \text{sign}(z) \cdot \max(|z| - \tau, 0) $$
这个式子看起来简单,但为什么会是这个形式?它是从一个一步优化问题推出来的。考虑优化问题:
$$ \min_{w} ; \frac{1}{2}(w - z)^2 + \tau |w| $$
其中第一项把w往z上拉,第二项要求w尽量小。解析求解需要分三种情况讨论,可以看一眼推导:
情况1: w > 0 f(w) = (1/2)(w - z)^2 + τ w f'(w) = w - z + τ = 0 => w* = z - τ 要求 w* > 0,即 z > τ 情况2: w < 0 f(w) = (1/2)(w - z)^2 - τ w f'(w) = w - z - τ = 0 => w* = z + τ 要求 w* < 0,即 z < -τ 情况3: w = 0 需要0属于次梯度集合,即 z 落在区间 [-τ, τ] 内 三种情况合并,得到: w* = sign(z) · max(|z| - τ, 0) = soft(z, τ)这个推导透露出非常深刻的含义。当z离0比较近(绝对值不超过τ)时,最优解直接就是0。为什么?因为此时如果把w从0挪到z,拟合误差的减少量,还比不上因为引入非零权重而付出的正则惩罚τ|w|,不划算。只有当z离0足够远(绝对值大于τ),才值得把w移动到z方向,但也不能完全到z,而是要收缩τ这么多。这就是软阈值名字的由来:不是硬性保留或砍掉,而是先砍掉τ,剩下的如果还是正的才保留。
那它和实际的L1优化有什么关系?在训练过程中,每一步迭代可以拆成两步。第一步先按常规梯度下降走一小步,得到中间变量v;第二步对这个v施加软阈值算子。写成迭代格式就是所谓的ISTA:
v = w^{(t)} - η · ∇L(w^{(t)}) w^{(t+1)} = soft(v, η·λ)注意这里的阈值是η·λ,不是λ。因为近端梯度推导里,软阈值算子的惩罚参数是在缩放过的损失里出现的,实际更新时要乘上学习率η。很多初学者照着公式抄,把阈值写成λ,结果发现稀疏效果完全不对,越调越迷糊。
到这里,软阈值算子为什么是L1正则化解这个问题,答案就很清晰了:L1正则化的近端算子就是软阈值,而近端梯度法就是在解决“不可导点怎么优化”的问题。每一次迭代中,软阈值都会把所有绝对值小于阈值的权重直接置零,这就是L1解稀疏性的最直接来源。
3.4 硬阈值和软阈值的区别
理解了软阈值,顺便把硬阈值也说了,因为两者容易混淆。硬阈值函数是:如果|z|大于阈值就保留z,否则直接置0。它对应L0范数(非零元素个数)的正则化,是最理想的稀疏化方案,但L0是非凸的,优化起来是NP难问题。软阈值则是对L1凸松弛的结果,不仅保留了稀疏化能力,还让整个优化问题变得可解。
两者的行为差异很微妙。硬阈值只做“保”或“斩”两个动作,留下的权重原封不动;软阈值不管权重最后是死是活,先砍掉阈值那么长的距离。在实际效果上,软阈值不会产生“阈值边缘突变”的问题,连续性和稳健性更好。这也是为什么工程上用L1而不是直接去求L0,我们牺牲了一点点理论上的最优稀疏性,换来了可以在大规模数据上高效求解的可能性。
4. 贝叶斯视角:先验分布决定正则化类型
4.1 MAP估计与先验
第三个视角来自贝叶斯统计。这个视角对我来说就像打开新世界大门:原来L1和L2的区别,本质上是你对权重分布的先验假设不一样。
在贝叶斯框架下,训练模型等价于求解最大后验估计。后验概率正比于似然乘以先验:
$$ p(\mathbf{w} | D) \propto p(D | \mathbf{w}) \cdot p(\mathbf{w}) $$
取负对数之后,最大化后验变成最小化两项之和:
$$ -\log p(\mathbf{w}|D) = -\log p(D|\mathbf{w}) - \log p(\mathbf{w}) $$
第一项就是普通的损失函数,第二项正是正则项。换句话说,正则化不是外来的“惩罚”,而是先验信念的自然表达。
4.2 高斯先验对应L2、拉普拉斯先验对应L1
如果假设权重服从均值为0的高斯分布,即 w ~ N(0, σ²) ,那么负对数先验就是:
$$ -\log p(w) = \frac{w^2}{2\sigma^2} + \text{常数} $$
这一项正是L2正则的形式。所以L2正则化等价于告诉模型:我相信权重都分布在0附近,但允许有一些小偏差,偏差的平方代价是逐渐增加的。
如果假设权重服从拉普拉斯分布,即 w ~ Laplace(0, b) ,它的概率密度在0处有个尖峰,两侧呈指数衰减。负对数先验是:
$$ -\log p(w) = \frac{|w|}{b} + \text{常数} $$
这一项正是L1正则。拉普拉斯分布和正态分布的一个关键区别是:拉普拉斯分布在0处有一个尖锐的峰值,概率质量更集中在0附近;同时它的尾部比正态分布更厚,对远离0的大权重相对宽容。这两个特性放在MAP估计里,就对应了“更倾向于精确的0”和“对大权重收缩相对温和”两个行为。
4.3 这对实际建模的启示
这个视角不仅仅是理论游戏,它直接影响一个建模决策:选L1还是选L2,本质上取决于你对问题结构的理解。如果你认为绝大多数特征都是噪声,只有少量特征真正对预测有贡献——比如基因数据、广告特征——那你的先验就是拉普拉斯分布,应该用L1。反之,如果你认为所有特征都有一定作用,只是大小不同——比如一些平稳的物理过程、图像像素——那高斯先验更合理,用L2。
还有一个值得注意的推论:L1的稀疏性错觉。很多人以为L1天然一定比L2更“好”,其实只是先验假设不同。某些场景下L1的稀疏解反而有害,比如两个强相关特征,L1可能随机选一个保留,导致模型不稳定。Zou和Hastie提出Elastic Net时正是为了解决这个问题,把L1和L2结合起来,用L2稳定相关特征的系数,用L1产生稀疏性,实践效果经常比单独用任何一个都好。
5. 工程实践:L1、L2怎么选、怎么用、怎么调
5.1 场景选择:特征稀疏化还是泛化稳定
到了落地环节,场景决定选择。我按自己的实践经验总结了一个大致规则,不一定适用所有情况,但作为起点是可靠的。
如果你面对的是高维稀疏特征,比如广告点击率预估、文本分类、用户画像这类动辄几十万上百万维的场景,L1几乎是刚需。原因很简单:维度越高,模型存储和推理成本越大,L1直接砍掉无用特征,线上性能收益立竿见影。而且这类场景的特征天然稀疏,很多维度的权重确实就应该是0。
如果特征是中小规模、维度几百到几千,或者你用的是深度神经网络,那L2(也就是weight decay)是更稳的选择。深度学习里参数量巨大,L1即使加进去也很难做到真正的稀疏化,因为深层网络的权重是层层复合的,单个权重是否为0对整体预测影响不大,强行加L1常常导致训练不稳定。
如果特征之间相关性很强,比如两个特征本质上是同一个信号的两种编码,这时候用裸的L1会随机丢弃其中一个,造成不稳定。这种情况建议用Elastic Net,也就是L1和L2一起上。它既享受L1的稀疏性,又靠L2把相关特征的系数拉近,工程上非常实用。
如果模型要求可解释性,比如风控评分卡、医疗辅助诊断,那L1加逻辑回归或者线性模型是很好的基线。稀疏线性模型可以直接对业务解释“哪些因素最重要、影响方向是什么”,比一堆黑盒模型更容易过合规评审。
5.2 具体配置与参数调整
配置正则化之前,有一个前置步骤我几乎每次都强调:先做特征标准化。原因藏在一个很容易被忽略的细节里——L2惩罚项中的平方项会让惩罚力度和特征尺度耦合。
举个例子,假设特征A的取值在0到1之间,特征B的取值在0到10000之间。在同一个线性模型里,为了产生相同幅度的预测变化,B对应的权重只需要A的万分之一。L2对权重平方的惩罚,换算到特征尺度上,就是对B的惩罚相对A来说被放大了,结果模型会更倾向于不使用B,这不是因为B不预测,而是因为数值尺度坑了它。L1同样存在这类问题,只是表现形式略有不同。所以在做L1/L2正则化之前,把特征标准化到相近尺度,让正则化公平对待每个特征,是最基本的准备工作。
在代码层面,不同框架写起来不一样。PyTorch里L2通常直接在优化器里配,L1需要手动加到loss里:
import torch # L2正则:通过weight_decay实现,等价于L2正则化 optimizer = torch.optim.SGD(model.parameters(), lr=0.01, weight_decay=1e-4) # L1正则:需要在loss里手动加 l1_lambda = 1e-5 l1_norm = sum(p.abs().sum() for p in model.parameters()) loss = ce_loss + l1_lambda * l1_normKeras/TensorFlow则可以直接在层上指定正则器:
from tensorflow.keras import regularizers model.add(Dense(64, kernel_regularizer=regularizers.l1(1e-5))) model.add(Dense(1, kernel_regularizer=regularizers.l2(1e-4)))调λ的时候,我习惯在log尺度上搜索,比如按 1e-6、1e-5、1e-4、1e-3、1e-2 这个倍数网格扫。不是只看验证集精度,还要同时观察正则化的直接效果:L1要监控非零权重的数量变化,L2要监控权重整体范数。这两类指标才是正则化真正作用的体现,光看loss容易被带偏。
5.3 踩坑记录:几个容易翻车的点
第一个坑是刚才提到的bias被顺带正则化。很多框架默认weight_decay是对所有参数生效的,但理论上偏置项不应该被惩罚。实际操作中,我不会手动去区分,而是在效果不对时检查一下这个问题。如果确实需要精确控制,可以按参数组分别设置:
optimizer = torch.optim.SGD([ {'params': model.weight, 'weight_decay': 1e-4}, {'params': model.bias, 'weight_decay': 0} ], lr=0.01)第二个坑是L1在Adam类优化器下的行为很怪。Loshchilov等人在2019年那篇AdamW论文里指出,L2正则和经典的weight decay在Adam里并不等价,因为Adam对每个参数的学习率做了自适应缩放,L2正则项会被这个缩放干扰。表现就是正则效果不稳,λ明明调大了,权重衰减却不明显。解决办法是使用解耦的weight decay,也就是AdamW,在更新时直接按比例缩小权重,而不是往梯度里加正则项。这个细节在工程上影响很大,我见过好几个项目换了AdamW之后,正则化表现就正常了。
第三个坑是L1在深度学习模型里的“假稀疏”。前面说过,深层网络权重即使有0,也不会表现为特征被剔除,因为每一层的0会被后面的矩阵变换传播开。我看到过有人在BERT后面的全连接层加L1,结果只是训练速度变慢,稀疏性根本没法利用。如果目的是模型压缩和稀疏化,考虑结构化剪枝、蒸馏这类专门方法,比单纯加L1有效得多。
6. 常见问题与排查技巧实录
6.1 问题排查速查表
把积累的常见问题整理成一张表,方便你直接对照排查。
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 加了L1后所有权重全部变0 | λ设得过大,惩罚超过拟合收益 | 调小λ,做log尺度搜索 |
| L1稀疏比例很高但精度明显下降 | λ过大,欠拟合 | 调小λ;考虑L1+L2的Elastic Net |
| 加L2后权重仍然很大 | λ太小,或特征未标准化 | 调大λ;先标准化再训练 |
| 加L2后精度不升反降 | λ过大,模型被压得太简单 | 调小λ,用验证集监控 |
| 使用Adam时L2权重衰减效果不稳 | Adam与L2正则不耦合 | 切换到AdamW,用解耦weight decay |
| 深度模型加L1没有稀疏效果 | 深层网络权重稀疏不等于特征稀疏 | 改用结构化剪枝或蒸馏 |
| L1在固定步数后损失不降 | 学习率过大或阈值λ设置太大 | 降低学习率;检查近端更新阈值是ηλ |
| 两个强相关特征被L1随机丢弃 | L1在相关特征前不稳定 | 改用Elastic Net,增加L2分量 |
6.2 关于软阈值算子的几个高频疑问
软阈值算子作为L1正则化的核心,实际工作中被问到的频率很高,我把最常见的几个疑问集中答一遍。
第一个疑问:为什么阈值是ηλ而不是λ?我前面推导时已经提过,这里再说得更直白一点。近端梯度法考虑的是“在某个点附近做一步近似优化”的局部问题,正则项在这个局部问题里的权重系数,等于全局λ乘以学习率η。所以在实现ISTA或者近端SGD时,软阈值的阈值参数一定是ηλ。如果你直接拿λ当阈值,相当于无形中把正则化强度放大了1/η倍,在深度学习这种学习率才1e-3的场景里,那模型权重会瞬间全部被干掉。
第二个疑问:软阈值为什么不是直接把小于阈值的系数清零,保留大于阈值的原值?这个问题就是对硬阈值和软阈值区别的疑问。因为硬阈值对应的优化问题是非凸的,求解困难且对噪声敏感。软阈值多做了一个“收缩”操作,连续性好,而且它是凸问题的精确解。实战中你会发现,软阈值的解在留存的系数上做一个向0的收缩,这样的系数在后继迭代中一般更稳,不会在边界处反复跳动。
第三个疑问:软阈值操作应该在权重更新之前还是之后?顺序必须是先按梯度更新,再做软阈值。因为近端梯度法的公式就是这么推导出来的:先走一步梯度方向,把“拟合”这部分的改善做完,再用软阈值去处理“稀疏化”那部分的要求。反过来操作就完全错了,相当于先砍权重再学拟合,目标函数就对接不上。
第四个疑问:L1正则化在特征完全共线的时候会怎么表现?两个完全相同的特征,L1出于稀疏化的惯性,会随机把其中一个权重置0,另一个保留全部权重。这在特征工程中是个隐患。解决方法也很简单,要么做特征去重和相关分析,要么用Elastic Net,靠L2把两个相关特征的权重拉得相对均匀,不至于出现“二选一”的随机性。
6.3 一个完整的调试思路示例
最后分享一个实际调试案例做个串烧。之前有个二分类模型,特征维度大约5万,跑L1后稀疏率只有10%,远远没达到预期。我按顺序排查,先看了特征是否标准化,发现数据来自不同业务线,有些特征量级差到千倍以上,这是L1被干扰的直接原因。标准化后稀疏率从10%提到40%。随后发现λ选太小,在log尺度上加了两个档位,稀疏率到了65%,验证集精度没有明显下滑。接着又发现Adam加L2的耦合问题,切换成AdamW后,训练曲线稳定了不少,最终模型权重的非零率停在58%,文件体积从180M降到约75M,线上推理耗时下降了约三成。整个过程看起来是在调参,实际上每一个步骤背后,都是前面讲的那几个数学原理在工作。
我个人在这些年踩过不少坑后最深的体会是:L1和L2不是谁替代谁的关系,而是两把用途不同的工具。理解它们的区别,不能停留在背结论,要能从几何、梯度、近端算子、贝叶斯这些不同角度都说得通。做到这一步,你在模型里加L1或者L2时,就不再是“别人都这么加所以我这么加”,而是清楚地知道自己在引入什么样的先验、付出什么代价、期待什么收益。最后再补一个小建议:如果你在一个新项目里拿不准选哪个,先用L2打个底,模型能正常收敛了,再根据业务是否需要稀疏性,谨慎引入L1或者Elastic Net,这会省掉很多排查时间。