损失、泛化与优化
本章目录
学完后,你应能独立完成
- 解释平方损失的偏差方差分解
- 手算 GD 更新和二次目标的稳定界
- 推导稳定的交叉熵梯度并检查数值误差
前置建议:线性回归
改变一个条件,观察发生什么
浏览器内即时教学计算;Python 实验结果另列于代码区。图形与数值一起核对。
经验风险与目标风险
经验风险是当前训练样本的平均损失;总体风险是目标分布下的新损失期望。ERM 最小化经验风险,正则化限制函数或参数。训练误差低不代表新风险低,测试估计的也只是指定训练政策在该评价分布下的效果。
平方损失对应条件均值,绝对损失对应条件中位数;Huber 在小误差处二次、大误差处线性。固定方差高斯、固定尺度 Laplace 和 Bernoulli 条件模型分别给出这些常见损失对应的似然解释。输入不需要正态。
偏差方差分解的期望到底对谁取
固定 x,写 Y=f*(x)+ε,条件噪声均值为零,训练集 D 与新噪声独立。令 m=E_D f̂_D(x)。误差可以拆成新噪声、平均预测偏差和训练随机性导致的偏离。平方展开后,交叉项在这些条件下消失。
这是对训练随机性与新噪声的期望,不是一次拟合就能观测到的三项。换测试分布之后,原分解中的函数和分布也改变。分类 0–1 损失不能照搬同一表达式。
梯度与曲率决定步幅
梯度给局部增大的方向,因此最小化时沿负梯度更新。Hessian 描述曲率。凸性不保证有限最优点存在;完全可分 Logistic 是典型反例。强凸带来更强的唯一性结论,但仍需存在性与明确的定义域。
- 正定二次目标的更新为 w←(I−ηH)w。
- 特征方向 j 的误差乘上 1−ηh_j。
- 每方向 |1−ηh_j|<1 才收缩,因此 0<η<2/h_max。
- 该精确界仅对应本二次目标,不是所有网络的万能学习率规则。
GD、SGD 与小批量
全批 GD 使用全部梯度;均匀抽样的单样本或小批均值,在给定训练集目标下是无偏梯度估计。训练集内洗牌并不等同于随机拆训练测试。权重或抽样概率改变时,需要保证抽样梯度与目标相匹配。
小批随机步骤不保证每一步损失都下降。梯度使用和还是平均会改变尺度;学习率、batch 大小、训练轮数和计算预算需要一起解释。Adam 的二阶矩不是 Hessian,也不是减去均值后的梯度方差。
稳定计算与梯度检查
交叉熵可以写成 softplus(z)−yz,使用 logaddexp(0,z) 而不是直接 exp(z)。对 logit 的导数是 p−y,再乘输入并平均。中央差分用两个邻近损失近似导数;步长太小有消减误差,太大有截断误差。
有限差分可以发现反传与损失不一致,但错误的损失和梯度也可能彼此一致。应先核对目标、样本/输出归一化和形状;ReLU 拐点处不要要求普通可微误差标准。
如何定位训练好、验证差
先检查时点、标签和评价对齐,再检查简单基线、优化和数据。训练/验证都差可能是特征弱或优化失败;训练好未来差可能是容量、搜索过拟合、分布变化或流程问题。一次分数不足以区分它们。
早停使用验证标签选择迭代位置,是模型选择。开发折选轮数、测试前锁定规则;把最终测试放进 early stopping 已经改变测试的性质。增加旧数据还同时改变训练年代,学习曲线不能完全隔离数量与漂移。
从算法到一次真实运行
代码在本地 Python 中执行,HTML 内展示参考实现和本轮运行结果。请先独立完成练习,再展开完整参考文件。
def logistic_loss_grad(Z, y, theta):
"""Z already includes an intercept column; mean negative log likelihood."""
z = Z@theta
loss = np.mean(np.logaddexp(0, z)-y*z)
grad = Z.T@(expit(z)-y)/len(Z)
return float(loss), grad
def logistic_gd(Z, y, rate=.1, steps=300):
theta = np.zeros(Z.shape[1]); history = []
for _ in range(steps):
loss, grad = logistic_loss_grad(Z,y,theta)
history.append(loss)
theta -= rate*grad
return theta, np.asarray(history)"""Standalone synthetic teaching example. Run locally with Python, not in the HTML."""
import json
import numpy as np
import pandas as pd
from scipy.special import expit
def clean(value):
if isinstance(value, dict): return {str(k):clean(v) for k,v in value.items()}
if isinstance(value,(list,tuple,np.ndarray)): return [clean(v) for v in value]
if isinstance(value,(np.integer,)): return int(value)
if isinstance(value,(float,np.floating)): return float(value) if np.isfinite(value) else None
if isinstance(value,(np.bool_,)): return bool(value)
return value
def print_result(result):
print(json.dumps(clean(result),ensure_ascii=False,allow_nan=False))
def logistic_loss_grad(Z, y, theta):
"""Z already includes an intercept column; mean negative log likelihood."""
z = Z@theta
loss = np.mean(np.logaddexp(0, z)-y*z)
grad = Z.T@(expit(z)-y)/len(Z)
return float(loss), grad
def logistic_gd(Z, y, rate=.1, steps=300):
theta = np.zeros(Z.shape[1]); history = []
for _ in range(steps):
loss, grad = logistic_loss_grad(Z,y,theta)
history.append(loss)
theta -= rate*grad
return theta, np.asarray(history)
if __name__ == "__main__":
rng=np.random.default_rng(42)
Z=np.column_stack([np.ones(12),rng.normal(size=(12,2))])
y=rng.integers(0,2,12); theta=rng.normal(size=3)
loss,gradient=logistic_loss_grad(Z,y,theta)
eps=1e-6
numeric=np.array([(logistic_loss_grad(Z,y,theta+eps*e)[0]-
logistic_loss_grad(Z,y,theta-eps*e)[0])/(2*eps) for e in np.eye(3)])
np.testing.assert_allclose(gradient,numeric,atol=1e-8)
curves={}
for rate in [.1,.25]:
w=np.ones(2); values=[]
for _ in range(20):
values.append(.5*np.sum(np.array([1.,10.])*w*w))
w-=rate*np.array([1.,10.])*w
curves[str(rate)]={"first_loss":values[0],"last_loss":values[-1]}
print_result({"gradient_max_error":abs(gradient-numeric).max(),"quadratic":curves,
"stable_loss_at_logit_1000":logistic_loss_grad(np.ones((1,1)),np.zeros(1),np.array([1000.]))[0]})
查看本轮本地运行记录 · 2026-10-12
{
"gradient_max_error": 1.2225154222278434e-10,
"quadratic": {
"0.1": {
"first_loss": 5.5,
"last_loss": 0.009124001815700365
},
"0.25": {
"first_loss": 5.5,
"last_loss": 24571849.602097858
}
},
"stable_loss_at_logit_1000": 1000.0
}先回答,再看提示和答案
H=diag(1,10),η=.25 为什么发散?
一级提示
逐方向看一次更新的倍数。
参考答案与条件
第二方向乘上 1−.25×10=−1.5,绝对值大于 1,振荡幅度增加。稳定界是 η<.2。
SGD 每一步都会降低训练目标吗?
一级提示
先写出目标、信息集和所需条件。
参考答案与条件
不会。无偏是对抽样梯度的条件期望而言,单步可走向相反方向。
最终测试只用于选 checkpoint,没有计算梯度,是否仍独立?
一级提示
先写出目标、信息集和所需条件。
参考答案与条件
不独立。用测试标签选择模型同样是选择,即使没有反传。
把知识迁移到一个新任务
写稳定 Logistic loss/gradient 和 GD;与中央差分对照。再把二次目标学习率从 .1 改成 .25,解释轨迹与损失。区分优化误差和未来研究误差。
这里保存学习笔记,不在浏览器执行 Python。A/B/C 由你按独立完成程度填写。
参考与进一步阅读
在线文档可能比本地固定版本更新;API 行为以已验证代码和版本记录为准。
掌握状态与复习
关掉资料独立完成后记 A;提示后完成记 B;需要重学记 C。
记录尽量保存在当前浏览器,建议定期导出备份。