跳转到内容

Ridge、Lasso 与因子合成

机器学习 · 机器学习 · P0
本章目录

学完后,你应能独立完成

  • 推导 Ridge、特征方向收缩和库参数换算
  • 推导一维 Lasso 并理解坐标下降
  • 从尺度、共线和时间验证选择正则强度

前置建议:线性回归

改变一个条件,观察发生什么

浏览器内即时教学计算;Python 实验结果另列于代码区。图形与数值一起核对。

01

在同一目标上定义惩罚

本课程用平均平方损失 SSE/(2n),Ridge 的惩罚为 λ‖β‖²/2,截距不罚。λ 的大小只有在目标归一化与特征尺度明确后才有意义。scikit-learn Ridge 的 SSE+alpha‖β‖²约定对应 alpha=nλ;Lasso 的平均损失约定对应 alpha=λ,不能在两者之间照抄一个数。

\[\hat\beta_{ridge}=(X_c^TX_c+n\lambda I)^{-1}X_c^Ty_c\]
  1. 中心化消去截距。
  2. 梯度为 X_cᵀ(X_cβ−y_c)/n+λβ。
  3. 令梯度为零得到线性系统。
  4. λ>0 时斜率目标严格凸;代码使用 solve,不显式求逆。
02

收缩不是给每个系数同乘一个数

设 X_c=UDVᵀ。沿第 j 个右奇异向量方向,拟合值相对 OLS 的收缩比例为 d_j²/(d_j²+nλ)。小奇异值方向更难稳定估计,因此受到更强的收缩。原始坐标中的一个系数是多个方向的叠加,其绝对值不保证随 λ 单调下降。

λ→∞ 时斜率趋零,含截距预测趋训练目标均值。Ridge 引入偏差,可能换来更大的方差下降;是否改善未来误差需要验证,并非一个普遍优于 OLS 的定理。

\[\hat y_c=U\,\mathrm{diag}\!\left(\frac{d_j^2}{d_j^2+n\lambda}\right)U^Ty_c\]
03

Lasso 的零来自次梯度条件

一维中心化后,令 s=xᵀx/n>0、c=xᵀy/n。去掉常数项,目标是 sβ²/2−cβ+λ|β|。在 β=0 处,L1 的次梯度包含一个区间;只要 |c|≤λ,零就可以满足最优条件。L2 的零处梯度只有零,通常只是连续收缩。

\[\hat\beta=\frac{\mathrm{sign}(c)(|c|-\lambda)_+}{s}\]
  1. β>0 时解为 (c−λ)/s,但必须满足 c>λ。
  2. β<0 时解为 (c+λ)/s,但必须满足 c<−λ。
  3. 剩余的 |c|≤λ 对应 β=0。
  4. 多维只有正交设计能一次逐坐标阈值;一般需反复重算其他列残差。
04

坐标下降与 Elastic Net

更新第 j 个系数时,先把该列旧贡献加回残差,计算它与部分残差的相关,再做软阈值,最后减去新贡献。这就把多维问题拆成条件一维问题。收敛检查可先用参数变化;更严格实现还会检查对偶间隙或 KKT 条件。

相关因子下 Lasso 可以任意选择可替代列,相同列时还可能多解。Elastic Net 加入 L2,使相关组更稳定,并在正 L2 条件下改善唯一性。稀疏不是因果识别,入选也不是可靠的选后显著性证明。

\[J=\frac{\mathrm{SSE}}{2n}+\lambda\left[a\|\beta\|_1+\frac{1-a}{2}\|\beta\|_2^2\right]\]
05

尺度与先验是建模选择

对原系数用同样惩罚时,不同单位会影响同一预测贡献的惩罚大小。训练折内标准化是一种常用的公平量纲设计,不是数学上存在解的必要条件。标准化均值、方差必须来自当前训练折。

高斯误差与高斯斜率先验下可以得到 Ridge 的 MAP;在固定方差、线性高斯条件下后验均值与 MAP 相同。Laplace 先验对应 Lasso 的 MAP,但一般不等于后验均值。先验解释需声明误差、方差与尺度条件。

06

在时间折中选 λ

每个开发折先用成熟过去标签拟合填补与缩放,再训练候选 λ,最后评分未来日期。窗长、因子子集和标准化方式也属于选择压力。不要看最终测试后再换 λ,然后继续称测试独立。

页面实验使用正交、标准化设计的解析解,以便看清收缩与置零。相关因子的真实路径由下方 Python 坐标下降计算;不能把这张解析图直接解释为任意因子组的路径。

从算法到一次真实运行

代码在本地 Python 中执行,HTML 内展示参考实现和本轮运行结果。请先独立完成练习,再展开完整参考文件。

regularization.py / 核心函数
def ols_fit(X, y):
    """X excludes intercept. Return intercept, slopes, rank of centered X."""
    X, y = np.asarray(X, float), np.asarray(y, float)
    if X.ndim != 2 or y.shape != (len(X),) or len(X) == 0:
        raise ValueError("Expected nonempty X(n,p) and y(n)")
    if not np.isfinite(X).all() or not np.isfinite(y).all():
        raise ValueError("Resolve nonfinite data first")
    mean = X.mean(axis=0)
    coef, _, rank, _ = np.linalg.lstsq(X-mean, y-y.mean(), rcond=None)
    return float(y.mean()-mean@coef), coef, int(rank)

def ridge_fit(X, y, lam):
    """SSE/(2n) + lam*||beta||^2/2; do not penalize intercept."""
    if not np.isfinite(lam) or lam < 0:
        raise ValueError("lam must be nonnegative and finite")
    if lam == 0:
        b, coef, _ = ols_fit(X, y)
        return b, coef
    X, y = np.asarray(X, float), np.asarray(y, float)
    ols_fit(X, y)  # Validate the input; not used as the regularized solution.
    mean = X.mean(0)
    xc, yc = X-mean, y-y.mean()
    coef = np.linalg.solve(xc.T@xc + len(X)*lam*np.eye(X.shape[1]), xc.T@yc)
    return float(y.mean()-mean@coef), coef

def soft_threshold(value, lam):
    return np.sign(value)*np.maximum(np.abs(value)-lam, 0)

def lasso_fit(X, y, lam, iterations=2000, tol=1e-9):
    """Centered cyclic coordinate descent; SSE/(2n)+lam*sum(abs(beta))."""
    if not np.isfinite(lam) or lam < 0:
        raise ValueError("Invalid penalty")
    X, y = np.asarray(X, float), np.asarray(y, float)
    ols_fit(X, y)
    mean, target_mean = X.mean(0), y.mean()
    xc, yc = X-mean, y-target_mean
    coef = np.zeros(X.shape[1]); residual = yc.copy()
    scale = np.mean(xc*xc, axis=0)
    converged = False
    for step in range(iterations):
        old = coef.copy()
        for j in range(len(coef)):
            residual += xc[:, j]*coef[j]
            c = xc[:, j]@residual/len(X)
            coef[j] = soft_threshold(c, lam)/scale[j] if scale[j] > 0 else 0
            residual -= xc[:, j]*coef[j]
        if np.max(np.abs(coef-old)) < tol:
            converged = True
            break
    return float(target_mean-mean@coef), coef, converged
下载本节 .pypython regularization.py依赖版本见首页,示例只使用合成数据。
查看本轮本地运行记录 · 2026-10-12
{
  "lambda": 0.05,
  "ridge_alpha": 3.0,
  "ridge_coef": [
    1.0348052927178804,
    -0.004242126423485157,
    0.9766647161195813
  ],
  "lasso_coef": [
    1.372627212578412,
    0.0,
    0.6391351448868879
  ],
  "lasso_converged": true,
  "ridge_reference_max_error": 2.6645352591003757e-15,
  "lasso_reference_max_error": 9.572640990995751e-08
}

先回答,再看提示和答案

Q01

n=80、λ=.1,Ridge 的 alpha 应是多少?

一级提示

把整式乘 2n。

参考答案与条件

8。这里目标采用 SSE/(2n)+λ‖β‖²/2;换了目标约定要重新推。

Q02

能把 OLS 系数一次软阈值当作一般 Lasso 解吗?

一级提示

先写出目标、信息集和所需条件。

参考答案与条件

一般不能。列不正交时每个坐标依赖其他列残差,需要迭代优化。

Q03

Ridge 的每个原坐标系数都会单调收缩吗?

一级提示

先写出目标、信息集和所需条件。

参考答案与条件

不保证。收缩在奇异向量方向具有清晰比例,原坐标是方向混合,可能非单调。

把知识迁移到一个新任务

实现 Ridge 与 Lasso 坐标下降;分别用常数列、重复列、不同单位的列测试。比较 coef 的稳定性和同一留出集合的误差,不凭一次系数入选宣布因子有效。

这里保存学习笔记,不在浏览器执行 Python。A/B/C 由你按独立完成程度填写。

参考与进一步阅读

在线文档可能比本地固定版本更新;API 行为以已验证代码和版本记录为准。

掌握状态与复习

关掉资料独立完成后记 A;提示后完成记 B;需要重学记 C。

记录尽量保存在当前浏览器,建议定期导出备份。