1. 为什么需要正则化:先搞清楚模型在什么情况下被带偏
我第一次认真研究正则化,不是在上课的时候,而是在调一个房价预测模型的时候。训练集上的R²已经刷到0.98,验证集却只有0.71,模型在训练集上几乎背下了每个样本的噪声,换了一批数据就原形毕露。这就是机器学习里最常见的翻车现场——过拟合。
正则化就是专门对付这个问题的。它不是某个花哨的模型,而是加在损失函数后面的一个惩罚项,核心作用就是让模型的权重不要长得太随意,逼着模型学一套更稳定、更简单的规律。对于正在学机器学习的同学来说,正则化几乎是绕不开的考点;对于已经在用sklearn、TensorFlow做项目的人来说,它又是调参和模型融合时最常被拿来救场的工具。
1.1 过拟合不是玄学,它藏在三个地方
我们常用下面这个式子描述一个线性模型的损失:
[
J(w) = \frac{1}{n}\sum_{i=1}^{n} L(y_i, \hat{y}_i) + \lambda R(w)
]
前半部分是模型在训练数据上的损失,后半部分是正则项,(\lambda) 是正则化系数。没有后半部分时,模型优化的唯一目标就是把训练集的损失压到最低。问题是,当模型太灵活、特征太多、数据量又不够的时候,它会想尽一切办法去拟合每一个训练样本,包括样本里那些纯属偶然的噪声点。
我见过不少新手在这个地方栽跟头,总以为“训练误差越低就代表模型越好”。其实过拟合往往就藏在三个信号里:一是训练损失长期低于验证损失,二是在验证集上的表现随着训练轮数先降后升,三是模型权重变得很大、很碎。前两个靠画学习曲线就能看出来,第三个则是正则化要解决的主要问题。
1.2 正则化怎么把模型“拉回来”
你可以把模型训练想象成一次自由创作。没有正则化的时候,模型就像一位完全放飞自我的作家,想怎么写就怎么写,最后确实能写得和训练素材一字不差,但稍微换个话题就彻底懵了。正则化相当于给这位作家定了几条写作规范:别用太生僻的词,别写太长的从句,老老实实把主干意思表达清楚。
放到数学上,正则项 (R(w)) 就是在惩罚那些过于大的权重。假设某个特征在训练集上碰巧和标签有很强的假相关性,模型就会给这个特征配一个大权重,而这个权重在验证集上很可能不成立。正则化提高了得到大权重的代价,让模型只有在证据非常充分的时候才敢用大权重,本质上是在“抑制模型的自由度”。
我们常说的偏差和方差,也可以放到同一个框架里看。正则化系数调大,模型变得更保守,方差降下来了,但偏差会增加,可能学不到足够复杂的关系。系数调小,模型又容易回到过拟合。正则化就是我们在偏差和方差之间拧动的一个旋钮。
提示:做期末复习或者面试准备时,正则化的核心价值用一句话概括就是:以可接受的偏差增加为代价,换取方差的大幅下降,从而提升模型在未知数据上的泛化能力。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 主流正则化家族:L1、L2、弹性网与前人经验
聊正则化,绕不开L1和L2。这两个算是正则化家族里的元老,几乎每一本经典教材都会讲,从周志华老师的《机器学习》,到李航老师的《统计学习方法》,再到吴恩达老师的课程,都会花不少篇幅。它们看起来只是范数符号的差别,实际表现却完全不同。
2.1 L2正则化(权重衰减)的原理与几何直觉
L2正则化也叫权重衰减、岭回归(Ridge Regression),它的正则项是所有参数平方和的一半:
[
R(w) = \frac{1}{2} \sum_{j=1}^{p} w_j^2
]
加上这个惩罚后,权重越大的参数受到的“惩罚”越重,梯度下降时每一步都会让权重往0的方向多收缩一点,所以叫权重衰减。
为什么 L2 能防过拟合?我记得自己在理解这个问题时,最有帮助的其实是几何视角。对于只有两个特征的模型,不带正则项时,损失函数的等高线是一圈一圈的椭圆,椭圆中心是最优解。加上了L2约束后,参数被限制在一个以原点为圆心的圆形区域内。圆形区域和椭圆等高线相切的位置,往往不是椭圆中心,而是偏向原点、权重更小的某个点。这个偏置让模型放弃了在训练集上“极致精准”的那个解,换来了更平滑的预测边界。
L2还有一个特别重要的性质:它不会把权重压到严格的0,而是让权重变得很小。也就是说,所有特征都会保留,只是贡献程度被调低了。这很适合特征之间没有太强相关性、你不想做特征筛选的场景。
2.2 L1正则化的稀疏性从哪来
L1正则化也叫做套索回归(Lasso Regression),正则项是参数绝对值之和:
[
R(w) = \sum_{j=1}^{p} |w_j|
]
L1和L2最大的区别在于:L1会把一部分权重变成精确的0。零权重就意味着对应的特征被模型彻底放弃,所以L1常常被当作特征选择工具来用。
稀疏解是怎么产生的?还是看几何。L1的约束区域是菱形,菱形的角落在坐标轴上。当椭圆等高线碰上一个角,切点恰好落在某个坐标轴上时,这时候另一个坐标对应的权重就是0。高维度下,L1的解会落在越来越多坐标轴构成的“低维子空间”边界上,于是大量权重变成0。
用贝叶斯的视角理解更通透。L2正则化等价于给参数加上一个均值为0的高斯先验,这种先验认为参数大概率落在0附近,但允许个别参数有较大偏离;L1正则化等价于拉普拉斯先验,这个分布在0处的概率密度极高,因此更容易直接“逼”出0解。这也是为什么L1天然适合做特征筛选。
2.3 弹性网正则化:当L1和L2一起用
现实使用中,L1的稀疏性让人很喜欢,但它有两个问题:第一,当特征数量大于样本数量时,L1最多只能选出和样本数一样多的非零特征;第二,如果一组特征之间有强相关性,L1往往只会随机挑其中一个,而不关心选哪一个,导致结果不稳定。
弹性网(Elastic Net)就是把L1和L2结合起来:
[
R(w) = \rho \sum_{j=1}^{p} |w_j| + \frac{1-\rho}{2} \sum_{j=1}^{p} w_j^2
]
它既保留了L1的稀疏性,又借助L2的收缩效果让彼此相关的特征可以一起被选入或剔除。在基因表达谱这类“特征成千上万、样本只有几十个”的数据集上,弹性网的表现通常显著优于单独的L1或L2。sklearn里的 ElasticNetCV 可以直接用交叉验证同时搜 (\rho) 和 (\lambda) ,非常方便。
2.4 除了权重惩罚,还有哪些正则化思路
很多人以为正则化只指L1和L2,其实正则化的思想覆盖面很广。早停法(Early Stopping)算一种:在训练过程中观察验证集误差,一旦验证集误差开始回升就提前终止训练,这样能防止模型在训练后期过度拟合噪声。数据增强也是一种非常自然的正则化,图像翻转、裁剪、加噪声,本质上都是让模型见到更多“不容易死记硬背”的数据变体。
在深度学习中,Dropout曾经是当之无愧的主角。它训练时随机丢弃一部分神经元,让模型不能过度依赖某条特定路径,相当于在多个子网络的集成效果之间取平均。近年来,一致性正则化(Consistency Regularization)也变得越来越常见:模型对同一个样本的不同增强版本应该输出一致的结果,这种约束让模型学到更鲁棒的表示,在很多半监督学习方案里起到关键作用。这些方法虽然形式上不同,但底层逻辑殊途同归:增加约束,压缩假设空间,提升泛化能力。
下面这张表把几个主流正则化方式放在一起对比,方便快速抓重点:
| 方法 | 正则项/机制 | 特点 | 适用场景 |
|---|---|---|---|
| L2 / 岭回归 | 参数平方和 | 权重整体变小,不产生精确0 | 特征较多且相关性不强的常规场景 |
| L1 / Lasso | 参数绝对值之和 | 产生稀疏解,能做特征选择 | 特征多、希望自动筛除冗余特征 |
| 弹性网 | L1 + L2混合 | 兼顾稀疏性与稳定性 | 高维数据、特征分组相关时 |
| Dropout | 随机丢弃神经元 | 防止网络依赖局部路径 | 深度神经网络 |
| 早停 | 提前终止训练 | 防止训练后期过拟合 | 几乎所有模型训练过程 |
| 数据增强 | 对输入施加变换 | 扩大有效样本空间 | 图像、文本、语音等场景 |
| 一致性正则化 | 约束不同增强下的输出一致 | 学到语义稳定的表征 | 半监督学习、自监督学习 |
注意:L1和L2的名字在教材里可能混用。sklearn中,Ridge是L2,Lasso是L1,ElasticNet是两者的组合;但在某些框架里“L1正则化”也可能指对损失直接加绝对值惩罚。原理一致,看公式最稳妥。
3. 正则化系数怎么选:从标准化到交叉验证
很多初学者都知道要加正则化,但一到动手就卡在“正则化系数((\lambda))到底设多少”这个问题上。有人直接默认0.01,有人干脆不调。实际上,选 (\lambda) 有一套清晰的操作流程,我平时给学员演示的时候,习惯按下面四步走。
3.1 第一步永远先做特征标准化
这一步太容易被忽略,但它在正则化里是生死攸关的。假设一个特征的单位是“平方米”,取值在几十到几百之间,另一个特征的单位是“万元”,取值在几万到几十万之间。L2惩罚对所有参数一视同仁地求平方和,如果某个特征的取值量纲特别大,它对应的权重只需要很小很小就能对预测产生巨大影响,惩罚项对这个权重几乎起不到作用;反过来,取值量纲很小的特征,权重会被压得特别狠。
解决办法就是对每个特征做标准化,常见的做法是减去均值再除以标准差:
[
x' = \frac{x - \mu}{\sigma}
]
标准化之后,所有特征都落在相近的量纲范围里,正则化惩罚才能对每个权重“公平执法”。sklearn里用 StandardScaler 一行搞定:
python复制from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
注意:标准化时只能用训练集的数据来估计均值和方法,然后把同一套参数应用到验证集和测试集上。见过有人把全部数据一起fit再切分,这会造成信息泄露,验证结果会偏乐观。
3.2 用交叉验证选 (\lambda),不要拍脑袋
(\lambda) 太小,正则化形同虚设;(\lambda) 太大,所有权重都被压向0,模型退化成了一条接近水平的线,直接欠拟合。正确的做法是用交叉验证在候选值里选一个,让验证集上的误差最小。
sklearn里最省事的方案是直接用带交叉验证的正则化模型:
python复制from sklearn.linear_model import RidgeCV, LassoCV, ElasticNetCV
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X_scaled, y, test_size=0.2, random_state=42
)
ridge_cv = RidgeCV(alphas=[0.1, 1.0, 10.0, 100.0], cv=5)
ridge_cv.fit(X_train, y_train)
print("最优 alpha:", ridge_cv.alpha_)
lasso_cv = LassoCV(alphas=[0.001, 0.01, 0.1, 1.0], cv=5, max_iter=100000)
lasso_cv.fit(X_train, y_train)
print("Lasso 最优 alpha:", lasso_cv.alpha_)
alpha数组怎么给?我的经验是从1e-4到1e2按对数间隔拉一个网格,比如用NumPy生成:
python复制import numpy as np
alpha_grid = np.logspace(-4, 2, 20)
为什么用对数间隔?因为(\lambda)对模型的影响在数量级层面,你从0.01调到0.02可能毫无变化,但从1.0调到0.1可能天差地别。对数网格能让你在几个数量级内快速定位合理的区间。
3.3 观察系数路径,判断正则化到底压住了什么
只拿到最优(\lambda)还不够,我建议你再画一张系数路径图。这条路径展示的是:随着(\lambda)从大到小变化,各个特征的系数如何从0附近逐渐长出来。画完之后你会非常直观地看到哪些特征先被“放行”,哪些特征直到最后才被允许拥有较大权重。
python复制import matplotlib.pyplot as plt
from sklearn.linear_model import lasso_path
alphas_lasso, coefs_lasso, _ = lasso_path(X_train, y_train, alphas=np.logspace(-3, 1, 100))
plt.plot(alphas_lasso, coefs_lasso.T)
plt.xscale("log")
plt.xlabel("alpha")
plt.ylabel("coefficients")
plt.title("Lasso path")
plt.show()
这张图还能帮你做二次判断:如果最优(\lambda)附近,某个特征的系数在0和0.5之间剧烈跳变,说明这个特征本身不稳定,后续建模要考虑是否手工剔除,或者用弹性网替代纯L1。
3.4 一个完整的实操例子
我拿一个模拟数据集演示一下完整流程。假设我们有200个样本、50个特征,其中只有5个特征真正和目标有关:
python复制from sklearn.datasets import make_regression
from sklearn.linear_model import Ridge, Lasso, ElasticNet
from sklearn.metrics import mean_squared_error
X, y = make_regression(n_samples=200, n_features=50, n_informative=5,
noise=0.5, random_state=1)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
models = {
"Ridge": Ridge(alpha=1.0),
"Lasso": Lasso(alpha=0.1, max_iter=100000),
"ElasticNet": ElasticNet(alpha=0.1, l1_ratio=0.5, max_iter=100000)
}
for name, model in models.items():
model.fit(X_train, y_train)
train_mse = mean_squared_error(y_train, model.predict(X_train))
test_mse = mean_squared_error(y_test, model.predict(X_test))
nonzero = np.sum(model.coef_ != 0)
print(f"{name}: train_mse={train_mse:.3f}, test_mse={test_mse:.3f}, nonzero={nonzero}")
在这个例子里,不优化(\lambda)时,Ridge的验证误差一般会比Lasso略高,因为额外45个无关特征虽然没有被清零,还是会带入部分噪声;Lasso则能直接把无关特征压成0。实际项目中究竟选谁,不能拍脑袋,还是要靠交叉验证对不同模型分别跑一遍,对比验证集上的误差。
提示:Lasso和ElasticNet的求解本质上是坐标下降法,当alpha特别小的时候,收敛速度会变慢,这时把max_iter调大一些(比如10万)能避免警告,也能保证结果真的收敛。
4. 常见问题与排查技巧实录
正则化这个选题,每年期末复习、面试备考时都会被反复问到。我在带学生的过程中收集了不少典型问题,这里按“症状—原因—解决方案”的思路做一份实录,希望能帮你少走弯路。
4.1 加了正则化之后,训练误差反而变高了?
这是正常的,而且恰恰说明正则化在起作用。正则化给目标函数增加了一个额外的惩罚项,模型不再以“训练损失最小”为唯一目标,所以训练集上的损失升高是意料之中的事。判断模型好坏不能只看训练误差,重点要看验证集误差有没有下降、训练和验证之间的差距有没有缩小。
但如果你发现验证集误差也跟着变差了,那就要检查是不是(\lambda)调得太大,模型被压得太狠,出现了欠拟合。这时候把(\lambda)往小调几个数量级即可。
4.2 L1正则化的特征选择结果每次跑都不一样?
这类问题在特征维度高、样本量少的时候特别常见。L1的稀疏解在相关特征之间做选择时带有随机性,交叉验证的分割方式一变,选出来的特征就可能不同。解决思路有两个:一是改用弹性网,让L2部分“拉住”相关特征,每次跑出来的非零特征集合会更稳定;二是做重采样,比如用Bootstrap抽样多次跑Lasso,只保留在大部分抽样中系数都不为零的特征。
4.3 忘记标准化,后果有多严重?
不少人在sklearn里直接拿原始特征跑Ridge,发现结果很差,排查了半天最后才发现是标准化没做。我印象很深的是一个天气预测的案例,温度特征本身取值就大,降雨量特征取值偏小,结果L2正则化把降雨量对应的权重压得几乎为0,模型几乎只看温度,预测自然很偏。标准化之后,两个特征的重要性才能被公平评估。这个问题在深度学习里同样存在,尤其使用L2正则或Weight Decay时,不规范化输入会导致不同维度受到不同程度的惩罚。
4.4 期末复习和面试常考的正则化要点
结合周志华《机器学习》、李航《统计学习方法》和吴恩达课程里的常见考点,我整理了一份速查清单:
- 过拟合的本质是什么?模型假设空间过大,把训练数据中的噪声也学进去了。
- 正则化为什么能缓解过拟合?通过惩罚复杂度,缩小假设空间,降低模型方差。
- L1和L2有什么区别?L1产生稀疏解、能做特征选择;L2让权重平滑缩小、不置零。
- 如何从贝叶斯角度理解L1和L2?L1对应拉普拉斯先验,L2对应高斯先验。
- 正则化系数越大,偏差和方差怎么变?系数越大,方差下降、偏差上升;系数越小,反之。
- (\lambda)如何选择?交叉验证,配合特征标准化和对数网格搜索。
- 为什么L1的解更容易落在坐标轴上?约束区域是菱形,边界有“尖角”,更容易和等高线在坐标轴附近相切。
- 正则化适用于线性模型吗?适用于几乎所有模型,线性模型、树模型(通过约束树深度、叶子节点数)、神经网络都有对应的正则化手段。
这几点如果都能自己推导一遍,期末复习和面试基本不会出大问题。
4.5 给初学者的行动清单
最后分享几条我自己的使用心得。刚开始做项目时,不要一上来就堆L1、L2、Dropout一堆正则化手段,先观察训练误差和验证误差的差距,确认有严重的过拟合之后再加。加的顺序上,线性模型先试Ridge,如果发现某些特征的系数很小且不稳定,再换Lasso或ElasticNet,并用交叉验证确定(\lambda)。深度网络里优先考虑早停和Dropout,之后再考虑Weight Decay。
还有一个小技巧:每次调完正则化系数,把训练误差、验证误差和模型非零参数个数一起记录下来。我见过很多人在调参时只盯着验证误差,最后选了一个验证误差最低但参数极其不稳定的模型,导致上线后每训练一次结果都变一次。记录这些指标能帮你判断模型是真的变好了,还是只是“这次运气好”。我自己踩过几次坑之后的体会是:正则化不是越强越好,也不是加上就一定有效,它更像一个调试工具,需要你耐心观察它到底改变了模型的什么行为。
