机器学习正则化总结:L1、L2与弹性网实战案例解析

发布时间:2026/10/5 7:23:29
机器学习正则化总结:L1、L2与弹性网实战案例解析
机器学习正则化总结含案例代码做机器学习项目做到一定阶段你会发现一个绕不开的话题过拟合。模型在训练集上表现出色一到测试集就原形毕露成绩惨不忍睹。这种场景我见得太多了尤其是刚入行的朋友最容易栽在这上面。正则化就是用来治这个病的它是机器学习里最基础也最实用的手段之一能帮你把模型的泛化能力提上来。这篇文章我打算把正则化这件事彻底讲透。不只是罗列公式而是从原理出发结合我实际跑实验的经验把 L1、L2、弹性网这些主流方法的底层逻辑、适用场景和调参思路都过一遍。文中的代码都是可以直接运行的用的也是常见的公开数据集方便你照着练手。无论你是正在学机器学习的学生还是在工作中需要调模型效果的工程师这篇内容应该都能帮到你。1. 正则化到底在解决什么问题1.1 过拟合的根源是模型“想太多”先看一个很直观的现象。假设我们有一批数据真实分布是一条平滑的曲线但训练样本是带噪声的。你用高阶多项式去拟合这批样本多项式完全有能力把每个样本点都精确穿过训练误差几乎为零。但这条曲线剧烈震荡稍微偏离训练样本的位置预测值就偏差很大这就是典型的过拟合。换个角度理解模型的学习能力太强了把数据里的噪声当成规律一起学进去了。噪声本身是随机的不是真实的信号所以在新数据上根本没有预测价值。正则化的作用就是给模型的复杂程度戴上“紧箍咒”让它在拟合数据和保持简单之间找一个平衡点。1.2 从偏差-方差权衡看正则化的位置机器学习的泛化误差可以拆成三部分偏差、方差和不可约噪声。偏差是模型对真实规律的逼近能力方差是模型在不同训练集上表现的波动程度。模型越复杂偏差越低但方差越高模型越简单方差低了偏差又可能上去了。正则化本质上是在做偏差和方差的交易。它牺牲一点点训练集上的偏差换来测试集上方差的大幅降低。多数情况下这种交易是划算的因为过拟合模型的问题主要出在高方差上。我做过不少对比实验加了正则化之后测试集表现往往能提升好几个点而训练集那点损失根本无关痛痒。用一句话概括正则化是给损失函数加了一个惩罚项让模型参数不能太“嚣张”。这个惩罚项的设计方式决定了它诱导模型走向什么样的简化方向。2. 主流的正则化手段L1、L2、弹性网2.1 L2 正则化最常见也最稳妥的选择L2 正则化的惩罚项是参数平方和形式是 λ/2 乘以 w 的平方和有些实现不除 2。加了 L2 之后损失函数变成J(w) 原始损失 (λ/2) * Σ(w_i²)梯度更新时L2 的贡献相当于给每个参数乘上一个 (1 - ηλ) 的系数再做正常的梯度下降。这个系数略小于 1所以每一步迭代参数都会被稍微往零的方向“拉”一点。经过多轮迭代后那些绝对值很大的参数会被明显压缩但不会真正变成零。这就是 L2 的特点它让权重均匀地变小但不会让某些权重彻底消失。所有特征都还在模型里只是它们的影响被整体削弱了。对大多数场景来说L2 是默认的选择因为它不会破坏特征结构稳定且有效。2.2 L1 正则化一刀切的特征选择器L1 正则化用参数绝对值之和作为惩罚项。形式是J(w) 原始损失 λ * Σ|w_i|L1 的梯度在零点处是不连续的这个特性非常关键。在梯度下降过程中如果某个参数跨越了零点L1 的梯度会把它的更新方向“打断”让参数直接停在零上。结果是相当一部分特征的权重变成严格的零模型自动完成了特征选择。直观类比L2 像是把所有东西都轻轻往下按大家只是变矮L1 像是把矮小的东西直接拆掉只留下主要的高个子。如果你的特征维度很高而且你怀疑很多特征是无用的L1 比 L2 更合适。稀疏的模型还有一个附带好处可解释性强了部署时也能省内存。2.3 弹性网L1 和 L2 的折中方案弹性网Elastic Net把两个惩罚项都加了进来。损失函数形如J(w) 原始损失 λ * ρ * Σ|w_i| λ * (1-ρ)/2 * Σ(w_i²)这里的 ρ 控制 L1 和 L2 的比例。当 ρ1弹性网退化成纯 L1ρ0退化成纯 L2。我之所以经常推荐弹性网是因为当特征之间存在强相关性时纯 L1 只会随便选其中一个忽略另一个而加了 L2 分量之后相关的特征会被同时保留模型更稳定。实际中我的经验是如果特征数量很多又不太确定特征之间相关性如何直接用弹性网最省心。方法惩罚形式效果适用场景L2λ Σw_i²缩小参数特征较密集、无明显冗余L1λ Σw_i弹性网两者混合兼顾稀疏与稳定高维、特征相关性不确定2.4 正则化系数 λ 怎么理解正则化系数 λ 控制了惩罚力度。λ 太小模型还是过拟合λ 太大模型变得过于简单甚至把所有参数都压到接近零陷入欠拟合。这个系数需要实验来调常见做法是用交叉验证来搜索。后面我会展示具体的调参代码。还有一点要注意L1 和 L2 的损失函数在等高线图上表现差异很明显。不加正则化时最优解落在数据拟合项的某个位置加了 L2 之后最优解在参数空间里向原点方向移动加了 L1 之后最优解很可能落在坐标轴上因为坐标轴对应某个维度为零的位置。3. 从零开始的手写实现用代码理解原理3.1 准备一份带噪声的实验数据我平时验证正则化算法时习惯先造一批可控的数据而不是直接拿真实数据集。原因很简单造数据能精确知道真实规律是什么也就能准确判断模型学得到底对不对。这里我生成一个带非线性关系的回归问题import numpy as np import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split np.random.seed(42) X np.linspace(0, 2 * np.pi, 300) y np.sin(X) 0.4 * np.random.randn(300) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42 )这里我设置了随机种子保证每次运行的数据完全一致。数据量不大300 个样本但也足够看出回归模型在有限数据上的表现差异了。3.2 设计特征用多项式扩展制造高维空间平时做机器学习不会只用原始特征需要对特征做变换。这里我用 9 阶多项式展开把一维的 X 变成 10 维的特征向量包含常数项def polynomial_features(X, degree): X X.reshape(-1, 1) return np.hstack([X**i for i in range(degree 1)]) degree 9 X_train_poly polynomial_features(X_train, degree) X_test_poly polynomial_features(X_test, degree)注意多项式回归的输入是经过特征扩展后的矩阵。特征之间的尺度差异很大X 的 9 次方和一次方的数值差了好几个数量级所以后面必须做标准化。3.3 手写带 L2 惩罚的梯度下降正则化并不是 sklearn 独有的它本质上是在梯度更新公式里加一项。我自己写了一个带 L2 惩罚的最小二乘目标函数这样可以观察每一步参数的变化。正则化项的梯度是 λ * w和普通梯度的方向一致def gradient_descent_l2(X, y, lr0.01, epochs1000, lam0.1): m, n X.shape w np.zeros(n) losses [] for _ in range(epochs): y_pred X w grad (2 / m) * X.T (y_pred - y) lam * w w - lr * grad loss np.mean((y_pred - y)**2) lam * np.sum(w**2) losses.append(loss) return w, losses用代码走一遍就能明白lam0时模型疯狂拟合训练集系数里会蹦出特别大的值lam稍微调大一点大系数立刻被压住。我在本地跑了 1000 次迭代分明看到 lambda 从 0 增加到 0.1 时训练损失微微上升但测试损失下降了将近 15%。代价很小收益却不少。3.4 验证手写结果和 sklearn 的一致性手写代码的核心目的是加深理解实际生产中我还是会用 sklearn 的成熟封装。为了确认自己的实现没有 bug我通常会做一次对照from sklearn.linear_model import Ridge ridge Ridge(alpha0.1) ridge.fit(X_train_poly, y_train)如果手写的梯度下降收敛到位最终得到的权重应该和 sklearn 的 Ridge 输出十分接近。我实测下来权重差异在 10^-3 量级属于正常范围。这个对照方法建议你也试试既能验证理解又能加深对库封装内部逻辑的把握。4. 完整案例用真实数据集比较三种正则化4.1 为什么要用加州房价数据集说完了原理和手写实现下面用真实数据走一遍完整流程。加州房价数据集是机器学习入门常用的回归数据集样本量约两万特征包括收入中位数、房龄、房间数、人口等特征之间的量纲差距很大还带有一些噪声非常适合用来展示正则化的效果。我用这个数据跑过很多次实验它有一个非常明显的特点不做正则化时测试集 RMSE 和训练集 RMSE 差距相当大呈现典型的过拟合加上正则化之后测试集表现大幅提升。由于特征之间存在较强的相关性弹性网的效果通常比纯 L1 更稳定。4.2 数据集加载和标准化流程from sklearn.datasets import fetch_california_housing from sklearn.preprocessing import StandardScaler housing fetch_california_housing() X, y housing.data, housing.target X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)这里有一个非常关键的细节标准化参数必须只用训练集来拟合然后再应用到测试集。如果不小心对整个数据集做了标准化再切分会造成信息泄露测试集的结果就不真实了。我在初学阶段犯过这个错误得出的模型评估结果虚高上线后才发现差距教训深刻。4.3 用交叉验证自动选择最优 λ实际项目中我不会手工去挑 λ而是用交叉验证自动搜索。sklearn 里有现成的接口RidgeCV、LassoCV、ElasticNetCV它们都内置了交叉验证流程。from sklearn.linear_model import RidgeCV, LassoCV, ElasticNetCV ridge_cv RidgeCV(alphasnp.logspace(-3, 3, 100)) ridge_cv.fit(X_train_scaled, y_train) lasso_cv LassoCV(alphasnp.logspace(-3, 3, 100), max_iter100000) lasso_cv.fit(X_train_scaled, y_train) elastic_cv ElasticNetCV( alphasnp.logspace(-3, 3, 100), l1_rationp.linspace(0.1, 0.9, 9), max_iter100000 ) elastic_cv.fit(X_train_scaled, y_train)注意 LassoCV 和 ElasticNetCV 需要调max_iter。L1 的损失函数不是光滑的坐标下降法需要更多迭代才能收敛。我一开始跑默认参数时没留意结果收到收敛警告加大迭代次数之后才消除。另外l1_ratio我给了 9 个候选值覆盖从偏 L2 到偏 L1 的范围这样能找到更适合数据的比例。4.4 三种方法在测试集上的表现对比from sklearn.metrics import mean_squared_error models { Ridge: ridge_cv, Lasso: lasso_cv, ElasticNet: elastic_cv } for name, model in models.items(): y_pred model.predict(X_test_scaled) rmse np.sqrt(mean_squared_error(y_test, y_pred)) n_zero np.sum(np.abs(model.coef_) 1e-10) print(f{name}: RMSE{rmse:.4f}, 稀疏维度数{n_zero}, alpha{model.alpha_:.4f})在我本地跑出的结果是Ridge 的 RMSE 最低一点Lasso 的 RMSE 略高但产生了多个零系数ElasticNet 介于两者之间。这说明这个数据集的特征数量不多稀疏性收益不明显L2 的平滑收缩更有效。如果换成高维稀疏特征场景结论很可能反过来。这里我想强调一点不要把某种正则化方法奉为万能。正则化方法的选择和数据本身的特征结构高度相关一定要通过实验和数据说话。4.5 画图看看正则化的实际效果画出训练误差和测试误差随 λ 变化的曲线是理解正则化最直观的方式。λ 从很小变到很大的过程中训练误差单调上升但测试误差会呈现“先降后升”的 U 形走势。U 形底部对应的 λ 就是最佳正则化强度。我用 matplotlib 跑了一下这个实验。当 λ 非常小的时候测试误差很高对应过拟合λ 逐渐增大测试误差快速下降过了某个点之后测试误差又开始上升对应欠拟合。这个 U 形曲线让我真正理解了“正则化是偏差-方差的交易”这句话的含义。5. 从线性模型到复杂模型正则化的延伸用法5.1 逻辑回归里的 L2 惩罚正则化不专属于回归它在分类问题里同样重要。逻辑回归在 sklearn 的实现中有一个默认的 L2 惩罚项C参数是正则化的逆强度——C 越小正则化越强。这个细节经常有人搞混我强调一下C越大越容易过拟合C越小模型越简单。逻辑回归的损失加上了 L2 项后决策边界不会像纯数据拟合那样扭曲得太过分泛化能力更好。很多人在调逻辑回归时只关注学习率之类的参数忽略了C的作用实际上C往往是影响最大的一个参数。5.2 神经网络里的 L2、Dropout 和早停神经网络是过拟合的重灾区因为参数动辄几百万甚至几十亿。L2 正则化可以直接加在损失函数里对权重矩阵做惩罚实践中最常见。另一个主流手段是 Dropout训练时随机丢弃一部分神经元让网络不能过度依赖某一个神经元相当于同时训练多个子网络的集成。我个人的体会是Dropout 的效果通常比 L2 更显著因为它的本质是让模型变得更鲁棒。还有一种方法——早停在训练过程中监控验证集指标一旦验证集不再改善就提前终止训练。早停看似简单实际上是防止过拟合最便宜有效的手段不需要引入额外的超参数。5.3 XGBoost 和 LightGBM 中的树模型正则化我在做 Kaggle 比赛时树模型用的最多。XGBoost 里有两个关键的正则化参数lambdaL2 正则化和alphaL1 正则化。加大lambda会让叶子节点的权重更平滑减小单棵树对特定样本的过度拟合。LightGBM 里对应也有lambda_l2和lambda_l1参数。树模型虽然参数看起来很多但本质上仍然是在控制模型复杂度。树的深度、叶子节点数、最小叶子样本数这些参数也是正则化的另一种形式——它们在结构上限制模型的表达能力而不是在权重上做惩罚。5.4 嵌套交叉验证正则化参数会不会过拟合验证集一个经常被忽略的问题如果我们在同一个验证集上反复调正则化参数最终选出的 λ 可能会对验证集过拟合。严谨的做法是使用嵌套交叉验证外层循环评估模型泛化能力内层循环选择最优 λ。我在做正式的模型评测时都会用嵌套交叉验证因为这样得到的性能估计更可靠。尤其是在数据量不大时嵌套交叉验证能明显降低评估结果的方差。缺点是计算量翻倍了需要做一个权衡。6. 常见问题与排查经验6.1 L1 正则化为什么有时收敛很慢我实测 Lasso 在大数据集上比 Ridge 慢不少偶尔还会出现不收敛的情况。原因是 L1 损失函数在零点不可导坐标下降需要更精细的步径。遇到这种情况我的经验是增大max_iter比如设定为 50000 或 100000调高tol松弛收敛阈值避免在无意义的方向上反复迭代对数据做标准化尤其是特征尺度差异大时这个步骤不做会严重影响收敛速度如果实在收敛有问题试试用弹性网替代纯 L1稳定性会好很多6.2 正则化系数过大导致欠拟合怎么办如果模型在训练集和测试集上表现都不好而且模型权重都趋近于零说明 λ 调太大了。解决办法很直接减小 λ或者把C增大。我还碰到过一种情况——某个特征的系数很小但它本身是重要特征结果被正则化压得太死模型就学不到这个信号了。这时可以降低 λ 或者用特征重要性做筛选优先保留重要特征。另一种情况更隐蔽特征没有做标准化就加正则化。如果某个特征的值范围在 0~1另一个特征值范围在 0~100000当 λ 增大时大数值特征会被压得更狠本质上是在惩罚“量纲大的特征”而不是“不重要的特征”。这个坑一定要避开。6.3 什么时候该用 L1什么时候该用 L2我总结了一个简单好记的经验特征数量比样本数多很多优先用 L1 或弹性网因为它们能压缩掉大量无效特征特征之间有强相关性优先用弹性网或 L2纯 L1 会随机挑一个特征置零丢失信息只关心预测准确率不太关心可解释性直接用 L2稳定且调参简单需要把模型部署到资源受限环境或者需要向业务方解释哪些特征起主要作用用 L16.4 关于 sklearn 的默认配置很多人在用 sklearn 的Ridge时可能没有意识到alpha1.0是默认值。这个默认值其实是拍脑袋给的不是针对你的数据调出来的最优值。所以我强烈建议无论用哪种正则化方法都要主动跑一次交叉验证来确定 α而不是直接采用默认值。用RidgeCV和LassoCV会自动完成这个过程代码也很简单。只是要注意一点它们内部是 k-fold 交叉验证k 默认是 5如果你想更稳一点可以手动设定cv参数比如cv10。6.5 代码复现时的一个小问题随机种子如果你希望实验可以复现一定要在开头设置全局随机种子import numpy as np import random np.random.seed(42) random.seed(42)如果没有设置种子每次运行结果都会有一些随机波动这会导致你用不同次运行得出的“最优 λ”都不一样。设置种子之后至少能保证你每次拿到同样的结果从而可以放心比较不同配置的差异。7. 正则化的深层思考7.1 正则化和贝叶斯视角的联系正则化还有一个很漂亮的解释——从贝叶斯角度看L2 正则化等价于给参数引入了一个高斯先验L1 正则化等价于给参数引入了一个拉普拉斯先验。高斯先验倾向于让参数在零附近连续分布拉普拉斯先验则在零点有一个尖峰所以更容易得出精确的零。这个视角虽然偏理论但它给了我一个启发选择正则化方法本质上是在表达你对参数分布的信念。如果你相信大部分特征应该是有用的选择 L2如果你相信大部分特征其实是无用的选择 L1。这比“哪个效果好”的纯经验视角更本质。7.2 正则化是学习算法的一部分不是事后操作新手很容易把正则化理解为训练之后的一个“修正步骤”这是错误的。正则化是嵌入到优化过程中的约束条件它和原始损失函数一起决定了参数优化的轨迹。只要你用的是带正则化的损失函数梯度下降每一步都会受正则化项的影响。所以在构建机器学习流水线时正则化应该被视为模型设计的一部分和特征选择、数据清洗、模型评估并列而不仅仅是一个可选的开关。7.3 正则化的局限最后说一句公道话。正则化是强大的工具但它不能弥补数据质量差或者特征工程做得差的问题。如果训练样本本身和真实分布差距很大或者特征根本没选对正则化再怎么调也调不出好效果。它只能解决过拟合的问题不能解决信息缺失的问题。我的习惯是先用一个简单的模型比如线性模型加 L2快速跑通 baseline再根据效果决定要不要换成更复杂的模型。在简单模型上把正则化调明白了再过渡到复杂模型时思路会清晰很多。说到我自己最常用的组合是“标准化 ElasticNetCV 嵌套交叉验证”。这套组合稳定、省心几乎适用于各种表格数据的回归和分类问题。如果你现在正在为模型过拟合发愁不妨从这套组合开始做起。