Skip to content

案例拆解:同一网络换六种配置的对照实验 ​

理论篇拆开了四种失败机制。这一篇把它们放进同一个网络、同一批数据、同一个随机种子里,每次只动一个变量,看每个机制到底值多少个百分点的验证准确率。

先说结论:八个轮次的预算下,单变量收益最大的是 Adam(+3.53),其次是 BatchNorm(+2.91)、输入标准化(+2.25),He 初始化几乎无感(+0.34),正则单独用甚至是负收益(−0.29)。但这不是「配置排行榜」,而是「在 8 轮、这个网络、这个优化器下」的排序。任何一项换掉预算,排序都会变。

一、控制变量:把六次实验压成一张表 ​

网络固定为 784 → 128 → 64 → 10,109,386 个参数。数据固定为 MNIST 50,000 张训练、10,000 张验证,batch=64,每轮 781 步。随机种子 20260815,40 轮的 batch 顺序预先生成,所有配置共用同一份。

配置本身是一个扁平字典,每次只改一个键:

python
base = {
    "sizes": [784, 128, 64, 10],
    "act": "relu",
    "init": "xavier",
    "input": "raw",      # C1 改成 "std"
    "norm": None,        # C3 改成 "bn"
    "dropout": 0.0,      # C5 改成 0.2
    "opt": "sgd",        # C4 改成 "adam"
    "lr": 0.01,          # C4 改成 1e-3
    "wd": 0.0,           # C5 改成 1e-4
    "epochs": 8,
}

batch 顺序也共用:

python
rng = np.random.default_rng(20260815)
epochs_idx = [rng.permutation(50000) for _ in range(40)]
# 每个配置的第 ep 轮都用 epochs_idx[ep],不重新抽样

为什么固定 batch 顺序:如果每个配置各自 shuffle,C0 与 C4 看到的梯度序列就不一样,8 轮的差异里混进了数据顺序的噪声。固定之后,六条曲线的前后差异只剩配置本身。这也意味着所有实验都只有一个种子,结论不能拿来做显著性检验——本章的用途是看量级,不是争 0.02 个百分点。

基线 C0 选的是一组「教科书默认」:原始 0–1 像素、Xavier 初始化、SGD 学习率 0.01、不加 BN、不加 Dropout。它不是故意设得很差——SGD 0.01 在这个网络上 8 轮能到 93.75%,是一个正常起点。

![六种配置的控制变量表](/ai-four-kings/第 3 章:训练工程/assets/ch3_control_vars.svg)

<!-- 配图:六行配置表,每行标注相对 C0 的唯一改动、8 轮验证 acc 条形与增量 -->

配置相对 C0 的唯一改动参数8 轮 val accΔ
C0基线:原始像素 + Xavier + SGD 0.01109,38693.75%—
C1输入标准化109,38696.00%+2.25
C2He 初始化109,38694.09%+0.34
C3每层加 BatchNorm109,79096.66%+2.91
C4Adam 1e−3109,38697.28%+3.53
C5Dropout 0.2 + Weight Decay 1e−4109,38693.46%−0.29

C3 的参数多了 404 个。这不是误差:128 + 64 + 10 = 202 个输出维度,每个 BN 有 γ、β 两个参数,202 × 2 = 404。注意输出层也建了 BN——这个选择后面还会出现,最终模型也是这么建的。

这张表最重要的信息不是排序,而是每一行的 Δ 都只对应一个改动。C1 只改输入处理,C2 只改初始化,C3 只加 BN,C4 只换优化器,C5 只加正则。没有「C4 = C1 + C3 + Adam」这种叠加行,因为一旦叠加,就说不清收益归谁。

一个几乎无感的变量:He 初始化 ​

C2 只涨了 0.34 个百分点,和 C0 的 93.75% 几乎贴在一起。第一反应是「He 没用」,但对照第一层的激活标准差:

配置第 1 轮 act std(层 1 / 层 2)第 8 轮 act std
C0 Xavier0.401 / 0.6850.482 / 0.913
C2 He0.399 / 0.7260.480 / 0.941
C1 标准化 + Xavier0.967 / 1.1971.156 / 1.481

C0 和 C2 的激活尺度几乎一模一样。原因是 C0 的输入还是原始 0–1 像素,整体尺度偏小,Xavier 与 He 的方差差异(2/912 对 2/784,约 1.16 倍)被压住了。C1 把输入标准化之后,同一层的激活标准差直接抬到 0.967,He 与 Xavier 的差异才显出来(理论篇的 0.662 对 0.614)。

踩坑记录

  • 现象:C2「He 初始化」相对 C0 只提升 0.34 个百分点,看起来像噪声。
  • 原因:输入仍是原始 0–1 像素,信号整体偏小;初始化的方差差异被输入尺度掩盖,第一层激活 std 只从 0.401 变到 0.399。
  • 修复:先统一输入尺度,再比较初始化。标准化之后,He 的第一层激活 std 到 0.662,Xavier 是 0.614,差异才可读。

这条的顺序很重要:先归一化,再谈初始化。反过来做,会得出「初始化不重要」的错误结论。

二、八轮曲线:差距主要在开局 ​

![六种配置的验证曲线](/ai-four-kings/第 3 章:训练工程/assets/ch3_six_curves.svg)

<!-- 配图:六条验证 acc 曲线,C0 灰、C1 黄、C2 紫、C3 绿、C4 蓝、C5 红;右侧最终值面板 -->

配置第 1 轮 val acc第 8 轮 val acc8 轮 val loss
C087.51%93.75%0.2270
C191.08%96.00%0.1377
C288.07%94.09%0.2142
C391.67%96.66%0.1357
C495.29%97.28%0.1075
C586.49%93.46%0.2246

C4 第 1 轮就到 95.29%,C0 第 1 轮只有 87.51%。八轮结束时 C4 97.28%、C3 96.66%、C1 96.00%,三条曲线都还没平——它们不是「收敛到不同的天花板」,而是「以不同速度爬同一座山」。8 轮的排名,本质是速度排名。

把 C0 与 C4 逐轮摊开:

轮次C0 train accC0 val accC4 train accC4 val acc
176.32%87.51%91.70%95.29%
289.42%90.19%96.45%96.50%
390.90%91.13%97.44%96.91%
491.69%91.70%98.13%96.91%
592.32%92.36%98.44%97.01%
692.89%92.72%98.84%97.30%
793.26%93.10%98.97%97.37%
893.77%93.75%99.18%97.28%

C4 从第 3 轮开始 val acc 就进入 96.9–97.3% 的窄带,训练 acc 却从 97.44% 一路涨到 99.18%;C0 则两条线几乎贴在一起。同样是「还没收敛」,C4 是「验证先到顶、训练继续拟合」,C0 是「两边都还在爬」。这两种「没收敛」对应的下一步动作完全不同。

C5 的曲线最慢:第 1 轮 86.49%,比 C0 还低 1.02。Dropout 0.2 在训练时随机丢掉 20% 的激活,前几轮有效容量更小;Weight Decay 又把权重往 0 拉。单独加正则、不加容量和轮数,收益自然是负的。

把增量拆开看:

![单变量收益](/ai-four-kings/第 3 章:训练工程/assets/ch3_six_delta.svg)

<!-- 配图:五根横向条形,从 −0.29 到 +3.53,0 处有竖线;底部注明收益不可相加 -->

单变量收益排序:Adam +3.53 > BatchNorm +2.91 > 输入标准化 +2.25 > He +0.34 > Dropout+WD −0.29。

但这些数字不能相加。2.25 + 2.91 + 3.53 = 8.69,如果真能叠加,C0 的 93.75% 应该变成 102.44%——不可能。真实情况是:同一个变量在不同组合里的边际收益会迅速变小。输入标准化在 SGD 0.01 下值 +2.25;换成 Momentum 0.01、He 初始化时,原始像素 97.29%、标准化 97.41%,只值 +0.12。BN 在 SGD 下的 8 轮增益是 +2.91,在 Momentum 下第 8 轮只差 +0.53,但到 97% 的轮次从 7 降到 3。同一个变量换了优化器,收益的量纲就变了。

最终配置验证 98.46%,比 C0 高 4.71 个百分点,但那个模型换了宽度(256/128)、换了轮数(30 轮)、加了早停。它只能说明「这套组合能到 98.46%」,不能拿来验证「单变量收益可以相加」。

设计取舍:8 轮还是 30 轮 ​

方案收益代价
A:8 轮快照六次实验几分钟跑完,差异集中在「前几轮速度」,适合看优化器与归一化排名被预算绑死:慢配置可能只是没跑够,C0 在 8 轮时仍明显欠拟合
B:30 轮跑满更接近最终性能,排名更稳六配置 × 30 轮的时间成倍增长;过拟合会混进来,单变量不再干净

这一篇选 A。它回答的是「哪个变量能更快把 loss 压下去」,不是「哪个配置最终最强」。最终强度留给实战篇的 30 轮。

三、学习率扫描:平台很宽,悬崖很陡 ​

固定 Adam,只扫学习率,从 1e−4 到 1e−1 取 8 个点,每个训 6 轮。

![Adam 学习率细扫](/ai-four-kings/第 3 章:训练工程/assets/ch3_lr_scan_case.svg)

<!-- 配图:上图 acc 随 lr 变化,下图 val loss;3e−2 起红色悬崖区,1e−3 绿色虚线 -->

学习率6 轮 val acc6 轮 val loss
1e−496.03%0.1312
3e−497.00%0.0986
1e−397.02%0.1149
2e−395.97%0.1543
3e−396.23%0.1492
1e−294.72%0.2263
3e−289.72%0.3913
1e−116.94%1.9158

平台在 3e−4 到 1e−3:97.00% 和 97.02%,差 0.02 个百分点。1e−4 还没跑够,6 轮只有 96.03%;2e−3 开始掉,1e−2 还有 94.72%,到 1e−1 只剩 16.94%,loss 1.9158——模型基本把输出推成了错误类的极端值。

平台内部 0.02 个百分点的差异不值得追。单种子、6 轮、尾批丢弃,这些都会带来同量级的波动。调参的正确粒度是先定数量级(1e−3 还是 1e−2),再在平台里选一个,而不是在小数点后两位上做文章。

四、优化器 × 学习率:没有通用组合 ​

同一个 6 轮预算,三种优化器 × 四个学习率:

![优化器与学习率网格](/ai-four-kings/第 3 章:训练工程/assets/ch3_grid_heatmap.svg)

<!-- 配图:3×4 热力网格,每格是 6 轮 val acc,蓝框标每行最佳;红差绿好 -->

优化器lr=1e−4lr=1e−3lr=1e−2lr=1e−1
SGD72.53%90.11%95.56%97.13%
Momentum90.20%95.60%97.17%94.07%
Adam96.03%97.02%94.72%16.94%

三行的最佳值只差 0.15 个百分点(97.17 / 97.13 / 97.02),但它们要的学习率差了两个数量级。SGD 的最佳点在 0.1,Momentum 在 0.01,Adam 在 1e−3。

这张表还暴露了「同一档学习率下谁更好」的陷阱:如果只看 1e−3,Adam 的 97.02% 远高于 SGD 的 90.11%,结论会是「Adam 完胜」;如果只看 0.1,SGD 的 97.13% 又高于 Adam 的 16.94%,结论会反过来。优化器和学习率必须一起选,单独比较任何一个都没有意义。

SGD 在 1e−4 只有 72.53%,因为每步更新的相对幅度太小,6 轮走不完;把 lr 放大 1000 倍到 0.1,它才到 97.13%。代价是再往上到 1.0 就 NaN(理论篇图①)。Adam 则在 1e−3 就到位,但对上界更敏感:1e−2 掉到 94.72%,1e−1 直接 16.94%。

「相对更新幅度」可以直接量:每一步结束时,参数变化量的范数除以更新前的权重范数。C0 与 C4 在各自的最佳学习率下:

配置第 1 轮(层 1 / 层 2 / 层 3)第 8 轮
C0:SGD 0.010.063% / 0.076% / 0.119%0.066% / 0.084% / 0.121%
C4:Adam 1e−30.291% / 0.136% / 0.082%0.220% / 0.138% / 0.067%

学习率差了 10 倍(0.01 对 1e−3),实际相对更新只差约 3 倍。因为 Adam 的分母 √v̂ 把梯度尺度吃掉了,lr 不再直接等于步长比例;SGD 的步长则完全由 lr 和梯度范数决定。这也解释了为什么两个优化器不能共用同一套学习率。

五、BN 前后:把「到 97%」从 7 轮压到 3 轮 ​

固定 Momentum 0.01、He 初始化、8 轮,只改输入与层间归一化。

![三种归一化的验证曲线](/ai-four-kings/第 3 章:训练工程/assets/ch3_norm_curves.svg)

<!-- 配图:raw / std / bn 三条验证 acc 曲线,绿色 97% 水平线,标出各自到达轮次 -->

输入处理到 97% 的轮次第 8 轮 val acc第 8 轮 val loss
原始像素797.29%0.0967
输入标准化497.41%0.0995
标准化 + BN397.82%0.0753

第 1 轮差距更明显:原始 93.67%、标准化 95.61%、加 BN 96.10%。到第 8 轮三者只差 0.53 个百分点,因为 8 轮足够让慢配置追上来。BN 的价值主要在「前几轮」和「稳」:它让第 1 轮就接近 96%,而不是从 93.67% 慢慢爬。

BN 真正管的是层间尺度漂移:

![BN 前后的层间激活尺度](/ai-four-kings/第 3 章:训练工程/assets/ch3_norm_layers.svg)

<!-- 配图:两层三组柱:初始化 / 无 BN / 有 BN,比较激活标准差 -->

层初始化训练 8 轮(无 BN)训练 8 轮(有 BN)
第 1 层0.6621.4890.552
第 2 层0.5802.1040.574

没有 BN 时,第 2 层的激活标准差从 0.580 涨到 2.104,是第 1 层 1.489 的 1.41 倍;网络越深,这个倍率越会滚雪球。BN 每层重新归一化,第 1 层 0.552、第 2 层 0.574,基本不随训练漂移。

代价也在表里:这个网络加 BN 多 404 个参数(C3 的 109,790 对 109,386),每个 BN 层训练时要算当前 batch 的均值方差,推理时要维护 running stats;batch 小到 8 或 16 时,统计量本身就不稳。

六、过拟合曲线:第 4 轮见底,之后在记噪声 ​

换一个更大的网络:784 → 512 → 256 → 10,535,818 个参数,30 轮,无正则。

![大网络的过拟合曲线](/ai-four-kings/第 3 章:训练工程/assets/ch3_overfit_curve.svg)

<!-- 配图:训练 loss 蓝色一路降、验证 loss 红色先降后升,ep4 绿色虚线,末端 gap 标注 -->

指标数值
训练 loss0.0114
验证 loss0.1753
训练 acc99.70%
验证 acc97.87%
测试 acc97.96%
验证 loss 最低轮次第 4 轮,0.1015

第 4 轮验证 loss 见底,之后从 0.1015 回升到 0.1753;训练 loss 还在从 0.0465 降到 0.0114。两条线在第 4 轮之后分叉,gap 从 0.055 拉到 0.164。这不是「模型变差了」——验证 acc 从 97.30% 涨到 97.87%,测试 97.96%——而是「模型对错误样本越来越自信」,loss 把这些自信的错误算了进去。

三种正则的对照:

![三种正则的验证 loss](/ai-four-kings/第 3 章:训练工程/assets/ch3_overfit_compare.svg)

<!-- 配图:无正则 / Dropout 0.3 / WD 1e−2 三条验证 loss,右侧测试 acc 面板 -->

配置验证 loss验证 acc测试 acc
无正则0.175397.87%97.96%
Dropout 0.30.084098.18%98.27%
Weight Decay 1e−20.144896.03%96.37%

Dropout 0.3 把验证 loss 压到 0.0840,测试 98.27%,是三种里最好的;Weight Decay 1e−2 把训练 loss 停在 0.145(无正则只有 0.0114),测试反而掉到 96.37%——正则太强,模型连训练集都拟合不好。

设计取舍:Dropout 0.3 还是 Weight Decay 1e−2 ​

方案收益代价
A:Dropout 0.3测试 98.27%,验证 loss 0.0840;训练时随机丢单元,逼网络学冗余特征训练时间变长;推理要切换 eval(),否则结果抖动
B:Weight Decay 1e−2实现最简单,任何优化器都自带 weight_decay强度稍大就欠拟合;本实验掉到 96.37%,比无正则还低 1.59

本章主线选 A,但最终模型把 Dropout 降到 0.2,Weight Decay 用 1e−4——只轻轻压,不伤拟合。强度靠验证集选,不靠默认值。

七、激活与初始化:差异只在最前面几轮 ​

![激活与初始化的前几轮速度](/ai-four-kings/第 3 章:训练工程/assets/ch3_act_init_speed.svg)

<!-- 配图:左:四种激活前 4 轮;右:四种初始化 10 轮;底部图例带最终值 -->

激活第 1 轮 val acc10 轮 val acc
ReLU95.50%97.23%
LeakyReLU 0.0195.46%97.23%
Sigmoid93.42%97.28%
Tanh94.99%97.11%

第 1 轮 ReLU 比 Sigmoid 高 2.08 个百分点,10 轮后两者只差 0.05,Sigmoid 甚至略高。浅网络 + Adam 把激活的梯度差异抹平了。这不是说激活不重要,而是说:两层网络上选激活,收益主要体现在前几轮;层数一多,Sigmoid 的 0.25 上限才会变成硬伤。

初始化的差距则是硬的:

初始化10 轮 val acc
零11.20%
小随机 0.0197.62%
Xavier97.38%
He97.23%

零初始化 10 轮都是 11.20%,不是「学得慢」,是「根本没学」。其余三种都在 97.2–97.6%,差异很小——只要方差量级对、对称性被打破,浅网络对初始化并不挑。小随机 0.01 反而最高,因为它配合 ReLU 也足够打破对称,而这个网络的宽度不需要精确的方差保持。

八、实验边界:这些结论不能往外推太远 ​

这一篇的十张图都建立在一组有限条件上,读结论时要带着边界:

边界具体设定影响
数据集只用 MNIST,固定 50k / 10k 划分换数据集,绝对准确率与排序都可能变
模型只有 MLP,没有卷积、没有残差结论不覆盖 CNN 的初始化与归一化行为
轮次六配置 8 轮、网格 6 轮、初始化 10 轮慢配置可能只是没跑够,排名被预算绑死
种子全部单种子 202608150.02–0.3 个百分点的差异不能当显著
测试集只在大网络过拟合实验里开了一次六配置的排名只在验证集上成立
学习率策略固定 lr,无 warmup、无衰减真实训练常用 cosine / step,最优 lr 会移动

最重要的一条是「轮次预算」。C0 在 8 轮时 93.75%,但理论篇的归一化实验里,同一个网络用 Momentum 0.01 跑 8 轮能到 97.29%——C0 的 SGD 0.01 只是还没走完。如果把六配置都跑到 30 轮,C0 与 C4 的差距会缩小,C2 与 C0 的差距可能消失。「哪个配置更好」必须先说「在多少轮、什么优化器、什么数据上」。

这也是为什么这一篇只给「速度排名」,不选「最终模型」。最终模型要跑满 30 轮、用测试集验收、再做错误分析,那是下一篇的事。

九、这一篇没解决的问题 ​

六种配置都只跑了 8 轮,只在验证集上比较,没有测试集、没有早停、没有错误分析,也没有回答「最终该用哪个模型」。下一篇把这些补齐:固定一个最终配置,跑满 30 轮,用测试集验收,再把 180 个错误逐类拆开。

自检 ​

合上笔记,试着回答:

  • 为什么六种配置每次只能动一个变量?C3 的参数为什么比 C0 多 404 个?
  • 8 轮快照能回答什么问题,不能回答什么问题?
  • 单变量收益为什么不能相加?+2.25 +2.91 +3.53 错在哪?
  • Adam 的平台上 3e−4 与 1e−3 只差 0.02,为什么不该继续细调?
  • 只看 1e−3 会得出「Adam 完胜」,只看 0.1 会得出相反结论,正确的比较方式是什么?
  • BN 把到 97% 的轮次从 7 压到 3,代价是什么?
  • 为什么 He 初始化在 C2 里几乎无感,在标准化之后才显出差异?
  • 学习率差 10 倍,为什么 C0 与 C4 的实际相对更新只差约 3 倍?
  • 这一篇的结论在什么条件下会失效?为什么不能拿 8 轮排名直接选最终模型?