01 / 评估的是泛化能力
三份数据,承担三种工作
泛化指模型把训练中学到的规律用于没见过的样本。训练误差低,只说明它对已经用来更新参数的数据拟合得好,不能单独证明泛化能力。
训练集
计算梯度、学习权重与偏置。
例如:拟合多项式的各项系数。
验证集
选择模型、超参数、阈值和停止时间。
例如:比较 3 阶与 9 阶模型。
测试集
方案确定后,评估最终表现。
不再根据这份结果反复调整方案。
验证集通常不直接参与梯度更新,但它会影响你的方案选择,因此也不能无限次尝试后把验证成绩当作独立的最终成绩。
怎样划分?
- 样本近似独立同分布时,可以随机划分;分类时常用分层抽样保留类别比例。
- 时间序列通常按时间划分,使用过去训练、未来评估。
- 同一个人、设备或重复来源的样本,必要时按组划分,避免高度相似数据跨集合。
例如 70%/15%/15% 只是参考比例,实际取决于数据量、类别分布和任务,不是固定规则。
02 / 拟合训练点,还是学到规律?
亲手制造一次过拟合
下面用多项式回归:ŷ=w₀+w₁x+w₂x²+…+wₖxᵏ。阶数 k 越高,曲线通常越灵活,但灵活不一定代表对新样本更好。
教学数据来自同一条平滑规律,加上固定的小扰动。训练集有 10 个点,验证集有 21 个点。虚线展示生成数据的真实规律,仅用于教学;实际项目通常不知道它。
实验 A:比较训练与验证误差
纵轴会随曲线自动调整;跨方案比较时,同时查看下面的 MSE 数值。
建议这样操作
- 选择 0 阶、λ=0:看训练和验证是否都拟合不好。
- 选择 3 阶、λ=0:看曲线是否能抓住整体趋势。
- 选择 9 阶、λ=0:训练误差接近零,验证误差是否反而增大?
- 保持 9 阶,把 λ 改为 0.001,观察曲线与误差变化。
验证结果之后,最后再查看测试集
先根据训练与验证结果选好参数,再点击下方按钮。测试集包含 23 个独立保留的教学样本;按钮会锁定参数,避免看了结果再继续调整。
重新加载网页可重做教学实验,但已看过的测试数据不会因此重新成为独立测试集。真实工作中,若已据它调参,应准备新的独立评估数据。
三个概念怎么区分?
| 现象 | 常见线索 | 可尝试的方向 |
|---|---|---|
| 欠拟合 | 训练、验证表现都较差 | 改善特征、增加合适的模型能力、检查优化是否充分 |
| 较好的拟合 | 训练与验证表现都符合任务要求 | 确认评估可靠,再做独立测试 |
| 过拟合 | 训练很好,验证较差 | 简化模型、增加数据、正则化、早停 |
训练与验证差距大,也可能有数据分布变化或评估错误,不能只看两个数字就断言原因。
偏差、方差与噪声
偏差描述模型在多次不同训练数据下的平均预测与真实规律之间的系统差异;方差描述训练数据变动时,模型预测的波动。噪声是数据中难以根据现有输入预测的随机部分。
能力太受限的模型常有较高偏差;过于灵活且数据少时,可能有较高方差。一次拟合图只能提供线索,不能直接测量完整的偏差与方差分解。
03 / 让模型为复杂系数付出代价
L2 正则化怎样改变目标?
原来只最小化训练 MSE;现在再加入系数大小的惩罚。本例不惩罚常数项 w₀:
λ=0 时不加惩罚;λ 增大时,较大的系数更昂贵。这样可能减少为追逐训练噪声产生的大幅弯曲,但太强也可能压制有用规律,导致欠拟合。
上面的实验直接求解这个回归目标的最优系数,不是第二节逐步梯度下降的动画。两种训练方式都可以学习模型参数。
神经网络还可采用其他约束方式,例如 Dropout;相关原理在深度学习阶段展开。正则化并不保证每个任务的效果都提高。
04 / 验证不再改善时停止
早停:保留最好时刻的参数
即使模型结构不变,继续训练也可能越来越贴合训练噪声。早停通过验证集判断什么时候停止,并保存验证表现最好时的参数。
实验 B:观察真实训练记录中的最佳检查点
仍使用上面的 9 阶模型,λ=0,从全零系数开始,以学习率 0.3 做全批量梯度下降。每 100 次更新检查一次验证 MSE;下图来自这份固定数据的实际计算。
patience=3 表示连续 3 次验证检查没有改善后停止。本图允许查看没有启用早停时的后续记录,以比较走势;真正采用早停的训练会在触发时结束,并恢复此前保存的最佳参数。
05 / 评估时有没有偷看答案?
数据泄漏会让结果看起来过好
数据泄漏是把评估时不应获得的信息引入训练或方案选择,导致离线成绩不能可靠代表实际使用效果。
| 问题例子 | 为什么有问题 | 正确做法 |
|---|---|---|
| 先用全部数据计算标准化均值,再划分 | 训练预处理使用了评估数据的统计信息 | 先划分;只在训练集拟合,再应用到验证与测试 |
| 重复邮件分别进入训练与测试 | 测试样本与已见数据高度相似 | 去重或按来源分组划分 |
| 用未来记录预测过去 | 实际预测时不可能知道未来信息 | 按时间与特征可获取时刻划分 |
| 根据测试成绩反复修改阈值 | 测试参与了方案选择 | 在验证集选择阈值,最后评估测试集 |
# 正确顺序(概念示例)
mean = X_train.mean(axis=0)
std = X_train.std(axis=0)
std = np.where(std == 0, 1, std)
X_train_scaled = (X_train - mean) / std
X_val_scaled = (X_val - mean) / std
X_test_scaled = (X_test - mean) / std
06 / 先数四种情况,再选择指标
为什么准确率高也可能没用?
用垃圾邮件识别举例:正类 y=1 是垃圾邮件,负类 y=0 是正常邮件。模型输出“属于垃圾邮件”的分数 p,用阈值决定预测类别。本例把样本视作验证数据,阈值在此选择。
这 20 封邮件中只有 4 封是垃圾邮件。如果全部判为正常,准确率有 80%,但一封垃圾邮件也找不出来。
实验 C:改变阈值,观察四种结果
规则:p ≥ t 时预测为垃圾邮件;p < t 时预测为正常邮件。
| 真实垃圾 y=1 | 真实正常 y=0 | |
|---|---|---|
| 预测垃圾 ŷ=1 | ||
| 预测正常 ŷ=0 |
TP:正确找到垃圾;FP:把正常邮件误判为垃圾;FN:漏掉垃圾;TN:正确保留正常邮件。
| 指标 | 当前值 | 计算式 / 回答的问题 |
|---|
若分母为 0,本讲义约定对应指标显示为 0;其他工具也可能显示“未定义”。F1 用等价计数公式计算:2TP/(2TP+FP+FN)。
F1 怎样把精确率和召回率结合起来?
例如 P=40%、R=50%,F1≈44.4%。其中一个很低时,F1 也会偏低;分母为零的显示约定见上方说明。
选哪个指标,取决于错一次的后果
- 精确率(precision):被判为垃圾的邮件,有多少是真的垃圾?高精确率有助于减少正常邮件被误伤。
- 召回率(recall):实际垃圾邮件,有多少被找到了?高召回率有助于减少漏检。
- F1:精确率与召回率的调和平均,兼顾二者;它不使用 TN,也不直接反映所有错误成本。
- 准确率(accuracy):全部邮件中判对了多少?类别不平衡时,需要同时查看其他指标与类别比例。
阈值降低时,预测为正的样本会增多;召回率不会下降,但精确率在有限数据上不一定每一步都下降。不能只靠默认的 0.5 阈值判断模型适合任务。
07 / 把模型训练放进可靠流程
一个项目的评估顺序
- 明确预测目标、正类定义、错误成本与评估指标。
- 检查重复来源、时间关系与数据分布,划分训练、验证、测试集。
- 只用训练集拟合预处理与参数,建立简单基线。
- 在验证集比较模型、正则化、停止时间和阈值,做错误分析。
- 冻结方案,再在测试集评估,记录样本数量和分布。
- 实际使用后继续关注分布变化;离线评估不保证未来效果永远不变。
08 / 让实验可复现
配套 NumPy 练习
下载第三节练习代码。代码复现多项式拟合与 L2 对比、真实早停检查记录、混淆矩阵与指标,运行后导出 CSV。在终端进入项目根目录后运行:
uv run python lessons/lesson-03-practice.py
输出目录是代码文件旁的 lesson-03-output。只依赖 NumPy,无需深度学习框架。
练习与验收
- 记录 0、3、9 阶模型在 λ=0 时的训练与验证 MSE,解释为什么训练最好不代表验证最好。
- 保持 9 阶,通过验证比较 λ,再锁定参数查看测试表现。
- 说明为什么早停在第 500 步触发,却恢复第 200 步参数。
- 手算 t=0.5 时的 TP、FP、FN、TN,以及四个分类指标。
- 将阈值改为 1,解释为什么准确率 80% 而召回率 0%。
- 举一个与你的数据任务有关的泄漏例子,并写出避免方法。
09 / 自测