5.3 梯度、条件数与正则化路径:收敛到什么比跑了多少轮更重要
同一份数据上,某个学习率可能让 loss 发散,更小的值又可能几乎不动。目标函数即使凸,优化速度仍取决于曲率、尺度、稀疏性和 solver。标准化常能改善这个问题,但“又跑了一千轮”本身不算诊断。
本课目标
- 推导 batch/minibatch gradient 的计算成本;
- 理解 convexity、Hessian 与 conditioning;
- 区分 L1、L2 与 Elastic Net 的几何和算法;
- 用 pipeline/CV 选择正则并验证收敛。
1. Batch gradient descent
线性回归 MSE:
每次 dense full-batch gradient 通常需要
下面是 Python 3 + NumPy 函数片段(partial),输入设计矩阵与响应向量。tolerance 是梯度范数的绝对阈值,max_steps 是更新预算;返回的 loss 与返回的 w 对应,预算耗尽会返回 converged=False。
import numpy as np
def fit_linear_gd(X, y, learning_rate, max_steps, tolerance):
X, y = np.asarray(X, dtype=float), np.asarray(y, dtype=float)
if X.ndim != 2 or y.ndim != 1 or len(y) == 0 or len(X) != len(y):
raise ValueError("X must be a nonempty matrix and y a matching vector")
if not np.isfinite(X).all() or not np.isfinite(y).all():
raise ValueError("X and y must be finite")
if (not np.isfinite(learning_rate) or learning_rate <= 0
or not np.isfinite(tolerance) or tolerance <= 0
or not isinstance(max_steps, int) or max_steps < 0):
raise ValueError("invalid step size, tolerance, or update budget")
w = np.zeros(X.shape[1])
for step in range(max_steps + 1):
with np.errstate(over="raise", invalid="raise"):
residual = X @ w - y
loss = float(np.mean(residual ** 2))
gradient = (2.0 / len(y)) * X.T @ residual
gradient_norm = float(np.linalg.norm(gradient))
if not np.isfinite(loss) or not np.isfinite(gradient_norm):
raise FloatingPointError("non-finite optimization state")
converged = gradient_norm <= tolerance
if converged or step == max_steps:
return w, {
"steps": step, "loss": loss,
"gradient_norm": gradient_norm, "converged": converged,
}
w -= learning_rate * gradient只跑固定轮数不能证明收敛。学习率极小时,loss 变化小也可能只是参数没怎么移动。本例检查 gradient norm;阈值仍依赖尺度和条件数,不能直接等同于预测误差界。用 max_steps=0 应得到零初始参数及其真实 loss,用一次更新则应得到新参数对应的 loss。
2. Convex 不等于优化轻松
Squared loss Hessian:
它总是 positive semidefinite;只有满足相应满秩条件时才 positive definite/严格凸。相关特征使 Hessian 条件数大,等高线狭长,固定 learning rate 沿陡方向震荡、沿平方向缓慢。
标准化改善许多优化器的 conditioning,也让正则惩罚可比较,但不是所有树模型/闭式求解都“必须标准化”。
3. SGD 与 minibatch
- SGD 用一个样本的无偏/随机梯度估计;
- minibatch 平衡向量化效率与梯度噪声;
- epoch 表示大致遍历一次数据,不代表独立样本;
- shuffle、batch size 和 learning-rate schedule 共同影响轨迹。
数据有时间/群组结构时,随机 shuffle 可能改变训练分布或破坏 stateful 模型。分布式 batch 增大还需调整学习率并验证泛化。
4. 选择 solver
平方损失和逻辑损失使用的 solver 不完全相同:
- QR/SVD/least-squares solver 直接解决平方损失;
- normal-equation variants(谨慎数值稳定);
- gradient/accelerated gradient;
- Newton、quasi-Newton(L-BFGS);
- coordinate descent;
- stochastic variance-reduced 方法。
选择看
5. L2、L1 与 Elastic Net
L2
平滑收缩,常在相关特征间分散权重并改善 conditioning;不是“鼓励权重均匀”。
L1
非光滑,最优解可含精确零;通常需 coordinate descent、proximal methods 等。相关特征中选哪一列可能不稳定,零系数不证明无预测信息。
Elastic Net
组合 L1/L2,在稀疏与相关组稳定性间调节。两个正则参数/混合比例都要在 validation 内选择。
6. 正则强度和尺度
不同库用 alpha、lambda、C=1/lambda,还可能对 loss 取 sum 或 mean,参数数值不能跨实现直接比较。
通常不惩罚 intercept;one-hot/drop-reference 策略会影响 penalty symmetry。数值特征未缩放时,同样系数 penalty 对原始单位的函数变化不同。
7. Pipeline 与 CV
下面的 Python 3 + scikit-learn 片段(partial)依赖样本对齐的 X、y、team_id。至少需要五个组,每折训练和验证都要有足够类别覆盖;时间泛化应改用预先设计的时间切分。这里搜索默认 L2 逻辑回归的 C,保留完整分组,并在折内拟合缺失值和缩放参数。
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV, GroupKFold
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
candidate = Pipeline([
("impute", SimpleImputer(strategy="median")),
("scale", StandardScaler()),
("model", LogisticRegression(max_iter=2000)),
])
search = GridSearchCV(
candidate,
param_grid={
"model__C": [0.01, 0.1, 1.0, 10.0],
},
scoring="neg_log_loss",
cv=GroupKFold(n_splits=5),
error_score="raise",
)
search.fit(X, y, groups=team_id)这段在 scikit-learn 1.9.0 验证,使用默认 lbfgs 和 L2 设置;没有显式传入该版本已弃用的 penalty 参数。换 solver 或 L1/Elastic Net 时先核对 LogisticRegression 官方参数说明。缩放、缺失处理和选择全部在 fold 内 fit。
8. 收敛警告不能忽略
不收敛可能来自:
- 尺度/条件数差;
- 完全分离;
- 正则太弱;
max_iter太小;- learning rate 太大;
- solver 与 penalty/稀疏输入不匹配;
- 数据含 NaN/Inf 或巨大 outlier。
只把 max_iter 提高十倍可能拖延根因。比较 objective、gradient、参数范围和不同 solver,并在同一 validation protocol 下验证。
9. 正则化路径与稳定性
沿
- train/validation loss;
- 非零系数数和系数路径;
- 折间入选频率;
- calibration/群体误差;
- 收敛状态和运行时间。
One-standard-error rule 可在性能相近时选择更简单/更强正则方案。最终选择仍依赖业务损失,而非“非零越少越好”。
10. 线性模型为何仍是强 baseline
- 训练和推断便宜;
- 稀疏高维可扩展;
- feature contribution 结构明确;
- 易校准、监控和部署;
- 对小数据常更稳定。
但 feature engineering、共线性、外推和概念漂移仍可能失效。解释性来自完整 pipeline 和特征语义,不是模型类标签。
常见误区
- GD 每轮是
:还要乘特征维度与数据布局。 - Hessian 总是正定:秩亏时只半正定。
- L1 自动找出真正变量:相关性和 lambda 会改变选择。
- 收敛 warning 只需增加轮数:先查尺度、分离和 solver。
练习
- 比较未缩放/缩放数据的 Hessian 条件数与 GD 轨迹。
- 对 rank-deficient X 判断 Hessian 特征值。
- 绘制 L1/Elastic Net 正则路径和折间选择频率。
- 对同一个平方损失目标,在相同 CV 下比较 QR、L-BFGS 与 SGD 的时间和 loss。
小结
线性模型的目标常是凸的,却仍需要正确的数值表示和 solver。正则化既改善泛化与稳定性,也改变 estimand;选择它要看 validation、系数路径和实际决策。
下一章进入树与集成:通过递归分区表示非线性和交互,同时面对深度、方差、boosting 目标与概率校准的新问题。