第 8 章 深度学习循环神经网络RNN

循环神经网络:让网络拥有记忆

本章来源:本文整理自《动手学深度学习》(zh.d2l.ai)第 8 章《循环神经网络》,原书作者 Aston Zhang、李沐、Zachary C. Lipton、Alexander J. Smola。

本章讲什么

前面讲的模型都假设数据是独立的——每个样本之间没有关系。但很多数据是序列:股票价格、文本、语音、地震记录……序列中当前的观测和过去的观测之间存在依赖。预测明天看今天,翻译这句话要看前一句。

本章介绍处理序列数据的基础模型:循环神经网络(RNN)。它让网络拥有"记忆",能够记住之前看过的内容。

序列模型

序列数据的核心问题是:预测 xtx_t 需要估计条件分布 P(xtx_t | 过去所有观测)。但直接用全部历史有两个问题:输入数量随时间增长、参数数量不固定。

两种近似策略:

  1. 自回归模型:只取最近 τ 个观测做回归。参数数量固定,但忽略了更早的历史。
  2. 隐变量自回归模型:维护一个对过去观测的总结 hth_t,用它更新预测和总结本身。hth_t 从未被直接观测到,所以叫"隐变量"。这是 RNN 的雏形。

马尔可夫假设进一步简化:只要依赖最近 τ 个观测就足够精确。τ=1 时就是一阶马尔可夫模型。

两个重要直觉:

  • 顺序即意义:"狗咬人"远不如"人咬狗"惊人——同样几个词,顺序不同含义天差地别。
  • 多步预测误差会累积:天气预报 24 小时内准,再远精度骤降。单步预测好,不代表多步预测好。

文本预处理

文本是最常见的序列数据。把原始文本变成模型可操作的数值序列,需要四步:

  1. 加载:把文本读成字符串。
  2. 词元化:把字符串拆成词元(token,可以是单词或字符)。
  3. 建词表:统计语料中所有唯一词元及其频率,按频率从高到低为每个词元分配数字索引。低频词元会被移除并统一映射到未知词元 <unk>;还要预留特殊词元:<pad>(填充)、<bos>(序列开始)、<eos>(序列结束)。
  4. 转换:把每条文本行变成词元索引列表。

这是所有 NLP 任务共用的基础流水线。

语言模型

语言模型的目标是估计一个文本序列的概率。它的价值:理想的语言模型能自己生成自然文本;还能消除语音识别中的歧义——"to recognize speech"和"to wreck a nice beach"读音相近,但前者语义正常,语言模型会选前者。

朴素做法是数频次:用词频估计单词概率,用二元组频次估计条件概率。但长组合在语料里几乎不出现,需要拉普拉斯平滑(在计数上加小常数)避免零概率。然而基于计数的方法依然不可行:要存下所有计数、忽略词义、长序列大部分从没出现过。

于是借助马尔可夫假设引入 n 元语法:只依赖前 n-1 个词。一元(unigram)、二元(bigram)、三元(trigram)分别对应 n=1、2、3。

真实的词频统计揭示了一条重要规律——齐普夫定律:第 i 个常用词的频率大约正比于 1/i 的某个幂。也就是说,词的分布是长尾的:少数词出现极频繁,绝大多数词很少出现。这告诉我们用计数+平滑建模会严重高估尾部词频,必须转向深度学习。

循环神经网络(RNN)

n 元语法要存储词表大小的 n 次方个数字,参数随 n 指数爆炸。RNN隐状态绕开这个问题:不再显式建模"前 n-1 个词",而是用隐状态 ht1h_{t-1} 把"到当前为止的序列信息"压缩起来,并且隐状态递归更新:ht=f(xt,ht1)h_t = f(x_t, h_{t-1})

对比多层感知机,RNN 的隐藏层计算多了一项——用上一时间步的隐状态参与当前隐状态的计算。关键性质:同一套参数在每个时间步重复使用,所以参数数量不随序列长度增长。这是 RNN 最核心的优势。

需要区分两个易混概念:

  • 隐藏层:输入到输出路径上的层(观测角度)。
  • 隐状态:作为"输入"参与下一时间步计算的状态(技术角度)。

RNN 可用来构建字符级语言模型:输入是"machin",标签是"achine",逐字符预测下一个字符。

评估指标是困惑度(perplexity):平均负对数似然的指数。完美模型为 1,最差为无穷大,均匀分布基线等于词表大小。可以理解为"下一个词元实际选择数的调和平均数"——越低越好。

训练 RNN 的几个实操要点

独热编码:把词元索引映射为只有一位为 1 的高维单位向量作为输入。

预热期(warm-up):预测时先用用户给的前缀字符逐个更新隐状态但不输出,之后才开始生成新字符。

梯度裁剪:当梯度范数超过某个阈值 θ 时,等比缩小梯度。这能快速修复梯度爆炸,防止训练不收敛。对 RNN 来说几乎必不可少。

分离梯度(detach):截断隐状态的反向传播链,把梯度计算限制在单个小批量内,控制计算量。

通过时间反向传播(BPTT)

BPTT(backpropagation through time)就是把反向传播应用到 RNN 上:把计算图按时间步逐次展开,用链式法则计算梯度。

问题出在隐状态的递归依赖上:展开后是矩阵高次幂的长链乘积。特征值小于 1 的项指数级衰减(梯度消失,参数学不动),大于 1 的项指数级发散(梯度爆炸)。

三种策略:

  • 完全计算:求完整的和,但极慢又不稳定,几乎不用。
  • 截断时间步:在 τ 步后截断求和。这是实践中一直在用的做法,效果很好——偏向短期影响,还有轻度正则化效果。
  • 随机截断:理论上优雅,但实践中并不比常规截断更好。

小结

  • 序列数据中当前观测依赖过去,自回归和隐变量自回归是两种建模策略。
  • 文本预处理四步:加载、词元化、建词表、转索引。
  • 语言模型估计序列概率,n 元语法靠马尔可夫假设压缩参数。
  • 齐普夫定律说明词频是长尾分布,必须用深度学习。
  • RNN 用隐状态压缩序列信息,参数共享、不随序列长度增长。
  • 困惑度是语言模型的评估指标,越低越好。
  • BPTT 展开时间步计算梯度,梯度消失/爆炸来自递归矩阵连乘,用截断和梯度裁剪缓解。

关键术语

术语 一句话解释
序列模型 对有序数据建立概率模型的框架
自回归模型 用最近 τ 个观测回归预测下一个值
隐变量自回归模型 维护不可观测的总结 hth_t 来更新预测
马尔可夫假设 预测只依赖最近 τ 个观测
词元 / 词表 文本基本处理单位 / 词元到索引的映射
语言模型 估计文本序列概率的模型
n 元语法 只依赖前 n-1 个词的马尔可夫近似
齐普夫定律 词频与排序近似成反比,词分布是长尾
循环神经网络(RNN) 用隐状态递归处理序列的网络
隐状态 存储到当前为止序列信息的隐藏变量
困惑度 语言模型的评估指标,越低越好
BPTT 把反向传播按时间步展开到 RNN 上
梯度裁剪 限制梯度范数,防止梯度爆炸