资讯动态

L1与L2正则化:从原理到实战,彻底解决过拟合

发布时间:2026/9/7 5:54:20 来源:尧图企业网站定制
1. 正则化到底在解决什么问题先说个我在实际训练中经常看到的场景模型结构没问题数据预处理也做了训练集上的loss一路降得很漂亮可一放到验证集上指标就像坐过山车一样波动甚至越来越差。很多新手第一反应是模型还不够复杂继续加层、加神经元结果越调越糟。这种情况十有八九是过拟合。过拟合的本质是模型把训练数据里的噪声也当作规律学进去了。打个比方你让一个学生做十道题他如果连每道题的标点符号都背下来考试遇到新题反而不会做。神经网络参数动辄几十万、几百万容量非常大如果不加约束它完全有能力“背下”所有训练样本。L1和L2正则化就是给模型加约束的两种最基础、最常用的手段。为什么偏偏是L1和L2因为它们给损失函数添加的惩罚项非常简单、可微L1在0点需要特殊处理后面细说而且数学性质清晰容易优化。简单说L2让权重趋向于变小但不归零L1让一部分权重直接变成零。这两种行为对应着两种完全不同的应用取向理解到这一层你才算真正会用它们。我见过不少资料把L1和L2并列介绍只讲公式不提背景导致很多人觉得它们只是“惩罚形式不同”。其实它们背后的逻辑、适用场景、调参策略差别很大。这篇文章我就从原理到实操把这两个正则化的来龙去脉讲透包括不常被提到但非常关键的坑。2. L2正则化的原理与实现2.1 L2的数学本质把权重往零的方向拖L2正则化在损失函数里加的是权重平方和形式一般是L L₀ λ/2 · Σ wᵢ²这里的L₀是原始损失比如交叉熵或均方误差wᵢ是网络里的权重λ是正则化系数。加1/2是为了求导时消掉系数2纯粹是数学上的偷懒不影响本质。看梯度就明白了对w求导后梯度里多出一项λw。用梯度下降更新时权重会变成w ← w - η(∂L₀/∂w λw) (1 - ηλ)w - η·∂L₀/∂w看到没每次更新前权重先乘一个小于1的系数(1-ηλ)这就是“权重衰减”weight decay这个名字的来历。它的意思是无论损失函数怎么要求权重每走一步都会被“瘦身”一点。所以L2正则化的效果是让所有权重偏向更小的值但极少精确等于0除非浮点误差碰上极端情况。直观理解模型的输出对某个特征的依赖被削弱了因为对应的权重被压小了。整体上模型变得更“平滑”不会因为某个输入特征的小小变化就被剧烈扰动。这就是为什么L2能减轻过拟合——它抑制了模型对单个特征的过度敏感。2.2 为什么L2能换来泛化能力泛化能力的提升可以借用偏差-方差分解来看。过拟合对应高方差模型在不同训练集上表现波动大。L2把权重限制在零附近的小范围内相当于限制了一族模型的复杂度——你能选择的模型变少了方差自然降下来。代价是可能略微增加偏差模型可能欠拟合但通常换来的方差降低更多总误差是下降的。从贝叶斯角度看L2等价于给权重加上一个均值为0的高斯先验。这个解释不常被提起但很有用它说明L2本身就假设“权重大概率是小值”和那些极端的、绝对值很大的权重天然不兼容。我在实际项目中观察到的现象是加了L2之后训练loss可能会比不加高一点点但验证集上的表现通常会提升。如果训练loss一点不涨那多半是λ设得太小了正则化根本没起作用。2.3 用PyTorch和TensorFlow实现L2在PyTorch里最简单的做法是利用优化器的weight_decay参数import torch import torch.nn as nn model nn.Sequential( nn.Linear(128, 64), nn.ReLU(), nn.Linear(64, 1) ) optimizer torch.optim.SGD(model.parameters(), lr0.01, weight_decay1e-4)这里有个必须注意的坑PyTorch的weight_decay实现是直接用λw加到梯度上而此前结的公式是λ/2·Σw²梯度为λw但有些框架或实现会把正则项写成λ·Σw²梯度就变成了2λw。这意味着同一个λ在不同框架里的实际惩罚力度可能相差一倍。我习惯统一用“权重衰减系数”来对比而不是死记λ的数值。TensorFlow/Keras这边更直观直接在层里加regularizerfrom tensorflow.keras import layers, regularizers model tf.keras.Sequential([ layers.Dense(64, activationrelu, kernel_regularizerregularizers.l2(0.001)), layers.Dense(1) ])Keras的l2(0.001)对应L L₀ 0.001·Σw²没有1/2。写代码前最好确认一下你要用的框架实现的是哪种形式。2.4 L2的适用场景L2是默认的正则化手段适用场景非常广网络结构复杂但数据量一般、没有明确的特征筛选需求、不知道该怎么入手调正则化的时候先加L2总不会错。它对所有权重一视同仁地微缩相当于“无差别减肥”不会破坏网络的整体结构。另一个实用点是L2训练出来的模型仍然保持稠密所有权重非零在GPU上计算效率不会受到影响。这一点和下面要讲的L1形成鲜明对比。3. L1正则化的原理与实现3.1 L1为什么能产生稀疏权重L1正则化加的是权重绝对值之和L L₀ λ · Σ|wᵢ|梯度变成了±λw0时梯度λw0时梯度-λ一个非常关键的性质是不管当前权重是多少惩罚项对梯度的贡献都是一个常数λ方向永远指向零。这意味着权重每次更新都固定向0靠近λ·η这一步不会像L2那样越靠近0力越小。许多权重被这个恒定推力一路推到0而一旦真正变成0由于在0点L1的次梯度是一个包含0的区间优化器可以稳定地把它“粘”在0处。这就是L1产生稀疏解的根本原因——等值的压缩力度加上0点处的特殊性质。几何上看更直观把带约束的优化问题看作在L1的“菱形”约束域里找使原损失最小的点。菱形有尖角这些角落在坐标轴上极值点很容易出现在轴上而轴上的点意味着其他坐标为0。L2是“圆形”约束域没有尖角极值点几乎不会刚好落在坐标轴上。3.2 L1稀疏性的实际价值L1的另一面是它天然适合用来做特征选择。假设输入有1000个特征真实起作用的只有50个用L1训练完后模型会把这50个特征对应的权重保留非零其余950个直接归零。结果就是一个自带特征选择能力的模型。在我实际做过的日志异常检测项目里特征维度有几百个很多是高度相关的冗余特征。一开始用L2模型准确率还行但部署到线上后特征采集链路偶尔会出问题缺特征时模型表现明显波动。后来改用L1关键特征被保留下来模型对缺特征的鲁棒性好了不少而且推理时还可以配合稀疏矩阵运算减少计算量。但注意L1带来的稀疏性在纯Dense网络里如果只是用来训练不会自动加快计算——只有当你把权重矩阵转成稀疏存储格式或者直接剪枝去掉零权重之后才能真正省内存、省计算。如果只是加了L1而不做后续处理计算效率不会变快这一点经常被误解。3.3 L1的实现与不可导点处理PyTorch里没有直接提供“L1正则化”的开关常见的做法是手动把L1惩罚加到loss上l1_lambda 1e-5 def l1_penalty(model): return sum(p.abs().sum() for p in model.parameters()) loss criterion(y_pred, y_true) l1_lambda * l1_penalty(model)这样在反向传播时PyTorch的autograd会自动计算|w|的“导数”——对于w0处PyTorch会返回0。这种做法严格说用的是次梯度实际训练中通常也能work因为神经网络里的权重很少会精确停留在不可导点附近。如果追求更严格的优化可以考虑近端梯度法Proximal Gradient。核心思想是先按正常梯度更新一步不含L1再通过一个“软阈值”操作把小的权重收缩到0def soft_threshold(w, threshold): return torch.sign(w) * torch.clamp(w.abs() - threshold, min0) # 训练循环内每次优化器step之后执行 with torch.no_grad(): for p in model.parameters(): p.data.copy_(soft_threshold(p.data, lr * l1_lambda))近端梯度在数学上有更严谨的收敛保证在需要充分释放L1稀疏性的时候我推荐优先试试这个方案而不是直接加l1_loss到loss里。Keras实现L1更简单和内建的l2并列layers.Dense(64, activationrelu, kernel_regularizerregularizers.l1(0.001))3.4 L1的适用场景L1适合用在特征筛选明确知道很多特征是噪声希望模型挑出关键特征模型压缩/剪枝训练后权重稀疏可以转成稀疏存储可解释性要求高的场景零权重意味着特征完全不参与决策分析起来更简单代价是如果特征之间有强的相关性L1只会随机选一个进去这可能导致选出来的特征不稳定。这时候可以上弹性网Elastic Net同时加L1和L2既能稀疏化又能稳定地处理相关特征。4. L1和L2的选择、组合与调参实战4.1 两种正则化行为对比表为了方便查阅我把它们的核心差异整理成一张表维度L2正则化L1正则化惩罚形式Σwᵢ²Σ|wᵢ|梯度贡献λw随权重变小而变小λ恒定不变权重行为整体趋向小值但非零一部分完全归零稀疏性无有等价先验高斯分布拉普拉斯分布特征选择不做特征漏除天然做特征筛选计算效率训练后仍为稠密网络GPU友好需配合稀疏存储/剪枝才能提速优化难度平滑标准梯度下降即可0点不可导需次梯度或近端梯度4.2 正则化系数λ怎么定很多新手第一反应是λ越大越好其实完全不是。λ太小正则化形同虚设λ太大模型被压成一个接近零的“常数模型”训练loss都下不去。我的调参经验大致分三步数量级起步L2从1e-4或1e-3开始试L1因为梯度恒定对λ更敏感通常从更小的1e-5或1e-4开始。对数网格搜索以10倍为步长比如1e-5、1e-4、1e-3、1e-2分别训练一轮小周期比如10个epoch以内观察验证集指标锁定最优区间后再在该区间内细调。结合早停法一起用就算λ选得合理模型也可能在训练后期过拟合。加early stopping观察验证集loss开始回升就停是性价比最高的“免费正则化”。我踩过的一个典型坑是在Adam里直接设很大的weight_decay。Adam自带自适应学习率会动态缩放每个参数的梯度导致weight_decay的实际效果被稀释得很厉害。用SGD时1e-4能用换到Adam可能得调成1e-2甚至更大才有效果。这也是为什么Loshchilov等人提出AdamW——把权重衰减从自适应梯度的计算中解耦出来让L2在Adam下也能稳定生效。现在用AdamW基本已经是主流配置。4.3 弹性网L1和L2一起上现实情况里单纯L1可能选特征选得太“霸道”单纯L2又不能移除无用特征。于是就有了弹性网L L₀ λ₁·Σ|wᵢ| λ₂·Σwᵢ²它兼顾了L1的稀疏性和L2对相关特征的稳定性。在Keras里直接layers.Dense(64, activationrelu, kernel_regularizerregularizers.l1_l2(l10.001, l20.001))PyTorch里就分别计算后手动加或者用第三方库如torchweightdecay。弹性网推荐在以下情况使用特征维度高、组内相关性强、希望得到稀疏解但又不想结果太随机。我自己做宽表特征工程时经常用它效果比单独用L1稳定不少。4.4 正则化的顺序dropout、BN与L1/L2怎么排实际工程里很少有只靠L1或L2打天下的情况一般会跟dropout、BatchNorm组合着用。我的使用习惯先加一个dropout通常加在全连接层前面rate从0.3试起再考虑L2因为它的副作用最小适合跟其他正则化搭配L1只在需要特征稀疏或模型压缩时用如果只是防过拟合L1对比L2并没有明显优势反而多一个λ要调。BatchNorm加不加正则化加但要注意BN本身能稳定训练、也有轻微正则化效果因为batch统计量引入了噪声加了BN之后模型对权重的尺度不那么敏感L2的实际影响会减弱。这时候可以适当增大一点weight_decay但别加太多否则模型可能欠拟合。这个平衡需要具体任务具体试我给不出一个“万能值”。5. 常见问题与避坑指南5.1 正则化之后训练loss不降了是加错了吗不是加错了而是λ可能太大。训练loss变高是正常现象因为损失函数里多了一项惩罚模型不再单纯追求拟合训练数据。关键看验证集如果验证集指标也在变差就把λ调小如果验证集还行那说明正则化在起作用训练loss高一点无所谓。但也得警惕另一种情况λ太大模型把权重压得几乎全是0输出基本等于bias这时候训练loss和验证loss都很差。解决方法是按对数网格将λ调小同时观察权重的分布。我习惯打印一下权重绝对值的中位数和最大值如果中位数不到1e-3很可能是正则化过头了。5.2 要不要对偏置bias做正则化不要。这是一个很常见但容易被忽略的细节。bias的作用是平移激活函数的位置它不会引起模型方差的大幅变化对拟合“噪声”的贡献也很小。对bias做正则化只会增加欠拟合风险没什么收益。在PyTorch里如果你用optimizer的weight_decay参数它默认会对所有参数做惩罚包括bias。想排除bias需要手动分组param_groups [ {params: [p for n, p in model.named_parameters() if bias not in n], weight_decay: 1e-4}, {params: [p for n, p in model.named_parameters() if bias in n], weight_decay: 0.0} ] optimizer torch.optim.SGD(param_groups, lr0.01)TensorFlow/Keras的l2正则化默认只作用于kernel不作用于bias除非你显式指定bias_regularizer这点设计得反而更省心。5.3 L1把所有权重都变成了0模型废了这种情况多半是λ起始值设置太大尤其是L1对λ更敏感。还有一个可能feature的尺度相差太大。L1对每个特征的权重惩罚力度相同但大尺度特征可以容忍更大的权重小尺度特征的权重很容易被压到0。所以用L1前一定先做特征标准化StandardScaler把小尺度特征拉回同一量级。我遇到过一位同事加了L1后发现模型输出变成一个常数查了半天发现是特征里有几个量纲特别大的列没处理。标准化一加L1立刻表现正常。别小看这个预处理步骤它比调λ优先级还高。5.4 用L1做了特征选择为什么模型精度反而下降了L1选特征的逻辑是在带宽正则化的约束下尽量拟合数据。但如果λ选择不当L1可能把一些“弱但有用”的特征也一锅端了。特征少了不代表精度一定掉却有可能会掉尤其是那些本来靠多个弱特征协同决策的任务。这时候有两个方案一是把λ调小允许更多特征进入模型二是改用弹性网给L1一点L2的“缓冲”让特征选择不要那么激进。弹性网在很多比赛实践中表现都优于纯L1就是因为它避开了这个坑。5.5 早停法可以替代正则化吗能替代一部分但不是完全替代。早停法通过在验证集loss开始变差时停止训练限制了模型对训练数据的“过度适应”从效果上看也是一种正则化。但它的可控性差——模型的权重大小、稀疏结构都不是显式控制的。L2/L1则能在整个训练过程中持续约束模型两者的机制不同最好的做法是组合使用正则化控制模型容量早停控制训练时间。我在生产环境里的常见组合是L2或AdamW early stopping 轻微的dropout已经能覆盖大部分表格数据和图像分类任务。只有在特定需求特征选择、模型压缩、高维稀疏输入出现时才专门引入L1或弹性网。5.6 权重衰减参数在不同框架的换算很多人在PyTorch和TensorFlow之间来回切的时候直接套用同一个λ数值结果效果对不上。前面提到过PyTorch weight_decay对应的是L L₀ λ/2·Σw²即梯度项是λwKeras的regularizers.l2里用的则是L L₀ λ·Σw²梯度项是2λw。同一个“0.001”PyTorch按1e-3衰减Keras实际按2e-3等效衰减。切框架时记得把λ换算一下或者直接跑个小实验对照loss对比我个人更推荐后者稳妥省事。关于L1和L2的选择我的最终建议很朴素如果只是想让模型更稳默认L2如果有明确的稀疏化诉求用L1如果想兼容两者且特征相关性强直接弹性网。正则化系数不是网络设计的一部分但它的重要性不亚于网络深度和宽度。我自己每次搭新模型都会先把baseline跑一遍不加正则化再加L2跑一遍记录对比表格然后再决定要不要动用L1。有了这份baseline调参时心里才有底不然很容易被各种因素搞得晕头转向。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价