Lecture 6 · 实践要点 I:数据划分、偏差与方差、正则化 · Setting up your ML Application & Regularization

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

课程源码 ZIP ↓

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

本篇目录

对应 Maps to:Coursera Deep Learning Specialization C2M1 “Practical Aspects of Deep Learning” 的前半部分(Train / Dev / Test Sets、Bias / Variance、Basic Recipe for Machine Learning、Regularization、Why Regularization Reduces Overfitting?、Dropout Regularization、Understanding Dropout、Other Regularization Methods);编程作业 “Regularization”(法国队守门员开球的二维数据,3 层网络,比较不加正则化 / L2 / dropout)。在 Stanford CS230 Fall 2025 的安排里,C2M1 在第 4 周(和课堂 Lecture 4 同一周);同期的 TA Section 4 题为 “Xavier Initialization and Regularization”,其中初始化的部分放在第 7 讲。 依据说明:Coursera 的视频和编程作业只对注册学员开放,Section 4 的材料不公开。本讲依据 Andrew Ng 在 C2M1 里广为人知的内容来写:数据划分的比例、四种 bias / variance 情形的例子、basic recipe 的流程、L2 正则化和 inverted dropout 的公式与实现、“为什么有效”的直觉、数据增强与 early stopping 的讨论。视频里的具体措辞是凭记忆复述的,细节可能和原视频略有出入。原作业的数据文件不公开,代码里合成了一个相似的二维数据集(field_data.py),所以数字和原作业不同;文中提到的原作业结果来自广为流传的作业预期输出,这里无法复现核对。dropout、early stopping、double descent 等内容依据原始论文(§10)。 配套代码 Code:code/lecture06/(numpy;核心模块 regularization.py + 数据模块 field_data.py + 画图工具 curves.py + 5 个脚本;01 约 5 秒,02 约 40 秒,03 约 1 分钟,04、05 各约 50 秒) 预计阅读时间:3–3.5 小时(含运行代码)


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

0.1 学习目标 Learning goals

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

  1. 说清训练集(train)、开发集(dev)、测试集(test)各自的用途;解释为什么小数据时用 60/20/20、百万级数据时 98/1/1 就够——dev / test 的大小由”要分辨多大的差别”决定,而不是由比例决定;知道 dev 和 test 为什么必须来自同一个分布,以及”只有 dev、没有 test”意味着什么。
  2. 只看两个数字(训练误差和 dev 误差)就诊断出 high bias / high variance / 两者都有 / 两者都没有,并且知道诊断的参照是 Bayes 错误率而不是 0。
  3. 按 Andrew Ng 的 basic recipe 选择下一步:先治 bias(更大的网络、训练更久、换架构),再治 variance(更多数据、正则化、换架构);解释为什么深度学习时代的 bias–variance trade-off “没那么 trade”。
  4. 写出 L2 正则化的 cost 和梯度(Frobenius 范数),解释它为什么叫 weight decay、为什么能减少过拟合(两个直觉),以及调试时为什么 cost 曲线要包含正则项。
  5. 从零实现 inverted dropout(蒙版存进 cache、除以 keep_prob),说清测试时为什么不用 dropout、“除以 keep_prob”解决了什么问题、dropout 为什么有效、为什么它让 cost 曲线不再单调。
  6. 了解另外两种正则化——数据增强和 early stopping,以及 early stopping 和”正交化”的冲突。
  7. 面对一个过拟合的模型(比如第 5 讲 32×32 的白天 / 黑夜网络),能设计一组对照实验来决定用哪种正则化,而不是凭感觉。

0.2 先修与本讲范围 Prerequisites & scope

默认你已经会 Assumed本讲只给直觉,后面几讲再展开 Previewed only
第 1 讲:训练循环;第 1 讲 tiny_nn.train 里的 weight_decay 和 keep_best(当时当作黑盒)输入归一化、梯度消失 / 爆炸、He / Xavier 初始化、梯度检验的系统讲解(第 7 讲)
第 3 讲:花朵 / 同心圆数据,Bayes 最优分类器(§8.2),隐藏单元数与过拟合的苗头(§8.4)mini-batch、Adam(第 8 讲;本讲有几个实验借用第 1 讲的 Adam);AdamW
第 4 讲:LL 层网络的积木 deep_nn.py(本讲的模块就建在它上面);参数 vs 超参数超参数怎样系统地搜索、Batch Norm 的正则化效果(第 9 讲)
第 5 讲:白天 / 黑夜案例,32×32 网络的过拟合(§2.4)human-level performance、avoidable bias、数据不匹配与 train-dev set、正交化(第 10 讲)

本讲把前几讲里反复出现、却一直没有展开的现象系统化:第 1 讲代码 03 里大网络在小数据时垫底、逻辑回归在大数据时饱和;第 2 讲 §9.3 位置一抖动,线性模板就既欠拟合又过拟合;第 3 讲 §8.4 里 80 个训练样本时 nh=50n_h = 50 的网络训练 1.000、测试 0.821;第 5 讲 §2.4 里 32×32 的网络训练 0.966、测试 0.869。这些都是同一件事的不同侧面:训练误差和”新数据上的误差”之间的关系。

0.3 本讲地图 Map

§1  训练 / 开发 / 测试集:各管什么;dev 要多大;为什么同分布;没有 test 会怎样   ← 代码 01
§2  偏差与方差:两个数字、Bayes 错误率、四个象限                                ← 代码 02 演示 1、3
§3  basic recipe:对症下药;trade-off 为什么不再那么 trade;一点现代视角          ← 代码 02 演示 2
§4  L2 正则化:公式、weight decay、仿原作业、λ 扫描、为什么有效、cost 曲线       ← regularization.py、代码 03
§5  dropout:inverted dropout、测试时、为什么有效、实践细节                     ← regularization.py、代码 04
§6  其他正则化方法:数据增强、early stopping 与正交化;救第 5 讲的 32×32 网络     ← 代码 05
§7–11  小结、常见误区、练习、延伸阅读、下一讲预告

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

符号含义代码里
train / dev / test error训练集 / 开发集 / 测试集上的错误率(本讲的表格大多报告错误率,和 Ng 讲 bias / variance 时一致)error_rate
Bayes errorBayes 错误率(最优误差):任何分类器都不可能低于它;我们的合成数据可以算出来bayes_error、bayes_accuracy
λ\lambdaL2 正则化的强度(regularization parameter),一个超参数lambd(lambda 是 Python 的保留字)
∥W[l]∥F2=∑i=1n[l]∑j=1n[l−1](Wij[l])2\lVert W^{[l]} \rVert_F^2 = \sum_{i=1}^{n^{[l]}} \sum_{j=1}^{n^{[l-1]}} \big(W^{[l]}_{ij}\big)^2第 ll 层权重矩阵的 Frobenius 范数的平方(所有元素的平方和)frobenius_sq 求的是所有层之和 ∑l∥W[l]∥F2\sum_l \lVert W^{[l]} \rVert_F^2,表格里写作 Σ‖W‖²
keep_probdropout 时每个单元被保留的概率(不是丢掉的概率)keep_prob
D[l]D^{[l]}第 ll 层的 dropout 蒙版(mask),和 A[l]A^{[l]} 同形状,元素是 0 / 1(True / False)cache 里的 D

0.5 关于数字 · About the numbers

讲义里所有实验数字都来自配套代码的实际输出(固定随机种子;每个演示用自己的随机数生成器;同一台机器上重复运行逐字相同;本讲没有计时类的数字)。数据都是合成的:足球场数据仿原作业、花朵和同心圆来自第 3 讲、白天 / 黑夜照片来自第 5 讲,“真相”由我们写下的公式决定,所以能算出 Bayes 错误率。好几处结果和我写代码前的预想不一样——最明显的是:在足球场数据上 L2 比 dropout 好得多,在”花朵 + 噪声特征”上正好相反——讲义按实际结果来写,这些情况列在代码 README 的”关于实验结果”里。


1. 训练 / 开发 / 测试集 · Train / dev / test sets

1.1 应用深度学习是一个高度迭代的过程

C2 这门课一开头,Andrew Ng 先泼了一盆冷水:开始一个新项目时,你不可能一次就猜对网络有几层、每层几个单元、学习率多大、用什么激活函数。即使是在某个领域(比如自然语言处理)做了很多年的人,到了另一个领域(计算机视觉、语音、广告、搜索、安全、物流……)也常常发现自己的直觉不太管用——最好的超参数和数据量、输入特征、硬件都有关系。

所以实际的工作方式是第 1 讲 §4.5 的循环:Idea → Code → Experiment,一圈一圈地转。转得快不快,很大程度上取决于你能多快、多可靠地判断一个想法是好是坏。这就是数据划分要解决的问题。

1.2 三份数据各管什么

数据用来做什么别名不能用来做什么
训练集 train用梯度下降拟合参数 W,bW, b评估泛化能力(模型见过它)
开发集 dev在多个模型 / 超参数之间做选择:哪个 λ、多少层、什么时候停hold-out cross-validation set、validation set(验证集)报告最终成绩(你按它挑过东西,它已经”被用过了”)
测试集 test在一切选择都做完之后,给最终系统一个无偏的成绩做任何选择(一旦按它挑了什么,它就变成了第二个 dev 集)

“dev”是 development 的缩写:它是你在开发过程中天天看的那份数据。第 4 讲代码 05 里”用同一份 dev 既挑学习率又报成绩会偏乐观”、第 1 讲 tiny_nn.train 的注释里”被用来挑最好一步的验证集,就不能再用来报告最终成绩”,说的都是这张表的最后一列。§1.5 会量一量”偏乐观”到底有多少。

1.3 比例:从 60/20/20 到 98/1/1

机器学习的”老规矩”是 70/30(训练 / 测试)或 60/20/20(训练 / dev / 测试)。Ng 说这在数据只有 100、1,000、10,000 个样本时依然合理。但在大数据时代,比如有 100 万个样本,拿出 20 万个做 dev 就太浪费了:dev 只需要大到能分辨你在比较的几个模型,1 万个通常就够,于是划分变成 98/1/1;数据再多,可以是 99.5/0.25/0.25。

为什么”1 万个就够”?先想一想:一个准确率在 90% 左右的模型,用 nn 个样本估计它的准确率,估计值会在真值附近抖动多少?

每个样本”答对 / 答错”是一次抛硬币,nn 个样本的平均值的标准差是

SE=p(1−p)n\text{SE} = \sqrt{\frac{p(1-p)}{n}}

它只和样本数 nn 有关,和总数据量、和划分比例都无关。代码 01 演示 1 用第 3 讲的花朵数据训练了 20 个不同的小网络(隐藏单元 3–50 × 2 个种子),再生成 20 万个新样本当作”整个真实世界”,每个模型在这 20 万个点上的准确率就是它的”真实准确率”。然后从这 20 万个点里反复抽 dev 集,看估计值抖动多少:

dev 样本数模型 A 的 dev 准确率的标准差(实测)公式 p(1−p)/n\sqrt{p(1-p)/n}dev 上 A 胜过 B 的比例
1000.02530.02670.641
1,0000.00810.00840.885
10,0000.00260.00271.000
100,0000.00080.00081.000

(A = 真实准确率最高的模型,0.9228;B = 真实准确率比它低 0.0053 的模型,0.9174。每种大小随机抽 400 次。)

  • 实测和公式几乎一样:估计的误差约为 1/n1/\sqrt{n} 量级。
  • 两个模型只差半个百分点时,100 个样本的 dev 集只有 64% 的机会挑对(50% 就是抛硬币),1,000 个时 88%,10,000 个时每次都挑对。

所以 dev 集要多大,取决于你关心多小的差别。把这个换算到两种划分方式上(准确率 0.9 时的 95% 误差范围 ±1.96·SE):

总数据量60/20/20 的 dev误差范围98/1/1 的 dev误差范围
1,000200±4.16%10±18.59%
10,0002,000±1.31%100±5.88%
1,000,000200,000±0.13%10,000±0.59%
10,000,0002,000,000±0.04%100,000±0.19%

100 万个样本时,98/1/1 的 dev 已经能分辨 0.6 个百分点的差别;多给 dev 的 19 万个样本拿去训练更划算。反过来,1,000 个样本时套用 98/1/1,dev 只有 10 个样本,几乎什么都分辨不了。比例是结果,不是原则。测试集的道理一样:它只需要大到让你对最终系统的成绩有足够的信心。

一个细节:两个模型在同一份 dev 集上比较时,它们在大多数样本上同对同错,真正决定胜负的只是”意见不同”的那一小部分样本。这种配对比较(paired comparison)比”两个独立估计相减”更灵敏,所以上表”A 胜过 B”的比例比单看标准差估计的要好一些。

本讲后面的实验都按这个原则定数据大小:足球场数据的 dev 集 2,000 个、测试集 5,000 个(原作业的测试集只有 200 个,按上表第一行,误差范围约 ±4%,不足以分辨 L2 和 dropout 之间一两个百分点的差别)。

1.4 dev 和 test 必须来自同一个分布

Ng 举的例子:你做一个”上传照片、识别猫”的 app。训练数据是从网上爬来的猫图——构图好、分辨率高;而 dev / test 来自用户用手机拍的照片——模糊、光线差。这时训练集和 dev / test 分布不同(mismatched train/test distribution),这在今天的深度学习里非常普遍,因为能大量获取的数据往往不是你最终要处理的数据。Ng 给的规则是:

dev 和 test 要来自同一个分布——而且是你将来真正要在上面表现好的那个分布。训练集可以来自别的分布。

为什么?dev 集是你的”靶子”:你接下来几个月的每一次改进,都是在让 dev 上的数字变好。如果 test 来自另一个分布,你很可能瞄了几个月的是另一个靶子。代码 01 演示 3 还是用那 20 个模型,在三种情形下”按 dev 挑模型、看它在 test 的分布上表现如何”(dev 集 2000 个样本,重复 1000 次取平均):

情形dev 分布上的准确率范围按 dev 挑中的模型在 test 分布上test 分布上最好的模型差距
同分布:dev、test 都来自下半场0.8914–0.92600.92530.92600.0007
dev 来自上半场,test 来自下半场0.8799–0.92630.90280.92600.0232
dev 来自外圈(容易),test 来自内圈(难)0.9400–0.97160.80620.82450.0183

(“上半场 / 下半场”:花朵数据里 x2>0x_2 > 0 / x2≤0x_2 \le 0 的点;“外圈 / 内圈”:离原点的距离 ≥ 2 / < 2。)

  • 同分布时,按 dev 挑出的模型几乎就是 test 上最好的(只差 0.0007)。
  • dev 来自上半场、test 来自下半场:两个”地区”难度差不多(准确率范围几乎一样),但”哪个模型最好”不一样——在上半场真实准确率最高的模型,在下半场的 20 个模型里排第 18。差距变成同分布时的 33 倍。Ng 在讲机器学习策略时(本讲义第 10 讲)举过一个类似的例子:dev 用美国、英国、欧洲等地区的数据,test 用印度、亚洲其他地区、澳大利亚的数据。
  • dev 来自容易的外圈:有 11 个模型在外圈上的真实准确率并列第一,dev 集分不出它们,而它们在真正关心的内圈上从 0.7874 到 0.8245 不等。dev 用”清晰的专业照片”、用户上传”模糊的手机照片”,就是这种情况。

如果训练集和 dev / test 不得不来自不同的分布,怎么判断问题出在”分布不同”还是”过拟合”?那需要再切出一份 train-dev 集,是第 10 讲”数据不匹配”的内容。

1.5 没有测试集行不行?

Ng 的回答是:可以,如果你不需要一个无偏的最终成绩。很多团队只有 train 和 dev,在 dev 上迭代、在 dev 上挑模型。但他提醒了一个常见的说法问题:有人把这份数据叫做”test set”,可它实际上是被用来做选择的 dev set。那么,“在 dev 上挑最好的、再报告它的 dev 分数”到底偏乐观多少?

代码 01 演示 2:20 个模型,每次随机抽一份 dev 集,挑出 dev 上最好的那个,记下它的 dev 准确率(“报告的成绩”)和它的真实准确率;再另抽一份没参与挑选的 test 集估计一次。重复 1000 次取平均:

dev 样本数报告的 dev 准确率它的真实准确率高估比最好的模型差另抽一份 test 的估计 − 真值
1000.93560.91420.02140.0086−0.0004
5000.92550.91910.00640.00370.0001
2,0000.92350.92190.00160.0008−0.0001
10,0000.92280.92270.00020.0001−0.0000
  • 20 个模型的真实准确率都在 0.8874–0.9228 之间,可是 dev 集只有 100 个时,“挑出来的最好的那个”平均报告 0.9356,比任何一个模型的真实准确率都高。你挑的恰恰是在这份 dev 上运气最好的那个——这叫赢家的诅咒(winner’s curse)。
  • 一份没参与挑选的 test 集给出的估计没有偏差(最后一列在 0 附近)。这就是测试集存在的意义:它是对最终系统的无偏估计(unbiased estimate)。
  • dev 越大,高估越小;10,000 个时几乎可以忽略。

所以”只有 dev、没有 test”在 dev 很大、候选不多时问题不大;在 dev 小、候选多(比如扫了几百组超参数)时,dev 上的分数会明显偏高。第 4 讲代码 05 演示 4 已经提醒过这一点,这里把它量了出来。


2. 偏差与方差 · Bias & variance

2.1 二维图上的直觉

Ng 用三张二维分类图引入这两个词:一条直线去分一个需要弯曲边界的数据集(欠拟合);一条适度弯曲的曲线(刚好);一条为了把每个训练点都分对而扭来扭去的曲线(过拟合)。这三种样子你在前几讲和本讲的图里都见过:

样子本讲义里的例子
欠拟合 underfitting一条直线,连训练集都分不好第 3 讲 §1.3:逻辑回归在花朵数据上,训练准确率 0.51
刚好 just right平滑地跟着真实规律走§4.4 的图:L2 正则化的网络,几乎和真实分界线重合
过拟合 overfitting为了包住个别噪声点,画出飞地和尖角§4.4 的图:不加正则化的网络,下方伸出一条”舌头”
  • high bias(偏差大):模型太简单,连训练数据的规律都抓不住。逻辑回归在花朵数据上(第 3 讲 §1.3)就是这样。
  • high variance(方差大):模型太灵活,把训练数据里的偶然细节(噪声)也当成规律记住了,换一批数据就不灵。第 3 讲 §8.4 里 80 个样本、nh=50n_h = 50 的网络就是这样。

“偏差”和”方差”这两个词来自统计学里对预测误差的分解:偏差衡量”模型的平均预测离真相有多远”,方差衡量”换一份训练集,模型会变多少”。在高维的真实问题里,我们画不出决策边界,所以需要一个不用画图的诊断方法。

2.2 两个数字:训练误差和 dev 误差

Ng 的方法只看两个数:训练误差(反映模型在见过的数据上学得怎样)和 dev 误差(反映在没见过的数据上怎样)。他举了四个例子(假设人几乎不会认错猫,即最优误差约 0%,并且训练集和 dev 集来自同一个分布):

训练误差dev 误差诊断读法
1%11%high variance训练集上很好,dev 上差了一大截:过拟合
15%16%high bias训练集上就不好(比最优误差高 15 个点),dev 只比它差一点:欠拟合
15%30%两者都有训练集上不好,dev 上更差
0.5%1%两者都没有好模型

规则可以写成两个差值:

训练误差−最优误差⏟bias 的大小,dev 误差−训练误差⏟variance 的大小\underbrace{\text{训练误差} - \text{最优误差}}_{\text{bias 的大小}}, \qquad \underbrace{\text{dev 误差} - \text{训练误差}}_{\text{variance 的大小}}

哪个大就先治哪个。注意这里没有一个”多少算大”的统一门槛:它取决于你的目标,也取决于两个差值谁更大(代码 02 为了打印诊断用了 3 个百分点的门槛,那只是一个教学约定)。

2.3 参照物:Bayes 错误率

上面的四个例子都假设”最优误差约 0%“。如果图片本身就很模糊,模糊到人也只能认对 85%,那么”训练 15% / dev 16%“就不是 high bias,而是已经接近天花板了。任何模型在这个任务上都不可能低于的那个误差,叫 Bayes 错误率(Bayes error,最优误差)——第 1 讲 §2.1 里”任何模型都消不掉的噪声”、第 3 讲 §8.2 里”Bayes 最优分类器”,说的都是它。

代码 02 演示 3 用第 3 讲的同心圆数据(两圈间距 0.5、噪声 0.3,很多点本来就落在对方的地盘里)展示参照物选错的后果:

网络训练误差dev 误差以为最优误差 = 0 时的诊断以 Bayes 错误率为参照
[2, 8, 1]0.21660.2194high bias两者都没有
[2, 64, 64, 1]0.21970.2240high bias两者都没有
[2, 256, 256, 1]0.24070.2460high bias两者都没有

同心圆的 Bayes 错误率约 0.2144。如果你以为最优误差是 0,22% 的训练误差看起来是严重的 high bias,basic recipe(§3)会让你去加大网络——表里加大了两次,一点没好,最大的那个还更差了一点(它的训练误差也更高,看起来是同样的 6000 步 Adam 没把这么大的网络训练好)。以 Bayes 错误率为参照,[2, 8, 1] 的训练误差只比它高 0.0022:模型已经到顶了,要再进步只能换更有信息量的输入。

真实任务里不知道 Bayes 错误率,常用”人类水平”(human-level performance)来估计它,“训练误差 − 人类水平”叫 avoidable bias(可避免的偏差)。这是第 10 讲的主题;本讲的合成数据都能直接算出 Bayes 错误率,所以可以先用它当尺子。

2.4 实验:同一个任务上的四个象限

能不能在同一个任务上把四种情况都造出来?代码 02 演示 1 用第 3 讲的花朵数据(Bayes 错误率约 5.6%),但给每个样本附加 8 个纯噪声特征:输入变成 10 维,只有 2 维有用——模拟真实数据里”输入很多、真正有用的很少”(一张图片里大部分像素和标签无关)。然后在”小网络 / 大网络”ד200 / 50,000 个训练样本”上各训练一次:

模型 / 训练样本数训练误差dev 误差训练 − Bayesdev − 训练诊断
小网络 [10, 2, 1],50,000 个样本0.33660.33920.28060.0026high bias
大网络 [10, 64, 64, 1],200 个样本0.00000.4180−0.05600.4180high variance
小网络,200 个样本0.29500.46980.23900.1748两者都有
大网络,50,000 个样本0.06160.07340.00570.0118两者都没有
  • 小网络 + 大量数据 → high bias:2 个隐藏单元画不出花朵的 6 个扇形(第 3 讲:至少要 3 个),训练和 dev 一样差。
  • 大网络 + 很少数据 → high variance:4,929 个参数对 200 个样本,训练误差 0——连标签噪声都背下来了(训练误差比 Bayes 错误率还低,本身就是过拟合的信号),dev 误差 0.4180。
  • 小网络 + 很少数据 → 两者都有:没学会花朵的形状(bias),却用噪声特征记住了一部分训练点(variance)。Ng 画过一条”大部分地方是直线、个别地方为了几个点急转弯”的紫色边界来说明这种情况;在高维输入里,“有的方向太简单、有的方向太灵活”的模型并不少见。
  • 大网络 + 大量数据 → 两者都没有:离 Bayes 只差 0.0174。想要它,需要足够大的模型和足够多的数据。

同一个任务、同一个训练程序,四种诊断完全不同。“下一步该做什么”取决于诊断,而不是取决于任务。

2.5 回头看前几讲

有了这套语言,前几讲里的几个现象可以重新读一遍:

前文训练 / 测试诊断
第 1 讲代码 03:逻辑回归从 n=1000n = 1000 起停在 0.834训练、测试都停在一个水平high bias(模型表达不了老师网络)
第 1 讲代码 03:大网络在 n=100n = 100–300 时垫底18,049 个参数对 240 个训练样本high variance
第 2 讲 §9.3:抖动 ±2 以上,线性模板连训练集都只有 0.87–0.90,测试差得更多两者都有
第 3 讲 §8.4:80 个样本,nh=50n_h = 50 训练 1.000、测试 0.821训练比 Bayes(0.9500)还准high variance
第 4 讲代码 04:条纹图像 3,000 个样本时两种网络都只会背训练近乎全对,测试约 0.5high variance(数据是瓶颈)
第 5 讲 §2.4:32×32 网络训练 0.966、测试 0.869high variance(§6.4 会救它)

3. 机器学习的基本配方 · Basic recipe for machine learning

3.1 流程

Ng 把诊断之后”该做什么”总结成一个流程(他称之为 basic recipe):

                ┌──────────────────────────────────────────────┐
                ↓                                              │
     high bias?(看训练集表现)  ── 是 ──→  更大的网络(更多单元 / 更多层)
                │                          训练更久 / 更好的优化算法(第 8 讲)
                │ 否                        (换一种网络架构)
                ↓                                              
     high variance?(看 dev 集表现) ── 是 ──→  更多数据
                │                          正则化(L2、dropout、数据增强……,§4–§6)
                │ 否                        (换一种网络架构)  ──→ 回到第一步
                ↓
              完成

两个要点:

  • 先看 bias,再看 variance。连训练集都没学好时,加数据、加正则化都没用;先让模型至少能拟合训练集。
  • 药要对症。high bias 时加数据是白费功夫(数据很贵);high variance 时加大网络也不解决问题。“换架构”在两边都出现,因为一个更合适的架构(比如图像用卷积,第 12 讲)可能同时降低两者,但它不像前几项那样系统,要靠试。

3.2 实验:对症下药

代码 02 演示 2 拿演示 1 里两个”有病”的模型,把每一种药都试一遍:

【A】high bias 的模型(小网络 [10, 2, 1],50,000 个样本,Bayes 0.0560):

做法训练误差dev 误差诊断
(起点)0.33660.3392high bias
训练久一倍(12,000 步)0.32720.3312high bias
数据多 4 倍(200,000 个样本)0.33720.3482high bias
网络加大到 [10, 3, 1]0.08120.0824两者都没有
网络加大到 [10, 8, 1]0.08360.0836两者都没有
网络加大到 [10, 64, 64, 1]0.06160.0734两者都没有

【B】high variance 的模型(大网络 [10, 64, 64, 1],200 个样本):

做法训练误差dev 误差诊断
(起点)0.00000.4180high variance
网络再加大到 [10, 256, 256, 1]0.00000.3792high variance
数据多 5 倍(1,000 个样本)0.00000.1742high variance
数据多 25 倍(5,000 个样本)0.00000.1176high variance
L2,λ = 10.00000.3930high variance
L2,λ = 30.00500.3964high variance
L2,λ = 100.39000.4358两者都有
dropout,keep_prob = 0.80.00000.3308high variance
dropout,keep_prob = 0.50.00000.2192high variance
对照:小网络 [10, 2, 1],200 个样本0.29500.4698两者都有
  • 【A】“训练更久”和”加数据”几乎没用,只多 1 个隐藏单元,dev 误差就从 0.3392 掉到 0.0824。模型已经尽力了,它只是表示不了花朵的形状。
  • 【B】加大网络治不了过拟合(dev 略好,但训练误差仍是 0、差距仍然巨大)。最有效的是更多数据:5,000 个样本时 dev 0.1176,而训练误差一直是 0——加数据只降 variance,不碰 bias。
  • 【B】正则化:dropout 很有效(keep_prob = 0.5 时 dev 0.2192),L2 却几乎没用:λ = 1、3 时训练误差还是 0,λ = 10 时一下子压过了头。这个结果和 §4、§5 的足球场数据正好相反(那里 L2 好、dropout 差)。一个可能的原因是这里的过拟合来自 8 个噪声特征,L2 一视同仁地缩小所有权重,分不清哪些输入有用——这只是猜测,没有专门验证。哪种正则化对你的问题有效,要在 dev 上试。

3.3 为什么深度学习时代的 trade-off 不再那么”trade”

传统机器学习里常说 bias–variance trade-off(偏差–方差权衡):模型简单一点,bias 升、variance 降;复杂一点,反过来。你只能在两者之间找一个平衡点。

表【B】里正好能看到这个经典的 trade-off:200 个样本时,从小网络换成大网络,训练误差从 0.2950 降到 0(bias 降了),dev − 训练的差距却从 0.1748 涨到 0.4180(variance 涨了)。但再加上 dropout,差距回到 0.2192(只比小网络略大),bias 仍然几乎为 0,dev 误差 0.2192,远好于小网络的 0.4698。

Ng 的总结是:在深度学习、大数据的时代,我们有了两个只动一边的工具——

  • 更大的网络(配合合适的正则化):几乎总是降低 bias,而不太伤 variance;
  • 更多的数据:几乎总是降低 variance,而不伤 bias。

只要这两样你都拿得到,就不必在 bias 和 variance 之间二选一:先用大网络把 bias 压下去,再用数据和正则化把 variance 压下去。这也是为什么 Ng 说”训练一个更大的网络几乎从来不会有坏处(只要正则化得当),主要的代价是计算时间”。这是深度学习在有数据的问题上表现这么好的原因之一。

3.4 一点现代视角

“大网络 + 正则化”的经验,后来的研究又往前推了几步,这里只点到为止:

  • 大网络能背下任何东西:Zhang et al. (2017) 发现,常见的图像分类网络能把随机打乱的标签也拟合到训练误差 0。所以”参数多少”本身不能解释为什么它们在真实标签上泛化得好;L2、dropout 这类显式正则化有帮助,但不是好的泛化的全部原因(训练方法本身,比如 SGD,也起作用)。
  • double descent(双下降):Belkin et al. (2019)、Nakkiran et al. (2020) 观察到,当模型大到刚好能拟合训练集(“插值阈值”附近)时,测试误差会出现一个峰;模型继续变大,测试误差又降下来。经典的 U 形曲线只是这条曲线的左半边。这和”大网络几乎不伤 variance”的经验是一致的;但峰的位置和高低受数据量、噪声、正则化影响,不是所有设置下都看得到。

对做项目来说,结论仍然是 Ng 的配方:用训练误差和 dev 误差诊断,对症下药。


4. L2 正则化 · L2 regularization

4.1 从逻辑回归开始

high variance 时最直接的药是更多数据,但数据常常很贵或者拿不到。正则化(regularization)是另一条路:在 cost 里加一项,惩罚”过于复杂”的模型。

以逻辑回归为例,L2 正则化把 cost 改成

J(w,b)=1m∑i=1mL(y^(i),y(i))+λ2m ∥w∥22,∥w∥22=∑j=1nxwj2=w⊤wJ(w, b) = \frac{1}{m}\sum_{i=1}^{m} \mathcal{L}\big(\hat y^{(i)}, y^{(i)}\big) + \frac{\lambda}{2m}\,\lVert w \rVert_2^2, \qquad \lVert w \rVert_2^2 = \sum_{j=1}^{n_x} w_j^2 = w^\top w

几个设计选择:

  • 只惩罚 ww,不惩罚 bb。ww 有 nxn_x 个数,bb 只有一个,加不加对结果几乎没有影响,所以 Ng 通常省掉。
  • λ\lambda 是一个超参数(正则化参数),要在 dev 集上调(§4.5)。Python 里 lambda 是保留字,Coursera 作业写成 lambd。
  • 除以 2m2m:除以 2 让求导后的系数干净;除以 mm 让正则项和交叉熵(mm 个样本的平均)同一个尺度。
  • L1 正则化 λm∥w∥1=λm∑j∣wj∣\frac{\lambda}{m}\lVert w \rVert_1 = \frac{\lambda}{m}\sum_j |w_j| 会让 ww 变得稀疏(很多分量恰好为 0),有人用它来压缩模型。Ng 说实践中这点压缩作用不大,L2 用得多得多。

4.2 神经网络:Frobenius 范数

对 LL 层网络,把每一层的权重矩阵都惩罚一遍:

J(W[1],b[1],…,W[L],b[L])=1m∑i=1mL(y^(i),y(i))+λ2m∑l=1L∥W[l]∥F2J\big(W^{[1]}, b^{[1]}, \dots, W^{[L]}, b^{[L]}\big) = \frac{1}{m}\sum_{i=1}^{m} \mathcal{L}\big(\hat y^{(i)}, y^{(i)}\big) + \frac{\lambda}{2m}\sum_{l=1}^{L} \lVert W^{[l]} \rVert_F^2

∥W[l]∥F2\lVert W^{[l]} \rVert_F^2 是矩阵所有元素的平方和(§0.4)。线性代数里矩阵的这种范数叫 Frobenius 范数(不叫”矩阵的 L2 范数”,后者另有所指)。代码里就是一行:

def frobenius_sq(parameters):
    return float(sum(np.sum(v ** 2) for k, v in parameters.items() if k.startswith("W")))

compute_cost_with_regularization(和作业同名)= 第 4 讲的 compute_cost + λ2m\frac{\lambda}{2m} × 上面这个数。

4.3 反向传播多一项,以及 “weight decay” 这个名字

正则项对 W[l]W^{[l]} 的导数是 λmW[l]\frac{\lambda}{m} W^{[l]},所以反向传播只要在每层的 dW[l]dW^{[l]} 上加一项:

dW[l]=1m dZ[l]A[l−1]⊤⏟原来的反向传播+λmW[l]dW^{[l]} = \underbrace{\tfrac{1}{m}\, dZ^{[l]} A^{[l-1]\top}}_{\text{原来的反向传播}} + \frac{\lambda}{m} W^{[l]}

regularization.py 的 L_model_backward_reg 里就是一行 grads[f"dW{l}"] = dW + (lambd / m_reg) * W。db[l]db^{[l]} 不变。代入梯度下降:

W[l]←W[l]−α[(原来的 dW[l])+λmW[l]]=(1−αλm)W[l]−α (原来的 dW[l])W^{[l]} \leftarrow W^{[l]} - \alpha\Big[(\text{原来的 } dW^{[l]}) + \frac{\lambda}{m} W^{[l]}\Big] = \Big(1 - \frac{\alpha\lambda}{m}\Big) W^{[l]} - \alpha\,(\text{原来的 } dW^{[l]})

每一步先把权重乘以一个略小于 1 的数,再按原来的梯度走一步。所以 L2 正则化又叫 weight decay(权重衰减)。代码 03 演示 2 在 α = 0.3、λ = 0.7、m = 211 时验证了两种写法:衰减系数 1−αλ/m=0.9990051 - \alpha\lambda/m = 0.999005,两种写法更新后的 WW 最大差别在 10−1610^{-16} 量级(浮点舍入)。训练过程中权重平方和 Σ‖W‖² 的变化(种子 1):

第几步01,0003,00010,00020,00030,000
不加正则化19.661.394.4146.7253.9320.6
L2,λ = 0.719.629.836.336.536.636.6

不加正则化时权重一路变大:交叉熵总能靠”把 WW 放大、让预测更自信”再降一点(第 2 讲 §4.4:在可分的数据上,交叉熵没有有限的最低点)。加了 L2,衰减的”往里拉”和交叉熵梯度的”往外推”在 36.6 附近达到平衡。

两个和后面几讲有关的注释:

  • 第 1 讲 tiny_nn.train 的 weight_decay 参数,就是在梯度里加 weight_decay * W,即这里的 λ/m\lambda/m。第 5 讲用的 weight_decay = 1e-4、m=8000m = 8000,相当于 λ=0.8\lambda = 0.8。
  • 用 Adam 这类自适应优化器(第 8 讲)时,“在梯度里加 λmW\frac{\lambda}{m}W“和”每步把 WW 乘以一个系数”不再等价:前者会被 Adam 的逐参数缩放改变。Loshchilov & Hutter (2019) 因此提出把两者分开的 AdamW(decoupled weight decay),它是今天训练大模型的标准选择。

4.4 实验:仿原作业

原作业的设定:你是法国足球队请来的 AI 专家,要建议守门员把球开到哪里。数据集里每个点是守门员开球后有球员头球触球的位置,标签是”碰到球的是法国队员还是对方队员”。作业用 3 层网络 [2, 20, 3, 1](ReLU,学习率 0.3,30,000 步普通梯度下降)比较三种做法。

原作业的数据不公开,field_data.py 合成了一个类似的数据集:场地 [−0.7,0.7]2[-0.7, 0.7]^2,真实分界线 x2=0.3sin⁡(3.5x1)x_2 = 0.3\sin(3.5 x_1),线的上方是法国队的”地盘”;分界线附近有一条模糊带(两队都可能抢到球),场上任何地方还有 4% 的”意外”(远离分界线、标签却是另一队)。训练集 211 个点(和原作业一样),dev 集 2,000 个、测试集 5,000 个(§1.3)。因为生成过程是我们写的,Bayes 错误率可以算出来:期望约 0.0675;在这三份具体的数据上,Bayes 最优分类器分别错 0.0521 / 0.0615 / 0.0756(每份数据里”意外点”的多少不同)。

代码 03 演示 1(网络、学习率、步数、Xavier 初始化都照原作业,3 个初始化种子):

模型训练误差dev 误差测试误差Σ‖W‖²和真实分界线不一致的面积
不加正则化(3 个种子平均)0.02840.07620.0920329.90.0382
L2,λ = 0.7(3 个种子平均)0.04900.06400.080537.00.0249

足球场数据:不加正则化、L2 和真实分界线

(这张图由 03_l2_regularization.py 生成,对应种子 1;终端里打印的是同样内容的字符画。图里的浅色背景是网络的预测区域。)

  • 不加正则化:训练误差 0.0284,比”上帝视角”的 Bayes 分类器在同样 211 个点上的 0.0521 还低——网络背下了一部分”意外点”。图里最明显的是下方伸出的那条”舌头”:为了包住一个落在对方地盘里的训练点,网络把一大片区域划给了法国队。dev 和测试误差都比训练误差高出一截:high variance。
  • L2(λ = 0.7):训练误差升到 0.0490,和 Bayes 分类器的 0.0521 差不多,不再死记噪声;dev 误差降到 0.0640、测试误差降到 0.0805;边界平滑,“和真实分界线不一致的面积”从 0.0382 降到 0.0249。权重平方和从 330 降到 37。
  • 3 个种子的结论一致。但两者在测试集上只差约 1.2 个百分点——这正是 §1.3 说的,测试集要有几千个点才看得清这样的差别。

作为对照,原作业广为流传的结果是:不加正则化训练约 95%、测试约 91.5%;L2(λ = 0.7)约 94% / 93%;dropout(keep_prob = 0.86)约 93% / 95%。趋势和我们的一样(L2 让训练准确率略降、测试准确率上升),但 dropout 那一行的结论在我们的数据上不成立(§5.6)。

4.5 λ 怎么选:扫描

λ 是一个超参数,要在 dev 上挑。代码 03 演示 3 对 8 个 λ 各训练 3 次(种子 1、2、3),“比最优多错多少”的参照是 Bayes 分类器在训练集上的 0.0521 和在 dev 集上的 0.0615:

λ训练误差dev 误差训练 − 最优dev − 最优Σ‖W‖²
00.02840.0762−0.02370.0147329.9
0.030.03320.0725−0.01900.0110155.7
0.10.05060.0627−0.00160.001277.4
0.30.03790.0655−0.01420.004053.0
0.70.04900.0640−0.00320.002537.0
20.06160.08350.00950.022016.1
60.06320.08480.01110.023311.8
250.49290.48950.44080.42800.0

λ 扫描:训练误差随 λ 上升,dev 误差先降后升

  • 训练误差随 λ 大体上升(0.0284 → 0.0632):λ 越大,模型越”不许”用大权重去迁就训练点。(λ = 0.1 和 0.3 之间的起伏是 3 个种子也抹不平的随机波动。)
  • dev 误差先降后升:λ = 0 时训练误差比最优还低 0.0237(在背噪声)、dev 多错 0.0147——high variance;λ = 0.1 时两个差值都几乎为 0;λ = 6 时训练误差反过来比最优高 0.0111(边界被压得太平,跟不上那条正弦形的分界线),dev 多错 0.0233——开始 high bias。这就是正则化强度从”variance 一侧”滑到”bias 一侧”的样子。
  • λ = 25:权重被压到几乎为 0,网络退化成”永远猜同一类”的常数,训练和 dev 误差都是 0.49 左右。
  • 最好的 λ 是一个区间:0.1–0.7 之间 dev 误差相差不到半个百分点,和 dev 集本身的随机误差(2,000 个点约 ±0.5%)差不多。按 dev 选出的是 λ = 0.1;选完以后才在测试集上看一次:测试误差 0.0804(不加正则化 0.0920;Bayes 分类器在这份测试集上 0.0756)。

4.6 为什么 L2 能减少过拟合:两个直觉

Ng 给了两个直觉。

直觉一:λ 很大 → 很多权重接近 0 → 网络像一个小得多的网络。 把 λ 设得非常大,为了让 cost 小,W[l]W^{[l]} 会被压到接近 0,很多隐藏单元的影响几乎消失,网络退化成一个很简单的模型,从 high variance 一路滑向 high bias。中间存在一个”刚好”的 λ。(Ng 特别说明:实际上并不是把单元”清零”,而是每个单元的影响都变小了。)演示 3 的 λ = 25 就是推过头的极端:网络变成了常数。

直觉二:权重小 → zz 小 → tanh 工作在近似线性的区域。 设隐藏层用 tanh。λ 大时 W[l]W^{[l]} 小,z[l]=W[l]a[l−1]+b[l]z^{[l]} = W^{[l]} a^{[l-1]} + b^{[l]} 也小;而 tanh 在 0 附近几乎是一条直线(tanh⁡(0.5)=0.462\tanh(0.5) = 0.462,和 0.5 只差 8%)。如果每一层都近似线性,整个网络就近似一个线性函数(第 1 讲 §2.4:线性叠线性还是线性),画不出为了包住几个噪声点而急转弯的边界。

代码 03 演示 4 把这个直觉量化:同一个网络换成 tanh 隐藏层(种子 1),看 λ 增大时三件事怎样变化——第 1 层的 zz 有多少落在 ∣z∣<0.5|z| < 0.5 的近似线性区;网络的 logit(sigmoid 之前的值)离”最好的线性函数”有多远(非线性程度 = 1 − R²);logit 对输入的梯度有多大(“坡有多陡”)。

λ训练误差dev 误差Σ‖W‖²∣z[1]∣<0.5\lvert z^{[1]}\rvert < 0.5 的比例非线性程度平均斜率最大斜率
00.03320.0790533.90.3750.23420.4203.9
0.10.04740.065090.20.6830.16713.354.5
0.70.04740.067031.40.9640.0888.020.6
60.06640.085012.41.0000.0305.08.3
  • λ 从 0 到 6,第 1 层落在近似线性区的比例从 0.375 升到 1.000,网络整体的非线性程度从 0.234 降到 0.030——直觉二被数字证实了。
  • 最大斜率的变化最明显(203.9 → 8.3):不加正则化的网络在某些地方有非常陡的”悬崖”,那正是飞地的边缘。L2 把函数变得平滑:输入挪一点,输出也只变一点。
  • 但 λ = 6 时非线性程度还有 0.030,网络并没有变成真正的线性函数。“每一层都线性”是帮助理解的极端情形;L2 做的是连续地把函数往”更简单、更平滑”的方向推,λ 是推的力度。

4.7 调试时,cost 曲线要包含正则项

Ng 的一个实用提醒:用 L2 时,你画来检查”梯度下降是否在下降”的 cost 曲线,要用包含正则项的新 J。如果只画交叉熵那一部分,你可能看不到它单调下降。

代码 03 演示 5 造了一个最容易看出差别的场景:接着演示 1 里过拟合的网络(种子 1,Σ‖W‖² = 320.6)继续训练,同时打开 L2(λ = 2,学习率 0.1,3000 步):

第几步完整的 J其中:交叉熵L2 项Σ‖W‖²训练误差
01.58410.06471.5194320.60.0284
1001.33730.06601.2714268.30.0332
3000.98480.07900.9058191.10.0332
10000.44210.12190.320267.60.0521
30000.26770.16730.100421.20.0521

3000 步里,完整的 J 一步都没有上升;而交叉熵从 0.0647 一路升到 0.1673,3000 步里升了 2866 步。只看交叉熵,你会以为训练出了 bug;其实一切正常——L2 正在用”训练集上的交叉熵变大一点”换”权重小得多”,这正是我们要的取舍。

一个诚实的补充:“J 每一步都应该下降”只在学习率足够小时严格成立。原作业的学习率 0.3 对这个网络来说偏大:即使不加正则化,从头训练时第 914 步以后 J 就开始不规则地上下抖动,30,000 步里有 17,202 步是上升的(每次上升的中位数只有 0.0009,总趋势仍在下降:0.7026 → 0.0647)。所以演示 5 用了 0.1 的学习率;换成 0.3,同样的实验里完整的 J 也有 506 步是上升的(总趋势仍从 1.5841 降到 0.2603)。实用的读法是看整体趋势和”有没有持续上升”,而不是死盯每一步。(大学习率下梯度下降的这种”边振荡边下降”的行为,Cohen et al. (2021) 称之为 edge of stability。)


5. Dropout

5.1 想法:每次随机”关掉”一部分单元

dropout(随机失活)是另一种非常常用的正则化方法,来自 Hinton 等人 2012 年的工作(Srivastava et al., 2014 的期刊论文给出了完整的版本)。想法简单得出奇:

训练时,对每个样本、每一步,让每个隐藏单元以概率 1−keep_prob1 - \text{keep\_prob} 被”关掉”(输出置 0),也就是以 keep_prob 的概率保留。

Ng 的图:一个每层几个单元的网络,每个单元抛一次硬币(比如 keep_prob = 0.5),被抛掉的单元连同它的进出连线一起删掉,剩下一个”瘦身版”的网络,在这一个样本上做前向和反向传播。下一个样本、下一步,再重新抛硬币,删掉另一批单元。于是每一步训练的其实是一个不同的、更小的网络。

5.2 inverted dropout 的实现

Ng 讲的标准实现叫 inverted dropout。以第 3 层为例,keep_prob = 0.8:

d3 = np.random.rand(a3.shape[0], a3.shape[1]) < keep_prob   # 1. 和 a3 同形状的蒙版:每个元素以 0.8 的概率为 True
a3 = np.multiply(a3, d3)                                     # 2. 关掉的单元输出 0
a3 /= keep_prob                                              # 3. 保留下来的放大 1/0.8 倍("inverted")

注意 d3 和 a3 同形状 (n[3],m)(n^{[3]}, m):每个样本关掉的单元都不一样。反向传播时,用同一个蒙版处理梯度:

dA[3]←dA[3]∗D[3]/keep_probdA^{[3]} \leftarrow dA^{[3]} * D^{[3]} / \text{keep\_prob}

前向时被关掉的单元,反向时梯度也是 0(这一步它”不在网络里”,不该被更新);保留下来的单元,梯度也放大同样的倍数。所以蒙版必须存进 cache。regularization.py 里对应的是:

  • dropout_forward(A, keep_prob, rng):上面的三行,返回 (A_out, D);
  • L_model_forward_dropout:逐层调用第 4 讲的 linear_activation_forward,每个隐藏层算完激活值以后做一次 dropout_forward,把 D 和 keep_prob 一起存进 cache(输出层不做 dropout);
  • L_model_backward_reg:第 ll 层收到的 dA[l]dA^{[l]} 先过 dropout_backward(乘以同一个 D 再除以 keep_prob),再穿过激活函数——顺序和前向正好相反。L2 的那一项也在这里。

一个细节:下一层 linear_forward 存下的 A[l−1]A^{[l-1]} 是 dropout 之后的激活值,所以 dW[l]=1mdZ[l]A[l−1]⊤dW^{[l]} = \frac1m dZ^{[l]} A^{[l-1]\top} 自动只更新”这一步被保留的单元”连出来的权重。

5.3 为什么要除以 keep_prob

先想一想:如果不做第 3 步,会发生什么?

下一层算的是 z[4]=W[4]a[3]+b[4]z^{[4]} = W^{[4]} a^{[3]} + b^{[4]}。keep_prob = 0.8 时,平均有 20% 的 a[3]a^{[3]} 被置 0,z[4]z^{[4]} 的期望就变成原来的约 0.8 倍。除以 0.8 恰好补回来:

E ⁣[a⋅dp]=ap⋅P(d=1)=ap⋅p=a\mathbb{E}\!\left[\frac{a \cdot d}{p}\right] = \frac{a}{p}\cdot \mathbb{P}(d = 1) = \frac{a}{p}\cdot p = a

代码 04 演示 1 拿一个训练好的网络的第 1 层激活值 A[1]A^{[1]}(形状 (20, 211)),抽 2000 次蒙版(keep_prob = 0.8),看平均值:

平均值 / 原值(整体)和原值的相对差别
A[1]A^{[1]},除以 keep_prob0.99990.0088
A[1]A^{[1]},不除0.79990.2001
下一层 z[2]z^{[2]},除以 keep_prob—0.0126
下一层 z[2]z^{[2]},不除—0.2303

除以 keep_prob 时,每个激活值和下一层 zz 的期望都不变(剩下的 1% 左右是 2000 次抽样的随机误差);不除时整体变成 keep_prob 倍。

为什么在乎期望不变?因为测试时不做 dropout(§5.4),所有单元都在。只有训练时的平均输入和测试时的输入一样大,下一层在训练中学到的”zz 大概多大”才在测试时继续成立。inverted dropout 把补偿放在训练时,测试时的代码就和没有 dropout 的网络一模一样。

5.4 测试时:不用 dropout;以及 dropout 在近似什么

测试(预测)时不用 dropout:你不希望同一张图片每次预测的结果都不一样。Ng 指出,如果训练时没有除以 keep_prob(2012 年最初的写法),测试时就必须记得手动补偿(把激活值或下一层的权重乘以 keep_prob,叫 weight scaling)。代码 04 演示 2 看看忘了补偿会怎样(keep_prob = 0.5,两个隐藏层,dev 集):

做法dev 错误率dev 交叉熵平均 ∣p−0.5∣\lvert p - 0.5\rvert
inverted dropout,测试时什么都不做0.07550.37100.4234
不除以 keep_prob 训练,测试时什么都不做0.07550.63640.4793
不除以 keep_prob 训练,测试时权重 × keep_prob0.07650.31570.4208

忘了补偿时,错误率几乎不变(这个网络里激活值整体放大基本不改变 logit 的符号),但预测概率变得过分自信,交叉熵翻了一倍(0.3157 → 0.6364)。如果你要用这个概率做决策(“概率 > 0.9 才报警”),这是一个不报错的真 bug。inverted dropout 让你根本不用操心这件事。

dropout ≈ 集成。 每一步训练一个随机子网络,这些子网络共享同一组权重;nn 个可以被关掉的单元,就有 2n2^n 个可能的子网络。测试时一次”全开”的前向传播,可以看作把这一大群子网络的预测平均起来的廉价近似。演示 2 的后半部分在测试时故意打开 dropout、抽 1000 个随机子网络:

  • 单个随机子网络的 dev 错误率平均 0.1906(最好 0.1690,最差 0.2125),比全开的网络差得多——所以测试时不能开着 dropout 只算一次;
  • 1000 个子网络的预测概率取平均:dev 错误率 0.0735;一次全开的前向传播:0.0755。两者预测的类别 99.8% 相同;
  • 但只是近似:两者的概率平均相差 0.1459,全开网络的概率更”尖”(平均 ∣p−0.5∣|p - 0.5| 0.4234 vs 0.2776)。对单层的 sigmoid 单元,权重缩放恰好对应子网络的(归一化的)几何平均(Baldi & Sadowski, 2013);对多层 ReLU 网络,它只是一个近似。

(把 dropout 开着做很多次预测再平均,后来被发展成一种估计模型不确定性的方法,叫 MC dropout(Gal & Ghahramani, 2016)。那是另一个话题。)

5.5 为什么有效:不能依赖任何一个特征

Ng 给了两个角度。

角度一:每次训练一个更小的网络。 小网络的过拟合能力弱,像是一种正则化。

角度二(更深入):从单个单元的视角看。 设想下一层的某个单元,它有 4 个输入。训练时,这 4 个输入中的任何一个都可能随时消失。于是它不能把宝押在任何一个输入上,只能把权重摊在所有输入上,每个都不太大。摊开的权重平方和更小——这和 L2 的效果类似。Ng 提到,可以严格地证明 dropout 相当于一种自适应的 L2 正则化:每个权重受到的惩罚大小,取决于和它相乘的激活值有多大(Wager et al., 2013 在广义线性模型上给出了这个结果)。

代码 04 演示 4 用一个最直接的方法检验”不能依赖任何一个特征”:把训练好的网络第 1 层的 20 个隐藏单元逐个拿掉(把它的输出权重置 0),看 dev 误差涨多少(3 个种子):

做法拿掉一个单元:最多涨(3 个种子)平均涨第 2 层权重的有效输入数(满分 20)
不用 dropout0.3105 / 0.1170 / 0.30900.03704.9 / 6.6 / 6.1
keep_prob = 0.860.0065 / 0.0015 / 0.00500.00048.6 / 6.4 / 4.7
keep_prob = 0.50.0075 / 0.0060 / 0.00350.00037.4 / 10.2 / 8.1

(“有效输入数”:第 2 层每个单元的 participation ratio (∑w2)2/∑w4(\sum w^2)^2 / \sum w^4 的平均,读作”这个单元实际上依赖第 1 层的几个单元”。)

  • 不用 dropout 的网络里有”顶梁柱”:拿掉其中一个单元,dev 误差最多涨 0.3105——网络把某一段边界完全押在了这一个单元上。用 dropout 训练的网络,拿掉任何一个单元,dev 误差最多只涨 0.0065。这是”不能依赖任何一个特征”最直接的证据。
  • 权重的”摊开”程度:keep_prob = 0.86 时,有效输入数的平均只从 5.9 升到 6.5,而且三个种子并不一致;keep_prob = 0.5 时到 8.6,才比较明显。另外,演示 3 会看到,dropout 网络的权重平方和并没有变小(330 → 401)。所以在这个小网络上,“dropout 类似 L2”更多体现在”冗余、摊开”上,而不是”总的权重变小”。

5.6 实验:仿原作业

代码 04 演示 3:和代码 03 完全相同的数据和网络,dropout 加在两个隐藏层上(每行 3 个种子的平均):

做法训练误差dev 误差测试误差Σ‖W‖²
不用 dropout0.02840.07620.0920329.9
keep_prob = 0.950.02840.07070.0879344.3
keep_prob = 0.86(原作业)0.03160.07370.0902400.8
keep_prob = 0.70.03630.07380.0905395.7
keep_prob = 0.50.04270.07650.0963217.9
第 1 层 0.7,第 2 层 10.03160.07430.0891408.1

这和我预想的不一样。 原作业里 dropout 的测试准确率最高(约 95%,L2 约 93%);在我们的合成数据上,dropout 只帮了一点点(dev 0.0762 → 最好 0.0707),远不如 L2(λ = 0.7 时 dev 0.0640,§4.4)。训练误差一直低于 Bayes 分类器在训练集上的 0.0521:dropout 没能阻止网络”背”噪声点,字符画里 keep_prob = 0.86 的边界仍有为了包住个别点画出的弯折。

一个可能的原因:这个网络很小,第 2 个隐藏层只有 3 个单元,keep_prob = 0.86 时它经常整整少掉三分之一,这种噪声对一个本来就很窄的层很”粗暴”;而 dropout 通常用在宽的、参数多的层上。这只是猜测,我没有专门做实验验证。再加上 §3.2 的结果(“花朵 + 噪声特征”上 dropout 很有效、L2 没用),结论只能是:哪种正则化更好,取决于具体的数据和网络,要在 dev 上比。不要因为某个作业、某篇文章里 dropout 赢了,就默认它在你的问题上也赢。

5.7 实践细节

  • 不同的层可以用不同的 keep_prob。参数多、容易过拟合的层(比如 Ng 举例里 7×7 的大权重矩阵)用小一点的 keep_prob(如 0.5);参数少的层用大一点(0.7);不担心过拟合的层用 1.0(不做 dropout)。代价是多了几个要调的超参数;另一种做法是只对部分层做 dropout、共用一个 keep_prob。演示 3 最后一行”只对 20 个单元的那一层做 dropout”就是这种做法。
  • 输入层通常不做 dropout,或者 keep_prob 很接近 1(比如 0.9),很少用 0.5。
  • 计算机视觉里 dropout 用得很多:输入维度太大(每个像素一个特征),数据几乎永远不够,几乎总在过拟合。但 Ng 强调,dropout 是一种正则化手段,没有过拟合就不需要它;在其他领域,不要因为”CV 的人都在用”就默认打开。
  • cost 不再有明确的定义,调试时先关掉。有 dropout 时,每一步的 JJ 是在一个不同的随机子网络上算的。代码 04 演示 5(种子 1):相邻两步的 JJ 相差的中位数是 0.0175,是不用 dropout 时(0.00092)的 19 倍,第 20000–20005 步的 6 个值在 0.10 和 0.21 之间乱跳;关掉 dropout、在整个训练集上算的 JJ 才有清楚的趋势(0.7026 → 0.0701)。Ng 的建议:先把 keep_prob 设成 1,确认 JJ 在单调下降、代码没有 bug,再打开 dropout。(上升的步数两者差不多,14,868 vs 17,202,都接近一半:不用 dropout 时学习率 0.3 偏大带来的小抖动,见 §4.7。区别在幅度。)
  • 梯度检验时关掉 dropout。dropout 让 JJ 成了随机的:J(θ+ε)J(\theta + \varepsilon) 和 J(θ−ε)J(\theta - \varepsilon) 用的是两个不同的子网络,差分里主要是蒙版的差别而不是 θ\theta 的变化。演示 5 和 regularization.py 的自检都量了一下:每次重新抽蒙版时相对误差 1.00e+00(完全对不上);固定蒙版(4.75e−09)或关掉 dropout(4.19e−09)就没问题。固定蒙版之所以可行,是因为蒙版固定后 JJ 就是”某一个子网络的 cost”,一个确定的函数,反向传播算的正是它的梯度。第 7 讲会系统地讲梯度检验。

6. 其他正则化方法 · Other regularization methods

6.1 数据增强 data augmentation

治 variance 最好的药是更多数据,但数据可能很贵。数据增强是一种廉价的替代:对训练样本做不改变标签的随机变换,“造出”新样本。Ng 举的例子:

  • 猫的图片左右翻转以后还是猫,训练集一下子翻倍;
  • 随机裁剪、缩放、轻微旋转以后还是猫;
  • 手写数字(OCR)可以加随机的旋转和扭曲(幅度要小——扭得太厉害,“4”可能就不像”4”了)。

增强出来的样本和原样本高度相关,所以它们的信息量不如同样多的、真正独立的新样本;但它们几乎免费。关键的设计问题是:哪些变换不改变标签? 这需要领域知识:左右翻转对猫没问题,对”识别字母 b 和 d”就不行;上下翻转对卫星图像没问题,对街景就不行。§6.4 会在第 5 讲的白天 / 黑夜照片上看到,选对了变换,效果可以比 L2、dropout 都好得多。

6.2 early stopping

训练时同时画两条曲线:训练误差(或训练集上的 JJ)和 dev 误差,横轴是迭代次数。训练误差通常一路下降;dev 误差往往先降后升。early stopping 就是在 dev 误差最低的地方停下来,取那时的参数。第 1 讲 tiny_nn.train 的 keep_best、第 3 讲 §8.4 里”测试准确率在第 300–500 步就到了最高点”,说的都是它。

代码 05 演示 1:代码 03 里不加正则化的足球场网络,每 250 步量一次 dev 误差(3 个种子):

种子dev 最低在第几步那时 dev那时测试那时 Σ‖W‖²那时训练 J训练到底:dev测试Σ‖W‖²训练 Jpatience 停在
17500.06150.079850.70.12850.06900.0878320.60.06475750
25000.06400.081843.80.13480.08300.0986406.30.05715500
312500.06450.080073.50.10610.07650.0896262.80.08206250

early stopping:训练误差一路下降,dev 误差先降后升

  • dev 误差在很早的时候(第 500–1250 步)就到了最低。停在那里的网络,测试误差平均 0.0805;训练到底是 0.0920。early stopping 的效果和调好的 L2(0.0805)差不多,而且只训练了一次,不用扫描 λ——这是它最大的优点。
  • 为什么它像 L2?权重从很小的初始值开始,随训练一路变大(§4.3 的表)。停下来的那一刻,Σ‖W‖² 只有 44–74(训练到底是 263–406),和 λ = 0.1、0.3 时的 77、53 相当。early stopping = 在”中等大小的 ‖W‖“处停下。
  • patience(耐心):实践中不会真的训练到底再回头找最低点,而是”连续若干次没有创新低就停”。这里设成 20 次(5000 步),训练在第 5500–6250 步就停了,省下了约 80% 的计算。regularization.model(..., patience=20) 实现了这个规则。
  • 用 dev 挑停在哪一步,同样会有 §1.5 的乐观偏差:挑出来那一步的 dev 误差(0.0615–0.0645)比它的测试误差乐观。曲线本身也很嘈杂(图里 dev 误差在 0.062–0.07 之间来回跳),“最低点”有一部分是运气。

6.3 early stopping 和正交化的冲突

Ng 指出了 early stopping 的一个缺点。他把机器学习的工作分成两件独立的事:

  1. 优化 cost JJ(梯度下降、Adam……,第 8 讲);
  2. 不要过拟合(正则化、更多数据……,本讲)。

最好每个”旋钮”只管一件事,这样调起来互不干扰——这个思想叫正交化(orthogonalization),第 10 讲会把它作为整个 ML 策略的基础。early stopping 用同一个旋钮(迭代次数)同时管两件事:停下的那一刻,JJ 还远没优化好(演示 1 里训练集上的 JJ 只降到 0.106–0.135,训练到底是 0.057–0.082),你没法单独改进其中一件。

替代方案是 L2:把 JJ 优化到底(只管优化),再单独调 λ(只管过拟合)。代价是要多训练几次——代码 03 为了画 λ 曲线训练了 8 × 3 = 24 次,early stopping 每个种子只训练了一次。Ng 说他自己如果算力允许,更倾向于用 L2 并尝试不同的 λ;但很多人用 early stopping,因为它便宜。两者也可以一起用。

6.4 把工具用起来:救一救第 5 讲的 32×32 网络

第 5 讲 §2.4 里,32×32 输入的白天 / 黑夜网络 [3072, 64, 1] 训练准确率 0.966、测试只有 0.869;第 5 讲的预告说”第 6 讲会知道这一行该怎么救”。代码 05 演示 2 用同样的数据(训练 8000 张、dev 1000 张、测试 3000 张,同样的种子),依次试本讲的每一种工具(每一行:Adam,学习率 0.001,mini-batch 128,3000 步,用 dev 集做 early stopping):

做法训练误差dev 误差测试误差城市夜景认对
32×32,第 5 讲的设置(λ = 0.8)0.00010.12000.12270.665
+ L2 加强 10 倍(λ = 8)0.00110.11700.12400.641
+ dropout(keep_prob 0.5)0.03050.08900.10470.756
+ 随机水平翻转0.02190.08100.09130.819
+ 翻转 + 平移 ±2 像素0.05410.05400.06230.975
+ 翻转 + 平移 + dropout0.06550.05100.07400.940
降到 16×16(第 5 讲的办法)0.02480.06000.07300.915

(这个世界的 Bayes 错误率是 0.05:10% 的黄昏照片标签是随机的。“城市夜景认对”:最难的那一类——只能靠 1 个像素的小路灯和阴天区分——被判对的比例。这里用的是本讲的训练函数,和第 5 讲的 tiny_nn 随机数用法不同,第一行的数字和第 5 讲不会逐位相同。)

  • 起点:训练集几乎全对、测试错一成多,典型的 high variance。
  • L2 加强 10 倍几乎没用;dropout 有一些帮助。
  • 数据增强最有效:只加随机水平翻转(白天 / 黑夜照片左右翻转后标签不变),测试误差降到 0.0913;再加上 ±2 像素的随机平移(四周用边缘像素补 2 圈,再随机裁出 32×32,即常见的 padding + random crop),降到 0.0623,离下限 0.05 只差一点点,城市夜景认对的比例从 0.665 升到 0.975。原因是:城市夜景的线索是位置随机的小亮点,全连接网络要在每个位置分别学”这里有个亮点”;平移把同一个亮点挪到不同的位置,等于免费给了它更多”别处有亮点”的例子。(第 12 讲的卷积网络会把”平移不变”直接写进架构里。)
  • 再叠加 dropout:dev 误差略好(0.0510 vs 0.0540),测试误差反而更差(0.0740 vs 0.0623)。1000 张的 dev 集分不出零点几个百分点的差别(§1.3);按 dev 选,你会选到它。正则化手段不是叠得越多越好,而 dev 集要大到能分辨你关心的差别,才靠得住。
  • 和第 5 讲的办法比:降到 16×16 测试误差 0.0730;在 32×32 上做数据增强(0.0623)比它还好——高分辨率里的信息是有用的,前提是有足够的数据(或者足够好的增强)喂饱它。这就是那一行”该怎么救”的答案:先诊断(high variance),再按 basic recipe 试”更多数据”一类的药,用 dev 集做对照。
  • 训练误差反而升高了(0.0001 → 0.0541):增强后的训练集更难背。正则化就是这样——牺牲一点训练集上的表现,换来 dev / 测试上的进步。

7. 小结 · Summary

  • 三份数据:train 拟合参数,dev 做选择,test 给无偏的最终成绩。准确率估计的误差约为 p(1−p)/n\sqrt{p(1-p)/n},只取决于 dev / test 的样本数:两个模型差 0.5 个百分点时,100 个样本的 dev 只有 64% 的机会挑对,10,000 个时每次都对。所以小数据用 60/20/20,百万级数据 98/1/1 就够(代码 01)。
  • dev 和 test 必须同分布,而且是你将来要面对的分布:dev 来自上半场时,“上半场最好的模型”在下半场排第 18 / 20。只有 dev 没有 test时,dev 上挑出来的分数偏乐观(100 个样本时高估 0.0214),一份没参与挑选的 test 集没有偏差。
  • bias / variance 的诊断:训练误差 − 最优误差(bias),dev 误差 − 训练误差(variance)。参照是 Bayes 错误率而不是 0:同心圆上 22% 的训练误差已经到顶,以为是 high bias 去加大网络,一点用都没有。同一个任务上可以造出四个象限(代码 02)。
  • basic recipe:先治 bias(更大的网络、训练更久、换架构),再治 variance(更多数据、正则化、换架构)。药要对症:high bias 时加数据没用,只多 1 个隐藏单元 dev 就从 0.3392 降到 0.0824。大网络 + 正则化几乎只降 bias,更多数据几乎只降 variance,所以 trade-off 不再那么紧。
  • L2 正则化:JJ 加 λ2m∑l∥W[l]∥F2\frac{\lambda}{2m}\sum_l \lVert W^{[l]}\rVert_F^2,dWdW 加 λmW\frac{\lambda}{m}W,等价于每步把 WW 乘以 1−αλ/m1 - \alpha\lambda/m(weight decay)。仿原作业:λ = 0.7 让测试误差从 0.0920 降到 0.0805;dev 误差随 λ 先降后升,最好的 λ 是一个区间。为什么有效:权重小 → 网络更简单、更接近线性、更平滑(最大斜率 203.9 → 8.3)。调试时画包含正则项的 JJ。
  • dropout:训练时每个单元以 keep_prob 保留,除以 keep_prob 让期望不变、测试时不用任何补偿;测试时不用 dropout,一次全开的前向传播近似一大群子网络的平均。“不能依赖任何一个特征”:拿掉一个单元,不用 dropout 的网络 dev 误差最多涨 0.3105,用 dropout 的只涨 0.0065。cost 曲线不再单调,调试和梯度检验时先关掉。
  • 哪种正则化更好是经验问题:足球场数据上 L2 明显好于 dropout,“花朵 + 噪声特征”上正好相反,白天 / 黑夜照片上数据增强最好。
  • 数据增强是便宜的更多数据(32×32 网络测试误差 0.1227 → 0.0623);early stopping 一次训练就能得到和调好的 L2 差不多的效果(0.0805),但把”优化 J”和”不过拟合”绑在一个旋钮上,违背正交化。

8. 常见误区 · Common pitfalls

  1. “dev 和 test 应该各占 20%。” 比例是结果,不是原则。dev / test 只要大到能分辨你关心的差别;百万级数据时 1% 就够(§1.3)。反过来,小数据时套用 98/1/1 会让 dev 小到没用。
  2. “dev 上最好的那个分数就是我的模型在新数据上的表现。” 不是。你按 dev 挑过东西,挑出来的分数偏乐观(赢家的诅咒,§1.5);要无偏的成绩,用一份没参与任何选择的 test 集。
  3. “训练数据和 dev / test 来源不同没关系,反正都是猫。” dev 和 test 之间必须同分布,否则你优化的是错的靶子(§1.4)。训练集和 dev / test 不同分布时的诊断方法见第 10 讲。
  4. “训练误差 15% 就是 high bias。” 要和最优误差比。如果连人都只能做到 85%,15% 已经到顶了(§2.3)。
  5. “模型表现不好,先多收集些数据。” 先诊断。high bias 时加数据几乎没用(§3.2【A】:数据多 4 倍,dev 从 0.3392 到 0.3482)。
  6. “正则化越强越好 / 正则化手段叠得越多越好。” λ 太大会把模型推到 high bias(λ = 25 时变成常数);在白天 / 黑夜上再叠一个 dropout,测试误差反而变差(§6.4)。
  7. “L2 正则化之后 cost 曲线上升了,说明有 bug。” 先看你画的是哪个 JJ。只看交叉熵,它确实可能一直升(§4.7);完整的 JJ 才应该下降。
  8. “测试时也要做 dropout”/“测试时要乘以 keep_prob”。 测试时不做 dropout;用 inverted dropout 时,测试时也不需要任何缩放(§5.3–5.4)。只有用了”不除以 keep_prob”的旧写法,测试时才需要乘以 keep_prob——而忘了这一步,交叉熵翻倍、不报错。
  9. “dropout 比 L2 先进,应该优先用。” 没有普遍的优劣,要在 dev 上比(§5.6、§3.2)。dropout 是正则化手段,没有过拟合就不需要它。
  10. “开着 dropout 做梯度检验,误差大说明反向传播错了。” dropout 让 JJ 变成随机的,梯度检验必然失败。关掉它,或者固定蒙版再查(§5.7)。

9. 练习 · Exercises

练习 1(dev 集要多大)。你的两个模型在 dev 上的准确率分别是 93.2% 和 93.6%。(a) 如果 dev 集有 500 个样本,单个准确率估计的标准差大约是多少?这 0.4 个百分点的差距可信吗?(b) 要让标准差降到 0.1 个百分点,dev 集大约需要多少个样本?(c) 你总共有 300 万个样本,按 98/1/1 划分,dev 有多少个?够不够用?

参考答案

(a) 0.932×0.068/500≈0.0113\sqrt{0.932 \times 0.068 / 500} \approx 0.0113,约 1.1 个百分点。0.4 个百分点的差距远小于一个标准差,不可信——可能只是这 500 个样本的运气(代码 01 演示 1:dev 100 个时,差 0.5 个百分点的两个模型只有 64% 的机会排对顺序)。 (b) n≈0.932×0.068/0.0012≈63,000n \approx 0.932 \times 0.068 / 0.001^2 \approx 63{,}000。 (c) 30,000 个。标准差约 0.932×0.068/30000≈0.0015\sqrt{0.932 \times 0.068 / 30000} \approx 0.0015,0.4 个百分点的差距约是它的 2.7 倍,比较可信(配对比较时更灵敏,§1.3 的注)。如果你关心的是 0.1 个百分点量级的改进,30,000 个还不够。

练习 2(诊断)。假设人类在下面几个任务上的错误率约为最优误差。诊断每种情况,并说出下一步最该做什么: (a) 人类 1%,训练 8%,dev 9%;(b) 人类 1%,训练 1.5%,dev 12%;(c) 人类 1%,训练 8%,dev 20%;(d) 人类 10%,训练 10.5%,dev 11%。

参考答案

(a) 训练 − 最优 = 7%,dev − 训练 = 1%:high bias。更大的网络、训练更久、换架构;加数据没用。 (b) 0.5% 和 10.5%:high variance。更多数据、正则化(L2、dropout、数据增强)、换架构。 (c) 7% 和 12%:两者都有。按 basic recipe 先治 bias(让训练集先学好),再治 variance;“大网络 + 正则化”可以同时改善两者。 (d) 0.5% 和 0.5%:两者都没有,已经接近最优。如果最优误差真的是 10%,再改进模型几乎没有空间;要么接受,要么改进输入(更清晰的数据、更多的信息)。如果没有”人类 10%“这个参照,你可能把 (d) 误诊成 high bias(§2.3)。

练习 3(L2 的梯度)。(a) 从 J=1m∑iL(i)+λ2m∑l∥W[l]∥F2J = \frac1m\sum_i\mathcal{L}^{(i)} + \frac{\lambda}{2m}\sum_l\lVert W^{[l]}\rVert_F^2 出发,推导 ∂J/∂Wij[l]\partial J/\partial W^{[l]}_{ij} 中正则项贡献的部分。(b) α = 0.3、λ = 0.7、m = 211 时,衰减系数 1−αλ/m1 - \alpha\lambda/m 是多少?如果交叉熵的梯度恰好为 0,30,000 步之后权重剩下原来的多少倍?(c) 把 m 换成 2110(数据多 10 倍),同一个 λ 的”衰减力度”怎样变化?这和”数据多时不太需要正则化”有什么关系?

参考答案

(a) ∥W[l]∥F2=∑i,j(Wij[l])2\lVert W^{[l]}\rVert_F^2 = \sum_{i,j}(W^{[l]}_{ij})^2,对 Wij[l]W^{[l]}_{ij} 求导得 2Wij[l]2W^{[l]}_{ij},乘以 λ2m\frac{\lambda}{2m} 得 λmWij[l]\frac{\lambda}{m}W^{[l]}_{ij}。写成矩阵就是 dW[l]dW^{[l]} 多一项 λmW[l]\frac{\lambda}{m}W^{[l]}。 (b) 1−0.3×0.7/211≈0.9990051 - 0.3 \times 0.7 / 211 \approx 0.999005(和代码 03 演示 2 一致)。0.99900530000=e30000ln⁡0.999005≈e−29.9≈1.1×10−130.999005^{30000} = e^{30000 \ln 0.999005} \approx e^{-29.9} \approx 1.1 \times 10^{-13}。 (c) 1−0.3×0.7/2110≈0.99991 - 0.3 \times 0.7 / 2110 \approx 0.9999,每步的衰减小了 10 倍。正则项 λ2m∥W∥2\frac{\lambda}{2m}\lVert W\rVert^2 前面有 1/m1/m,数据越多,同一个 λ 的正则化作用越弱、交叉熵(数据)的话语权越大——数据多时本来就不容易过拟合,正好需要更少的正则化。(这也意味着换了数据量以后,最好的 λ 可能要重新调。)

练习 4(dropout 的期望)。某个隐藏单元的激活值 a=2a = 2,keep_prob = 0.75。(a) inverted dropout 之后,它可能取哪两个值?各自的概率?期望是多少?(b) 不除以 keep_prob 时期望是多少?(c) 求 inverted dropout 之后这个值的方差。keep_prob 越小,方差怎样变化?这和”keep_prob 越小,正则化越强”有什么关系?

参考答案

(a) 以 0.75 的概率为 2/0.75≈2.6672 / 0.75 \approx 2.667,以 0.25 的概率为 0。期望 0.75×2.667=20.75 \times 2.667 = 2,和原值相同。 (b) 0.75×2=1.50.75 \times 2 = 1.5,变成原来的 keep_prob 倍。 (c) E[x2]=0.75×(2/0.75)2=4/0.75≈5.333\mathbb{E}[x^2] = 0.75 \times (2/0.75)^2 = 4/0.75 \approx 5.333,方差 =5.333−4=1.333= 5.333 - 4 = 1.333。一般地,方差 =a2(1−p)/p= a^2 (1-p)/p:keep_prob = 0.5 时是 a2a^2,0.9 时只有 0.11a20.11a^2。keep_prob 越小,训练时注入的噪声越大,网络越”不敢”依赖单个单元——正则化越强;太小则噪声大到学不好,滑向 high bias(代码 04 演示 3:keep_prob = 0.5 时训练误差升到 0.0427、dev 误差回到不用 dropout 的水平)。

练习 5(反向传播与蒙版)。(a) 为什么 dropout 的反向传播必须用前向时同一个蒙版 D[l]D^{[l]},而不是重新抽一个?(b) 第 ll 层前向是”先激活、再 dropout”,反向时 dropout_backward 和”穿过激活函数”哪个在前?(c) 被关掉的单元,它的输入权重 W[l]W^{[l]} 的那一行在这一步会被更新吗?它的输出权重 W[l+1]W^{[l+1]} 的那一列呢?

参考答案

(a) 反向传播算的是”这一次前向传播所对应的那个函数”的梯度。前向用的是蒙版 DD 定义的那个子网络;换一个蒙版,就是另一个函数的梯度,对不上(代码 04 演示 5:每次重新抽蒙版时梯度检验的相对误差是 1.00e+00)。 (b) 反向的顺序和前向相反:先 dropout_backward(乘以 D/keep_probD/\text{keep\_prob}),再穿过激活函数(乘以 g′(Z)g'(Z))。 (c) 被关掉的单元 jj:dropout_backward 让 dAj[l]=0dA^{[l]}_j = 0,于是 dZj[l]=0dZ^{[l]}_j = 0,它的输入权重(W[l]W^{[l]} 的第 jj 行)的梯度只剩 L2 项(不加 L2 时为 0),这一步只有衰减、没有学习。它的输出权重(W[l+1]W^{[l+1]} 的第 jj 列):dW[l+1]=1mdZ[l+1]A[l]⊤dW^{[l+1]} = \frac1m dZ^{[l+1]}A^{[l]\top},而 A[l]A^{[l]} 的第 jj 行(dropout 之后)是 0,所以那一列的梯度也只剩 L2 项。注意蒙版是逐样本的:一个 mini-batch 里,单元 jj 只在被关掉的那些样本上没有贡献,在其他样本上照常贡献梯度。

练习 6(先预测再运行:dropout 加在更宽的网络上)。§5.6 猜测”dropout 在这个任务上效果不好,可能是因为网络太窄”。把 04_dropout.py 里的 DIMS 改成 [2, 100, 100, 1],只运行演示 3(可以把 main 里其他演示注释掉;每次训练会慢几倍)。先预测:不用 dropout 时 dev 误差会比 [2, 20, 3, 1] 更高还是更低?keep_prob = 0.86 和 0.5 时呢?然后运行验证。

参考答案

预期(具体数字以你运行的结果为准):更宽的网络不加正则化时更容易过拟合,训练误差可能更低、dev 误差可能更高;dropout 在宽层上”关掉一部分单元”不再那么粗暴(100 个单元关掉 14 个,剩下的仍然很多),所以 dropout 的相对收益可能更大。但这只是一个假设——如果结果显示 dropout 依然不如 L2,那就说明原因不在宽度,可能在数据本身(比如噪声集中在少数”意外点”上,L2 的”压平”正好对症)。无论结果如何,这个练习的重点是:对”为什么”的猜测,要用一个只改一个变量的对照实验去检验。

练习 7(先预测再运行:early stopping 的 patience)。在 05_early_stopping_and_augmentation.py 里把 PATIENCE 从 20 改成 4(只等 1000 步)和 60(等 15,000 步)。先预测:停下的位置、返回的”最好的一步”、以及测试误差会怎样变化?patience 太小有什么风险?

参考答案

预期:patience = 4 时会停得更早(dev 误差最低点在第 500–1250 步,之后 1000 步内没有创新低就停);返回的”最好的一步”多半不变,因为这几个种子的最低点出现得很早。patience = 60 时停得更晚甚至训练到底,最好的一步也大概率不变。风险:dev 曲线很嘈杂(§6.2 的图),如果 dev 误差在某个平台上停留一段时间后才继续下降(第 1 讲代码 04 里 sigmoid 深网的训练曲线就先卡在平台上、过很久才冲出来),太小的 patience 会在平台上提前放弃。实践中 patience 要和 dev 曲线的噪声、评估间隔一起考虑;具体数字以你运行的结果为准。

练习 8(设计数据增强)。对下面每个任务,说出两种你认为不改变标签的变换,和一种会改变标签(不能用)的变换:(a) 识别照片里有没有猫;(b) 识别手写数字 0–9;(c) 从胸部 X 光片判断是否有肺炎;(d) 第 5 讲的触发词检测(10 秒音频)。

参考答案

(a) 可以:左右翻转、随机裁剪 / 缩放、轻微的亮度和颜色变化。不能:裁剪到把猫切掉(标签会变成”没有猫”)。上下翻转也要小心(真实照片里猫很少倒立,会引入不存在的样本)。 (b) 可以:小角度旋转(±10° 左右)、轻微的弹性扭曲、小幅平移。不能:左右翻转(2、3、5、7 会变成不像任何数字的东西)、旋转 180°(6 和 9 互换)。 (c) 可以:轻微的亮度 / 对比度变化、小幅平移和缩放。要小心:左右翻转——心脏通常在左侧,翻转会造出解剖上不存在的图像;是否可用要和放射科医生讨论。这说明增强需要领域知识。 (d) 可以:混入不同的背景噪声、轻微改变音量和语速(第 5 讲 §5.3 的合成数据本身就是一种增强)。不能:把音频倒放(“activate”倒放以后不再是触发词);在”activate”刚结束的位置截断,会改变哪些帧该标 1。

练习 9(early stopping 与 L2 的对应)。代码 05 演示 1 里,early stopping 停下时 Σ‖W‖² 在 44–74 之间;代码 03 演示 3 里,λ = 0.1、0.3 时 Σ‖W‖² 是 77.4、53.0。(a) 为什么说 early stopping 和”选一个中等的 λ”效果类似?(b) 两者在”需要训练几次”上有什么区别?(c) 结合正交化,说说你在一个新项目里会怎样选择。

参考答案

(a) 权重从很小的初始值开始,随训练逐渐变大;在中途停下,等于把 ‖W‖ 限制在一个中等的大小。L2 用 λ 直接惩罚 ‖W‖,λ 中等时得到的 ‖W‖ 也是中等的。两者都在”‖W‖ 太小(high bias)“和”‖W‖ 太大(high variance)“之间取一个中间值。代码里两者的测试误差也几乎一样(0.0805 vs 0.0805)。 (b) early stopping:每个种子训练一次,就”顺便”试遍了从小到大的 ‖W‖。L2:每个 λ 都要单独训练到收敛(代码 03 扫了 8 个 λ × 3 个种子 = 24 次)。 (c) 一种合理的做法:算力宽裕时用 L2(或 L2 + 训练到收敛),把”优化”和”正则化”两个旋钮分开调,出了问题容易定位;算力紧张或者只是快速试一个想法时,用 early stopping 当作便宜的默认选项。实践中两者也常常一起用(比如很多框架默认保留 dev 上最好的检查点)。


10. 延伸阅读 · Further reading

课程材料

  • Coursera Deep Learning Specialization,C2M1 “Practical Aspects of Deep Learning”(Andrew Ng)——本讲的技术内容;编程作业 “Regularization”。
  • Andrew Ng, Machine Learning Yearning(免费电子书)——关于 dev / test 集怎么建、多大、为什么同分布,以及 bias / variance 的章节,是本讲 §1–§3 的展开版,也是第 10 讲的前身。
  • Goodfellow, Bengio & Courville (2016). Deep Learning, 第 5.2–5.4 节(容量、过拟合、欠拟合、bias / variance)和第 7 章 “Regularization for Deep Learning”(L2、数据增强、early stopping、dropout 都在里面)。

正则化方法的原始论文

  • Krogh & Hertz (1992). A simple weight decay can improve generalization. NeurIPS 4. ——weight decay 的经典分析。
  • Hinton, Srivastava, Krizhevsky, Sutskever & Salakhutdinov (2012). Improving neural networks by preventing co-adaptation of feature detectors. arXiv:1207.0580. ——dropout 的第一篇论文。
  • Srivastava, Hinton, Krizhevsky, Sutskever & Salakhutdinov (2014). Dropout: A simple way to prevent neural networks from overfitting. JMLR 15. ——dropout 的完整版本,包括测试时的权重缩放和”集成”的解释。
  • Baldi & Sadowski (2013). Understanding dropout. NeurIPS. ——证明了单层 sigmoid 单元上”权重缩放 = 子网络的归一化几何平均”(§5.4)。
  • Wager, Wang & Liang (2013). Dropout training as adaptive regularization. NeurIPS. ——dropout ≈ 自适应的 L2(§5.5)。
  • Gal & Ghahramani (2016). Dropout as a Bayesian approximation: Representing model uncertainty in deep learning. ICML. ——MC dropout(§5.4 的括号)。
  • Prechelt (1998). Early stopping — but when? In Neural Networks: Tricks of the Trade. ——early stopping 的停止准则(patience 一类的规则)。
  • Simard, Steinkraus & Platt (2003). Best practices for convolutional neural networks applied to visual document analysis. ICDAR. ——手写字符上的数据增强(弹性扭曲)。
  • Loshchilov & Hutter (2019). Decoupled weight decay regularization. ICLR. ——AdamW:为什么在 Adam 里 L2 ≠ weight decay(§4.3)。

现代视角

  • Zhang, Bengio, Hardt, Recht & Vinyals (2017). Understanding deep learning requires rethinking generalization. ICLR. ——网络能拟合随机标签。
  • Belkin, Hsu, Ma & Mandal (2019). Reconciling modern machine-learning practice and the classical bias–variance trade-off. PNAS. ——double descent。
  • Nakkiran, Kaplun, Bansal, Yang, Barak & Sutskever (2020). Deep double descent: Where bigger models and more data hurt. ICLR.
  • Cohen, Kaur, Li, Kolter & Talwalkar (2021). Gradient descent on neural networks typically occurs at the edge of stability. ICLR. ——§4.7 里大学习率时 cost 的上下抖动。

11. 下一讲预告 · Next lecture

本讲解决的是”模型学到的东西能不能推广到新数据”:怎样划分数据来衡量它、怎样诊断 bias 和 variance、怎样用正则化控制 variance。但还有一类问题出现在更早的地方——优化本身能不能顺利进行。第 1 讲代码 04 里 sigmoid 深网的梯度传到第 1 层只剩输出层的 1/2000,第 4 讲代码 05 里大学习率让 ReLU 全部死掉;本讲 §4.7 里,学习率 0.3 让 cost 一直在上下抖动。

第 7 讲:实践要点 II——输入归一化、梯度消失 / 爆炸、初始化与梯度检验。 这是 C2M1 的后半部分:

  • 输入归一化:为什么把每个特征减去均值、除以标准差,能让损失曲面”更圆”、梯度下降快得多(第 1 讲 Standardizer 的来历);
  • 梯度消失 / 爆炸:深网里激活值和梯度为什么会随层数指数级地变大或变小;
  • Xavier / He 初始化:从”保持每层方差不变”推出 Var(W)=1/n[l−1]\text{Var}(W) = 1/n^{[l-1]} 和 2/n[l−1]2/n^{[l-1]}(本讲的足球场网络用的就是前者);
  • 梯度检验:双边差分、相对误差,以及几条实战注意事项——其中两条你已经在本讲见过:正则项别漏(§4.7、gradient_check_reg),和 dropout 一起用时先关掉 dropout(§5.7)。
← 返回课程目录