第16章 让神经网络处理序列
“狗追猫”和“猫追狗”使用完全相同的三个词,词袋表示也完全相同,但谁在追谁恰好由顺序决定。
再看一句没有说完的话:“因为道路结冰,学校今天决定……”读到最后一个词以前,我们已经在利用前文预测后续。若只看当前位置的词向量,“决定”的表示无法告诉模型是谁决定、为什么决定,以及下一词更可能是“停课”还是“开花”。
语言、声音、股票、传感器和动作轨迹都有共同特点:数据按顺序到达,当前位置的意义可能依赖此前信息。处理这类数据,模型不仅要看“现在是什么”,还要保存某种“到目前为止发生了什么”。
循环神经网络(recurrent neural network, RNN)给神经网络增加一个隐藏状态。每读入一个位置,它都把当前输入与上一时刻状态组合,生成新的状态。状态继续传到下一位置,使同一套网络能够处理不同长度的序列。
静态词向量缺少当前语境
第 15 章为每个词表项保存一个向量。这使“猫”和“狗”可以共享统计关系,却仍有两个明显限制。
第一,静态向量本身没有顺序。“不太好”和“太不好”包含相近词语,组合意义却不同。第二,同一个词在不同句子里需要不同解释。“苹果发布新设备”和“苹果含有膳食纤维”中的“苹果”不应得到完全相同的上下文表示。
最直接的办法,是把固定长度窗口中的多个词一起输入模型。但窗口只能覆盖预设范围;窗口扩大后参数与计算增加,也仍不清楚怎样处理任意长度。
序列模型改用递推:不一次读完整条序列,而是从左到右更新一个摘要。这个摘要叫作隐藏状态(hidden state)。它不要求原样保存所有词,只需保留对当前任务有用的信息。
RNN 每一步都更新同一份状态
设第 \(t\) 个输入词的向量为 \(x_t\),上一时刻隐藏状态为 \(h_{t-1}\)。一个基本 RNN 可以写成:
\[ h_t=\tanh(W_xx_t+W_hh_{t-1}+b_h) \]
若当前位置需要输出,再计算:
\[ o_t=W_oh_t+b_o \]
\(W_x\) 把当前输入写入状态,\(W_h\) 决定旧状态怎样影响新状态,\(W_o\) 把状态映射到输出。关键是:所有时间步使用同一组参数。
x_1 -> [RNN] -> h_1
| |
x_2 -> [RNN] -> h_2
| |
x_3 -> [RNN] -> h_3 -> 输出
每个 [RNN] 是同一单元在不同时间步的展开,不是三套独立参数。
Jeffrey Elman 展示过简单循环网络怎样从序列预测中形成对词类与结构有用的内部状态。(Elman 1990年) 模型没有被人工告知“名词”或“动词”标签,而是在预测下一个元素时逐步组织状态空间。
核心机制
RNN 在每个位置读取当前输入 \(x_t\) 和上一状态 \(h_{t-1}\),用共享参数生成新状态 \(h_t\)。状态让此前信息影响当前预测;共享参数让同一种更新规则能够用于不同长度和不同位置的序列。
状态是压缩摘要,不是无限录像
隐藏状态通常是固定长度向量。无论输入有 10 个词还是 1,000 个词,基础 RNN 都要把已读历史压进相同数量的数值中。
这带来一种有用偏置:模型必须选择什么值得保留。做情感分类时,它可能保存否定、程度和情绪线索;做语音识别时,它可能保存近期声音模式;做括号匹配时,它需要跟踪尚未闭合的结构。
但固定状态也意味着信息会竞争空间。新输入不断覆盖旧状态,早期细节可能逐步淡化。隐藏维度更大可以增加容量,却不保证模型知道怎样长期保存正确内容。
状态也不是人类可直接阅读的句子摘要。信息通常分布在许多维度中,同一维度会参与多个功能。我们可以通过探针、扰动或可视化研究状态,但不能仅凭一个单元的高响应就断言它“代表主语”。
把循环沿时间展开
RNN 图上只有一个循环单元,训练时可以把它沿时间展开:
h_0 -> h_1 -> h_2 -> h_3 -> h_4
^ ^ ^ ^
x_1 x_2 x_3 x_4
展开后,它看起来像一个很深的前馈网络,只是每一层共享权重。序列有 100 个位置,就出现 100 次状态更新。
损失可以只在最后产生,例如整句分类;也可以每个位置都有,例如预测下一个字符。反向传播会从所有损失沿展开图向前面的状态和共享参数传递,这叫作随时间反向传播(backpropagation through time, BPTT)。
共享参数在多个时间步被使用,所以它的最终梯度是各次使用产生的贡献之和。模型由此学到一套重复更新规则,而不是为句首、句中和句尾分别记住一套答案。
技术深潜:长期梯度为什么困难
若忽略输入项,隐藏状态从 \(h_{t-1}\) 到 \(h_t\) 的局部导数可以简写为矩阵 \(J_t\)。较晚损失对较早状态的影响包含一串矩阵乘积:
\[ \frac{\partial L_t}{\partial h_k} = \frac{\partial L_t}{\partial h_t} J_tJ_{t-1}\cdots J_{k+1} \]
若这些矩阵典型方向上的尺度反复小于 1,乘积随时间距离增加而趋近零,早期状态几乎收不到梯度;若反复大于 1,梯度可能爆炸。
Bengio、Simard 与 Frasconi 分析了梯度方法学习长期依赖的困难。(Bengio 等 1994年) 它不仅是 sigmoid 的问题:循环权重的谱性质、激活饱和、序列长度和任务所需时间跨度都会影响梯度。
训练时常使用截断 BPTT:把很长序列切成若干段,只在有限步内反向传播。它降低内存和计算成本,也明确限制了一次梯度能跨越的距离。梯度裁剪可以抑制爆炸,但不能恢复已经消失的信息。
长期依赖不是普通 RNN 的强项
设一句话开头是“我出生在法国……”,中间隔了许多描述,结尾要求预测“我能流利地说___语”。模型需要把“法国”跨过很多无关词保留下来。
基础 RNN 理论上可以把信息传到任意远,实际训练却常偏向近期线索。早期状态反复经过非线性变换,不仅梯度会衰减,表示本身也可能被后续输入覆盖。
这解释了一个重要区别:“存在计算路径”不等于“训练能够可靠利用这条路径”。网络结构允许远距离影响,只是必要条件;优化过程还必须把权重调整到能保留信息的位置。
增加隐藏维度、改变初始化、使用 ReLU、归一化和残差式连接都可能帮助,但 1990 年代最有影响的方案,是给状态加入更明确的信息通道和门控。
LSTM 用门控制写入、保留和读出
长短期记忆网络(long short-term memory, LSTM)由 Sepp Hochreiter 与 Jürgen Schmidhuber 提出,目标是让误差信号沿较稳定的状态通道传播。(Hochreiter 和 Schmidhuber 1997年)
LSTM 把状态分成两部分:
- 细胞状态 \(c_t\):承担较直接的长期信息传递;
- 隐藏状态 \(h_t\):暴露给当前输出和下一层的表示。
它还使用取值在 0 到 1 之间的门:
- 遗忘门决定旧细胞状态保留多少;
- 输入门决定新候选内容写入多少;
- 输出门决定当前细胞内容暴露多少。
门不是人工规则。它们由当前输入与上一隐藏状态经过可训练线性层和 sigmoid 得到,因此不同句子、不同位置可以做不同选择。
技术深潜:LSTM 的状态更新
把当前输入与上一隐藏状态拼接后,三个门和候选内容可写为:
\[ \begin{aligned} f_t&=\sigma(W_f[x_t,h_{t-1}]+b_f) \\ i_t&=\sigma(W_i[x_t,h_{t-1}]+b_i) \\ o_t&=\sigma(W_o[x_t,h_{t-1}]+b_o) \\ g_t&=\tanh(W_g[x_t,h_{t-1}]+b_g) \end{aligned} \]
细胞状态和隐藏状态更新为:
\[ \begin{aligned} c_t&=f_t\odot c_{t-1}+i_t\odot g_t \\ h_t&=o_t\odot\tanh(c_t) \end{aligned} \]
\(\odot\) 表示逐元素乘法。若某些维度的 \(f_t\) 接近 1、\(i_t\) 接近 0,旧信息可以近似原样通过;需要改写时,输入门再开放相应维度。后来加入的遗忘门使网络能主动清除不再相关的内容。(Gers 等 2000年)
这条加法状态通路比基础 RNN 每步完全重写状态更利于梯度传播,但门仍会饱和,状态容量仍有限,训练仍可能失败。LSTM 是改善长期依赖的结构,不是无限记忆装置。
GRU 是另一种门控折中
门控循环单元(gated recurrent unit, GRU)在 RNN Encoder-Decoder 中使用更新门与重置门,把细胞状态和隐藏状态合并,参数通常少于 LSTM。(Cho 等 2014年)
LSTM 与 GRU 没有对所有任务都固定的胜负。数据规模、序列长度、隐藏维度、优化器和计算预算都会改变结果。重要的是它们共享同一思想:让网络学习何时保留旧状态、何时写入新信息,而不是每一步无条件覆盖全部状态。
序列任务不只有一种输入输出形式
循环结构可以用于多种任务:
| 形式 | 例子 | 常见输出位置 |
|---|---|---|
| 多对一 | 句子情感分类 | 最后状态或汇总状态 |
| 一对多 | 根据类别生成序列 | 每个生成位置 |
| 多对多等长 | 逐帧标注、词性标注 | 每个输入位置 |
| 多对多变长 | 翻译、摘要 | 独立解码序列 |
若整个输入在预测前都已知,可以使用双向 RNN:一条从左向右读,一条从右向左读,再合并两个方向的状态。它适合序列标注和编码,却不能直接用于只能看到过去的实时预测或因果生成,因为反向状态偷看了未来。
不同样本长度不一时,批训练常补齐短序列,并用掩码告诉损失哪些位置只是填充。忘记掩码会让模型把填充符当成真实数据,也会污染最终状态。
从一个序列变成另一个序列
机器翻译的输入和输出长度通常不同。序列到序列模型(sequence-to-sequence, seq2seq)把任务拆成两个循环网络:
- 编码器依次读取源语言,得到最终状态;
- 解码器以该状态为起点,逐词生成目标语言;
- 每一步输出一个词的概率,再把选中的词送入下一步;
- 生成结束符后停止。
Sutskever、Vinyals 与 Le 展示了多层 LSTM 在端到端机器翻译中的能力。(Sutskever 等 2014年) Cho 等人的 RNN Encoder-Decoder 也把变长序列编码到连续表示,再从中生成目标序列。(Cho 等 2014年)
这类系统减少了人工设计短语表和重排序规则的需求。编码器与解码器可以共同训练:目标译文每个位置的交叉熵损失,通过解码器和编码状态传回源语言表示。
训练时有正确前词,生成时只有自己的前词
训练解码器时,常把真实目标序列的前一个词作为下一步输入,这叫作教师强制(teacher forcing)。它让每个位置都在合理前缀上学习,训练更快、更稳定。
真正生成时没有未来答案。模型必须把自己刚生成的词送回去。如果前一步选错,后续输入就偏离训练时见过的前缀,错误可能继续积累。这种训练与推理条件差异常称为暴露偏差。
自回归序列的联合概率可以分解为:
\[ P(y_{1:T}\mid x)= \prod_{t=1}^{T}P(y_t\mid y_{<t},x) \]
每个位置都依赖此前已生成内容。贪心解码每次选最高概率词,可能早早进入较差路径;束搜索保留多个候选前缀,但增加计算,也不保证更符合事实或人的偏好。
一个固定向量为什么成为瓶颈
早期 seq2seq 常要求编码器把整句压进最终状态。短句尚可,长句中的名字、数字、从句和对应关系会争夺同一固定向量。
翻译目标词时,解码器真正需要的信息还会变化。生成一个人名时应关注源句的人名,生成动词时应关注动作,处理代词时又要寻找对应实体。让最终状态一次性保存所有细节,再要求解码器自行取出,负担很重。
Bahdanau、Cho 与 Bengio 让解码器在每一步对全部编码器状态计算权重,再汇总当前最相关的信息。(Bahdanau 等 2015年) 这就是下一阶段的注意力机制:模型不再只依赖一个固定摘要,而是动态决定“此刻该看源序列哪里”。
注意力没有让序列问题消失。位置、掩码、对齐、长序列计算和生成错误仍需处理。但它改变了信息访问方式,也为 Transformer 完全移除循环依赖提供了基础。
RNN 会在哪些地方失败
循环模型常见限制包括:
- 长距离信息被覆盖,梯度无法稳定传回;
- 每一步依赖上一步,训练难以像卷积那样在所有位置完全并行;
- 隐藏状态容量固定,长序列压缩困难;
- 自回归生成中的早期错误会改变后续输入;
- 模型可能学习数据中的表面顺序,而不是因果关系;
- 训练序列与部署序列长度或节奏变化时性能下降。
在实时传感器、流式语音和小型设备上,RNN 的顺序状态更新仍有价值。Transformer 并没有让所有循环模型失去意义。结构选择取决于延迟、内存、数据量、序列长度和是否允许看到未来。
P06 将在后续实践阶段训练小型字符预测模型,对比基础 RNN 与 LSTM 的损失、长期记忆和生成错误,并作为注意力模型的对照。实践继续后置,不影响主线阅读。
下一章把“编码器把输入压成表示、解码器从表示恢复输出”的结构用于更广泛的生成问题。模型不再只把一句话变成另一句话,而要学习一类图片、声音或数据的整体规律,并从潜在表示中产生新样本。
本章小结
- 序列的意义依赖顺序和历史,静态词向量或词袋无法独立表达这些关系。
- RNN 用共享参数把当前输入与上一隐藏状态组合,使此前信息能够影响当前输出。
- 隐藏状态是任务相关的压缩摘要,不是对全部历史的无损录像。
- 时间展开让循环网络可以使用反向传播训练;长链上的导数乘积会造成梯度消失或爆炸。
- LSTM 和 GRU 用门控控制保留、写入与读出,改善长期依赖,但不保证无限记忆或稳定训练。
- Seq2seq 用编码器读取输入、解码器自回归生成输出,使变长序列映射可以端到端学习。
- 教师强制让训练更容易,却造成训练前缀与实际生成前缀不一致;生成错误可能逐步积累。
- 固定长度编码向量会成为长序列瓶颈,促使模型在每个解码位置动态读取源状态,由此引出注意力机制。
思考问题
- 为什么双向 RNN 适合句子分类,却不能直接用于只能看见过去的实时续写?
- LSTM 的遗忘门始终接近 1 是否一定更好?它可能带来什么问题?
- 教师强制训练损失很低,但自由生成质量很差时,应优先检查哪些训练与推理差异?
延伸阅读
- Elman 的论文适合理解简单循环网络怎样在序列预测中形成内部结构。(Elman 1990年)
- Bengio、Simard 与 Frasconi 的论文解释了长期依赖为何对梯度学习困难。(Bengio 等 1994年)
- Hochreiter 与 Schmidhuber 的 LSTM 论文技术密度较高,适合结合门控公式阅读。(Hochreiter 和 Schmidhuber 1997年)
- Sutskever、Vinyals 与 Le 以及 Cho 等人的工作展示了神经序列到序列翻译的早期路线。(Sutskever 等 2014年; Cho 等 2014年)
- Bahdanau、Cho 与 Bengio 的论文是从固定向量瓶颈进入注意力机制的直接桥梁。(Bahdanau 等 2015年)