Lecture 8 · 优化算法:mini-batch、动量、RMSprop、Adam 与学习率调度 · Optimization Algorithms

Deep Learning · Lecture 08。个人学习讲义与配套实验,按主题整理并持续修订。

课程源码 ZIP ↓

个人整理的学习笔记,非官方讲义;课程来源与整理说明见课程介绍。配套示例未逐一运行验证。

本篇目录

对应 Maps to:Coursera Deep Learning Specialization C2M2 “Optimization Algorithms” 的全部视频(Mini-batch Gradient Descent、Understanding Mini-batch Gradient Descent、Exponentially Weighted Averages、Understanding Exponentially Weighted Averages、Bias Correction in Exponentially Weighted Averages、Gradient Descent with Momentum、RMSprop、Adam Optimization Algorithm、Learning Rate Decay、The Problem of Local Optima);编程作业 “Optimization Methods”(月牙数据、三层网络、mini-batch 64,比较 GD / Momentum / Adam;较新的版本加了学习率衰减)。在 Fall 2025 的课表里,C2M2 和 C2M1 排在同一周(那一周的课堂讲座讲对抗样本与生成模型,见第 18 讲)。 依据说明:Coursera 的视频和编程作业要注册才能看,所以本讲不引用其中的原文。内容依据 Andrew Ng 在 C2M2 里广为人知的讲法(伦敦一年的气温、“小球滚下碗”的比喻、Adam 的默认超参数、几个学习率衰减公式、高维空间里的鞍点),以及原始文献:Polyak(1964)的 heavy ball、Hinton 的 Coursera 讲义(RMSprop)、Kingma & Ba(2015,Adam)、Dauphin et al.(2014,鞍点)等(见 §13)。作业里的 sklearn make_moons 换成了第 3 讲 planar_data.py 里用 numpy 重写的等价版本,所以具体的点和数字和作业不同。文中提到的作业细节(默认学习率、epoch 数、作业的结论)凭对这份公开作业的了解写成,不同版本之间可能有出入。 配套代码 Code:code/lecture08/(numpy 从零实现;5 个脚本 + 2 个工具模块;04 约 75 秒(有 --quick),其余 3–12 秒) 预计阅读时间:3 小时(含运行代码)


0. 本讲导航 · How to read this lecture

0.1 学习目标 Learning goals

学完这一讲,你应该能够:

  1. 说清楚 batch / mini-batch / stochastic 梯度下降的取舍:每一步的准确度、每个 epoch 走几步、向量化效率。能从”按步数""按样本数""按耗时”三种口径读懂一张收敛曲线,知道 mini-batch 训练的代价曲线为什么会抖。
  2. 写出指数加权平均 vt=βvt−1+(1−β)θtv_t = \beta v_{t-1} + (1-\beta)\theta_t,用 (1−ε)1/ε≈1/e(1-\varepsilon)^{1/\varepsilon} \approx 1/e 解释”约等于最近 1/(1−β)1/(1-\beta) 个数的平均”;说出偏差修正为什么需要、怎么做。
  3. 用”病态的碗”解释普通梯度下降为什么走之字形、为什么步数和条件数 κ\kappa 成正比。
  4. 写出 Momentum、RMSprop、Adam 的更新公式,说出每一项的作用:momentum 平均掉震荡、允许更大的学习率;RMSprop 按维度缩放步长;Adam 两者结合再加偏差修正。知道 Adam 的默认超参数 β1=0.9\beta_1 = 0.9、β2=0.999\beta_2 = 0.999、ε=10−8\varepsilon = 10^{-8}。
  5. 知道这些方法各自的局限:同一学习率下 momentum 不一定更快,它也会冲过谷底;按维度缩放只对”和坐标轴对齐”的病态有效;常数学习率的 RMSprop / Adam 停不稳;Adam 不是”不用调学习率”。
  6. 写出几种学习率衰减公式,解释 mini-batch 噪声造成的”噪声球”,知道现代常用的 warmup + cosine。
  7. 解释为什么在高维空间里局部最优很少是问题,梯度为 0 的点大多是鞍点,真正拖慢训练的是平台;知道 Adam 为什么会”部分掩盖”梯度消失。

0.2 先修与本讲范围 Prerequisites & scope

默认你已经会 Assumed本讲只给直觉,后面再展开 Previewed only
第 2 讲 §4:梯度下降、学习率、”2/曲率2/\text{曲率} 以上会发散”为什么输入归一化让碗变圆、Hessian 与条件数的系统讲法(第 7 讲)
第 4 讲:L 层网络的前向 / 反向(deep_nn.py)超参数的系统搜索、Batch Norm(第 9 讲)
第 1 讲:tiny_nn.train 用 mini-batch、默认用 Adam(当时是黑盒)PyTorch 里的优化器(torch.optim,第 9 讲起)
一点线性代数:特征值(只在 §9 用到)

第 1 讲的 tiny_nn.py 把”优化”这一块当黑盒:train 每一步取一个 mini-batch,Adam 类的注释写着”第 8 讲详细推导,这里只给直觉”。这一讲就把它拆开。第 4 讲代码 03、04 借用过这个 Adam,第 5 讲的 case_tools.ArrayAdam 也是同一个公式。读完这一讲,这些代码里的每一行你都能自己写出来。

0.3 本讲地图 Map

§1  优化要解决的三个问题:每一步太贵 / 方向不好 / 步长一成不变
§2  Mini-batch 梯度下降:切块、代价曲线为什么抖、三种口径、batch 大小        ← 代码 01
§3  指数加权平均:伦敦的气温(合成)、1/(1−β) 天、偏差修正                   ← 代码 02 演示 1–3
§4  Momentum:病态的碗、平均掉震荡、"小球"的比喻和它的局限                    ← 代码 03 演示 1、2、4
§5  RMSprop:按维度缩放步长                                                  ← 代码 03 演示 2
§6  Adam:两者结合 + 偏差修正;各自最好的学习率;旋转的碗                     ← 代码 02 演示 4、代码 03 演示 3
§7  在网络上比较:复刻 "Optimization Methods" 作业                           ← 代码 04 演示 3–4
§8  学习率衰减:噪声球、常见公式、warmup + cosine                            ← 代码 04 演示 1、2、5
§9  局部最优的问题:鞍点与平台                                               ← 代码 05
§10–14  小结、常见误区、练习、延伸阅读、下一讲预告

工具模块 optimizers.py 里是本讲所有算法的从零实现,函数名和 Coursera 作业一样(random_mini_batches、initialize_velocity、update_parameters_with_momentum、initialize_adam、update_parameters_with_adam、update_lr、schedule_lr_decay、model……),前向和反向直接用第 4 讲的 deep_nn.py。

0.4 记号 Notation(沿用第 1 讲 §0.4,本讲新增)

符号含义例子
X{t},Y{t}X^{\{t\}}, Y^{\{t\}}第 tt 个 mini-batch(花括号上标;圆括号 (i)(i) 是第几个样本,方括号 [l][l] 是第几层)X{t}X^{\{t\}} 形状 (nx,B)(n_x, B)
BBmini-batch 大小(Ng 的视频里没有专门的字母,代码里叫 mini_batch_size)64、128、256、512
epoch把训练集完整过一遍m=300m = 300、B=64B = 64 时 1 个 epoch = 5 步
θt\theta_t、vtv_t§3:第 tt 天的观测值、到第 tt 天为止的指数加权平均
β\betamomentum / 指数加权平均的系数0.9
vdW,sdWv_{dW}, s_{dW}梯度的指数加权平均(“一阶矩”)、梯度平方的指数加权平均(“二阶矩”)形状和 WW 相同
β1,β2\beta_1, \beta_2Adam 里 vv 和 ss 的系数(RMSprop 里 ss 的系数也写 β2\beta_2)0.9、0.999
ε\varepsilon防止除以 0 的小数10−810^{-8}
tt§4–6:参数更新的次数(从 1 数起),偏差修正要用
α0\alpha_0、decay_rate学习率衰减的起始学习率、衰减速度
κ\kappa条件数:最陡方向的曲率 / 最平方向的曲率f=12(x2+25y2)f = \tfrac12(x^2 + 25y^2) 的 κ=25\kappa = 25

本讲所有平方、开方、除法作用在梯度上时都是逐元素的:dW2dW^2 是 dWdW 的每个元素各自平方,不是矩阵乘法。

0.5 关于数字 · About the numbers

讲义里所有实验数字都来自配套代码的实际输出(固定随机种子,同一台机器上重复运行逐字相同;只有代码 01 演示 3 的计时每次不同)。代码 04 完整运行约 75 秒,讲义里的数字来自完整运行,--quick 的数字会不一样。气温、“老师网络”、月牙都是合成数据,二次函数和鞍点是写下来的公式,所以我们知道真相。好几处实验结果和我写代码前的预想不一样:同一学习率下 momentum 并不更快,作业里”Adam 赢”主要是学习率的原因,等等。讲义按实际结果来写,这些情况都列在代码 README 的”关于实验结果”里。


1. 优化要解决的三个问题 · What problems are we solving?

第 2 讲的梯度下降只有一行:W:=W−α dWW := W - \alpha\, dW。它在逻辑回归上工作得很好,为什么还需要一整讲的”优化算法”?

先想想训练一个真实的网络时,时间花在哪里。总时间 ≈ 每一步的代价 × 需要走的步数。这两项各有自己的问题,再加上一个和时间有关的问题:

问题症状本讲的对策
每一步太贵mm 是几百万时,算一次梯度要把几百万个样本过一遍,走一步要等很久mini-batch:每一步只看一小块数据(§2)
每一步的方向不好代价函数是一个被压扁的碗,梯度不指向最低点,而是横着指向”最近的碗壁”,于是走之字形momentum、RMSprop、Adam:平均掉震荡、按维度调整步长(§4–6)
步长一成不变开头需要大步走得快,最后需要小步停得准;mini-batch 的噪声让常数学习率停不准学习率衰减(§8)

还有第四个问题,它和地形本身有关:代价函数上有很多梯度为 0 或接近 0 的地方。早年大家最担心”坏的局部最优”,Ng 用这个模块的最后一个视频解释,在高维空间里真正的麻烦是鞍点和平台(§9)。

本讲用五个”试验场”,每个都选得能把一种现象看清楚:

  • “老师网络”分类任务(第 1 讲 dl_data.py):看 batch 大小(§2)和深层 sigmoid 网络的平台(§9.4);
  • 一年的每日气温(合成):看指数加权平均(§3);
  • 二维二次函数 f=12(x2+κy2)f = \tfrac12(x^2 + \kappa y^2):看各种优化器的轨迹(§4–6);
  • 月牙数据(第 3 讲 planar_data.py):复刻 Coursera 作业(§7、§8);
  • 一个二维鞍点和随机对称矩阵:看局部最优的问题(§9)。

2. Mini-batch 梯度下降 · Mini-batch gradient descent

2.1 一步该看多少数据?

第 2–4 讲的梯度下降每一步都用全部 mm 个样本算 JJ 和梯度,这叫 batch gradient descent(全批量梯度下降)。Ng 的例子:m=5,000,000m = 5{,}000{,}000。向量化之后一次算 500 万个样本也不算太慢,但每走一步都要把 500 万个样本全部过一遍——在看完所有数据之前,参数一点都没动。

想法很自然:把训练集切成很多小块,每看完一块就更新一次参数。 每块 1000 个样本,就是 5000 块:

X=[x(1)⋯x(1000)⏟X{1} ∣ x(1001)⋯x(2000)⏟X{2} ∣ ⋯ ∣ ⋯x(m)⏟X{5000}]X = \big[\underbrace{x^{(1)} \cdots x^{(1000)}}_{X^{\{1\}}}\ \big|\ \underbrace{x^{(1001)} \cdots x^{(2000)}}_{X^{\{2\}}}\ \big|\ \cdots\ \big|\ \underbrace{\cdots x^{(m)}}_{X^{\{5000\}}}\big]

X{t}X^{\{t\}} 的形状是 (nx,1000)(n_x, 1000),Y{t}Y^{\{t\}} 是 (1,1000)(1, 1000)。算法是两层循环:

for epoch in range(num_epochs):            # 外层:一个 epoch = 把训练集完整过一遍
    打乱训练集,切成 mini-batch X^{1}, ..., X^{T}
    for t in 1..T:                         # 内层:每个 mini-batch 更新一次
        前向:用 X^{t} 算 A^{[L]}
        cost:J^{t} = (1/B) Σ_{i ∈ 第 t 块} L(ŷ^(i), y^(i))      (+ 正则项,第 6 讲)
        反向:算 dW^{[l]}、db^{[l]}(只用这一块的样本)
        更新:W^{[l]} := W^{[l]} − α dW^{[l]},b 同理

前向、反向、更新的公式和第 4 讲一模一样,只是把 XX 换成了 X{t}X^{\{t\}}、把 mm 换成了 BB。全批量梯度下降一个 epoch 走 1 步,mini-batch 一个 epoch 走 5000 步(代码 01 演示 1(a))。在大数据上,mini-batch 几乎总是快得多,这也是第 1 讲 tiny_nn.train 默认用 batch 128 的原因。

2.2 切 mini-batch:打乱、切块、最后一块

optimizers.random_mini_batches 和 Coursera 作业的同名函数一样做两件事:

  1. 打乱(shuffle):用同一个随机排列同时重排 XX 和 YY 的列,保证每个输入仍然配着自己的标签(自检里专门检查了这一点);
  2. 切块(partition):按顺序每 BB 列切一块。mm 不能被 BB 整除时,最后一块是”不满的”。

代码 01 演示 1(b):月牙数据 m=300m = 300、B=64B = 64,切出 5 块,大小是 [64, 64, 64, 64, 44]。最后一块怎么办有两种常见做法:

  • Coursera 作业(以及 random_mini_batches 的默认值):保留这个 44 个样本的小块;
  • 第 1 讲的 tiny_nn.train:剩下的样本不够一个 batch,就丢掉,直接重新打乱开始下一个 epoch(相当于 drop_last=True)。这个 epoch 有 44 个样本(14.7%)没用上,但下个 epoch 没用上的是另一批,长期看每个样本都会被用到。

两种都对。PyTorch 的 DataLoader 也有 drop_last 这个选项。mm 远大于 BB 时两者几乎没有区别;保留的坏处是最后一块的梯度噪声更大,丢掉的坏处是每个 epoch 有一小部分数据没看到。

为什么一定要打乱? 月牙数据是按类别排好的:前 150 个全是类别 0,后 150 个全是类别 1。演示 1(c) 训练 500 个 epoch,看最后一个 epoch 里每块的情况:

第 1 块第 2 块第 3 块第 4 块第 5 块测试准确率
每个 epoch 重新打乱:类别 1 比例0.520.560.470.520.41
          cost0.0710.0290.1300.1030.1500.968
不打乱:类别 1 比例0.000.000.661.001.00
    cost0.3190.0480.1390.0590.0370.962

不打乱时,每个 epoch 的最后两块全是类别 1,网络被推向”猜 1”;下一个 epoch 一开头就是全是类别 0 的一块,cost 冲到 0.319。代价曲线上会出现一个每个 epoch 重复一次的锯齿。诚实地说,在这个小问题上最终测试准确率差不多(0.962 vs 0.968)。数据按时间、按来源排序时,不打乱的害处会大得多。打乱几乎没有成本,所以默认总是打乱。

2.3 代价曲线为什么会抖

用全批量梯度下降时,学习率合适的话 JJ 每一步都下降。换成 mini-batch,把每一步的 J{t}J^{\{t\}} 画出来,曲线会上下抖,但总体趋势向下。Ng 的解释是:有的 mini-batch 碰巧”容易”,有的碰巧”难”(比如有几个标错的样本),所以 J{t}J^{\{t\}} 本身就有起伏。

更根本的原因是:mini-batch 的梯度是真梯度的一个带噪声的估计。设 gg 是全部 mm 个样本的平均梯度,gBg_B 是随机抽 BB 个样本的平均梯度。gBg_B 的期望正好是 gg(无偏),但它有方差:

E ∥gB−g∥2=σ2B⋅m−Bm−1\mathbb{E}\,\|g_B - g\|^2 = \frac{\sigma^2}{B}\cdot\frac{m - B}{m - 1}

其中 σ2\sigma^2 是”单个样本的梯度和 gg 的平均平方距离”,m−Bm−1\frac{m-B}{m-1} 是”不放回抽样”的修正(B=mB = m 时为 0)。误差按 1/B1/\sqrt{B} 缩小:想把噪声减半,batch 要大 4 倍。

代码 01 演示 2 在刚初始化的网络 [10, 32, 32, 1] 上(“老师网络”任务,4096 个样本),对每个 BB 随机抽 200 个 mini-batch:

BB相对误差 ∥gB−g∥/∥g∥\|g_B - g\|/\|g\|理论值cos⁡(gB,g)\cos(g_B, g) 平均是下坡方向的比例这块 cost 的标准差
12.6322.756−0.04144%1.157
81.0060.9740.70398%0.436
640.3350.3420.960100%0.140
5120.1100.1140.995100%0.046
40960.0000.0001.000100%0.000

几个值得停下来想的地方:

  • 实测和理论值吻合,误差确实按 1/B1/\sqrt{B} 缩小:BB 从 1 到 64(64 倍),误差从 2.63 降到 0.34(约 1/8)。
  • 单个样本的梯度几乎全是噪声:它和真梯度的平均夹角余弦接近 0,只有 44% 的样本给出”下坡方向”(沿它走一小步,全体 cost 会下降)。也就是说,随便挑一个样本,按它的梯度走一小步,更可能让整体 cost 上升。SGD 照样能工作,因为很多步的随机误差会互相抵消,平均下来朝着下坡走。§3 的指数加权平均是同一个道理。
  • B=64B = 64 时 gBg_B 和 gg 的夹角余弦已经是 0.96,方向基本对了。花 64 倍的计算把梯度算到 B=4096B = 4096 那么准,换来的方向改进并不多。这就是 mini-batch 划算的原因。
  • 最后一列:mini-batch 上算出来的 cost 本身也在抖。所以训练日志里”每一步的 cost”抖动,一部分是参数在变,一部分只是这一块数据碰巧难或碰巧容易。看趋势要看平均(比如每个 epoch 的平均,Coursera 作业就是这样记录的),或者定期在整个训练集 / 验证集上算一次。

2.4 三种极端的取舍:按步数、按样本数、按耗时

Ng 把 batch 大小的两个极端说得很清楚:

  • B=mB = m:batch gradient descent。每一步方向最准,但每一步太贵。
  • B=1B = 1:stochastic gradient descent(SGD,随机梯度下降)。每一步只看一个样本,方向很吵,而且完全丢掉了向量化的好处。
  • 中间:mini-batch。两头的好处各占一部分。

代码 01 演示 3 把这个取舍量化了:同一个网络、同一组初始参数、普通梯度下降,学习率都是 0.1(SGD 另加一组 0.01),比较全训练集上的 cost。

(a) 按更新步数(走了同样多步之后):

10 步100 步1000 步
全批量 B=m=4096B = m = 40960.5820.3820.296
B=256B = 2560.5850.3830.298
B=64B = 640.5910.3840.303
B=8B = 80.6000.4740.339
SGD B=1B = 1,lr 0.11.4360.5130.409
SGD B=1B = 1,lr 0.010.7940.5950.406

(b) 按看过的样本数(1 epoch = 4096 个样本):

1 epoch5 epoch30 epoch30 epoch 的步数
全批量0.8280.6230.48430
B=256B = 2560.5460.3950.326480
B=64B = 640.4170.3400.2621,920
B=8B = 80.3710.3080.16415,360
SGD,lr 0.10.5100.4310.630122,880
SGD,lr 0.010.3750.3040.166122,880

(c) 按耗时(下面是我运行的一次,计时数字每次都不一样):

每步耗时每个样本降到 cost ≤ 0.3:epoch / 耗时
全批量717 µs0.18 µs926.00 / 0.666 s
B=256B = 25679 µs0.31 µs59.00 / 0.074 s
B=64B = 6448 µs0.74 µs16.38 / 0.050 s
B=8B = 834 µs4.28 µs4.50 / 0.079 s
SGD,lr 0.131 µs30.62 µs未达到
SGD,lr 0.0130 µs29.75 µs4.88 / 0.606 s

batch 大小的三种口径

三张表讲了三个不同的故事:

  1. 按步数,全批量最好:每一步的梯度最准。
  2. 按样本数,结论反过来:1 个 epoch 里全批量只走了 1 步,B=64B = 64 走了 64 步。30 个 epoch 后全批量才到 0.484,B=64B = 64 已经 0.262,B=8B = 8 到了 0.164。很多步”吵但便宜”的小步,加起来比少数几步”准但贵”的大步走得远。 学习率合适的 SGD(lr 0.01)按样本数也很省数据(0.166)。lr 0.1 的 SGD 噪声太大,cost 一直来回跳。
  3. 按耗时,mini-batch 最快:SGD 处理一个样本的时间是全批量的约 170 倍(这一次运行的数字;我运行了几次,都在 170–190 倍之间)。一次只算一个样本,Python 的函数调用、numpy 的调度开销完全摊不薄。在 GPU 上这个差距更悬殊,因为 GPU 需要足够大的矩阵才能把成千上万个计算核心填满。

所以实践中几乎总是用 mini-batch:既保留了向量化(每个样本便宜),又每个 epoch 走很多步。

一个说明:这里所有 BB 用的是同一个学习率。最合适的学习率本身会随 BB 变化(BB 越大噪声越小,通常可以用更大的学习率;Goyal et al. 2017 的”linear scaling rule”建议 batch 放大 kk 倍时学习率也放大 kk 倍,并配合 warmup),所以更严格的比较应该给每个 BB 各自调学习率。这个演示只是展示三种口径的差别,不是给出”最好的 BB”。

2.5 mini-batch 大小怎么选

Ng 的经验法则:

  • 训练集小(比如 m≤2000m \le 2000):直接用全批量梯度下降,mini-batch 带来的好处不大。
  • 否则:常用 64、128、256、512。这些都是 2 的幂,Ng 的理由是计算机内存的布局方式让 2 的幂有时跑得更快。这是一个惯例,在现代硬件上差别通常不大,不必拘泥。1024 也有人用,但少一些。
  • 确保一个 mini-batch 的数据(连同前向时存下的 cache)能放进 CPU / GPU 的内存。一个 batch 的激活值所占的内存和 BB 成正比(第 4 讲 §4.4 讲过 cache 的代价)。放不下的话,要么减小 BB,要么用”梯度累积”(gradient accumulation:几个小 batch 的梯度加起来再更新一次,效果接近一个大 batch)。

BB 本身也是一个超参数,它和学习率是联动的(上面的说明)。一个常被讨论的现象是:很大的 batch 有时泛化得差一些,一种解释是小 batch 的梯度噪声起了某种正则化的作用(Keskar et al. 2017 的”sharp minima”假说;也有研究认为调好学习率和训练时长之后差别可以消除)。这方面没有定论。实用的做法是:在显存允许、训练速度合适的范围里选一个 BB,然后围绕它调学习率。


3. 指数加权平均 · Exponentially weighted averages

下面三种优化器(momentum、RMSprop、Adam)共用一个”零件”:指数加权平均(exponentially weighted average,统计里也叫 exponentially weighted moving average)。Ng 先用三个视频把这个零件讲透,再拿它去组装优化器。我们也照这个顺序。

3.1 伦敦的气温(合成)

Ng 的例子是伦敦一年里每天的气温:一串很吵的数 θ1,θ2,…,θ365\theta_1, \theta_2, \dots, \theta_{365}。我们想要一条平滑的曲线,看出季节的变化。

代码 02 用一份合成的数据代替真实的伦敦气温:季节曲线 12−7cos⁡(2π(d−15)/365)12 - 7\cos\big(2\pi(d - 15)/365\big) °C(1 月中旬最冷约 5 °C,7 月中旬最热约 19 °C),加上每天独立的高斯噪声(标准差 3 °C)。这一年最低 −1.7 °C,最高 25.4 °C。真实天气的噪声前后几天是相关的,这里用独立噪声,是为了让”噪声被平均掉多少”有一个干净的公式。另外,Ng 在视频里用华氏度,这里换成了摄氏度。

3.2 公式

v0=0,vt=β vt−1+(1−β) θt(3.1)v_0 = 0, \qquad v_t = \beta\, v_{t-1} + (1-\beta)\,\theta_t \tag{3.1}

每来一个新数,就把旧的平均打 β\beta 折,再加上新数的 (1−β)(1-\beta) 份。代码 02 演示 1 用三个 β\beta:

β\beta≈ 平均几天噪声剩下(°C)理论值最热的一天滞后(天)季节起伏保留和季节曲线的总误差(°C)
0.521.751.73第 198 天1100%1.76
0.9100.800.69第 206 天996%1.17
0.98500.290.30第 237 天4084%3.37

(季节曲线本身在第 197 天最热。因为平均是线性的,“噪声剩下多少”只对噪声做平均来量,“滞后多少”只对季节曲线做平均来量,两者互不干扰。表中只看第 61 天以后,避开开头的”热身期”,开头的问题在 §3.5 讲。)

指数加权平均:三个 β

这张表就是 Ng 那三条曲线(黄、红、绿)的定量版:

  • β\beta 越大,曲线越平滑:噪声从每天 3 °C 降到 1.75、0.80、0.29 °C。对独立噪声,理论值是 3(1−β)/(1+β)3\sqrt{(1-\beta)/(1+\beta)}(练习 3)。0.9 那一行实测 0.80 和理论 0.69 差得多一点,是因为 β\beta 大时相邻几天的平均值高度相关,305 天里的样本标准差本身就有不小的随机误差。
  • 代价是滞后:β=0.98\beta = 0.98 的曲线把最热的一天推迟了 40 天,季节起伏也只剩 84%(夏天不够热、冬天不够冷)。Ng 说绿线”适应得更慢”、“往右移”,就是这个意思。
  • 两种误差此消彼长:β=0.5\beta = 0.5 噪声大,β=0.98\beta = 0.98 滞后大,总误差在 β=0.9\beta = 0.9 时最小。β\beta 是一个超参数,最好的值取决于信号变化得多快、噪声有多大。

在优化里,“信号”是真正的下坡方向,“噪声”是 mini-batch 的随机性和来回的震荡。Momentum 默认用 β=0.9\beta = 0.9,就是在”平滑”和”反应速度”之间取的一个折中。

3.3 为什么”约等于最近 1/(1−β)1/(1-\beta) 天的平均”

把 (3.1) 一层层展开:

vt=(1−β)[θt+β θt−1+β2θt−2+⋯+βt−1θ1]+βtv0v_t = (1-\beta)\big[\theta_t + \beta\,\theta_{t-1} + \beta^2\theta_{t-2} + \cdots + \beta^{t-1}\theta_1\big] + \beta^t v_0

kk 天以前的数拿到的权重是 (1−β)βk(1-\beta)\beta^k,按 β\beta 的幂指数衰减,“指数加权”的名字就是这么来的。权重衰减到多小可以算”不算了”?Ng 的说法是降到今天的 1/e1/e 左右。令 ε=1−β\varepsilon = 1 - \beta,那么 N=1/εN = 1/\varepsilon 天以前的权重相对于今天是

βN=(1−ε)1/ε  → ε→0   1e≈0.368\beta^{N} = (1-\varepsilon)^{1/\varepsilon} \;\xrightarrow{\ \varepsilon \to 0\ }\; \frac1e \approx 0.368

所以 vtv_t 大约是最近 N=1/(1−β)N = 1/(1-\beta) 天的平均。代码 02 演示 2:

β\betaN=1/(1−β)N = 1/(1-\beta)今天的权重NN 天前的权重βN\beta^N最近 NN 天的权重之和平均”年龄”(天)半衰期(天)
0.520.5000.125000.25000.7501.01.0
0.9100.1000.034870.34870.6519.06.6
0.98500.0200.007280.36420.63649.034.3
0.99910000.0010.000370.36770.632999.0692.8

βN\beta^N 一列从 0.25 逐渐逼近 1/e=0.36791/e = 0.3679。这只是一个经验说法,不是精确的窗口:最近 NN 天的权重加起来只有约 63%,另外 37% 分给了更早的日子。另一种同样合理的”窗口长度”是平均年龄 β/(1−β)\beta/(1-\beta)(练习 2),β=0.98\beta = 0.98 时是 49 天,它正好是一条直线(线性变化的信号)会被拖后的天数。演示 1 的季节曲线是弯的,实测滞后是 9 天(β=0.9\beta = 0.9)和 40 天(β=0.98\beta = 0.98)。

3.4 实现:一行代码,一个数的内存

v = 0
for theta in data:
    v = beta * v + (1 - beta) * theta     # optimizers.ewa 就是这一行

不管平均多少天,只需要记住一个数。如果真的保存最近 50 天的数据再求平均,要 50 倍的内存。对一个有上亿参数的网络,每个参数都要这样一份”平均”,这个差别就很要紧了。这也是为什么 Adam 额外的内存只是参数量的 2 倍(每个参数一份 vv、一份 ss)。

3.5 偏差修正 · Bias correction

(3.1) 有一个问题:v0=0v_0 = 0 是一个”假的”初始值。第 1 天 v1=(1−β)θ1v_1 = (1-\beta)\theta_1,β=0.98\beta = 0.98 时只有当天气温的 2%。代码 02 演示 3:

第 tt 天当天气温vtv_t(不修正)1−βt1-\beta^tv^t=vt/(1−βt)\hat v_t = v_t/(1-\beta^t)前 tt 天简单平均
18.290.170.02008.298.29
210.100.360.03969.209.19
50.930.570.09615.936.02
107.281.230.18296.746.74
507.423.400.63585.345.36
20019.5315.680.982415.9611.51

(β=0.98\beta = 0.98;完整的表和 β=0.9\beta = 0.9 的表见代码输出。)

前几十天的 vtv_t 都被那个 0 往下拉。前 10 天的平均气温是 6.74 °C,v10v_{10} 却只有 1.23 °C。原因就在展开式里:θ1,…,θt\theta_1, \dots, \theta_t 的权重 (1−β)(1+β+⋯+βt−1)(1-\beta)(1 + \beta + \cdots + \beta^{t-1}) 加起来是 1−βt1 - \beta^t,不是 1。剩下的 βt\beta^t 那部分权重给了 v0=0v_0 = 0。修正的办法就是把权重重新归一化:

v^t=vt1−βt(3.2)\hat v_t = \frac{v_t}{1 - \beta^t} \tag{3.2}

修正后 v^1=θ1\hat v_1 = \theta_1 恰好成立(表的第一行)。开头几天 v^t\hat v_t 很接近”前 tt 天的简单平均”,因为 tt 远小于 1/(1−β)1/(1-\beta) 时各天的权重几乎一样。tt 变大以后 βt→0\beta^t \to 0,修正自动消失:第 200 天 1−βt=0.98241-\beta^t = 0.9824,修正前后只差 0.28 °C。(第 200 天的 v^t\hat v_t 和”前 200 天的简单平均”差得很远,这不是 bug:指数加权平均本来就更看重最近的日子,春夏在升温。)

什么时候需要修正? 偏差大约持续 1/(1−β)1/(1-\beta) 步。Momentum 用 β=0.9\beta = 0.9,10 步左右偏差就没了,Ng 说实践中很多人不做修正。Adam 的 β2=0.999\beta_2 = 0.999,偏差要持续上千步,必须修正(§6.3)。


4. Momentum · Gradient descent with momentum

4.1 病态的碗:梯度下降为什么走之字形

Ng 讲 momentum 时画了一张图:一个被压扁的椭圆碗,梯度下降在窄的方向上来回震荡,在长的方向上慢慢挪。代码 03 把这张图变成可以运行的实验:

f(x,y)=12 (x2+κ y2),∇f=(x, κy),从 (−4,1) 出发f(x, y) = \tfrac12\,(x^2 + \kappa\,y^2), \qquad \nabla f = (x,\ \kappa y), \qquad \text{从 } (-4, 1) \text{ 出发}

κ\kappa 叫条件数(condition number):最陡方向的曲率 / 最平方向的曲率。κ\kappa 越大,碗越扁,问题越病态(ill-conditioned)。梯度下降每走一步,两个方向各自按一个固定倍数收缩:

x←(1−α) x,y←(1−κα) yx \leftarrow (1 - \alpha)\,x, \qquad y \leftarrow (1 - \kappa\alpha)\,y

代码 03 演示 1(κ=25\kappa = 25;“收敛步数”是 ff 降到初始值的 10−610^{-6} 以下并且一直保持的步数):

学习率 α\alphaxx 方向倍数 1−α1-\alphayy 方向倍数 1−25α1-25\alpha收敛步数
0.010.9900.750641
0.030.9700.250212
0.060.940−0.500105
0.0769(最优)0.923−0.92387
0.0790.921−0.975264
0.0810.919−1.025发散

相同学习率下的四条轨迹

这就是第 2 讲 §4.2”学习率超过 2/曲率2/\text{曲率} 就会发散”在两个方向上的版本:

  • 学习率被陡的方向卡住。α>2/25=0.08\alpha > 2/25 = 0.08 时 ∣1−25α∣>1|1 - 25\alpha| > 1,yy 越震越大,发散。yy 方向的倍数是负数时,yy 每一步变号,这就是图里左上角 GD 的之字形。
  • 在平的方向上,α≤0.08\alpha \le 0.08 意味着 xx 每步最多缩小 8%,只能慢慢挪。最优学习率 α∗=2/(1+κ)\alpha^* = 2/(1+\kappa) 让两个方向收缩得一样快,每步乘以 (κ−1)/(κ+1)(\kappa-1)/(\kappa+1)。
  • 步数和 κ\kappa 成正比:最优学习率下,κ=10/25/100/1000\kappa = 10 / 25 / 100 / 1000 分别要 35 / 87 / 346 / 3454 步。

深度网络的代价函数在不同方向上的曲率可以差好几个数量级。两种对策:改地形,让碗变圆(第 7 讲的输入归一化,第 9 讲的 Batch Norm);或者改走法,也就是本讲的 momentum、RMSprop、Adam。

4.2 算法

Momentum 的想法:不直接用当前的梯度走,而是用最近若干步梯度的指数加权平均走。 在第 tt 次迭代:

vdW=β vdW+(1−β) dW,vdb=β vdb+(1−β) dbW=W−α vdW,b=b−α vdb(4.1)\begin{aligned} v_{dW} &= \beta\, v_{dW} + (1-\beta)\, dW, \qquad & v_{db} &= \beta\, v_{db} + (1-\beta)\, db \\ W &= W - \alpha\, v_{dW}, \qquad & b &= b - \alpha\, v_{db} \end{aligned} \tag{4.1}

vv 初始化为 0(initialize_velocity),形状和对应的参数相同。超参数是 α\alpha 和 β\beta,β=0.9\beta = 0.9 是很常用的默认值(“平均最近约 10 步的梯度”),一般不用调。对照 update_parameters_with_momentum,每个参数就是两行。

4.3 为什么有效,以及一个常被说错的地方

Ng 的直觉解释:在 yy 方向(陡的方向),梯度每一步正负交替,平均起来接近 0,震荡被”平均掉”了;在 xx 方向(平的方向),梯度一直指向同一边,平均值仍然那么大,前进不受影响。于是轨迹更直接地朝着最低点走。

这个直觉是对的,但要说准确,它带来的到底是什么好处。代码 03 演示 2 在 κ=25\kappa = 25 的碗上,让四种方法用相同的学习率:

α=0.01\alpha = 0.010.030.0750.10.31.0
GD64121283发散发散发散
Momentum(β=0.9\beta = 0.9)587128128124131135
RMSprop(β2=0.9\beta_2 = 0.9)停在 2e−05停在 1e−04停在 9e−04停在 2e−03停在 1e−02停在 2e−01
Adam11533671421351282262

(“停在 xx“表示 3000 步时 f/f0=xf/f_0 = x,没有收敛。RMSprop 和 Adam 两行在 §5、§6 解释。)

先看前两行,和很多人的直觉不一样:

  1. Momentum 的第一个好处是”能用更大的学习率”:α≥0.1\alpha \ge 0.1 时 GD 发散,momentum 从 0.03 到 1 都能收敛,而且步数几乎一样(124–135)。它对学习率的选择宽容得多。演示 4(c) 算出了它能用的学习率上限:β=0.9\beta = 0.9 时是 GD 的 19 倍(理论值 2(1+β)/((1−β)κ)2(1+\beta)/\big((1-\beta)\kappa\big),和实测 1.520 完全一致,练习 6)。
  2. 在 GD 也能用的学习率下,momentum 并不更快:α=0.075\alpha = 0.075 时 GD 83 步,momentum 128 步。原因在公式 (4.1):vv 是梯度的平均。方向一致时,vv 稳定在 dWdW 本身那么大,步子不会比 GD 大。所以同一个 α\alpha 下,平的方向上 momentum 并不加速。
  3. Momentum 的速度有一个”地板”:α\alpha 从 0.03 到 1,步数都在 130 左右。β=0.9\beta = 0.9 时,误差每一步最好也只能乘以约 0.9≈0.95\sqrt{0.9} \approx 0.95,不能缩得更快(这是 heavy-ball 方法的一个性质,这里不推导)。于是 ff 每步最多只能缩小到原来的约 0.9 倍,降到 10−610^{-6} 要 ln⁡106/ln⁡(1/0.9)≈131\ln 10^6 / \ln(1/0.9) \approx 131 步。要更快,就得调 β\beta(练习 8)。

那 momentum 什么时候真正占优?碗很扁的时候。代码 03 演示 3 给每种方法各自挑最好的学习率(§6.4 的表):κ=1000\kappa = 1000 时 GD 要 2420 步,momentum 144 步,快约 17 倍;但 κ=10\kappa = 10 时 momentum(119 步)反而比 GD(39 步)慢。理论上,调好 α\alpha 和 β\beta 的 momentum 需要的步数大约和 κ\sqrt{\kappa} 成正比,而 GD 和 κ\kappa 成正比(Polyak 1964)。真实网络的代价函数通常是很病态的,这正是 momentum 有用的地方。

4.4 “小球滚下碗”的比喻,以及它的局限

Ng 给了一个物理比喻:把代价函数想成一只碗,参数是一个小球。dWdW 像加速度,vdWv_{dW} 像速度,β\beta 像摩擦力(β<1\beta < 1 让速度不会无限增大)。小球有惯性,所以不会每一步都被”最近的碗壁”拉着拐弯。

这个比喻很有用,但要知道它在哪里不准。代码 03 演示 4(b)(α=0.06\alpha = 0.06、前 150 步):

  • 小球会冲过谷底:ff 比上一步升高的次数,GD 0 次,momentum 60 次。GD 始终在 x<0x < 0 的一侧,momentum 的 xx 最远冲到了 +0.249(最低点在 x=0x = 0)。图里右上角那条轨迹(α = 0.075)也冲过了最低点,在它的右侧绕了一圈才回来。所以用 momentum 时,代价不一定单调下降,偶尔上升不一定是学习率太大。
  • “速度”不是越滚越快的物理速度:在 Ng 的写法里,方向一致时 vv 稳定在梯度本身那么大,不会无限加速(§4.3 第 2 条)。真实的小球在一个长下坡上会越滚越快。
  • 惯性对不太扁的碗可能是坏事:κ=10\kappa = 10 时,β=0.9\beta = 0.9 的小球在谷底来回晃,比 GD 慢 3 倍。

4.5 两种写法

很多资料和框架里的 momentum 写成

vdW=β vdW+dW,W=W−α′ vdWv_{dW} = \beta\, v_{dW} + dW, \qquad W = W - \alpha'\, v_{dW}

(没有 1−β1-\beta)。这时梯度方向一致时 vv 稳定在 dW/(1−β)dW/(1-\beta),是 Ng 写法的 10 倍,所以 α′=(1−β)α\alpha' = (1-\beta)\alpha 时两种写法完全等价。代码 03 演示 4(a) 验证了 200 步的轨迹最大只差 4.4e−16(浮点舍入)。Ng 更喜欢带 1−β1-\beta 的写法,因为 vv 和 dWdW 是同一个量级,换 β\beta 时不用重新调 α\alpha。PyTorch 的 torch.optim.SGD(momentum=0.9) 用的是不带 1−β1-\beta 的写法,在不同框架之间搬运超参数时要注意。

(还有一个常见的变体 Nesterov momentum:先按惯性走一步,再在那个位置算梯度。Ng 的课里没有讲,感兴趣可以看 Sutskever et al. 2013。)


5. RMSprop · Root mean square prop

5.1 按维度缩放步长

Momentum 解决震荡的办法是”平均掉”。RMSprop 换了一个思路:震荡的方向梯度大,就在那个方向上走小步;平缓的方向梯度小,就走大步。 每个参数各自记住”最近梯度有多大”:

sdW=β2 sdW+(1−β2) dW2(逐元素平方)W=W−α dWsdW+ε(5.1)\begin{aligned} s_{dW} &= \beta_2\, s_{dW} + (1-\beta_2)\, dW^2 \qquad (\text{逐元素平方}) \\ W &= W - \alpha\,\frac{dW}{\sqrt{s_{dW}} + \varepsilon} \end{aligned} \tag{5.1}

bb 同理。s\sqrt{s} 近似”最近一段时间梯度的均方根”(root mean square,名字就是这么来的)。用它去除梯度,每个参数的步长就被归一化了:在 Ng 那张图里,竖直方向(他用 bb 代表)梯度大,sdbs_{db} 大,除完之后步子小;水平方向(WW)梯度小,sdWs_{dW} 小,除完之后步子相对大。效果相当于给每个参数配了一个自己的学习率。

几个细节:

  • ε\varepsilon(比如 10−810^{-8})只是为了防止除以 0:某个参数的梯度一直是 0 时 s=0\sqrt{s} = 0。它一般不用调。
  • 为什么用 β2\beta_2 这个名字? 为了和 momentum 的 β\beta 区分,Adam 里两个都要用。
  • β2\beta_2 取多少? RMSprop 最早出现在 Hinton 在 Coursera 上的神经网络课程里(不是一篇论文,Ng 在视频里特意提到这一点),那份讲义用的是 0.9。PyTorch 的 RMSprop 默认 0.99,Keras 默认 0.9。本讲的代码用 0.9,原因见下一条。
  • RMSprop 没有偏差修正:ss 从 0 开始,第 1 步 s=(1−β2) dW2s = (1-\beta_2)\,dW^2,步长变成 α/1−β2\alpha/\sqrt{1-\beta_2} 乘以梯度的符号。β2=0.9\beta_2 = 0.9 时这是 3.16α3.16\alpha,β2=0.999\beta_2 = 0.999 时是 31.6α31.6\alpha(练习 7)。所以不带偏差修正的 RMSprop 不宜配太大的 β2\beta_2。

5.2 RMSprop 在碗里:快到附近,却停不下来

回到 §4.3 那张表的 RMSprop 一行:每一格都”停在”一个和 α\alpha 有关的水平上,从 α=0.01\alpha = 0.01 时的 2×10−52 \times 10^{-5} 到 α=1\alpha = 1 时的 0.2。图里左下角的轨迹几乎是直奔最低点去的,但到了附近就停不下来。

原因是:β2=0.9\beta_2 = 0.9 时分母 s\sqrt{s} 只记得最近约 10 步。梯度变小时分母跟着变小,两者相除,步长始终约等于 α\alpha,和离最低点还有多远无关。于是它在最低点附近以 α\alpha 的幅度来回跳。要精确收敛,就得让 α\alpha 随时间变小,也就是 §8 的学习率衰减。演示 3 里 RMSprop 只有在很小的 α\alpha(约 0.001–0.003)下才精确收敛,要两三千步。

这其实是”按梯度大小归一化”的两面:好处是步长不依赖梯度的尺度,坐标轴方向上曲率差 1000 倍也不怕;坏处是梯度变小时它不会自动放慢。


6. Adam · Adaptive moment estimation

6.1 两者结合,再加偏差修正

Ng 说,深度学习的历史上,很多人提出过各种优化算法,后来发现它们往往只在少数问题上好用,大家就对新的优化算法有点怀疑。Momentum、RMSprop,以及把两者合起来的 Adam,是少数被证明在很多种网络上都好用的。Adam(Kingma & Ba 2015)的名字来自 Adaptive moment estimation。

初始化 vdW=0v_{dW} = 0、sdW=0s_{dW} = 0(initialize_adam)。第 tt 次迭代(tt 从 1 数起):

vdW=β1vdW+(1−β1) dWMomentum 的部分("一阶矩":梯度的平均)sdW=β2sdW+(1−β2) dW2RMSprop 的部分("二阶矩":梯度平方的平均)vdWcorrected=vdW1−β1t,sdWcorrected=sdW1−β2t偏差修正(§3.5)W=W−α vdWcorrectedsdWcorrected+ε(6.1)\begin{aligned} v_{dW} &= \beta_1 v_{dW} + (1-\beta_1)\, dW && \text{Momentum 的部分("一阶矩":梯度的平均)}\\ s_{dW} &= \beta_2 s_{dW} + (1-\beta_2)\, dW^2 && \text{RMSprop 的部分("二阶矩":梯度平方的平均)}\\ v^{\text{corrected}}_{dW} &= \frac{v_{dW}}{1-\beta_1^t}, \quad s^{\text{corrected}}_{dW} = \frac{s_{dW}}{1-\beta_2^t} && \text{偏差修正(§3.5)}\\ W &= W - \alpha\,\frac{v^{\text{corrected}}_{dW}}{\sqrt{s^{\text{corrected}}_{dW}} + \varepsilon} \end{aligned} \tag{6.1}

bb 同理。分子是”平均下来往哪边走”(平均掉震荡),分母是”这个参数的梯度通常有多大”(按维度缩放)。两者相除之后,每个参数一步大约走 α\alpha 那么远;梯度方向稳定时恰好是 α\alpha(练习 7)。这一点很重要:Adam 的步长和梯度本身的尺度无关,§7 和 §9.4 会看到它带来的好处和坏处。

6.2 超参数

超参数作用常用值要不要调
α\alpha学习率论文建议 0.001(PyTorch 的默认值)要调,最重要的超参数
β1\beta_1一阶矩(梯度平均)的系数0.9几乎不调
β2\beta_2二阶矩(梯度平方平均)的系数0.999(Adam 作者推荐)几乎不调
ε\varepsilon防止除以 010−810^{-8}几乎不调(练习 9 看它太大会怎样)

Ng 的原话大意是:实践中大家用 β1,β2,ε\beta_1, \beta_2, \varepsilon 的默认值,只调 α\alpha。

6.3 偏差修正为什么必须做

§3.5 说 momentum 可以不做偏差修正。Adam 为什么不行?代码 02 演示 4(a) 设想梯度一直等于同一个数 gg。做了修正时,Adam 每一步恰好走 α⋅sign(g)\alpha \cdot \text{sign}(g);不做修正时步长变成 α(1−β1t)/1−β2t\alpha (1-\beta_1^t)/\sqrt{1-\beta_2^t}。下表是”不修正的步长 / 应有的步长”:

第 tt 步12510205010030010003000
Adam(β1=0.9,β2=0.999\beta_1 = 0.9, \beta_2 = 0.999)3.164.255.806.536.244.503.241.961.261.03
Momentum(β=0.9\beta = 0.9)0.100.190.410.650.880.991.001.001.001.00
RMSprop(β2=0.9\beta_2 = 0.9)3.162.291.561.241.071.001.001.001.001.00
  • Adam 不修正时,分子 v1=0.1gv_1 = 0.1g 被压小了 10 倍,分母 s1=0.001 ∣g∣≈0.032∣g∣\sqrt{s_1} = \sqrt{0.001}\,|g| \approx 0.032|g| 被压小了约 32 倍。分母偏得更厉害,于是步长反而偏大。分子的偏差 10 步左右就消失了,分母的偏差要上千步,所以放大倍数先升后降:第 12 步最大(6.57 倍),到第 1,751 步才回到 1.1 倍以内。不做偏差修正的 Adam,前几百步的实际学习率是你设定值的好几倍。
  • Momentum 不修正时正好相反:开头步子偏小,只是”热身慢一点”,不危险。这就是 Ng 说”momentum 一般不做修正”、而 Adam 论文专门强调要修正的原因。

演示 4(b) 在月牙数据上验证([2, 5, 2, 1]、mini-batch 64、1000 个 epoch,5 个种子的测试准确率):

种子 0种子 1种子 2种子 3种子 4
α = 0.01,做偏差修正0.9650.9680.9690.9710.970
α = 0.01,不修正0.9690.9710.9690.9690.968
α = 0.07,做偏差修正0.9650.9670.9690.9680.953
α = 0.07,不修正0.5000.9680.9680.9620.916

α=0.01\alpha = 0.01 时修不修正都一样。α=0.07\alpha = 0.07 时,不修正的 5 次里有 1 次准确率停在 0.5。代码检查了这一次:第 5 步之后,第 2 个隐藏层的 2 个 ReLU 单元已经全部”死掉”(对所有样本都输出 0),而修正后的版本还有 1 个活着。头几步被放大 3–6 倍的步子把 ReLU 推死了,和第 4 讲代码 05”学习率太大,ReLU 全部死掉”是同一种失败。偏差修正几乎不花计算,却让 Adam 在稍大的学习率下也更安全。

6.4 各自用最好的学习率:扁的碗和斜的碗

§4.3 的”同一个 α\alpha“对 Adam 不太公平:GD 的一步是 α×\alpha \times 梯度,Adam 的一步约等于 α\alpha,两者的 α\alpha 单位不一样。所以 Adam 在 α=0.01\alpha = 0.01 时最慢(1153 步),因为它每步只走约 0.01。

代码 03 演示 3 让每种方法在 41 个学习率(0.001 到 10,对数均匀)里各挑最好的那个。为了看清”按维度缩放”到底在做什么,同一个碗还旋转了 45°:碗的形状完全不变,只是扁的方向不再和坐标轴对齐。

κ\kappa碗的方向GDMomentumRMSpropAdam
10和坐标轴对齐391191724119
10旋转 45°391191823121
100和坐标轴对齐3711282137124
100旋转 45°3711282951139
1000和坐标轴对齐24201442048123
1000旋转 45°24201443000 步内未收敛733

(格子里是收敛步数;每种方法最好的学习率见代码输出。)

  • GD 和 Momentum 对旋转完全不敏感(每对两行一模一样):它们的更新规则不在乎坐标轴朝哪边。
  • Adam 在碗和坐标轴对齐时,不管 κ\kappa 多大都只要 119–124 步:它给每个坐标配了自己的步长,相当于把扁碗按坐标轴拉圆了。
  • 碗转了 45° 以后,扁的方向是 xx 和 yy 的混合,按坐标缩放就帮不上忙了:κ=1000\kappa = 1000 时 Adam 从 123 步变成 733 步,比 momentum 还慢。
  • RMSprop(β2=0.9\beta_2 = 0.9,没有动量)只有很小的 α\alpha 才能精确收敛(§5.2),所以要上千步。

对深度网络意味着什么? 网络里不同参数的梯度尺度确实常常差很多(§9.4 有一个例子:同一个网络里,第 1 层和输出层的梯度差 3000 多倍)。这部分病态和坐标轴是对齐的,Adam 能修正。不和坐标轴对齐的那部分,它和 momentum 一样只能靠惯性。这是 Adam 成为很好的默认选择的原因,也是它不会在所有问题上都赢的原因。

最后,代码 03 演示 3 还把 κ=25\kappa = 25 的 Adam(α=0.2\alpha = 0.2,这个碗上它最好的学习率)跑了 20000 步:第 1000 步时 f/f0f/f_0 已经是 3×10−463 \times 10^{-46},但第 3000 步以后又回升,最高到 10−210^{-2}(第 5344 步)。梯度变得极小以后,分母 s^\sqrt{\hat s} 也慢慢变小,等效学习率越来越大,最后又被”踢”了出来。常数学习率的 Adam 停不稳,这和 RMSprop 停不下来是同一类问题,药方都是学习率衰减(§8)。在 mini-batch 训练里梯度一直带着噪声,不会小到这个程度,所以通常不会这么极端,但道理是一样的。

6.5 和第 1 讲的黑盒对答案;Adam 的后续

直接运行 optimizers.py 会做一次自检:本讲的 update_parameters_with_adam 和第 1 讲 tiny_nn.Adam 在同一串随机梯度上走 200 步,参数的最大差别是 0.0e+00(逐位相同)。第 1 讲的黑盒,第 4 讲代码 03、04 借用的优化器,第 5 讲的 ArrayAdam,全都是公式 (6.1)。

Adam 之后还有很多改进,最值得知道的一个是 AdamW(Loshchilov & Hutter 2019)。第 6 讲的 L2 正则化是把 λW\lambda W 加进梯度,可是加进梯度的这一项在 Adam 里也会被 s^\sqrt{\hat s} 除,正则化的强度因此因参数而异。AdamW 把 weight decay 从梯度里拿出来,直接对参数做 W←W−αλWW \leftarrow W - \alpha\lambda W。今天训练 Transformer 和大语言模型,默认几乎都是 AdamW。另外,理论上 Adam 在某些构造出来的问题上可以不收敛(Reddi et al. 2018),有研究发现调好的 SGD + momentum 在一些图像任务上泛化得比 Adam 好(Wilson et al. 2017)。所以 Adam 是”很好的默认选择”,不是”永远最好”。


7. 在网络上比较:复刻 “Optimization Methods” 作业 · GD vs Momentum vs Adam on a network

7.1 设定

Coursera 的编程作业 “Optimization Methods” 在月牙数据(sklearn.datasets.make_moons,300 个点,噪声 0.2)上训练一个三层网络 [2, 5, 2, 1](ReLU + sigmoid 输出,He 初始化),mini-batch 64,学习率 0.0007,较新的版本训练 5000 个 epoch(早期版本是 10000),比较 mini-batch GD、Momentum、Adam。作业报告的结果大约是:GD 和 Momentum 在训练集上约 80%,Adam 约 94%(具体数字随版本略有不同)。作业的结论是:momentum 在这么小的学习率和这么简单的数据上作用几乎可以忽略,Adam 明显更好。

代码 04 演示 3 用 optimizers.model()(作业里 model() 的复刻)和第 3 讲的 numpy 版月牙数据重做一遍,加了一个独立生成的 2000 个样本的测试集,每种优化器跑 3 个种子(种子决定初始化和每个 epoch 的打乱顺序):

优化器种子训练集 cost训练准确率测试准确率
GD0 / 1 / 20.3780 / 0.5151 / 0.36300.850 / 0.833 / 0.8570.860 / 0.849 / 0.871
Momentum0 / 1 / 20.3780 / 0.5158 / 0.36300.850 / 0.833 / 0.8570.860 / 0.849 / 0.871
Adam0 / 1 / 20.0991 / 0.0892 / 0.08890.960 / 0.967 / 0.9600.966 / 0.965 / 0.971

月牙数据上的 cost 曲线

和作业的结论一致:Adam 明显好,GD 和 Momentum 几乎一模一样(训练 cost 最大相差 0.0008),而且 cost 还在缓慢下降,远没有训练完。(左图里 Adam 的曲线在第 1000 个 epoch 前后也有一段几乎平的”台阶”,然后又降下去:平台不只出现在 GD 身上,§9 再说。)

GD 和 Momentum 一样,正是 §4.3 的预言:同一个 α\alpha 下,Ng 写法的 momentum 并不比 GD 走得快,它的好处是”允许更大的 α\alpha“,而 0.0007 小到根本没有震荡可以平均。

7.2 换学习率之后

那么,Adam 赢是因为它”更聪明”,还是因为 0.0007 对 GD 来说太小了?代码 04 演示 4 把学习率换成 0.007、0.07、0.7(3 个种子的测试准确率:平均,括号里是最低–最高;“×k”表示有 k 次停在 0.5):

优化器α = 0.0007α = 0.007α = 0.07α = 0.7
GD0.860(0.849–0.871)0.959(0.952–0.966)0.963(0.957–0.966)0.808(0.495–0.965)×1
Momentum0.860(0.849–0.871)0.959(0.953–0.967)0.965(0.963–0.967)0.941(0.883–0.971)
Adam0.967(0.965–0.971)0.967(0.965–0.970)0.965(0.964–0.967)0.500(0.500–0.500)×3
  • 学习率加大 10 倍,GD 和 Momentum 就到了 0.959;再加大 10 倍,三种方法几乎一样(0.963 / 0.965 / 0.965)。作业里 Adam 的优势,主要来自”0.0007 对 GD 来说太小、对 Adam 来说刚好”。
  • 为什么同一个 α\alpha 对两者意义不同?代码量了一下:这个网络刚初始化时梯度各元素的均方根只有 0.0030–0.0323(3 个种子)。GD 的一步是 α×\alpha \times 梯度,Adam 的一步约等于 α\alpha。同样写 α=0.0007\alpha = 0.0007,Adam 的实际步子是 GD 的 31–338 倍。
  • Adam 也要调学习率:α=0.7\alpha = 0.7 时 3 次全部停在 0.5(ReLU 全部死掉)。GD 和 Momentum 在 0.7 时也开始出问题(GD 有 1 次停在 0.5;momentum 3 次都没有,但最低只有 0.883,这和”momentum 允许更大的学习率”一致,只是 3 个种子说明不了太多)。

7.3 那到底用哪个?

把 §4–7 合起来,可以给出一个诚实的总结:

  • Adam 的最大实际好处是”好调”:步长不太依赖梯度的尺度,默认的 β1,β2,ε\beta_1, \beta_2, \varepsilon 几乎不用动,α\alpha 从 0.001 附近开始试,常常一次就能用。网络里不同参数的梯度尺度差很多时(§9.4),它的优势更明显。这是它成为默认选择的原因。
  • 调好学习率之后,三者在这个任务上差不多。Momentum 的价值是让更大的学习率可用、在很病态的问题上快得多;它和 GD 共用同一个 α\alpha 时并不更快。
  • 哪一个都不是”不用调”。不同的问题上排名可能不同(§6.4 的旋转碗、§6.5 的文献)。

这也是 Ng 在课上的建议:Adam 是一个很好的默认选择,但学习率 α\alpha 一定要调。第 9 讲会讲怎样系统地调(在对数尺度上随机搜索)。


8. 学习率衰减 · Learning rate decay

8.1 为什么要衰减:噪声球

用 mini-batch 训练时,每一步的梯度都带着噪声(§2.3)。学习率是常数的话,参数走到最低点附近之后,会在一个”球”里随机游走,停不下来。学习率越大,球越大。Ng 的建议是:开头用大的学习率走得快,后面慢慢减小,让参数在最低点附近一个更小的范围里打转。

代码 04 演示 2 用一个能精确算出最优解的问题来看这件事:线性回归,10 个特征、1000 个样本,mini-batch 大小 10,普通梯度下降。表中是第 ee 个 epoch 里各步的 J(w)−J(w∗)J(w) - J(w^*) 的平均(0 = 到达最低点):

学习率epoch 1epoch 3epoch 10epoch 30
常数 α = 0.13.9e−016.2e−036.0e−036.4e−03
常数 α = 0.013.3e+007.0e−021.8e−041.4e−04
1/(1 + epoch),α₀ = 0.13.9e−011.3e−031.9e−041.6e−05
指数 0.9^epoch,α₀ = 0.13.9e−014.7e−031.7e−033.0e−05
cosine(按步),α₀ = 0.13.9e−016.1e−034.3e−031.9e−06
  • 常数 α = 0.1:第 3 个 epoch 就降到 6.2e−03,之后再也下不去了。不是算法坏了,是噪声球。
  • 常数 α = 0.01:开头慢得多,但最后停得低(1.4e−04,是 α = 0.1 的 1/47)。
  • 衰减:开头和 α = 0.1 一样快,最后比 α = 0.01 还低。前期大步走得快,后期小步停得准,两者兼得。

注意两种”停不下来”的区别:这里是 mini-batch 的噪声造成的,全批量梯度下降在这个凸问题上用常数学习率就能精确收敛;§5.2、§6.4 里 RMSprop 和 Adam 停不稳,是因为它们的步长不随梯度变小。两种情况的药方都是让学习率随时间变小。(理论上,SGD 要收敛到最优点,学习率需要满足 ∑tαt=∞\sum_t \alpha_t = \infty、∑tαt2<∞\sum_t \alpha_t^2 < \infty,这是 Robbins & Monro 1951 的经典条件。αt∝1/t\alpha_t \propto 1/t 满足它,常数学习率不满足。)

8.2 常见的衰减公式

记 1 个 epoch = 把训练集过一遍。Ng 视频里的第一个公式:

α=11+decay_rate×epoch_num α0(8.1)\alpha = \frac{1}{1 + \text{decay\_rate} \times \text{epoch\_num}}\ \alpha_0 \tag{8.1}

他的例子:α0=0.2\alpha_0 = 0.2、decay_rate = 1 时,epoch 1、2、3、4 的学习率是 0.1、0.0667、0.05、0.04(代码 04 演示 1)。其他常见的形式:

  • 指数衰减:α=0.95epoch_num α0\alpha = 0.95^{\text{epoch\_num}}\,\alpha_0;
  • α=kepoch_num α0\alpha = \dfrac{k}{\sqrt{\text{epoch\_num}}}\,\alpha_0,或者用 mini-batch 的步数 tt 代替 epoch 数:kt α0\dfrac{k}{\sqrt t}\,\alpha_0;
  • 阶梯衰减(discrete staircase):每隔一段时间学习率减半,或者除以 10;
  • 手动衰减:训练一个模型要好几天时,有人会盯着学习曲线,看到它变平了就手动把学习率调小。

代码 04 演示 1 打出了几种调度在 100 个 epoch 里的样子(α0=0.1\alpha_0 = 0.1):

调度epoch 0141020406099
1/(1 + 1·epoch)0.10000.05000.02000.00910.00480.00240.00160.0010
1/(1 + 0.1·epoch)0.10000.09090.07140.05000.03330.02000.01430.0092
每 20 epoch 衰减一次0.10000.10000.10000.10000.05000.03330.02500.0200
指数 0.95^epoch0.10000.09500.08150.05990.03580.01290.00460.0006
阶梯:每 30 epoch 减半0.10000.10000.10000.10000.10000.05000.02500.0125
warmup 5 + cosine0.02000.04000.10000.09930.09400.07010.03770.0000

几种学习率调度

公式 (8.1) 在开头降得最猛:decay_rate = 1 时第 1 个 epoch 就砍掉一半。衰减的快慢完全由 decay_rate 决定,它本身就是一个要调的超参数。较新版本的 Coursera 作业正是从这一点出发的:先用 (8.1) 每个 epoch 衰减一次,发现 5000 个 epoch 下学习率很快就小到几乎不动;再改成”每隔 time_interval 个 epoch 才衰减一次”(fixed interval scheduling,代码里的 schedule_lr_decay):

α=11+decay_rate×⌊epoch_num/time_interval⌋ α0\alpha = \frac{1}{1 + \text{decay\_rate} \times \lfloor \text{epoch\_num} / \text{time\_interval} \rfloor}\ \alpha_0

8.3 现代做法:warmup + cosine

今天训练大模型,最常见的调度是 warmup + cosine,代码里的 warmup_cosine:

  • warmup(热身):最开始的一小段(比如几百到几千步)里,学习率从接近 0 线性升到 α0\alpha_0。Goyal et al.(2017)用 batch 8192 训练 ResNet 时用它解决训练初期的不稳定;Transformer 的原论文(Vaswani et al. 2017)也用了 4000 步的 warmup。一个直觉:刚开始时参数是随机的,梯度大而且方向不稳定,Adam 的二阶矩估计 ss 也还不准(§6.3 的表说明了开头的 ss 有多不可靠),先走小步更安全。
  • cosine:之后学习率沿余弦曲线从 α0\alpha_0 降到 0(或一个很小的值):α=αmin⁡+12(α0−αmin⁡)(1+cos⁡(πp))\alpha = \alpha_{\min} + \tfrac12(\alpha_0 - \alpha_{\min})(1 + \cos(\pi p)),pp 是训练进度(0 到 1)。前期降得慢、中间快、最后又慢慢贴近底部。来自 Loshchilov & Hutter(2017,SGDR)。

经典的 ImageNet 图像分类训练则常用阶梯衰减,比如每 30 个 epoch 把学习率除以 10。

8.4 在月牙数据上的效果,以及它排在第几位

代码 04 演示 5 在月牙数据上比较几种调度。起始学习率 α0=0.07\alpha_0 = 0.07,选法是写代码时定下的规则:“演示 4 里三种优化器、所有种子都没有停在 0.5 的最大学习率”。每格是 3 个种子平均的”训练集 cost / 测试准确率”:

调度最后的 αGDMomentumAdam
常数0.070.0995 / 0.9630.0929 / 0.9650.0907 / 0.965
每个 epoch 衰减 1/(1+epoch)1.4e−050.6207 / 0.6960.6282 / 0.6900.3047 / 0.880
每 1000 epoch 衰减一次0.0140.0924 / 0.9650.0929 / 0.9660.0838 / 0.968
cosine(降到 0)6.9e−090.0915 / 0.9650.0925 / 0.9660.0825 / 0.967
  • 每个 epoch 都按 1/(1 + epoch) 衰减太快了:5000 个 epoch 后学习率只剩 α0\alpha_0 的 1/5000,绝大部分时间几乎不动,GD 的训练 cost 停在 0.6207。这就是作业里演示的问题。
  • 温和的衰减(每 1000 epoch 一次、cosine)比常数学习率的训练 cost 略低一点,测试准确率几乎不变。这个任务只有 300 个样本,mini-batch 噪声不大,网络也很小,学习率衰减能做的事有限。

这和 Ng 的建议一致:学习率衰减在他”要调的东西”清单里排得比较靠后,先把常数学习率 α\alpha 调好,衰减是锦上添花(第 9 讲会讲他的超参数重要性排序)。在大数据、训练几十到几百个 epoch 的任务上,衰减的作用要大得多,§8.1 的噪声球就是原因。


9. 局部最优的问题 · The problem of local optima

9.1 低维直觉 vs 高维现实

早年讲神经网络优化时,常见的一张图是一个二维的、坑坑洼洼的曲面,有很多局部最小值。大家担心梯度下降会卡在某个很差的局部最小里出不来。

Ng 说,这种直觉来自低维的图,在高维空间里会误导你。梯度为 0 的点,在每一个方向上可能向上弯,也可能向下弯。要成为局部最小,必须所有方向都向上弯。如果网络有 20,000 个参数,每个方向”独立地抛硬币”决定弯上还是弯下,那么全部向上的概率大约是 2−200002^{-20000}。所以高维空间里梯度为 0 的点,绝大多数是鞍点(saddle point):有的方向向上弯,有的方向向下弯,像马鞍。

9.2 随机对称矩阵:比抛硬币还极端

在一个梯度为 0 的点附近,J≈J0+12Δ⊤HΔJ \approx J_0 + \tfrac12\Delta^\top H \Delta,HH 是二阶导数矩阵(Hessian)。HH 的特征值全 > 0 是局部最小,全 < 0 是局部最大,有正有负是鞍点。我们不知道真实网络的 HH 长什么样,代码 05 演示 1 用最简单的”随机”版本当作漫画:H=(G+G⊤)/2H = (G + G^\top)/\sqrt2,GG 的元素是独立的标准正态分布(物理学里叫 GOE 随机矩阵)。每个维数生成 400,000 个:

nn局部最小局部最大鞍点2−n2^{-n}(抛硬币)局部最小的个数
15.00e−015.00e−010.00005.00e−01199,951
21.46e−011.46e−010.70752.50e−0158,542
32.49e−022.49e−020.95021.25e−019,955
42.56e−032.48e−030.99506.25e−021,023
51.07e−041.52e−040.99973.12e−0243
62.50e−061.00e−051.00001.56e−021

维数每加 1,局部最小的比例掉得比抛硬币还快得多。6 维时 40 万个里只有 1 个。为什么?抛硬币的论证假设每个方向独立,可随机矩阵的特征值之间互相”排斥”。要所有特征值同时为正,是一个代价极高的集体事件。可以证明这个概率在 nn 很大时大约按 e−cn2e^{-c n^2} 衰减,c=ln⁡3/4≈0.27c = \ln 3/4 \approx 0.27(Dean & Majumdar 2006),比 2−n2^{-n} 快得多。(n=2n = 2 的 0.146 可以手算出来,见练习 10。)

维数更高时,看特征值里负数占多少:n=10n = 10、100、1000 时平均都是一半左右(0.499、0.499、0.500)。n=1000n = 1000 的 10 个矩阵里最少 0.499、最多 0.502,离”局部最小要求的 0”差得很远。

要小心这个漫画的局限。 真实网络的 Hessian 不是随机矩阵。Dauphin et al.(2014)等人的研究(建立在 Bray & Dean 2007 的随机场理论之上)发现,临界点的”负方向个数”和它的 cost 高低有关:cost 高的临界点大多是有很多下降方向的鞍点,cost 越接近最低,负方向越少。在低 cost 的区域里确实有很多局部最小,只是它们的 cost 大多和全局最小差不多(Choromanska et al. 2015 在一个简化模型里得到类似的结论)。所以准确的说法是:对大网络,“卡在一个很差的局部最小里”很少是真正的问题。

9.3 鞍点:逃出来要多久

既然到处是鞍点,梯度下降会不会卡在鞍点上?代码 05 演示 2 用一个最简单的鞍点:

f(x,y)=12x2+14y4−12y2f(x, y) = \tfrac12 x^2 + \tfrac14 y^4 - \tfrac12 y^2

(0,0)(0, 0) 是鞍点(xx 方向向上弯,yy 方向向下弯,f=0f = 0),(0,±1)(0, \pm1) 是两个最小值(f=−14f = -\tfrac14)。从 (1,δ)(1, \delta) 出发:xx 方向很快滑到 0,点被”吸”到鞍点旁边;能不能离开,全看 yy 方向那一点点 δ\delta。每格是”第一次 ∣y∣>0.5|y| > 0.5 的步数 / 在鞍点附近(∣f∣<10−3|f| < 10^{-3})停留的步数”:

δ = 0.1δ = 0.001δ = 1e−06δ = 1e−09δ = 0
GD,α = 0.119 / 067 / 12139 / 83212 / 155—
Momentum,α = 0.132 / 0105 / 19216 / 128326 / 238—
RMSprop,α = 0.12 / 02 / 02 / 03 / 0—
Adam,α = 0.15 / 06 / 06 / 08 / 0—
RMSprop,α = 0.0126 / 030 / 030 / 031 / 1—
Adam,α = 0.0137 / 044 / 144 / 045 / 0—

(”—“表示 5000 步内没有离开。)

  • GD:鞍点附近 yy 每步只放大 (1+α)=1.1(1+\alpha) = 1.1 倍,从 δ\delta 长到 0.5 要 log⁡(0.5/δ)/log⁡1.1\log(0.5/\delta)/\log 1.1 步。δ\delta 每小 1000 倍,就多花约 70 步(67 → 139 → 212)。这段时间里 ff 几乎不变,训练曲线上就是一段平台。 鞍点本身不会把 GD 永远困住,但会让它停很久。
  • Momentum(Ng 的写法,同一个 α)比 GD 还慢(326 vs 212 步):vv 是梯度的平均,一个持续变大的梯度被平均之后总是”慢半拍”。换成它允许的更大学习率,它会快起来。
  • RMSprop / Adam 几乎不在乎 δ:δ\delta 从 10−110^{-1} 到 10−910^{-9},Adam(α = 0.01)都是 37–45 步。yy 方向的梯度再小,除以它自己的均方根之后也是 1 的量级,于是 yy 方向一开始就以约 α\alpha 的速度往外走。这就是 Ng 说”momentum、RMSprop、Adam 能帮你更快地离开平台”时,最直接的一个机制。(这里鞍点的方向和坐标轴对齐;斜的时候效果会打折扣,和 §6.4 同样的道理。)
  • 正好在鞍点上(δ = 0):梯度恰好是 0,四种方法全都一步不动。鞍点是”不稳定的平衡”,但没有任何扰动就永远不会离开。

最后一条引出 mini-batch 噪声的一个好处。演示 2 让点正好从 y=0y = 0 出发,但给每一步的梯度加一点点噪声(模拟 mini-batch),各跑 200 次:噪声标准差 10−610^{-6} 时 GD 逃出来的中位数是 159 步,10−310^{-3} 时 87 步,200 次全部逃了出来。第 1 讲 tiny_nn.train 的注释说”梯度里的随机性有时还有好处”,指的就是这一类情形。

9.4 平台:真正的麻烦,以及 Adam 怎样”部分掩盖”梯度消失

Ng 的结论是:真正拖慢训练的不是局部最优,而是平台(plateau):一大片梯度接近 0 的区域,参数要在上面挪很久才能走出去。§9.3 的鞍点附近就是一种平台。另一种我们已经见过:第 1 讲代码 04 演示 3 里,5 个 sigmoid 隐藏层的网络用普通 SGD 训练,4000 步都停在 50% 的准确率上。

代码 05 演示 3 重做这个实验(同一个”老师网络”任务、同样的网络结构、mini-batch 128、4000 步),换几种优化器:

优化器达到 80% 验证准确率的步数4000 步后的验证准确率
SGD,α = 0.1—0.494
SGD,α = 1.034000.828
Momentum,α = 0.1—0.505
Momentum,α = 1.021000.879
RMSprop,α = 0.0013000.859
Adam,α = 0.0012000.865
Adam,α = 0.011000.883

用默认学习率的 Adam 200 步就离开了平台。为什么?看前 100 步里每一层权重的梯度和实际更新量的均方根:

第 1 层第 2 层第 3 层第 4 层第 5 层输出层第 1 层 / 输出层
梯度(SGD α = 0.1 时)7.1e−069.6e−062.8e−051.7e−048.9e−042.4e−022.9e−04
SGD α = 0.1 的更新量7.1e−079.6e−072.8e−061.7e−058.9e−052.4e−032.9e−04
梯度(Adam α = 0.001 时)1.2e−051.4e−054.5e−052.2e−041.1e−032.8e−024.3e−04
Adam α = 0.001 的更新量9.0e−046.8e−044.0e−043.5e−043.0e−043.0e−043.0
  • SGD 的更新量 = α×\alpha \times 梯度。梯度从输出层往输入层一层层变小(梯度消失,第 1 讲 §4.4),第 1 层的更新量只有输出层的 3×10−43 \times 10^{-4} 倍,靠近输入的几层几乎不动。
  • Adam 把每个参数的梯度除以它自己的均方根,各层的更新量都变成了约 α\alpha 的量级(第 1 层 / 输出层 = 3.0)。梯度本身仍然差了几千倍,但步子差不多大了。这正是 §6.4 说的”和坐标轴对齐的病态”:每一层自己的梯度尺度不同,Adam 按参数修正了它。

这就是第 1 讲 tiny_nn.py 里那句注释”Adam 的自适应步长会部分掩盖梯度消失”的意思。为什么说部分?

  1. 梯度消失本身并没有被修好:第 1 层的梯度仍然很小,里面的”信号”相对噪声可能也更弱。如果梯度小到和 ε=10−8\varepsilon = 10^{-8} 一个量级,除法就失效了,Adam 也救不回来(练习 9 把 ε\varepsilon 改大来模拟这种情况)。
  2. 从根上解决梯度消失的办法是 ReLU(第 1、3 讲)、好的初始化(第 7 讲)、Batch Norm(第 9 讲)、残差连接(第 13 讲)。
  3. 反过来,正因为 Adam 会掩盖它,用 Adam 训练得不错,并不说明网络里没有梯度消失。要诊断,就像这里一样直接看每一层的梯度。这也是第 1 讲代码 04 比较 sigmoid 和 ReLU 时特意用普通 SGD 的原因。

10. 小结 · Summary

  • Mini-batch:每一步只用 BB 个样本。按步数全批量最准,按样本数小 batch 更省,按耗时 mini-batch 最快(代码 01:B=64B = 64 在 0.050 s 降到 cost ≤ 0.3,全批量 0.666 s,SGD 每个样本慢约 170 倍)。mini-batch 梯度的误差按 1/B1/\sqrt{B} 缩小(2.63 → 0.34 → 0.11),所以代价曲线会抖。每个 epoch 打乱;最后一块可留可丢;常用 64–512,确保放得进内存。
  • 指数加权平均 vt=βvt−1+(1−β)θtv_t = \beta v_{t-1} + (1-\beta)\theta_t ≈ 最近 1/(1−β)1/(1-\beta) 个数的平均,理由是 (1−ε)1/ε≈1/e(1-\varepsilon)^{1/\varepsilon} \approx 1/e。β\beta 越大越平滑也越滞后(β=0.98\beta = 0.98 滞后 40 天)。偏差修正 v^t=vt/(1−βt)\hat v_t = v_t/(1-\beta^t) 修正 v0=0v_0 = 0 造成的开头偏低。
  • 病态的碗:GD 的学习率被陡的方向卡住,平的方向走得慢,步数 ∝ κ\kappa(35 / 87 / 346 / 3454)。
  • Momentum 用梯度的平均走:平均掉震荡,允许更大的学习率(β=0.9\beta = 0.9 时上限是 GD 的 19 倍),在很扁的碗上快得多(κ=1000\kappa = 1000:144 vs 2420 步)。但同一学习率下它不比 GD 快,会冲过谷底,β=0.9\beta = 0.9 对不太扁的碗惯性太大。
  • RMSprop 用梯度均方根按维度缩放步长;步长约等于 α\alpha,常数学习率下停不下来。
  • Adam = momentum + RMSprop + 偏差修正,默认 β1=0.9\beta_1 = 0.9、β2=0.999\beta_2 = 0.999、ε=10−8\varepsilon = 10^{-8},只调 α\alpha。偏差修正必须做(不修正时前几百步步长被放大到最多 6.57 倍)。按维度缩放只对和坐标轴对齐的病态有效(旋转后 123 → 733 步)。
  • 作业的复刻:α=0.0007\alpha = 0.0007 时 Adam 0.967、GD / Momentum 0.860;α=0.07\alpha = 0.07 时三者都在 0.963–0.965。Adam 的主要优势是”好调”,不是”终点更好”;它也会在大学习率下失败。
  • 学习率衰减:mini-batch 噪声让常数学习率停在一个”噪声球”里(线性回归:常数 0.1 停在 6.4e−03,cosine 到 1.9e−06)。公式 α0/(1+decay_rate⋅epoch)\alpha_0/(1 + \text{decay\_rate}\cdot\text{epoch})、指数、阶梯、手动;现代常用 warmup + cosine。衰减太快会让训练停住。在 Ng 的清单里它排得比较靠后。
  • 局部最优:高维空间里梯度为 0 的点几乎都是鞍点(随机矩阵:6 维时 40 万个里只有 1 个局部最小);真正的麻烦是平台。GD 离开鞍点的时间 ∝ log⁡(1/δ)\log(1/\delta),Adam 几乎不受影响,梯度噪声能帮忙离开”正好的”鞍点。Adam 让各层的更新量变得一样大,从而部分掩盖梯度消失。

11. 常见误区 · Common pitfalls

  1. “mini-batch 训练的 cost 在抖,说明学习率太大了。” 不一定。mini-batch 的梯度和 cost 本身就有噪声(代码 01 演示 2)。要看的是趋势:用每个 epoch 的平均,或者定期在整个训练集 / 验证集上评估。持续上升或者变成 nan 才是学习率太大。
  2. “SGD 就是 batch = 1 的梯度下降。” 原课里的 stochastic gradient descent 指 B=1B = 1;但今天很多人(包括 PyTorch 的 torch.optim.SGD)把 mini-batch 梯度下降也叫 SGD。读论文和代码时,看清楚他们的 batch 是多大。
  3. “Momentum 让小球越滚越快,所以同一个学习率下比 GD 快。” Ng 写法里方向一致时 vv 稳定在梯度本身那么大,同一个 α\alpha 下并不更快(代码 03 演示 2:83 vs 128 步;代码 04:GD 和 Momentum 结果几乎相同)。它的好处是允许更大的学习率、在很病态的问题上快得多。
  4. “用了 momentum,cost 就应该单调下降。” 小球会冲过谷底,cost 偶尔上升是正常的(代码 03 演示 4:150 步里 60 步在上升)。
  5. “Adam 不用调学习率。” β1,β2,ε\beta_1, \beta_2, \varepsilon 几乎不用调,α\alpha 一定要调。月牙数据上 α=0.7\alpha = 0.7 时 Adam 3 次全部失败(代码 04 演示 4)。
  6. “偏差修正可有可无。” 对 momentum 基本如此;对 Adam 不是:不修正时前几百步的实际学习率是设定值的 3 到 6.6 倍,学习率稍大就可能把网络训坏(代码 02 演示 4)。
  7. “Adam 训练得好,所以网络里没有梯度消失。” Adam 会把各层的更新量拉到同一个量级,掩盖梯度消失(代码 05 演示 3)。诊断要直接看各层的梯度。
  8. “学习率衰减越快越好 / 一定能提升结果。” 衰减太快会让训练提前停住(代码 04 演示 5:1/(1 + epoch) 衰减 5000 个 epoch,GD 的 cost 停在 0.62)。先调好常数学习率,衰减是锦上添花。
  9. “训练卡住了,一定是掉进了局部最小。” 在大网络里更可能是平台或鞍点附近(梯度很小但不为 0)。可以试试换优化器(Adam)、调大学习率、检查是不是梯度消失(各层梯度的大小),而不是换随机种子重来。

12. 练习 · Exercises

练习 1(手算指数加权平均)。三天的气温是 θ=(10,12,8)\theta = (10, 12, 8),β=0.9\beta = 0.9,v0=0v_0 = 0。(a) 算出 v1,v2,v3v_1, v_2, v_3。(b) 算出偏差修正后的 v^1,v^2,v^3\hat v_1, \hat v_2, \hat v_3。(c) 为什么 v^1\hat v_1 恰好等于 θ1\theta_1?

参考答案

(a) v1=0.1×10=1.0v_1 = 0.1 \times 10 = 1.0;v2=0.9×1.0+0.1×12=2.1v_2 = 0.9 \times 1.0 + 0.1 \times 12 = 2.1;v3=0.9×2.1+0.1×8=2.69v_3 = 0.9 \times 2.1 + 0.1 \times 8 = 2.69。三个数都远小于真实气温,这就是 v0=0v_0 = 0 造成的偏差。

(b) v^1=1.0/0.1=10\hat v_1 = 1.0/0.1 = 10;v^2=2.1/(1−0.81)=2.1/0.19≈11.05\hat v_2 = 2.1/(1-0.81) = 2.1/0.19 \approx 11.05;v^3=2.69/(1−0.729)=2.69/0.271≈9.93\hat v_3 = 2.69/(1-0.729) = 2.69/0.271 \approx 9.93。

(c) v1=(1−β)θ1v_1 = (1-\beta)\theta_1,修正项 1−β1=1−β1 - \beta^1 = 1-\beta,两者一除就是 θ1\theta_1。一般地,如果 θ\theta 一直等于常数 cc,vt=c(1−βt)v_t = c(1-\beta^t),修正后 v^t=c\hat v_t = c 对所有 tt 都成立。这就是偏差修正”把权重归一化成 1”的意思。(可以用 optimizers.ewa([10, 12, 8], 0.9, bias_correction=True) 验证。)

练习 2(权重之和与平均年龄)。(a) 证明 vtv_t 展开后 θ1,…,θt\theta_1, \dots, \theta_t 的权重之和是 1−βt1 - \beta^t。(b) 当 t→∞t \to \infty 时,”kk 天前”的权重是 (1−β)βk(1-\beta)\beta^k。证明平均年龄 ∑kk(1−β)βk=β/(1−β)\sum_k k(1-\beta)\beta^k = \beta/(1-\beta)。(c) β=0.98\beta = 0.98 时这是多少天?和代码 02 演示 1 实测的滞后(40 天)比较,为什么不一样?

参考答案

(a) 权重之和 (1−β)(1+β+⋯+βt−1)=(1−β)⋅1−βt1−β=1−βt(1-\beta)(1 + \beta + \cdots + \beta^{t-1}) = (1-\beta)\cdot\frac{1-\beta^t}{1-\beta} = 1-\beta^t(等比数列求和)。

(b) 利用 ∑k≥0kβk=β/(1−β)2\sum_{k\ge0} k\beta^k = \beta/(1-\beta)^2(对 ∑βk=1/(1−β)\sum \beta^k = 1/(1-\beta) 两边关于 β\beta 求导再乘 β\beta),所以 ∑kk(1−β)βk=(1−β)⋅β/(1−β)2=β/(1−β)\sum_k k(1-\beta)\beta^k = (1-\beta)\cdot\beta/(1-\beta)^2 = \beta/(1-\beta)。

(c) 49 天。对一条直线 θt=a+bt\theta_t = a + bt,指数加权平均(tt 很大时)恰好等于 a+b(t−49)a + b(t - 49),也就是”拖后 49 天”。季节曲线是弯的:在最热的那几天,曲线已经开始往下拐,平均里混进的”更早的日子”反而没那么热,所以峰值的滞后小一些(40 天),同时峰值也被压低(起伏只剩 84%)。

练习 3(噪声被平均掉多少)。设 θt\theta_t 是独立的噪声,方差 σ2\sigma^2。证明 tt 很大时 Var(vt)=σ2 1−β1+β\text{Var}(v_t) = \sigma^2\,\frac{1-\beta}{1+\beta}。σ=3\sigma = 3、β=0.9\beta = 0.9 时,vtv_t 的标准差是多少?和代码 02 演示 1 的”理论值”一列对照。

参考答案

vt=∑k(1−β)βkθt−kv_t = \sum_k (1-\beta)\beta^k\theta_{t-k},各项独立,所以 Var(vt)=(1−β)2σ2∑kβ2k=(1−β)2σ2⋅11−β2=σ2 1−β1+β\text{Var}(v_t) = (1-\beta)^2\sigma^2\sum_k \beta^{2k} = (1-\beta)^2\sigma^2 \cdot \frac{1}{1-\beta^2} = \sigma^2\,\frac{1-\beta}{1+\beta}(用了 1−β2=(1−β)(1+β)1-\beta^2 = (1-\beta)(1+\beta))。

σ=3\sigma = 3、β=0.9\beta = 0.9:30.1/1.9≈0.693\sqrt{0.1/1.9} \approx 0.69 °C,就是表里的理论值。对比”平均最近 N=10N = 10 天”的简单平均,标准差是 3/10≈0.953/\sqrt{10} \approx 0.95。指数加权平均的”有效样本数”是 (1+β)/(1−β)=19(1+\beta)/(1-\beta) = 19,比 NN 大,因为它的权重拖得更长。

练习 4(数 mini-batch)。m=1,000,000m = 1{,}000{,}000,B=512B = 512。(a) 用 random_mini_batches 的默认做法,每个 epoch 有多少个 mini-batch?最后一块有多少个样本?(b) 训练 20 个 epoch 一共更新多少次参数?(c) 换成 drop_last=True,每个 epoch 有多少样本没用上?

参考答案

(a) ⌊1,000,000/512⌋=1953\lfloor 1{,}000{,}000/512 \rfloor = 1953 个满的块,1953×512=999,9361953 \times 512 = 999{,}936,剩下 64 个样本组成第 1954 块。所以每个 epoch 1954 个 mini-batch。

(b) 20×1954=39,08020 \times 1954 = 39{,}080 次。(全批量梯度下降 20 个 epoch 只更新 20 次。)

(c) 64 个(0.0064%),完全可以忽略。这就是”mm 远大于 BB 时两种做法几乎没有区别”。

练习 5(先预测再看表:GD 在更扁的碗里)。f=12(x2+100y2)f = \tfrac12(x^2 + 100y^2)。(a) GD 不发散的最大学习率是多少?(b) 最优学习率 α∗\alpha^* 是多少?每一步误差乘以多少?(c) 粗略估计 ff 降到初始值的 10−610^{-6} 需要多少步(提示:ff 每步大约乘以倍数的平方)。然后对照代码 03 演示 1 的表。

参考答案

(a) yy 方向曲率 100,α<2/100=0.02\alpha < 2/100 = 0.02。

(b) α∗=2/(1+100)≈0.0198\alpha^* = 2/(1 + 100) \approx 0.0198,两个方向的倍数都是 ±99/101≈±0.9802\pm 99/101 \approx \pm 0.9802。

(c) ff 每步乘以 0.980220.9802^2,需要 ln⁡106/(−2ln⁡0.9802)≈13.8/0.040≈345\ln 10^6 / (-2\ln 0.9802) \approx 13.8/0.040 \approx 345 步。代码实测 346 步。这个估计对 κ=1000\kappa = 1000 给出约 3450 步(实测 3454)。步数正比于 κ\kappa:碗扁 10 倍,就要多走 10 倍的步数。

练习 6(momentum 能用的最大学习率)。heavy-ball 方法在一个曲率为 λ\lambda 的方向上不发散的条件是 0<ηλ<2(1+β)0 < \eta\lambda < 2(1+\beta),其中 η\eta 是”heavy-ball 写法”的步长。(a) Ng 的写法 (4.1) 相当于 η=α(1−β)\eta = \alpha(1-\beta)(§4.5)。由此推出 Ng 写法下 α\alpha 的上限。(b) κ=25\kappa = 25(最陡的曲率 25)、β=0.9\beta = 0.9 时是多少?是 GD 的几倍?(c) β=0.99\beta = 0.99 呢?对照代码 03 演示 4(c)。

参考答案

(a) α(1−β)λ<2(1+β)\alpha(1-\beta)\lambda < 2(1+\beta),即 α<2(1+β)(1−β)λ\alpha < \dfrac{2(1+\beta)}{(1-\beta)\lambda}。β=0\beta = 0 时退化成 GD 的 2/λ2/\lambda。

(b) 2×1.9/(0.1×25)=1.522 \times 1.9/(0.1 \times 25) = 1.52,是 GD(2/25=0.082/25 = 0.08)的 19 倍。代码二分查找的结果正是 1.520。

(c) 2×1.99/(0.01×25)=15.922 \times 1.99/(0.01 \times 25) = 15.92,是 GD 的 199 倍。但上限高不等于收敛快:β\beta 越大,惯性越大,在不太扁的碗里来回晃得越厉害(练习 8)。

练习 7(Adam 的第一步)。(a) 证明:做了偏差修正的 Adam,第 1 步对每个参数的更新恰好是 −α⋅sign(dW)-\alpha\cdot\text{sign}(dW)(忽略 ε\varepsilon),和梯度的大小无关。(b) 不做偏差修正时,第 1 步是多少?(c) 没有偏差修正的 RMSprop,β2=0.999\beta_2 = 0.999 时第 1 步是多少?β2=0.9\beta_2 = 0.9 呢?

参考答案

(a) v1=(1−β1)gv_1 = (1-\beta_1)g,s1=(1−β2)g2s_1 = (1-\beta_2)g^2;修正后 v^1=g\hat v_1 = g,s^1=g2\hat s_1 = g^2,所以更新量 =α g/g2=α⋅sign(g)= \alpha\, g/\sqrt{g^2} = \alpha\cdot\text{sign}(g)。这说明 Adam 的 α\alpha 直接就是”每个参数第一步走多远”,和梯度的单位无关。这也解释了代码 04 里”同样的 α=0.0007\alpha = 0.0007,Adam 的步子是 GD 的几十到几百倍”。

(b) α⋅0.1g/(0.001∣g∣)=α⋅0.1/0.0316≈3.16α\alpha\cdot 0.1g/(\sqrt{0.001}|g|) = \alpha \cdot 0.1/0.0316 \approx 3.16\alpha(代码 02 演示 4 表的第一列)。

(c) α g/(1−β2)g2=α/1−β2\alpha\, g/\sqrt{(1-\beta_2)g^2} = \alpha/\sqrt{1-\beta_2}:β2=0.999\beta_2 = 0.999 时约 31.6α31.6\alpha,β2=0.9\beta_2 = 0.9 时约 3.16α3.16\alpha。这是本讲的 RMSprop 用 β2=0.9\beta_2 = 0.9 的原因之一。

练习 8(改代码:β 也是超参数)。在 03_optimizer_trajectories.py 的 demo_best_lr 里,momentum 默认 β=0.9\beta = 0.9。把循环里的 run_grid(name, A, z0, GRID) 改成 run_grid(name, A, z0, GRID, beta=0.5)(beta 只影响 momentum,其他三种方法会忽略它)。先预测:κ=10\kappa = 10 时 momentum 还会比 GD(39 步)慢吗?κ=1000\kappa = 1000 时呢?然后运行验证。

参考答案

我运行的结果(每种 κ\kappa 各自挑最好的学习率):

κ\kappaGDMomentum β=0.9\beta = 0.9Momentum β=0.5\beta = 0.5
103911919
10002420144958

β=0.5\beta = 0.5 在 κ=10\kappa = 10 时只要 19 步,比 GD 还快一倍;但在 κ=1000\kappa = 1000 时要 958 步,远不如 β=0.9\beta = 0.9。理论上最优的 β\beta 大约是 ((κ−1)/(κ+1))2\big((\sqrt\kappa - 1)/(\sqrt\kappa + 1)\big)^2:κ=10\kappa = 10 时约 0.27,κ=1000\kappa = 1000 时约 0.88。碗越扁,越需要大的惯性。深度网络通常很病态,所以 0.9 是一个合理的默认值。

练习 9(改代码:ε 太大会怎样)。在 05_saddles_and_plateaus.py 的 train_deep_sigmoid 里,把 opt = Optimizer(name, params) 改成 opt = Optimizer(name, params, epsilon=1e-3)。先预测:Adam(α = 0.001)还能 200 步离开平台吗?第 1 层的更新量会变成多少量级?

参考答案

我运行的结果:Adam 要 2400 步才到 80%(原来 200 步),第 1 层更新量的均方根从 9.0e−04 掉到约 6.4e−06。原因:第 1 层的梯度只有 10−510^{-5} 量级,比 ε=10−3\varepsilon = 10^{-3} 小得多,分母 s^+ε≈ε\sqrt{\hat s} + \varepsilon \approx \varepsilon,更新量变成 α g/ε≈10−3×10−5/10−3=10−5\alpha\,g/\varepsilon \approx 10^{-3}\times10^{-5}/10^{-3} = 10^{-5} 量级,相当于学习率为 α/ε=1\alpha/\varepsilon = 1 的普通 SGD。输出层的梯度(10−210^{-2})仍然比 ε\varepsilon 大,还是被归一化。这个练习说明了 §9.4 的”部分”:梯度小到和 ε\varepsilon 一个量级时,Adam 的”掩盖”就失效了。ε\varepsilon 是”防止除以 0”的,不要随便调大(有些框架和任务会把它设成 10−610^{-6} 或更大,那是另外的考虑)。

练习 10(★ 二维随机 Hessian)。H=(accb)H = \begin{pmatrix} a & c \\ c & b\end{pmatrix},a,b∼N(0,2)a, b \sim N(0, 2)、c∼N(0,1)c \sim N(0, 1) 相互独立(代码 05 演示 1 的 n=2n = 2)。证明 HH 两个特征值都为正的概率是 12(1−1/2)≈0.146\tfrac12(1 - 1/\sqrt2) \approx 0.146。(提示:令 u=(a+b)/2u = (a+b)/2、w=(a−b)/2w = (a-b)/2,特征值是 u±w2+c2u \pm \sqrt{w^2 + c^2}。)

参考答案

u,wu, w 都是 N(0,1)N(0, 1) 且相互独立(a,ba, b 方差相同,所以和与差不相关),和 cc 也独立。两个特征值都为正 ⇔ u>ru > r,其中 r=w2+c2r = \sqrt{w^2 + c^2} 服从 Rayleigh 分布,密度 re−r2/2r e^{-r^2/2}(r≥0r \ge 0)。所以

P=∫0∞Φˉ(r) re−r2/2 drP = \int_0^\infty \bar\Phi(r)\, r e^{-r^2/2}\, dr

(Φˉ(r)=P(u>r)\bar\Phi(r) = P(u > r))。分部积分:=[−e−r2/2Φˉ(r)]0∞−∫0∞e−r2/2φ(r) dr=12−12π∫0∞e−r2dr=12−12π⋅π2=12(1−12)≈0.1464= \big[-e^{-r^2/2}\bar\Phi(r)\big]_0^\infty - \int_0^\infty e^{-r^2/2}\varphi(r)\,dr = \tfrac12 - \frac{1}{\sqrt{2\pi}}\int_0^\infty e^{-r^2}dr = \tfrac12 - \frac{1}{\sqrt{2\pi}}\cdot\frac{\sqrt\pi}{2} = \tfrac12\big(1 - \tfrac{1}{\sqrt2}\big) \approx 0.1464。

代码的蒙特卡洛结果是 1.46e−01(58,542 / 400,000)。比”抛两次硬币都是正面”的 0.25 小得多:两个特征值不是独立的,它们互相排斥(uu 要同时超过 +r+r 和 −r-r)。


13. 延伸阅读 · Further reading

课程材料

  • Coursera Deep Learning Specialization,C2M2 “Optimization Algorithms”(Andrew Ng)与编程作业 “Optimization Methods”。
  • Goodfellow, Bengio & Courville (2016). Deep Learning, Chapter 8 “Optimization for Training Deep Models”(免费在线):mini-batch、momentum、自适应学习率、鞍点与平台的系统讲法。
  • Ruder (2016). An overview of gradient descent optimization algorithms. arXiv:1609.04747。各种优化器的一张总表,适合查公式。

算法的原始出处

  • Robbins & Monro (1951). A stochastic approximation method. Annals of Mathematical Statistics。随机梯度方法和学习率条件的源头。
  • Polyak (1964). Some methods of speeding up the convergence of iteration methods.(heavy ball / momentum)
  • Sutskever, Martens, Dahl & Hinton (2013). On the importance of initialization and momentum in deep learning. ICML。momentum 和 Nesterov momentum 在深度学习里的作用。
  • Hinton, Srivastava & Swersky. Neural Networks for Machine Learning, Lecture 6e “rmsprop”(Coursera 课程讲义,2012)。
  • Kingma & Ba (2015). Adam: A method for stochastic optimization. ICLR。
  • Loshchilov & Hutter (2019). Decoupled weight decay regularization. ICLR。AdamW。

学习率调度与 batch 大小

  • Loshchilov & Hutter (2017). SGDR: Stochastic gradient descent with warm restarts. ICLR。cosine 调度。
  • Goyal et al. (2017). Accurate, large minibatch SGD: Training ImageNet in 1 hour. linear scaling rule 与 warmup。
  • Keskar et al. (2017). On large-batch training for deep learning: Generalization gap and sharp minima. ICLR。
  • McCandlish, Kaplan, Amodei et al. (2018). An empirical model of large-batch training. “梯度噪声尺度”与临界 batch 大小。

Adam 的局限

  • Wilson et al. (2017). The marginal value of adaptive gradient methods in machine learning. NeurIPS。
  • Reddi, Kale & Kumar (2018). On the convergence of Adam and beyond. ICLR。

鞍点与损失曲面

  • Dauphin et al. (2014). Identifying and attacking the saddle point problem in high-dimensional non-convex optimization. NeurIPS。
  • Bray & Dean (2007). Statistics of critical points of Gaussian fields on large-dimensional spaces. Physical Review Letters。
  • Dean & Majumdar (2006). Large deviations of extreme eigenvalues of random matrices. Physical Review Letters。
  • Choromanska et al. (2015). The loss surfaces of multilayer networks. AISTATS。
  • Goodfellow, Vinyals & Saxe (2015). Qualitatively characterizing neural network optimization problems. ICLR。

14. 下一讲预告 · Next lecture

第 9 讲:超参数调优、Batch Normalization、Softmax 与深度学习框架。 这一讲我们多了好几个超参数:mini-batch 大小、β\beta、β1\beta_1、β2\beta_2、ε\varepsilon、衰减速度……再加上学习率、层数、每层单元数,到底先调哪个、怎么调?第 9 讲会讲:

  • Ng 的超参数重要性排序(学习率第一;β\beta、mini-batch 大小、隐藏单元数其次……),为什么随机搜索比网格搜索好,为什么学习率和 1−β1-\beta 要在对数尺度上采样;
  • Batch Normalization:把每一层的输入也”归一化”,让地形更好走(第 7 讲的输入归一化推广到每一层),以及它为什么常常能让你用更大的学习率;
  • softmax 与多分类;
  • 框架替你做了什么:从第 9 讲起换成 PyTorch。框架里的优化器(torch.optim.SGD(momentum=0.9)、torch.optim.Adam)的每一个超参数,都能对应到这一讲的公式上。注意 PyTorch 的 momentum 用的是 §4.5 里”不乘 1−β1-\beta“的写法。
← 返回课程目录