正则化:用可验证的约束换取更稳的泛化
前两章里,我们分别让线性回归拟合连续数值,让逻辑回归估计类别概率。只看训练集时,增加特征、提高多项式次数、扩大网络宽度,通常都能把训练损失继续压低。真正麻烦的地方在于:训练损失下降,并不保证新样本上的误差也下降。
正则化处理的就是这段落差。它给学习过程加入一个偏好,例如“系数不要沿着数据稀少的方向任意放大”“只有少量特征需要保留”“不要把训练一直做到账面损失最低”。这种偏好可能写成目标函数里的惩罚,也可能通过提前停止、随机丢弃单元或数据增强实现。
这一章先把范围收窄到最经典的参数范数惩罚:Ridge、Lasso 和 Elastic Net。我们会从目标函数、约束几何和求解器一路走到工程实践,解释为什么截距通常不受罚、为什么特征必须在每个交叉验证折内缩放、为什么不同库里的 lambda、alpha 和 C 不能只靠名字换算。最后再做一个完整实验,把选择超参数、检查路径、保留测试集和解释结果连成一条可复现流程。
先判断模型在什么地方失败
先别急着把“验证集表现差”直接翻译成“加大正则化”。同一个验证误差,可能来自三类完全不同的问题:
欠拟合 :训练误差已经很高,模型连训练数据里的主要规律都没有学到。继续增强惩罚通常只会让它更差。
过拟合 :训练误差很低,验证误差明显更高,模型对训练样本的偶然细节太敏感。正则化可能有用。
分布或流程问题 :训练集和验证集来自不同时间、不同设备,或预处理发生了泄漏。此时只调惩罚强度,往往是在掩盖真正的问题。
以一元多项式回归为例,次数从 1 1 1 增加到 15 15 15 后,模型家族的表达能力变强。但“次数高”不自动等于“有效复杂度高”。如果高次项的系数被强烈收缩,最终曲线仍可能很平滑。反过来,一个参数数量不多的模型,在病态特征方向上也可能极不稳定。
所以正则化更准确的作用不是“让模型参数变少”,而是限制学习算法能轻易选到的解。对 Ridge 来说,它降低了模型沿弱数据方向的敏感度;对 Lasso 来说,它还会把一部分系数推到精确的零。模型结构可以保持不变,有效自由度和对样本扰动的反应却会改变。
我们可以用训练误差和验证误差做第一轮诊断:
参数大小也必须结合参数化方式理解。若把“面积”从平方米改成平方厘米,系数会缩小一万倍,预测函数却没有变化。神经网络里还可能存在多层之间的缩放等价。因此,“小参数就是简单模型”只在特征尺度、目标函数和参数化都明确时才有可操作意义。
训练误差低不等于泛化好,先判断失败类型再调正则化。
正则化不是验证集表现差时的通用按钮。先确认训练误差、验证误差、数据切分和预处理都在回答同一个问题,再决定是否需要更强的复杂度约束。
1 某模型的训练误差和验证误差都很高,而且两者接近。最合理的第一步是什么?
A 立刻把 L2 惩罚增大一百倍 B 先检查模型表达能力、特征和优化是否造成欠拟合 C 用测试集反复选择正则化强度 D 只保留训练误差最低的那次随机初始化
把复杂度偏好写进目标函数
设训练数据为 ( x i , y i ) i = 1 m (x_i,y_i)_{i=1}^{m} ( x i , y i ) i = 1 m ,模型参数分成截距 b b b 和系数向量 。很多正则化方法都可以写成同一个模板:
min b , w 1 m ∑ i = 1 m ℓ ( y i , f b , w ( x i ) ) + λ Ω ( w ) \min_{b,w}
\frac{1}{m}\sum_{i=1}^{m}\ell\bigl(y_i,f_{b,w}(x_i)\bigr)
+\lambda\,\Omega(w) b , w min m 1
第一项是经验损失,要求模型解释训练数据;第二项是惩罚,表达我们偏好的参数形态;λ ≥ 0 \lambda\ge0 λ ≥ 0 控制两者的权衡。λ = 0 \lambda=0 λ = 0 时退回无惩罚训练,λ \lambda λ 增大时,优化器更愿意牺牲一部分训练拟合来满足约束。
最常见的三个惩罚是:
Ω L 2 ( w ) = 1 2 ∥ w ∥ 2 2 = 1 2 ∑ j = 1 p w j 2 \Omega_{\mathrm{L2}}(w)=\frac{1}{2}\lVert w\rVert_2^2
=\frac{1}{2}\sum_{j=1}^{p}w_j^2 Ω L2 ( w ) = 2 1 ∥ w ∥
Ω L 1 ( w ) = ∥ w ∥ 1 = ∑ j = 1 p ∣ w j ∣ \Omega_{\mathrm{L1}}(w)=\lVert w\rVert_1
=\sum_{j=1}^{p}|w_j| Ω L1 ( w ) = ∥ w ∥ 1 = j
Ω E N ( w ) = ρ ∥ w ∥ 1 + 1 − ρ 2 ∥ w ∥ 2 2 , 0 ≤ ρ ≤ 1 \Omega_{\mathrm{EN}}(w)
=\rho\lVert w\rVert_1
+\frac{1-\rho}{2}\lVert w\rVert_2^2,
\qquad 0\le\rho\le1 Ω EN ( w ) = ρ ∥ w ∥ 1 +
惩罚形式还可以换成约束形式。以 L2 为例,在适当条件下,下面两个问题会沿着同一条解路径移动:
min b , w 1 m ∑ i = 1 m ℓ i ( b , w ) + λ 2 ∥ w ∥ 2 2 \min_{b,w}\ \frac{1}{m}\sum_{i=1}^{m}\ell_i(b,w)
+\frac{\lambda}{2}\lVert w\rVert_2^2 b , w min m 1
min b , w 1 m ∑ i = 1 m ℓ i ( b , w ) subject to ∥ w ∥ 2 2 ≤ t \min_{b,w}\ \frac{1}{m}\sum_{i=1}^{m}\ell_i(b,w)
\quad\text{subject to}\quad
\lVert w\rVert_2^2\le t b , w min m 1
λ \lambda λ 大致对应更小的可行半径 t t t ,但两者不是用一个通用公式逐值换算。它们的关系取决于损失、数据和最优解。L1 也有类似的“惩罚—约束”对应。
几何图像能解释 L1 和 L2 的差别。二维参数空间里,损失的等高线通常像一组椭圆。L2 约束是圆,切点可以落在圆周的任意位置,所以系数常被连续收缩但不恰好等于零。L1 约束是菱形,菱形的尖角落在坐标轴上,等高线更容易先碰到尖角,于是某个系数会精确为零。
这张图提供直觉,但不要把它当成完整证明。高维、相关特征、非唯一解和不同损失都会改变细节;真正决定最优解的是凸优化条件,而不只是“菱形有尖角”这句话。
L1 的尖角与最优性条件共同促成稀疏解。
L2 与 Ridge 把不稳定方向压下来
在线性回归里,先把特征列中心化并标准化,把目标 y y y 中心化,这样截距可以单独恢复。采用本章的平均损失约定,Ridge 目标为:
J ( w ) = 1 2 m ∥ X w − y ∥ 2 2 + λ 2 ∥ w ∥ 2 2 J(w)
=\frac{1}{2m}\lVert Xw-y\rVert_2^2
+\frac{\lambda}{2}\lVert w\rVert_2^2 J ( w ) = 2 m 1 ∥ X w − y ∥ 2
它的梯度是:
∇ J ( w ) = 1 m X T ( X w − y ) + λ w \nabla J(w)
=\frac{1}{m}X^T(Xw-y)+\lambda w ∇ J ( w ) = m 1 X T ( X w − y ) +
若使用梯度下降,更新为:
w ← w − η [ 1 m X T ( X w − y ) + λ w ] w\leftarrow
w-\eta\left[\frac{1}{m}X^T(Xw-y)+\lambda w\right] w ← w − η [ m 1 X T ( X w −
把两部分拆开看,可以写成:
w ← ( 1 − η λ ) w − η 1 m X T ( X w − y ) w\leftarrow
(1-\eta\lambda)w
-\eta\frac{1}{m}X^T(Xw-y) w ← ( 1 − η λ ) w − η m 1 X T ( X
在最朴素的梯度下降设定下,这就是常说的“权重衰减”:每次先把权重乘上一个略小于 1 1 1 的因子,再走数据损失的梯度。但在自适应优化器、复杂参数化或分层正则设置中,“L2 惩罚”和实现层面的 weight_decay 不一定完全等价,不能只看名字判断。
令梯度为零,可得中心化问题的闭式解:
w ^ r i d g e = ( X T X + m λ I ) − 1 X T y \hat{w}_{\mathrm{ridge}}
=\left(X^TX+m\lambda I\right)^{-1}X^Ty w ^ ridge = ( X T X + mλ I
当 λ > 0 \lambda>0 λ > 0 时,对任意非零向量 v v v :
v T ( X T X + m λ I ) v = ∥ X v ∥ 2 2 + m λ ∥ v ∥ 2 2 > 0 v^T(X^TX+m\lambda I)v
=\lVert Xv\rVert_2^2+m\lambda\lVert v\rVert_2^2>0 v T ( X T X + mλ I ) v = ∥ X v ∥
因此这个矩阵正定且可逆。这里的结论针对所有系数都受 L2 惩罚的中心化问题。若把未受罚截距也塞进参数向量,再把正则矩阵左上角设为零,就要额外检查设计矩阵和截距列的秩,不能笼统地说“只要 λ > 0 \lambda>0 λ > 0 就一定可逆”。
Ridge 为什么能缓解共线性,可以从奇异值分解看得更清楚。令:
X = U Σ V T X=U\Sigma V^T X = U Σ V T
沿第 j j j 个右奇异向量方向,Ridge 的系数贡献含有因子:
σ j σ j 2 + m λ \frac{\sigma_j}{\sigma_j^2+m\lambda} σ j 2 + mλ σ j
普通最小二乘在非零奇异值方向使用 1 / σ j 1/\sigma_j 1/ σ j 。当 σ j \sigma_j σ j 很小时,普通最小二乘会放大该方向上的噪声;Ridge 的分母增加了 m λ m\lambda mλ ,小奇异值方向被压得更强。它不是把所有方向机械缩成同一比例,而是优先抑制数据支持较弱的方向。
这也解释了 Ridge 在高度相关特征中常比普通最小二乘稳定:多个近似重复的特征让某些方向的奇异值很小,单个系数可能随样本轻微变化而大幅摆动,预测却相近。Ridge 用一点偏差换取更小的方差和更稳定的系数组合。
Ridge 优先压制数据支持较弱、最容易不稳定的方向。
3 若目标函数为 J(w)=||Xw-y||²/(2m)+lambda·||w||²/2,则正则项对梯度的贡献是 ____。
截距、特征尺度与数值求解要一起处理
正则化写对公式只是第一步。工程里最常见的错误,往往发生在截距、单位和预处理顺序上。
截距通常不受惩罚
截距 b b b 表示所有特征取基准值时的整体水平。若目标整体增加 100 100 100 ,合理模型只需让截距增加 100 100 100 ;若惩罚截距,预测会因为目标零点的任意选择而被拉向零。于是经典线性模型通常只惩罚 w w w ,不惩罚 b b b 。
“通常”不是“所有库都绝对如此”。某些求解器会把截距实现成一个常数合成特征,其权重是否受罚、受罚强度如何,取决于实现参数。读 API 时要确认目标函数,而不是只看 fit_intercept=True。
惩罚会看见特征单位
假设模型同时使用“房屋面积(平方米)”和“房龄(年)”。把面积改成平方厘米后,为了保持相同预测,面积系数要缩小很多。L1/L2 惩罚直接作用于系数数值,于是单位变化会改变优化器认为哪个特征“昂贵”。
因此,连续特征通常要在正则化前标准化:
x i j s c a l e d = x i j − μ j s j x_{ij}^{\mathrm{scaled}}
=\frac{x_{ij}-\mu_j}{s_j} x ij scaled = s j
均值 μ j \mu_j μ j 和尺度 s j s_j s j 只能从训练数据估计。在交叉验证中,它们必须在每个训练折里重新拟合,不能先对全量数据标准化再切折。
解线性系统,不显式求逆
闭式公式写成矩阵逆便于推导,代码里却应解线性方程:
import numpy as np
def ridge_closed_form (X, y, lambda_value):
"""X 与 y 已中心化;所有 X 列均为待惩罚特征。"""
X = np.asarray(X, dtype = np.float64)
y = np.asarray(y, dtype = np.float64)
if X.ndim != 2 or y.ndim != 1 :
raise ValueError ( "X 必须是二维数组,y 必须是一维数组" )
if X.shape[ 0 ] != y.shape[ 0
np.linalg.solve 避免显式构造逆矩阵,通常更快也更稳定。大型稀疏数据则适合迭代求解器。无论用哪种方法,都要检查收敛状态,而不是把“程序返回了系数”当成“优化已经完成”。
范数惩罚会看见单位,因此缩放和截距处理属于目标函数的一部分。
4 只要模型的预测函数不变,把某个特征从米改成厘米就不会影响 L1 或 L2 正则化的解。
L1 与 Lasso 用不可导点产生稀疏解
Lasso 在线性回归中的常用目标是:
J ( w ) = 1 2 m ∥ X w − y ∥ 2 2 + λ ∥ w ∥ 1 J(w)
=\frac{1}{2m}\lVert Xw-y\rVert_2^2
+\lambda\lVert w\rVert_1 J ( w ) = 2 m 1 ∥ X w − y ∥ 2
它和 Ridge 的关键差异不只是把平方换成绝对值。∣ w j ∣ |w_j| ∣ w j ∣ 在 w j = 0 w_j=0 w j = 0 处不可导,其导数要用次梯度描述:
∂ ∣ w j ∣ = { { 1 } , w j > 0 [ − 1 , 1 ] , w j = 0 { − 1 } , w j < 0 \partial |w_j|=
\begin{cases}
\{1\}, & w_j>0 \\
[-1,1], & w_j=0 \\
\{-1\}, & w_j<0
\end{cases} ∂ ∣ w j ∣ =
在最优点,若数据损失对某个坐标的推动足够小,就能被零点处的整个区间抵消,于是 w j = 0 w_j=0 w j = 0 满足最优性条件。这是 Lasso 产生精确零系数的代数原因。
对标准化的平方损失做坐标下降时,每次固定其他坐标,只更新 w j w_j w j 。令 r ( j ) r^{(j)} r ( j ) 表示去掉第 j j j 个特征贡献后的部分残差,更新会经过软阈值函数:
S ( z , λ ) = sign ( z ) max ( ∣ z ∣ − λ , 0 ) S(z,\lambda)
=\operatorname{sign}(z)\max(|z|-\lambda,0) S ( z , λ ) = sign ( z ) max ( ∣ z ∣ − λ , 0 )
w j ← S ( 1 m x j T r ( j ) , λ ) 1 m ∥ x j ∥ 2 2 w_j\leftarrow
\frac{
S\left(\frac{1}{m}x_j^Tr^{(j)},\lambda\right)
}{
\frac{1}{m}\lVert x_j\rVert_2^2
} w j ← m 1
当坐标与残差的相关推动不超过阈值时,分子直接变成零。坐标下降可以循环更新各系数;近端梯度法则把普通梯度步与同一个软阈值算子组合起来。这些方法绕开了“在零点硬算普通导数”的错误。
零点处的次梯度区间让微弱推动被惩罚完全抵消。
Lasso 的稀疏性很有用,但要把解释边界说清:
系数为零表示在当前数据、缩放、惩罚和损失下,这个解没有使用该特征;它不证明特征在现实中无关。
高度相关的一组特征可能互相替代,Lasso 会选其中一个,也可能在不同切分里换人。
非零系数经过收缩,直接把它当成无偏效应估计并不合适。
用交叉验证选择 λ \lambda λ 是为了预测性能,不自动提供传统假设检验里的有效 p p p 值。
所以 Lasso 可以做预测型筛选和压缩,却不是因果发现或稳定变量识别的快捷方式。实际解释时应检查不同折、不同随机种子或自助样本下的入选稳定性。
5 Lasso 把某个标准化特征的系数压到 0,最严谨的解释是哪一个?
A 该特征在现实世界中一定没有因果作用 B 该特征在当前数据与惩罚设定的最优解中未被使用 C 该特征与目标的相关系数一定为 0 D 换一个训练样本后该特征仍必然保持为 0
Elastic Net 在稀疏与相关特征稳定性之间折中
当特征几乎不相关,而且我们相信只有少量特征真正有用,Lasso 的稀疏性很自然。可现实数据常出现成组相关特征:同一设备的多个传感器、同一行为的不同时间窗统计、文本中语义相近的词频。
Elastic Net 同时使用 L1 和 L2:
J ( w ) = 1 2 m ∥ X w − y ∥ 2 2 + λ [ ρ ∥ w ∥ 1 + 1 − ρ 2 ∥ w ∥ 2 2 ] J(w)
=\frac{1}{2m}\lVert Xw-y\rVert_2^2
+\lambda
\left[
\rho\lVert w\rVert_1
+\frac{1-\rho}{2}\lVert w\rVert_2^2
\right] J ( w ) = 2 m 1 ∥ X w − y ∥
ρ = 1 \rho=1 ρ = 1 时是 Lasso,ρ = 0 \rho=0 ρ = 0 时是 Ridge;中间值把稀疏性和 L2 的稳定收缩结合起来。L1 仍能制造零系数,L2 则让强相关特征更有机会共同进入模型,而不是由一次微小样本扰动决定“只留谁”。
Elastic Net 在稀疏性与相关特征稳定性之间折中。
这里有两个超参数:
λ \lambda λ 控制总惩罚强度;
ρ \rho ρ 控制 L1 与 L2 的混合比例。
不能先用全量数据决定 ρ \rho ρ ,再声称交叉验证只调了 λ \lambda λ 。只要某个选择看过验证结果,它就是调参流程的一部分。常见做法是在同一个交叉验证搜索里联合考察一组 ρ \rho ρ 和对数尺度的 λ \lambda λ ,再用独立测试集评估最终方案。
Elastic Net 也不是“必然比 Ridge 和 Lasso 好”。如果真实信号分散在许多小系数上,Ridge 可能更合适;如果信号极稀疏且特征相关性弱,Lasso 可能更简洁。混合惩罚增加了一个选择维度,收益要由验证数据证明。
6 在大量强相关特征的回归问题中,选择 Elastic Net 的合理动机有哪些?
同一种惩罚可以接在线性回归和逻辑回归后面
正则化惩罚约束的是参数,前面的数据损失仍由任务决定。对线性回归,常用平方损失:
J l i n e a r ( b , w ) = 1 2 m ∑ i = 1 m ( b + x i T w − y i ) 2 + λ Ω ( w ) J_{\mathrm{linear}}(b,w)
=\frac{1}{2m}\sum_{i=1}^{m}
\left(b+x_i^Tw-y_i\right)^2
+\lambda\Omega(w) J linear ( b , w ) = 2 m
对二元逻辑回归,令:
p i = σ ( b + x i T w ) p_i=\sigma(b+x_i^Tw) p i = σ ( b + x i T w )
正则化目标是平均交叉熵加惩罚:
J l o g i s t i c ( b , w ) = − 1 m ∑ i = 1 m [ y i log p i + ( 1 − y i ) log ( 1 − p i ) ] + λ Ω ( w ) J_{\mathrm{logistic}}(b,w)
=-\frac{1}{m}\sum_{i=1}^{m}
\left[
y_i\log p_i+(1-y_i)\log(1-p_i)
\right]
+\lambda\Omega(w) J logistic ( b , w ) = −
L2 情况下,两者的正则梯度都多出 λ w \lambda w λ w ,但数据损失的梯度不同。线性回归的残差是连续预测减目标,逻辑回归的残差项来自概率 p i − y i p_i-y_i p i − y i 。不要因为最终梯度长得相似,就把两个损失混为一谈。
惩罚可以相同,任务对应的数据损失仍然不同。
在逻辑回归里,收缩系数会改变 logit 的幅度和决策边界。更强惩罚往往让概率更靠近基准水平,但这不保证概率自动校准,也不保证默认阈值 0.5 0.5 0.5 符合业务代价。选择正则化强度时,应使用任务真正关心的验证指标;模型锁定后,再在独立数据上检查排序、阈值和校准。
求解器与惩罚也必须匹配。某些求解器只支持 L2,有的支持 L1,有的才支持 Elastic Net。L1 的不可导性意味着普通牛顿法或未经修改的平滑优化器不能直接照搬。成熟库会明确列出兼容组合;如果组合不支持,应该换求解器或惩罚,而不是忽略警告。
7 给线性回归和逻辑回归都加 L2 正则后,下列说法正确的是哪一个?
A 两者的数据损失因此变得相同 B 两者都会增加与 w 成正比的正则梯度,但数据损失仍不同 C 逻辑回归的截距必须参与 L2 惩罚 D 正则化后分类阈值必须固定为 0.5
MAP 先验是一种解释,不是正则化的唯一身份
惩罚目标可以从最大后验估计得到。设似然为 p ( D ∣ w ) p(D\mid w) p ( D ∣ w ) ,先验为 p ( w ) p(w) p ( w ) ,MAP 选择:
w ^ M A P = arg max w p ( D ∣ w ) p ( w ) \hat{w}_{\mathrm{MAP}}
=\arg\max_w p(D\mid w)p(w) w ^ MAP = arg w max p ( D
取负对数后:
w ^ M A P = arg min w [ − log p ( D ∣ w ) − log p ( w ) ] \hat{w}_{\mathrm{MAP}}
=\arg\min_w
\left[
-\log p(D\mid w)-\log p(w)
\right] w ^ MAP = arg w min
若各系数独立服从零均值高斯先验:
w j ∼ N ( 0 , τ 2 ) w_j\sim\mathcal{N}(0,\tau^2) w j ∼ N ( 0 , τ 2 )
则负对数先验在忽略常数后与 ∥ w ∥ 2 2 \lVert w\rVert_2^2 ∥ w ∥ 2 2 成正比,于是得到 L2 惩罚。若采用零中心 Laplace 先验,负对数先验与 ∥ w ∥ 1 \lVert w\rVert_1 ∥ w ∥ 1 成正比,于是得到 L1 惩罚。
先验对应是特定条件下的 MAP 解释,不是完整贝叶斯预测。
这层对应有三个前提容易被省略:
惩罚系数与似然的噪声尺度、先验尺度和损失是否取平均有关;
MAP 只返回后验密度的一个最大点,不等于对整个后验积分的完整贝叶斯预测;
用交叉验证选择惩罚强度,是按预测表现调超参数,不等于已经从数据中完成了严格的先验推断。
因此,“L2 就是高斯先验”适合帮助理解,但最好说成“在特定似然、尺度和 MAP 点估计下,L2 与零均值高斯先验对应”。同一个正则化方法还可以从稳定性、约束优化、数值条件改善或泛化控制的角度解释。
8 只要目标函数里出现 L2 惩罚,训练结果就等同于完整贝叶斯后验预测。
lambda、alpha 与 C 必须按目标函数换算
正则化教程里最危险的一句简写是“C=1/lambda”。方向上,许多分类 API 的 C C C 越小,正则化越强;但精确数值还取决于损失是否取平均、惩罚是否带 1 / 2 1/2 1/2 、样本权重如何归一化,以及库把参数放在损失前还是惩罚前。
看三个常见目标就能发现差别。
本章 Ridge 采用:
1 2 m ∥ X w − y ∥ 2 2 + λ 2 ∥ w ∥ 2 2 \frac{1}{2m}\lVert Xw-y\rVert_2^2
+\frac{\lambda}{2}\lVert w\rVert_2^2 2 m 1 ∥ X w − y ∥ 2 2 +
某些 Ridge API 采用:
∥ X w − y ∥ 2 2 + α ∥ w ∥ 2 2 \lVert Xw-y\rVert_2^2
+\alpha\lVert w\rVert_2^2 ∥ X w − y ∥ 2 2 + α ∥ w ∥ 2 2
某些逻辑回归 API 则采用加权平均损失,再加:
r ( w ) S C \frac{r(w)}{SC} S C r ( w )
其中 S S S 可能是样本权重总和。只说 C C C 是“逆正则化强度”能判断方向,却不足以在两个实现间复制同一个最优解。
实践中按下面顺序处理:
找到文档给出的完整目标函数;
对齐损失的求和或平均方式;
对齐惩罚的 1 / 2 1/2 1/2 、混合比例和截距处理;
检查样本权重、类别权重是否改变归一化;
再推导参数映射,并用一个小数据集核对系数和预测。
求解器容差也会影响比较。Lasso 在 λ \lambda λ 较小时可能需要更多迭代;如果优化尚未收敛,表面上的“不同库结果不一致”也许只是一个求解器提前停了。比较前要看迭代次数、停止准则和收敛警告。
9 把一个库中的正则化参数迁移到另一个库时,最可靠的做法是什么?
A 只要参数都叫 alpha 就直接复制 B 统一令 C=1/lambda,不必看损失缩放 C 对照两个库的完整目标函数、权重归一化和截距处理后推导映射 D 选择能得到最小训练误差的参数
交叉验证选择强度,流水线守住数据边界
正则化强度是超参数,不能用训练误差选择。训练误差通常随着惩罚减弱而下降,会偏向最复杂、最不受约束的解。也不能反复查看测试集;一旦测试结果参与选择,测试集就成了验证集。
一个可靠的流程是:
先留出最终测试集;
在剩余训练数据内做交叉验证;
每一折只用该折训练部分拟合缺失值、缩放和特征工程;
在该折验证部分计算任务指标;
选择超参数后,用全部训练数据重新拟合流水线;
最后只在测试集上评估一次。
对于独立同分布样本,可以用 K 折交叉验证。类别不平衡时常用分层切分;同一患者、用户或设备有多条记录时要按组切分;时间预测必须保持时间顺序。切分策略要模拟部署时真正遇到的“未见数据”,不能为了分数稳定就打乱现实边界。
最隐蔽的泄漏发生在预处理。若先用全量数据计算均值和标准差,再做 K 折,验证折的信息已经进入缩放参数。单看每个标签似乎没有泄漏,模型却获得了验证分布的统计信息。正确做法是把 StandardScaler 和模型放入同一条 Pipeline,让交叉验证在每个训练折里调用 fit。
缩放、特征选择和模型训练都必须留在每个训练折内部。
下面是一个无泄漏的 Ridge 搜索骨架:
import numpy as np
from sklearn.linear_model import Ridge
from sklearn.model_selection import GridSearchCV, KFold
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
pipeline = Pipeline([
( "scale" , StandardScaler()),
( "model" , Ridge()),
])
search = GridSearchCV(
estimator = pipeline,
param_grid
对数网格比线性网格更适合跨多个数量级探索。若最优值落在搜索边界,应向外扩展网格,而不是直接把边界当成答案。数据量小或调参自由度很高时,还可以使用嵌套交叉验证估计整个选择流程的泛化性能。
10 哪些操作必须限制在每个交叉验证训练折内部完成?
正则化路径与替代工具回答不同问题
单独报告一个“最佳 λ \lambda λ ”会丢掉很多信息。正则化路径把每个系数随惩罚强度的变化画出来:
Ridge 系数通常连续收缩,很少精确变成零;
Lasso 系数会在某些阈值进入或离开模型;
Elastic Net 的路径介于两者之间,相关特征更可能成组变化。
路径可以帮助我们发现三个问题。第一,搜索范围是否覆盖了“几乎无惩罚”到“多数系数被压小”的区域。第二,哪些特征只在极窄参数范围内出现。第三,相关特征是否在不同折里轮流入选。
选择规则也不必只盯着平均验证误差的绝对最小值。当几个候选的验证表现差异落在折间波动内,可以优先选择更强正则化、更稀疏或更易部署的方案。这是一种带有工程偏好的选择原则,不应伪装成“统计上唯一正确”的答案;要同时报告验证均值、波动和选择理由。
参数惩罚之外,还有其他常被统称为正则化的工具,它们解决问题的路径不同:
目标相似不代表机制相同,工具应按数据和模型条件选择。
这些方法可以组合,但不能互相偷换结论。图像随机平移有效,是因为标签对小幅平移通常不变;对医疗数值随意加噪声未必合理。早停要用验证信号确定停止点;Dropout 在训练和推理阶段有不同的计算规则。把它们都叫“防过拟合”只说明目标相似,不说明机制和适用条件相同。
11 关于正则化路径和其他正则化工具,下列哪些说法正确?
把正则化实验做成可复现流程
最后把概念落到一个完整实验。我们构造一个回归数据集:前两个特征强相关,真实信号分布在三个特征上,其余特征是噪声。任务不是证明某个算法永远最好,而是比较 Ridge、Lasso 和 Elastic Net 在同一数据边界、同一指标下的表现与系数结构。
import numpy as np
from sklearn.base import clone
from sklearn.linear_model import ElasticNet, Lasso, Ridge
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import GridSearchCV, KFold, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
rng = np.random.default_rng( 7 )
m = 600
x0 =
运行前先做四个预期:
Ridge 多半保留全部系数,但噪声特征的系数会被收缩;
Lasso 多半得到更少非零系数,却可能在 x 0 x_0 x 0 和 x 1 x_1 x 1 之间偏向其中一个;
Elastic Net 可能同时保留两个相关信号,并压掉一部分噪声特征;
三者测试 RMSE 的差距未必很大,系数结构却可能明显不同。
实验结果要从三个层次读。
先看选择过程
检查最优 alpha 是否落在网格边界,交叉验证各折的波动是否远大于候选差异,求解器是否给出收敛警告。若这些条件没过,先扩大网格、增加迭代或调整切分,不要急着解释系数。
再看泛化表现
比较交叉验证 RMSE 和一次性测试 RMSE。测试分数明显更差,可能只是有限样本波动,也可能表示调参过度、切分不匹配或训练集与测试集分布不同。不要回头根据这次测试结果继续选模型;若这样做,就需要新的最终保留集。
最后看结构稳定性
记录非零系数个数、相关特征的系数组合和不同折中的路径。对于 x 0 x_0 x 0 、x 1 x_1 x 1 这样的强相关变量,单个系数不稳定而两者总贡献稳定,是常见现象。预测任务可以关注联合贡献;若任务要求解释单个特征,则需要更谨慎的设计和额外稳定性分析。
完整实验最后应保存数据切分种子、预处理流水线、参数网格、评分函数、库版本和最终模型。这样别人才能区分“方法真的不同”和“实验设置没有对齐”。
正则化实验的交付物不只是一个超参数。至少要保留:目标函数约定、无泄漏流水线、交叉验证结果与波动、系数路径、最终测试表现,以及对稀疏性和相关特征的边界说明。
12 在完整实验中,Lasso 的测试 RMSE 与 Elastic Net 接近,但它在不同交叉验证折里轮流选择两个强相关特征。最合理的结论是什么?
A Lasso 已证明其中一个特征没有作用 B 预测表现接近,但单个特征的选择不稳定,解释时应报告这种不确定性 C 应查看测试集后继续调参,直到只选固定特征 D 只要系数为零,就无需检查特征尺度
i = 1
∑
m
ℓ
(
y i
,
f b , w
(
x i
)
)
+
λ Ω ( w )
2
2
=
2 1 j = 1 ∑ p w j 2
=
1
∑
p
∣
w j
∣
2 1 − ρ
∥
w
∥ 2 2
,
0
≤
ρ ≤
1
i
=
1
∑
m
ℓ i
(
b
,
w
)
+
2 λ ∥ w ∥ 2 2
i = 1
∑
m
ℓ i
(
b
,
w
)
subject to
∥
w
∥ 2 2
≤
t
2
+
2 λ ∥ w ∥ 2 2
λ
w
y
)
+
λ
w
]
w
−
y )
)
− 1
X T
y
2
2
+
mλ ∥ v ∥ 2 2 >
0
x ij − μ j
]:
raise ValueError ( "X 与 y 的样本数必须一致" )
if lambda_value < 0 :
raise ValueError ( "lambda_value 必须非负" )
m, p = X.shape
system = X.T @ X + m * lambda_value * np.eye(p)
rhs = X.T @ y
return np.linalg.solve(system, rhs)
2
+
λ ∥ w ∥ 1
⎩
⎨
⎧
{ 1 } , [ − 1 , 1 ] , { − 1 } , w j > 0 w j = 0 w j < 0
∥
x j
∥ 2 2
S ( m 1 x j T r ( j ) , λ )
2 2
+
λ [ ρ ∥ w ∥ 1 + 2 1 − ρ ∥ w ∥ 2 2 ]
1
i = 1 ∑ m
( b + x i T w − y i ) 2
+
λ Ω ( w )
m
1
i = 1 ∑ m
[ y i log p i + ( 1 − y i ) log ( 1 − p i ) ]
+
λ Ω ( w )
∣
w ) p ( w )
[ − log p ( D ∣ w ) − log p ( w ) ]
2 λ
∥
w
∥ 2 2
=
{
"model__alpha"
: np.logspace(
-
4
,
4
,
25
)},
scoring = "neg_root_mean_squared_error" ,
cv = KFold( n_splits = 5 , shuffle = True , random_state = 42 ),
n_jobs =- 1 ,
)
search.fit(X_train, y_train)
best_model = search.best_estimator_
test_predictions = best_model.predict(X_test)
rng.normal(
size
=
m)
x1 = x0 + rng.normal( scale = 0.08 , size = m) # 与 x0 强相关
noise_features = rng.normal( size = (m, 10 ))
X = np.column_stack([x0, x1, noise_features])
true_w = np.zeros(X.shape[ 1 ])
true_w[[ 0 , 1 , 4 ]] = [ 2.0 , 2.0 , - 1.5 ]
y = X @ true_w + rng.normal( scale = 1.0 , size = m)
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size = 0.25 ,
random_state = 42 ,
)
cv = KFold( n_splits = 5 , shuffle = True , random_state = 42 )
alphas = np.logspace( - 4 , 2 , 30 )
experiments = {
"Ridge" : (
Ridge(),
{ "model__alpha" : alphas},
),
"Lasso" : (
Lasso( max_iter = 50_000 ),
{ "model__alpha" : alphas},
),
"ElasticNet" : (
ElasticNet( max_iter = 50_000 , random_state = 42 ),
{
"model__alpha" : alphas,
"model__l1_ratio" : [ 0.1 , 0.5 , 0.9 ],
},
),
}
results = {}
for name, (estimator, grid) in experiments.items():
pipeline = Pipeline([
( "scale" , StandardScaler()),
( "model" , clone(estimator)),
])
search = GridSearchCV(
pipeline,
param_grid = grid,
scoring = "neg_root_mean_squared_error" ,
cv = cv,
n_jobs =- 1 ,
return_train_score = True ,
)
search.fit(X_train, y_train)
prediction = search.predict(X_test)
test_rmse = mean_squared_error(
y_test,
prediction,
) ** 0.5
coefficient = search.best_estimator_.named_steps[ "model" ].coef_
results[name] = {
"best_params" : search.best_params_,
"cv_rmse" : - search.best_score_,
"test_rmse" : test_rmse,
"nonzero" : int (np.count_nonzero(np.abs(coefficient) > 1e-8 )),
"coef_x0" : float (coefficient[ 0 ]),
"coef_x1" : float (coefficient[ 1 ]),
}
for name, result in results.items():
print (name, result)
正则化 - 解决过拟合问题 | 自在学