☰
L1正则化原理与PyTorch实战:从稀疏解到模型压缩的完整指南
2026/10/22 2:56:19 网站建设 项目流程

这几年训练模型,L1正则化算是工具箱里比较特殊的一个角色。它不参与网络结构设计,也不像学习率调整那样直接改变优化路线,但只要你需要把模型变轻、把特征选出来,基本绕不开它。L1正则化的做法非常直白:在原始损失函数后面加一项所有参数绝对值之和,让模型在训练过程中被迫把一部分权重推向0,最终得到一堆稀疏解。这个能力在模型压缩、可解释性分析和特征筛选这些场景里特别实用,也是很多老手在调模型时最后一步用来“收骨头”的手段。这篇内容我尽量用工程视角讲一讲L1正则化从原理到实操的完整链路,包括它为什么能稀疏、在代码里怎么落地、怎么调系数、以及跟L2正则化怎么配合,适合正在做深度学习训练、对正则化有基本概念但想深入应用的读者。

1. 先搞清楚L1正则化到底在优化什么

1.1 标准公式与每个符号的含义

大多数资料里给出的都是这样一个损失函数:

L_total = L_data(w) + λ Σ|w_i|

拆开看就三部分。L_data(w)是原始损失,比如分类任务里的交叉熵,回归任务里的均方误差,这部分决定模型预测好不好。后面的Σ|w_i|是L1正则项,它对所有权值取绝对值再加和,本质上是在统计“模型参数的总量级”。最后一个λ是正则化系数,控制惩罚力度。

这个公式放在训练过程里的意思是:模型每更新一步,不仅要让损失下降,还要让权重的绝对值之和尽量小。换句话说,原本模型只关心预测准不准,现在多了一个约束——“尽量少用参数”。

为什么这个约束有效?一个比较直觉的理解是:如果一个特征对最终预测的贡献不大,那么它在权重上表现出的绝对值就会偏大,L1的梯度会持续把它往0方向推。这个推动力是恒定的,不随权重变小而减弱,所以最终会直接把不重要的参数“清零”。

1.2 稀疏解为什么被看重

很多初学者会问:L1正则化到底图什么?毕竟在测试集上看,加了L1往往不比不加L1的精度高多少。它的价值通常体现在三个地方。

第一个是模型体积。深度学习模型动辄几百万参数,但真正起作用的往往是少数。训练完成后如果大部分权重都是0,就可以做剪枝,把零权重对应的连接删掉,推理时的存储和计算量都会大幅下降。尤其是在移动端或嵌入式设备上,这个收益非常实际。

第二个是可解释性。当一个模型只依赖少数几个特征做判断时,它的行为逻辑就容易追踪。比如在医疗辅助诊断里,医生会比较关心模型到底看了哪些指标,L1正则化天然会给出一个“只用少量特征”的模型,这在某些行业里比黑盒大模型更有说服力。

第三个是防止过拟合。参数自由度减少了,模型能记住的噪声就少了。典型的例子是文本分类里,如果词表很大,模型很容易记住一些只在训练集出现的罕见词组合,L1会让这些词对应的权重归零,泛化能力反而更好。

1.3 一个最简单的直观示例

用一个三特征的线性回归来体会一下这个过程。假设真实关系是 y = 2x1,而x2和x3是纯噪声。不加正则时,模型为了让训练集上的均方误差趋近于0,可能会给x2和x3配上不为0的系数,强行拟合那些噪声点。加了L1之后,模型会发现与其在x2和x3上付出权重代价,不如把这些权重设为0,反正对最终损失的减少量也不大。

等到训练结束,你去看权重矩阵,大概率看到w1保留在一个合理值附近,w2和w3非常接近0。这个结果就是稀疏解。

值得注意的是,L1正则化没有直接从名字上看出来“特征选择”功能,但这个功能是稀疏性的天然副产品。你在实际项目里如果看到某个特征的权重被置0,就可以把它理解为模型认为这个特征不重要。

2. 为什么偏偏是L1能产生稀疏:数学与几何视角

2.1 绝对值函数在0点不可导这件事

L1正则项是全网络里唯一自带“尖角”的操作。绝对值函数在x=0处不可导,这件事在数学上是个麻烦,但在稀疏化上恰恰起了关键作用。

先回忆一下L2的梯度:对w^2求导得到2w。当w从1缩小到0.1时,梯度从2变成0.2,越接近0,推力越小。所以L2只能把权重缩小到很小,但很难让它精确变成0。

L1不一样。对|w|求导,在w大于0时梯度是1,在w小于0时梯度是-1。这个梯度的特点是:无论权重已经多小了,推力始终是恒定的。这意味着当某个权重在0附近震荡时,L1会持续“補一脚”,把它向0推。一旦权重到达0,由于左右两侧梯度方向相反,这个点其实是一个很稳定的“停靠站”。

我用一个比喻来帮助理解。L1正则项像一个V形槽,球滚到槽底时两侧都有向上的斜坡,它很容易停住;L2正则项像一个抛物面,越靠近底部越平缓,球会一直往低处滑,但几乎无法彻底停下。

实际操作里,SGD训练出来的权重也很少是数学意义上严格的0,更多是落在1e-5、1e-6这个量级。不过这个量级跟0已经没有区别,后期剪枝时用阈值过滤一下就得到真正的稀疏网络了。

2.2 几何解释:菱形与椭圆的相遇

从几何上理解L1稀疏性,是很多教材里都会讲的一个角度。我尽量用文字把这个画面描述清楚。

假设模型只有两个权重w1和w2。原始损失的等高线是一圈一圈的椭圆,中心对应着无正则情况下的最优解。L1正则项对权重加了一个约束:|w1| + |w2| ≤ C,这个约束画在坐标系里是一个菱形,顶点正好落在坐标轴上。L2对应的约束是w1^2 + w2^2 ≤ C,画出来是圆。

带约束的最优解,就是“椭圆等高线”和“约束区域”相切的那个点。菱形的顶点在坐标轴上,等高线跟顶点相切的概率,远高于跟圆弧上的其他点相切。一旦切点落在轴上,就意味着另一个权重正好是0,这就是稀疏。

这个解释也能看出为什么L2很难稀疏。圆没有“尖角”,等高线与圆相切时,大部分情况都在非轴的位置,所以两个权重一般都不为0。

理解了几何意义之后,很多疑惑都会解开。比如为什么L1在特征高度相关时不稳定,因为相关特征的椭圆等高线会变得很扁,这时等高线和菱形的切点可能在两个顶点之间来回跳,选A还是选B有一定随机性。

2.3 约束优化与KKT条件

如果想把L1正则化背后的数学说得更严谨一些,可以把它看作一个带约束的优化问题。原始形式是:

min L_data(w) + λ|w|_1

等价于:

min L_data(w) 使得 |w|_1 ≤ C

这里的C跟λ是一一对应的,只是写法不同。KKT条件告诉我们,在有约束的最优解处,每个权重w_i要么落在可行域的内部,要么落在边界上。当某个权重落在边界且非0时,需要满足“梯度方向的平衡”;当权重为0时,允许梯度方向在一个“次梯度”区间内波动,这个宽松的条件让0点成为一个候选解。

说得直白一点:KKT条件给“权重等于0”开了一扇门,而L2的KKT条件没有这么宽容。这也是L1能在优化过程中稳定找到稀疏解的数学保障。

我在项目里做稀疏化时,并不会真的去推导KKT条件,但理解这一点对判断模型状态很有帮助,比如当某个权重在0附近来回抖动时,你知道这可能是优化器在次梯度的“模糊地带”徘徊,而不是训练发散。

3. 实践落地:在PyTorch里实现L1正则化

3.1 一种最直接的实现方式

写代码前先想清楚一件事:什么时候把L1加进损失里。最常见的方式是在计算loss之后,手动加一个“L1惩罚项”,把整个值作为最终loss进行反向传播。

下面这段代码是我在实际项目里经常用的模板:

import torch def l1_penalty(model, lambda_l1=1e-5): reg = 0.0 for name, param in model.named_parameters(): if param.requires_grad: reg += torch.sum(torch.abs(param)) return lambda_l1 * reg # 训练循环里 optimizer.zero_grad() output = model(batch) loss = criterion(output, target) + l1_penalty(model, lambda_l1=1e-5) loss.backward() optimizer.step()

这段代码有几个细节值得注意。

第一个细节是named_parameters()遍历的是所有可训练参数,包括卷积层的权重、全连接层的权重、BatchNorm里的gamma和beta。如果你想做得精细一点,可以对不同层用不同系数,甚至排除某些层。比如通常不对bias施加正则惩罚,因为bias是平移量,不存在“特征是否重要”的问题。

第二个细节是lambda_l1这个系数要跟loss的尺度匹配。如果你的原始损失是0.1量级,lambda取1e-2可能就把正则项放大了,导致网络只做“减重”而不做“学习”。反过来如果原始损失本身就大,比如几百,lambda太小则看不到稀疏效果。

第三个细节是,这种直接相加的方式在梯度上等价于在每个参数的梯度上额外加上一个常数符号项。这也是SGD下L1能产生真正稀疏解的原因。如果换用Adam,因为Adam会自适应调整每个参数的学习率,L1的效果会被明显削弱,这一点后面细说。

3.2 三种实现路径的对比

除了在loss后面手动加正则项,还有两种常见的做法。一种是“近端梯度法”,另一种是用优化器自带的权重衰减参数。

近端梯度法(Proximal Gradient)的做法是:正常用梯度更新权重,更新完之后再执行一个“软阈值操作”:

def soft_threshold(param, threshold): zero_mask = param.abs() < threshold param[zero_mask] = 0 param[param > threshold] -= threshold param[param < -threshold] += threshold

这个方法的优势是得到的权重会精确等于0,而不是无限接近0。缺点是它打破了标准优化器的参数更新逻辑,跟动量和Adam的配合要多测试。

优化器的weight_decay参数则需要注意:PyTorch里weight_decay默认对应的是L2正则,不是L1。很多初学者在这里踩过坑,把weight_decay当成了L1,结果发现模型确实变轻了但稀疏率几乎为0。我建议把weight_decay和L1分开理解:weight_decay是平滑地把参数縮小,L1是硬性地把参数清零。

三种方式放在一起对比:

实现方式核心思路优点缺点适用场景
loss叠加L1项在损失函数里加参数绝对值求和实现简单,通用性强权值得到的多为近似0绝大多数训练任务
近端梯度/软阈值更新后对参数做阈值截断能得到精确0权重与优化器耦合,调参成本高剪枝前需要干净稀疏解
weight_decay参数优化器内置参数缩放一行代码实际是L2,无稀疏效果稳定训练防过拟合

我的建议是:先从第一种方式开始,因为最简单,效果也够用。等你的项目真正到了剪枝阶段,再考虑近端梯度法。

3.3 正则化系数λ的选择技巧

λ是L1正则化里最重要的超参数,选大了模型会“空掉”,选小了等于没加。

常见的做法是对数网格搜索。如果你不知道起点,先从1e-4、3e-4、1e-3、3e-3、1e-2这个序列里选几个值跑一下,观察训练集上的loss和验证集上的表现。L1正则项在训练日志里最好单独打印出来,方便判断正则项是否在合理范围内。

实际操作中我还发现一个好用的策略:分段加入L1。比如前20个epoch正常训练,让模型先把主要特征学好,第20个epoch之后再把L1加进去,并把λ设置成一个“逐步上升”的值。这样做的好处是,模型早期不会因为正则项太强而学不到东西,后期又能把权重中的冗余部分挤出去。

另一个经验是按层分组设置λ。卷积层的参数数量大但单个参数对结果的影响相对分散,全连接层参数相对集中,实际项目中我会把全连接层的λ设成卷积层的2到5倍。这个做法不是为了理论正确,而是我实测下来稀疏效果比统一λ更可控。

训练过程中要监控的指标不是loss本身,而是“稀疏度”,即权重绝对值小于某个阈值的参数占比。比如阈值为1e-6,统计一下全网络里被“清零”的比例。这个指标比loss更直观地反映L1到底在起作用。

4. L1与L2的异同,以及弹性网络

4.1 L2正则化为什么不能稀疏

顺着前面讲的梯度逻辑再说一下L2。L2正则项在损失函数里表现为Σw_i^2,梯度是2w。权重越小,梯度越小,最终会逼近0但永远不等于0。你可以把它理解成“把蛋糕慢慢抹平”,但永远抹不到没有。

这个特性让L2在绝大多数深度学习训练里成为默认的防过拟合工具,尤其是加上weight_decay之后,训练曲线会比不带正则稳定很多。但如果你做的是特征筛选或模型压缩,L2帮不上忙,它只是把所有权重均匀缩小,并不区分“重要参数”和“不重要参数”。

我遇到过一个实际案例:一个文本分类模型,原始权重矩阵大概有500万个非零参数,加了L2训练之后,参数还是500万个非零,只是标准差小了一点。后来换成L1,训练完统计下来只剩20万个非零权重,这时才真正做到了“瘦身”。

4.2 一张表看懂L1和L2

对比维度L1正则化L2正则化
惩罚形式权值绝对值之和权值平方之和
梯度形式常数符号,在0处不可导线性比例,平滑
稀疏能力强,可产生大量0权重弱,几乎不产生0权重
特征选择天然支持需要额外后处理
对离群点容忍度较低,可能将个别异常特征清零较高,权重分布更平滑
优化行为常量梯度,0点稳定梯度随权重衰减

这个对比在实际工作中最直接的指导是:如果你要的是“模型更加谨慎地使用参数”,选L2;如果你要的是“删掉一部分参数”,选L1。

4.3 弹性网:L1+L2的互补

单纯用L1有两个问题。一个是在特征高度相关时,L1会随机地选其中一个,而不关心哪个真的更重要,导致结果不太稳定;另一个是当特征数大于样本数时,L1最多只能选出与样本数相同数量的非零系数,这在基因表达这类数据上是硬伤。

弹性网络(Elastic Net)就是干这个用的。它的形式是:

L_total = L_data(w) + λ1 Σ|w_i| + λ2 Σw_i^2

L2部分的加入带来两个好处。一是让高度相关的特征倾向于同时被选入或被剔除,而不是“二选一”,这被称为组效应;二是当特征数远大于样本数时,模型仍然能稳定求解。

我在做高维稀疏特征筛选时,通常会先跑一次弹性网,看L1部分把哪些特征清零,再用L2部分来控制整体权重的发散程度。如果遇到L1结果在多次实验里选出的特征集合差异很大,加上L2之后这种现象会明显减少。

4.4 特征标准化这个前置操作

L1正则化有一个容易被忽略的敏感点:特征尺度。如果某个特征的取值范围是0到1000,另一个特征的范围是0到1,L1正则项的惩罚对前者的权重更苛刻,因为只要它的权重大一点,绝对值就上去了,模型会倾向于清零大尺度特征,哪怕它实际很重要。

所以在使用L1之前,把特征做标准化几乎是必须的。对于线性模型来说,至少要减去均值除以标准差,让特征处于同一个量纲。对于深度模型里的图像输入,虽然不是严格的“特征尺度”问题,但输入标准化依然能显著影响L1是否公平地作用在各个卷积通道上。

这个操作看起来小,但它决定了一个模型是在“做特征选择”还是在“看量纲下菜”。我早期在某个项目里没做标准化,L1选出来的特征全是大数值特征,做完标准化之后,才真正选出了业务上认可的指标。

5. 那些年踩过的坑:常见问题与排查思路

5.1 稀疏率上不去怎么办

如果训练快结束了,统计稀疏率发现连1%都不到,先不要急着加λ。根据我的经验,最可能的原因是优化器选错了。

用Adam或者AdamW配合L1,稀疏效果通常不理想。因为Adam的每个参数学习率是自适应缩放的,它对L1这种“恒定梯度”不敏感,相当于把L1的推力按参数的历史梯度做了归一化,推力被削弱。SGD和SGD+Momentum对L1更友好。如果你不想换优化器,那就只能把λ调大几倍看效果。

另外一个常见原因是,你当前模型用的是BatchNorm,而且L1加在了所有参数上。BatchNorm会把这种稀疏趋势抵消掉不少,这部分单独在下一节说。

如果λ确实已经调大还是没有稀疏度,那就要检查你对“稀疏”的判定标准是不是太严了。很多权重会落在1e-4、1e-3量级,而不是严格等于0。你可以把阈值放宽到1e-4再统计一次,再结合后续剪枝时的实际操作来决定阈值。

5.2 与BatchNorm共存的困惑

BatchNorm是深度学习里一个非常有效的组件,但它和L1正则化之间天然存在张力。原因是BatchNorm在归一化之后会乘以一个可学习的缩放参数γ,即使某个权重被L1压到很小,网络仍然可以通过γ来放大这个通道的输出,从而绕开L1的稀疏效果。

很多人在模型的卷积层后面接BatchNorm,然后对卷积层参数加L1,结果稀疏率很低。这并不一定是实现错了,而是结构上就不太配合。

一种对策是把L1加到BatchNorm的γ参数上,让每个通道的缩放参数变稀疏,这样在通道级别上也能达到类似“剪枝”的效果。另一种对策是在使用L1时,可以先尝试去掉中间层的BatchNorm,看看稀疏效果有没有明显提升。这个取舍没有标准答案,取决于稀疏化的收益是否大于BatchNorm带来的训练稳定性收益。

5.3 学习率与优化器的隐性影响

L1正则化在SGD下的行为,很大程度上受学习率影响。学习率大了,L1的常数梯度会在权重为0附近造成明显振荡,可能把已经被“清零”的权重又推了出去;学习率小了,L1的稀疏化进度会变得非常慢,可能训练了好几个epoch都看不到明显变化。

我的习惯是配合分段学习率来使用L1。前期学习率较高时,先正常训练让模型学习主要模式;中期切换学习率之后,再逐渐加大L1的权重。这个节奏很像先拼图再收边角,模型会更容易接受稀疏化的过程。

如果你用的是Adam,trick是适当调高λ,但也要警惕训练后期loss出现抖动。L1在Adam下的抖动是一个经典现象,我一般看到抖动就停止增加λ,甚至略微调低,免得稀疏率“虚高”之后在验证集上又回落。

5.4 相关特征“轮流入选”的问题

这个坑比较隐蔽。当两个特征高度相关时,L1对它们的惩罚并不会同时发生,而是倾向于选择一个、忽略另一个。这本身不是问题,问题在于这个选择可能不稳定:一轮训练选的是特征A,重训一遍可能就变成特征B,让你怀疑结果是否可靠。

解决办法是引入弹性网。加一点L2,能缓和这种振荡,让相关特征的权重保持在一起。我在做业务特征筛选时,一般不会用纯L1,至少会加一个非常小的L2项,哪怕λ2只有λ1的十分之一,稳定性也会明显改善。

还可以通过多轮训练的“特征出现频率”来判断哪些特征真正重要。如果一个特征在多次训练里都能被保留下来,它才是稳定信号;如果只是在某一次实验里被选中,大概率是噪声。

实操中我认为值得分享的一个小习惯

L1正则化最好的使用场景不在训练初期,而是作为模型训练后期和剪枝之前的一道工序。我先正常训练一个模型,达到满意的验证精度后,加载最优权重,再用较小的学习率和适当的L1系数继续训练十几个epoch,看一下哪些权重被逐渐清除。这个时候观察到的稀疏模式,比从随机初始化就开始加L1要稳定得多,也不容易影响模型的初始学习能力。

我也习惯把每次实验的稀疏度变化记录成训练日志的一部分。稀疏度曲线的形状能透露很多信息:如果它在某个epoch突然跳升,说明L1正在集中清除一批冗余参数;如果一直平坦,说明λ设置过小或者优化器不配合。这个曲线比单独看loss曲线更能帮助你判断正则项的实际作用。

另外一个值得留意的方向,是L1正则化和剪枝、蒸馏这类后处理技术的配合。L1只是给你一个稀疏的骨架,后续怎么把它变成真正加速的模型,还需要结合结构化剪枝或者通道剪枝。如果你正在做模型部署,我建议把L1当作第一道关卡,先筛掉明显不重要的参数,再用结构化剪枝把对应的通道、连接彻底移除。这样既能获得精简模型,又不会在布署阶段发现网络结构里全是零散的稀疏点而难以加速。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询