Heng's Blog

普通前馈神经网络把每个输入独立处理,无法自然地表达“前面发生了什么”。但序列中的当前元素通常依赖之前的信息:理解一个词需要参考前文,判断一帧中的动作也需要观察此前的画面。

Recurrent Neural Network(RNN)的核心想法,是在处理当前输入的同时保留一个隐藏状态。这个状态随着序列向前传递,成为网络对“截至当前时刻的信息”的压缩表示。

本文根据 USC CSCI 566 Lecture 7: Recurrent Neural Networks 整理。下文用单下划线表示 vector,用双下划线表示 matrix;scalar 不加下划线。

Vanilla RNN 怎样更新状态#

Vanilla RNN 的典型状态更新是 ht‾=tanh⁡(Wx‾‾xt‾+Wh‾‾ht−1‾+b‾)\underline{h_t}=\tanh(\underline{\underline{W_x}}\underline{x_t}+\underline{\underline{W_h}}\underline{h_{t-1}}+\underline{b})。

其中:

  • xt‾\underline{x_t} 是第 tt 个输入;
  • ht−1‾\underline{h_{t-1}} 是上一个时间步留下的隐藏状态;
  • Wx‾‾\underline{\underline{W_x}} 将当前输入投影到隐藏空间;
  • Wh‾‾\underline{\underline{W_h}} 决定过去的信息怎样影响当前状态;
  • ht‾\underline{h_t} 是结合当前输入与过去信息后得到的新状态。

初始状态 h0‾\underline{h_0} 通常设为零,也可以作为参数学习。如果任务要求每个时间步都产生输出,还可以计算 yt‾=softmax⁡(Wy‾‾ht‾+by‾)\underline{y_t}=\operatorname{softmax}(\underline{\underline{W_y}}\underline{h_t}+\underline{b_y})。

这条公式最重要的地方不是 tanh,而是 ht−1‾→ht‾\underline{h_{t-1}}\rightarrow\underline{h_t} 的递归关系。当前状态会继续参与下一步计算,因此信息可以沿着 h0‾→h1‾→⋯→hT‾\underline{h_0}\rightarrow\underline{h_1}\rightarrow\cdots\rightarrow\underline{h_T} 向后传递。

时间展开与参数共享#

RNN 可以沿时间展开成一条计算链:

h₀ ──[x₁]──▶ h₁ ──[x₂]──▶ h₂ ──[x₃]──▶ h₃ ── … ──▶ hₜ
       f_W          f_W          f_W                 f_W
text

图中的每个 fWf_W 都是同一个函数,并且复用同一组 Wx‾‾\underline{\underline{W_x}}、Wh‾‾\underline{\underline{W_h}} 和 b‾\underline{b}。因此,序列变长并不会增加模型参数量;网络只是在不同时间步重复执行同一种状态更新。

参数共享也赋予了 RNN 一个重要假设:同一种局部规律可以出现在序列的不同位置。某个词出现在第 33 个位置还是第 3030 个位置,网络都使用同一套规则处理它。

一种结构,多种序列任务#

改变在哪些时间步提供输入和读取输出,同一个 RNN 就可以表达多种任务:

形式输入与输出例子
many-to-one整个序列产生一个输出文本分类、视频动作识别
many-to-many每个输入位置产生一个输出序列标注、逐帧分类
one-to-many一个输入逐步产生一个序列图像描述、序列生成
sequence-to-sequence先编码输入序列,再生成输出序列机器翻译

在生成任务中,上一步生成的 yt−1‾\underline{y_{t-1}} 还可以作为下一步输入。于是模型不仅传递隐藏状态,也会根据自己此前生成的结果继续向后生成。

RNN 怎样训练#

训练时,先沿整个序列完成前向传播,得到各时间步的损失;再把展开后的 RNN 看成一个很深、但各层共享参数的网络,沿时间反向传播梯度。这个过程叫 Backpropagation Through Time(BPTT,随时间反向传播)。

如果序列很长,保存完整计算图的成本会很高。Truncated BPTT 会继续把隐藏状态传向未来,但只在一个较短的时间窗口内反向传播。它降低了计算和显存成本,代价是窗口之外的依赖无法直接获得梯度。

为什么长期依赖很难学习#

从较晚的时间步向较早的时间步反向传播时,梯度会反复经过 Wh‾‾\underline{\underline{W_h}} 和 tanh 的导数:

  • 如果这些连乘的尺度持续小于 11,梯度会随时间距离迅速缩小,形成梯度消失(vanishing gradient);
  • 如果尺度持续大于 11,梯度会快速增大,形成梯度爆炸(exploding gradient)。

梯度裁剪(gradient clipping)可以在梯度范数过大时将它缩小,因此主要用于控制梯度爆炸;它不能从根本上恢复已经消失的梯度。LSTM 和 GRU 后来通过更明确的信息通道与门控,改善了长期依赖的学习。

从 RNN 到 attention#

RNN 用一个固定大小的 ht‾\underline{h_t} 持续压缩历史信息,同时要求时间步按顺序计算。序列很长时,早期信息既可能在状态中逐渐丢失,也很难通过梯度获得训练信号。

Attention 改变了这个信息路径:模型不必只依赖最后一个隐藏状态,而是可以在需要时直接读取不同时间步的表示。Additive Attention 的评分函数看起来很像 Vanilla RNN,因为两者都使用“两个线性投影相加,再经过 tanh”的计算模板;区别是 attention 的分数不会作为下一个位置的隐藏状态继续递归传递。

所以,理解 RNN 最重要的是抓住三点:隐藏状态把过去带到现在,同一组参数在所有时间步共享,而这种递归链既让网络能够处理可变长度序列,也带来了顺序计算和长期梯度传播的问题。

Recurrent Neural Networks: 用隐藏状态处理序列
https://heng-blog.pages.dev/blog/rnn
Author 杨苏恒
Published at August 18, 2026