RNN - LSTM - GRU
循环神经网络 (Recurrent Neural Network,RNN) 是一类具有短期记忆能力的神经网络,因而常用于序列建模。本篇先总结 RNN 的基本概念,以及其训练中时常遇到梯度爆炸和梯度消失问题,再引出 RNN 的两个主流变种 —— LSTM 和 GRU。
Vanilla RNN
Vanilla RNN 的主体结构:

上图中 都是向量,公式如下:
其中
式中的两个矩阵 可以合并:
注意到在计算时,每一 time step 中使用的参数 是一样的,也就是说每个步骤的参数都是共享的,这是RNN的重要特点。
和普通的全连接层相比,RNN 除了输入 外,还有输入隐藏层上一节点 ,RNN 每一层的输出就是这两个输入用矩阵 ,和激活函数进行组合的结果。从 式可以看出 和 都是与 全连接的,下图形象展示了各个时间节点 RNN 隐藏层记忆的变化。随着时间流逝,最初的蓝色结点保留地越来越少,这意味着RNN对于长时记忆的困难。

Vanishing & Exploding Gradient Problems
RNN 对于长时记忆的困难主要来源于梯度爆炸 / 消失问题,下面进行说明。RNN 中 Loss 的计算图示例:

总的 Loss 是每个 time step 的加和 :
由 backpropagation through time (BPTT) 算法,参数的梯度为:
其中 包含一系列 矩阵,
由于 RNN 中每个 time step 都是用相同的 ,所以由 式可得:
由于 为方阵,对其进行特征值分解:
由于上式是连乘 次 :
连乘的次数多了之后,则若最大的特征值 ,会产生梯度爆炸; ,则会产生梯度消失 。不论哪种情况,都会导致模型难以学到有用的模式。
下左图显示一个 time step 中 tanh 函数的计算结果,右图显示整个神经网络的计算结果,可以清楚地看到哪个区域最容易产生梯度爆炸/消失问题。

梯度爆炸的解决办法:
(1) Truncated Backpropagation through time:每次只 BP 固定的 time step 数,类似于 mini-batch SGD。缺点是丧失了长距离记忆的能力。

(2) Clipping Gradients: 当梯度超过一定的 threshold 后,就进行 element-wise 的裁剪,该方法的缺点是又引入了一个新的参数 threshold。同时该方法也可视为一种基于瞬时梯度大小来自适应 learning rate 的方法:

梯度消失的解决办法
(1) 使用 LSTM、GRU等升级版 RNN,使用各种 gates 控制信息的流通。
(2) 在这篇论文 ( https://arxiv.org/pdf/1602.06662.pdf ) 中提出将权重矩阵 初始化为正交矩阵。正交矩阵有如下性质:, 正交矩阵的特征值的绝对值为 。证明如下, 对矩阵 有:
由于 为特征向量, ,所以 ,这样连乘之后 不会出现越来越小的情况。
(3) 反转输入序列。像在机器翻译中使用 seq2seq 模型,若使用正常序列输入,则输入序列的第一个词和输出序列的第一个词相距较远,难以学到长期依赖。将输入序列反向后,输入序列的第一个词就会和输出序列的第一个词非常接近,二者的相互关系也就比较容易学习了。这样模型可以先学前几个词的短期依赖,再学后面词的长期依赖关系。见下图正常输入顺序是 ,反向是 ,则 与第一个输出词 接近:

LSTM
虽然 Vanilla RNN 理论上可以建立长时间间隔状态之间的依赖关系,但由于梯度爆炸或消失问题,实际上只能学到短期依赖关系。为了学到长期依赖关系,LSTM 中引入了门控机制来控制信息的累计速度,包括有选择地加入新的信息,并有选择地遗忘之前累计的信息,整个 LSTM 单元结构如下图所示:

式 的输入都一样,因而可以合并:
为时刻 t 的候选状态, 控制 中有多少新信息需要保存, 控制上一时刻的内部状态 需要遗忘多少信息, 控制当前时刻的内部状态 有多少信息需要输出给外部状态 。
下表显示 forget gate 和 input gate 的关系,可以看出 forget gate 其实更应该被称为 “remember gate”, 因为其开启时之前的记忆信息 才会被保留,关闭时则会遗忘所有:
forget gate | input gate | result |
---|---|---|
1 | 0 | 保留上一时刻的状态 |
1 | 1 | 保留上一时刻 和添加新信息 |
0 | 1 | 清空历史信息,引入新信息 |
0 | 0 | 清空所有新旧信息 |
对比 Vanilla RNN,可以发现在时刻 t,Vanilla RNN 通过 来保存和传递信息,上文已分析了如果时间间隔较大容易产生梯度消失的问题。 LSTM 则通过记忆单元 来传递信息,通过 和 的调控, 可以在 t 时刻捕捉到某个关键信息,并有能力将此关键信息保存一定的时间间隔。
原始的 LSTM 中是没有 forget gate 的,即:
这样 恒为 。但是这样 会不断增大,容易饱和从而降低模型性能。后来引入了 forget gate ,则梯度变为 ,事实上连乘多个 同样会导致梯度消失,但是 LSTM 的一个初始化技巧就是将 forget gate 的 bias 置为正数(例如 1 或者 5,如 tensorflow 中的默认值就是 ),这样一来模型刚开始训练时 forget gate 的值都接近 1,不会发生梯度消失 (反之若 forget gate 的初始值过小则意味着前一时刻的大部分信息都丢失了,这样很难捕捉到长距离依赖关系)。 随着训练过程的进行,forget gate 就不再恒为 1 了。不过,一个训好的模型里各个 gate 值往往不是在 [0, 1] 这个区间里,而是要么 0 要么 1,很少有类似 0.5 这样的中间值,其实相当于一个二元的开关。假如在某个序列里,forget gate 全是 1,那么梯度不会消失;某一个 forget gate 是 0,模型选择遗忘上一时刻的信息。
LSTM 的一种变体增加 peephole 连接,这样三个 gate 不仅依赖于 和 ,也依赖于记忆单元 :
注意 input gate 和 forget gate 连接的是 ,而 output gate 连接的是 。下图来自 《LSTM: A Search Space Odyssey》,标注了 peephole 连接的样貌。

GRU
相比于 Vanilla RNN (每个 time step 有一个输入 ),从上面的 式可以看出 一个 LSTM 单元有四个输入 (如下图,不考虑 peephole) ,因而参数是 Vanilla RNN 的四倍,带来的结果是训练起来很慢,因而在2014年 Cho 等人提出了 GRU ,对 LSTM 进行了简化,在不影响效果的前提下加快了训练速度。

在式 中 forget gate 和 input gate 是互补关系,因而比较冗余,GRU 将其合并为一个 update gate。同时 GRU 也不引入额外的记忆单元 (LSTM 中的 ) ,而是直接在当前状态 和历史状态 之间建立线性依赖关系。

为时刻 t 的候选状态, 控制 有多少依赖于上一时刻的状态 ,如果 ,则式 与 Vanilla RNN 一致,对于短依赖的 GRU 单元,reset gate 通常会更新频繁。 控制当前的内部状态 中有多少来自于上一时刻的 。如果 ,则会每步都传递同样的信息,和当前输入 无关。
另一方面看, 与 LSTM 中的 角色有些类似,因为将上面的 式代入 式可以得到:
最后是 cs224n 中提出的 RNN 训练 tips:

/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异