30u30 中文导读 / 30 天目录 / 第 3 天

第 3 天 · dropout 只加在层间,不加在时间方向

第 1 单元Recurrent Neural Network RegularizationWojciech Zaremba, Ilya Sutskever, Oriol VinyalsarXiv · 2014
这是导读,不是翻译。先用 10 分钟拿到这篇论文的思想地图,再去读 原论文 →,去原仓库的 03_RNN_Regularization/ 目录 → 动手写代码。
给孩子讲:这一天有 10 岁孩子能听懂的讲义,带猜字游戏和动手活动,可打印。打开儿童讲义 → · PDF 简体 · PDF 繁體

一句话

直接给 RNN 加 dropout 会破坏记忆,效果反而变差。这篇短论文只在层与层之间的非循环连接上加 dropout,时间方向的循环连接保持原样。这个改动让大 LSTM 可以放心加大:Penn Treebank 测试困惑度从无正则小模型的 114.5 降到 78.4。

论文讲了什么

问题。2014 年,dropout 已是前馈网络和卷积网络的标准正则化方法。但大家发现,它用在 RNN 上效果不好。结果是 RNN 只能用小模型,模型一大就过拟合。作者的目标很具体:让大 LSTM 也能用 dropout。

原因。假设在循环连接 h_{t-1} → h_t 上每一步都随机丢弃单元。噪声会随时间步累积。序列越长,记忆被破坏的次数越多,网络很难长时间保存信息。

方法。把多层 LSTM 的连接分成 2 类:

写成公式:第 l 层在时间步 t 的下层输入从 h_t^{l-1} 改为 D(h_t^{l-1}),其中 D 是 dropout 算子。上一时间步的 h_{t-1}^l 原样输入。这样,一条信息从输入流到输出,不管跨越多少时间步,只穿过 L+1 次 dropout,L 是层数。

主实验:Penn Treebank 词级语言模型。测试困惑度如下:

训练细节。中型和大型模型都沿时间展开 35 步。中型模型训练 39 轮,梯度范数裁剪到 5,在 1 块 NVIDIA K20 GPU 上约需半天。大型模型训练 55 轮,裁剪阈值为 10,约需 1 整天。

另外 3 个任务。作者用同一方法验证了通用性:

论文没有理论推导。它的贡献是一个清楚的工程判断,加上 4 个领域的实验证据。

关键概念

丢弃法(dropout)
训练时以概率 p 把每个单元的输出置 0。网络因此不能依赖任何单个单元,相当于同时训练许多共享权重的子网络。测试时关闭 dropout。
循环连接与非循环连接(recurrent / non-recurrent connection)
循环连接沿时间方向传递记忆,从 h_{t-1} 到 h_t。非循环连接沿深度方向传递特征,从第 l-1 层到第 l 层。本文只在后者上加 dropout。
困惑度(perplexity)
语言模型的标准指标,等于每个词平均交叉熵的指数 exp(loss)。困惑度 78 可以理解为:模型每一步平均在 78 个候选词之间犹豫。越低越好。
过拟合(overfitting)
模型记住了训练数据的噪声和细节,训练损失很低,验证损失却很高。数据少、模型大时最容易发生,正则化就是为了抑制它。
Penn Treebank(PTB)
语言模型的经典小数据集,训练集约 93 万词,词表 1 万词。小数据配大模型极易过拟合,所以它常用来检验正则化方法。

历史位置:为什么值得读

dropout 由 Hinton 团队 2012 年提出,Srivastava 等人 2014 年在 JMLR 发表完整论文(第 12 天)。它在卷积网络上效果显著,但在 RNN 上一直用不好。

这篇论文的作者是 Zaremba(当时在纽约大学)以及 Google Brain 的 Sutskever 和 Vinyals。同一时期,Sutskever 和 Vinyals 在做 seq2seq 机器翻译,需要训练更大的 LSTM。论文解决的是他们自己的实际问题:大 LSTM 怎样不过拟合。

这个方案成为 2015–2017 年 LSTM 语言模型的默认配置。PyTorch 的 nn.LSTM(dropout=...) 参数就只在层间加 dropout,最后一层不加。后续工作继续改进:

和清单其他天的关系:它直接用到第 2 天的 LSTM 和第 12 天的 dropout。第 4 天会从信息论角度解释,为什么给权重加噪声能防止过拟合。

学前准备

在原仓库怎么学

原仓库第 3 天用 NumPy 实现带正则化的 LSTM。注意:练习 1 来自论文,练习 2–5 是仓库作者补充的通用正则化方法。建议按下面的顺序学:

  1. 先读论文第 3 节和表 1,再打开 README.md 看本天的结构。
  2. 读 implementation.py。先看 dropout_forward 和 dropout_backward,再看 RegularizedLSTM 把 dropout 放在了哪几条路径上。
  3. 运行 train_minimal.py,用 data/sample_text.txt 训练,观察验证损失和早停的触发时机。
  4. 打开 notebook.ipynb,用 visualization.py 画训练与验证损失曲线、dropout 对激活值的影响、权重分布。
  5. 做 exercises/ 里的 5 个练习:dropout、层归一化、权重衰减、早停、完整训练流程。
  6. 对照 exercises/solutions/ 检查答案,最后用 CHEATSHEET.md 复习。

建议:做练习 1 时,把 dropout 分别放在循环连接和非循环连接上,各训练一次,比较验证损失。这一组对照就是论文的核心论点。自己跑出来的结果,比读结论更有说服力。

今天再看

还活着的:“只在一部分路径上加噪声”的思路一直在用。Transformer 的标准配置在残差分支、注意力权重和嵌入上加 dropout,不在残差主干上加。原理和本文相同:不要破坏需要长距离传递信息的通道。

变化了的:大语言模型预训练通常把 dropout 设为 0。数据量远大于模型能记住的量,训练只过一遍或几遍数据,过拟合不是主要矛盾。微调(fine-tuning)和小数据任务仍常用 dropout,例如 LoRA 的 lora_dropout 参数。

值得记住的教训:评估一个正则化方法,要先看它作用在哪条路径上。同一个 dropout,放对位置是改进,放错位置是破坏。

容易卡住的地方

延伸阅读