1 上节内容补充:Dropout 与归一化
1.1 Dropout 的训练与测试行为
Dropout 是一种常用正则化方法。在训练阶段,每次前向传播都会以某个概率随机把一部分神经元输出置为 \(0\)。这个“丢弃概率”是超参数,常见选择是 \(p=0.5\)。直观上,Dropout 会阻止网络过度依赖某些特定神经元,使不同特征之间形成更稳健的组合,从而降低过拟合风险。
训练时某个激活值 \(a\) 可能被保留,也可能被置零。如果保留概率为 \(q=1-p\),那么训练阶段的期望激活大约是 \(q a\)。但测试阶段所有神经元都始终启用,因此需要让测试阶段的输出尺度与训练阶段的期望输出一致。常见做法有两种:
| 做法 | 训练阶段 | 测试阶段 | 说明 |
|---|---|---|---|
| 标准 Dropout | 随机置零 | 将激活乘以保留概率 \(q\) | 测试时补偿训练时的期望缩小 |
| Inverted Dropout | 随机置零后除以 \(q\) | 不做额外缩放 | 深度学习框架中更常见,测试更方便 |
考试重点:Dropout 的核心不是简单“减少神经元数量”,而是在训练时引入随机子网络;测试时要处理尺度匹配问题,使测试输出接近训练时的期望输出。
1.2 归一化不能完全弥补错误初始化
归一化方法,例如 LayerNorm 或 BatchNorm,通常能改善优化过程,让激活尺度更稳定,也可能减轻错误初始化带来的训练困难。在二维输入、两层 ReLU 网络的玩具例子中,加入归一化可以显著降低误差。
但归一化并不能彻底解决初始化不合适的问题。权重初始化仍然会影响优化路径、梯度传播和可学习表示的形状。特别是在非线性网络中,如果初始化让一部分神经元长期处于无效区域,或让信号在层间过度放大/衰减,归一化只能缓解部分现象,不能保证模型一定达到同样好的解。
一个有用的直觉是:归一化有时在几何上不会改变输入所在的关键区域。例如二维 ReLU 问题中,LayerNorm 不改变输入点所在象限时,它可能保留了分类所需的结构。但这也说明归一化是否合适依赖任务和表示形式,不能机械套用。
2 序列建模问题与 RNN 的基本思想
2.1 为什么需要序列模型
前面讨论的普通神经网络、卷积网络等模型通常默认输入有固定形状,例如一张固定大小的图像,输出一个类别分数。许多真实任务的输入或输出却天然是序列(sequence):
| 任务 | 输入 | 输出 | 类型 |
|---|---|---|---|
| 图像字幕(Image Captioning) | 单张图像 | 单词序列 | one-to-many |
| 动作预测 | 视频帧序列 | 动作类别 | many-to-one |
| 视频字幕 | 视频帧序列 | 文本序列 | many-to-many |
| 帧级视频分类 | 视频帧序列 | 每帧类别 | many-to-many |
| 语言模型 | 字符/词序列前缀 | 下一个字符/词 | many-to-many 或自回归 |
序列建模的关键困难是:序列长度可变,而且当前输出常常依赖过去的信息。例如在视频中判断动作,需要整合多帧的运动线索;在语言中预测下一个词,需要记住前面已经出现的语义和语法结构。
2.2 RNN 的核心思想:内部状态
循环神经网络(Recurrent Neural Network, RNN) 的核心是维护一个随时间更新的隐藏状态(hidden state)。当模型逐步读取序列 \(x_1,x_2,\dots,x_T\) 时,隐藏状态像“记忆”一样把过去的信息压缩到向量中。
最一般地,可以写成:
\[ h_t = f_W(h_{t-1}, x_t) \]
其中:
| 符号 | 含义 |
|---|---|
| \(x_t\) | 第 \(t\) 个时间步的输入向量 |
| \(h_{t-1}\) | 上一个时间步的隐藏状态 |
| \(h_t\) | 当前时间步的新隐藏状态 |
| \(f_W\) | 带参数 \(W\) 的状态更新函数 |
如果每个时间步都需要输出,还可以加一个输出函数:
\[ y_t = g_{W_{hy}}(h_t) \]
最重要的结构特点是:所有时间步共享同一组参数。无论序列长度是 5 还是 500,RNN 都反复应用同一个状态更新规则。这让模型能够处理任意长度输入,并对不同时间位置使用相同的模式识别机制。
3 Vanilla RNN 的数学形式与手工例子
3.1 Vanilla / Elman RNN
最经典的 RNN 常称为 Vanilla RNN 或 Elman RNN。它的隐藏状态通常是一个向量,并使用类似下面的更新公式:
\[ h_t = \tanh(W_{hh}h_{t-1} + W_{xh}x_t + b_h) \]
输出可以写为:
\[ y_t = W_{hy}h_t + b_y \]
若输出表示类别分数,再接 softmax 得到概率:
\[ p_t = \mathrm{softmax}(y_t) \]
这里 \(W_{hh}\) 控制历史状态如何传递,\(W_{xh}\) 控制当前输入如何写入状态,\(W_{hy}\) 控制隐藏状态如何映射到输出。RNN 的表达能力来自“当前输入 + 历史状态”的组合。
3.2 检测连续两个 1 的手工 RNN
一个简单例子是输入二进制序列,输出当前位置是否出现了连续两个 \(1\)。例如:
\[ X = 010111011 \]
对应输出可以是:
\[ Y = 000011001 \]
这个任务是 many-to-many 序列建模:每个输入位置都对应一个输出。要判断当前位置是否应该输出 \(1\),模型至少要知道两件事:
- 当前输入 \(x_t\) 是否为 \(1\)。
- 上一个输入 \(x_{t-1}\) 是否为 \(1\)。
因此隐藏状态可以设计成保存“前一个输入”和“当前输入”等信息。使用 ReLU 作为简化激活函数时,可以手工构造权重,使状态在每一步复制、更新并计算类似
\[ \max(x_t + x_{t-1} - 1, 0) \]
的逻辑。这个式子只有在 \(x_t=1\) 且 \(x_{t-1}=1\) 时才为 \(1\),因此能检测连续的 \(1\)。
这个例子的重点不是手工权重本身,而是说明 RNN 的隐藏状态可以被看成一个可学习的、随时间移动的信息寄存器。实际训练中,这些权重不会手工设定,而是通过反向传播从数据中学习。
4 RNN 的计算图与常见输入输出结构
4.1 展开后的计算图
虽然 RNN 在定义上是“循环”的,但训练时通常会把它沿时间展开成一个很深的计算图:
\[ h_1 = f_W(h_0,x_1),\quad h_2 = f_W(h_1,x_2),\quad \dots,\quad h_T = f_W(h_{T-1},x_T) \]
每个时间步看起来像一层网络,但它们共享同一组权重 \(W\)。这带来两个后果:
- 模型大小不随序列长度增长:长序列只是重复使用同一个模块。
- 梯度会跨很多时间步传播:训练长依赖时容易出现梯度消失或梯度爆炸。
4.2 Many-to-many
Many-to-many 指每个输入时间步都有一个输出,例如帧级视频分类、词性标注、字符级语言建模等。总损失通常是各时间步损失之和:
\[ L = \sum_{t=1}^{T} L_t \]
每个 \(L_t\) 会通过对应的输出 \(y_t\) 反向传播,同时梯度还会沿隐藏状态链条传回更早时间步。
4.3 Many-to-one
Many-to-one 指读完整个序列后只输出一个结果,例如视频动作分类或情感分类。模型通常使用最后一个隐藏状态 \(h_T\) 作为整个序列的表示:
\[ y = g(h_T) \]
这种结构要求 \(h_T\) 能保留前面所有重要信息。如果序列很长,Vanilla RNN 往往难以把早期信息稳定传到最后。
4.4 One-to-many 与自回归生成
One-to-many 指输入一次条件信息,然后生成一个序列。图像字幕就是典型例子:先用 CNN 得到图像特征,再让 RNN 逐词生成描述。
生成时有两种常见输入方式:
| 时间步输入 | 说明 |
|---|---|
| 固定条件向量 | 每一步都接收同一个图像或上下文特征 |
| 上一步生成结果 | 把上一步预测的词作为下一步输入,形成自回归生成 |
在语言生成中,模型通常先输入
<START>,预测第一个词;再把采样出的词输入下一步,直到生成
<END>。
5 通过时间反向传播(BPTT)与截断 BPTT
5.1 Backpropagation Through Time
通过时间反向传播(Backpropagation Through Time, BPTT) 是训练 RNN 的标准方法。做法是先沿整个序列前向计算隐藏状态和损失,再沿展开后的时间计算图反向传播梯度。
对于长度为 \(T\) 的序列,损失可能依赖所有时间步:
\[ L = \sum_{t=1}^{T} L_t \]
由于所有时间步共享参数 \(W\),最终对 \(W\) 的梯度是各时间步贡献的累加:
\[ \frac{\partial L}{\partial W} = \sum_{t=1}^{T} \frac{\partial L_t}{\partial W} \]
BPTT 的优点是理论上能把后面时间步的监督信号传回前面时间步。缺点是长序列会消耗大量内存和计算,而且梯度跨很多步传播时很不稳定。
5.2 Truncated BPTT
截断 BPTT(Truncated BPTT) 把长序列分成较短片段。模型前向时可以把隐藏状态持续传下去,但反向传播只在当前片段内进行,不追溯到无限远的过去。
例如序列长度为 \(1000\),可以每 \(50\) 个时间步做一次反向传播。这样模型仍然能在前向过程中携带长期状态,但梯度只回传 \(50\) 步,计算和显存压力更可控。
| 方法 | 优点 | 局限 |
|---|---|---|
| 完整 BPTT | 梯度覆盖整个序列,理论上最完整 | 长序列代价高,梯度不稳定 |
| 截断 BPTT | 更省内存和计算,实践中常用 | 难以直接学习超过截断窗口的依赖 |
对于 many-to-one 的单输出任务,即使只有最后一步有显式损失,反向传播仍然会沿隐藏状态链条把梯度传到前面的时间步。截断后,早期时间步只能通过较短范围内的梯度更新获得学习信号。
6 字符级语言模型(Character-level Language Model)
6.1 训练目标
字符级语言模型把文本看成字符序列。假设词表是
\[ \mathcal{V}=\{h,e,l,o\} \]
训练序列是
hello。模型在每个时间步读入当前字符,并预测下一个字符:
| 输入 | 目标输出 |
|---|---|
h |
e |
e |
l |
l |
l |
l |
o |
每个字符通常表示成 one-hot 向量。RNN 输出经过 softmax 后得到对下一个字符的概率分布,再用交叉熵损失训练。
6.2 采样过程
测试时,字符级语言模型可以逐字符生成文本:
- 给定起始字符或起始 token。
- RNN 输出下一个字符的概率分布。
- 从该分布中采样或选择概率最高的字符。
- 把生成的字符作为下一步输入。
- 重复直到达到长度限制或生成结束符。
这种生成方式是自回归(autoregressive) 的:每一步的输入依赖上一步模型自己生成的结果。因此早期错误可能会影响后续生成,采样温度、贪心解码、beam search 等策略都会改变生成质量。
6.3 Embedding 层的意义
如果输入是 one-hot 向量 \(x_t\),乘以矩阵 \(W\) 时,本质上只是取出 \(W\) 的某一列:
\[ W x_t = W_{[:,i]} \]
其中 \(i\) 是当前字符的索引。因此,语言模型常在输入和 RNN 隐藏层之间加入嵌入层(embedding layer)。Embedding 层把离散字符或词映射成连续向量,使模型能学习字符/词之间的相似性和统计结构。
考试重点:one-hot 乘矩阵等价于查表;embedding layer 是一个可学习查表操作,而不是复杂的非线性变换。
6.4 min-char-rnn 与生成代码
Karpathy 的 min-char-rnn.py 用很短的 Python
代码实现字符级 RNN,展示了 RNN
语言模型的基本训练和采样过程。训练初期生成文本通常混乱;随着训练进行,模型会先学到局部字符模式,再逐渐学到更长的格式结构。例如在代码数据上训练时,模型可能逐渐学会缩进、括号、关键字和注释风格。
这说明 RNN 的隐藏状态确实能捕捉某些跨时间的模式,但捕捉非常长距离、严格逻辑一致的依赖仍然困难。
7 RNN 的可解释性、优缺点与视觉语言应用
7.1 可解释隐藏单元
对训练好的 RNN 进行分析时,有时可以找到具有明显语义的隐藏单元。例如在代码或文本上训练的字符级 RNN 中,某些单元可能近似追踪:
- 当前是否位于引号内部。
- 当前行长度或换行结构。
- 是否处于
if语句相关区域。 - 代码嵌套深度。
- 注释或字符串范围。
这些现象说明隐藏状态不是完全不可理解的黑箱。某些维度可能自动学到类似有限状态机的功能,用连续值表示离散结构。但这种可解释性并不保证每个单元都有清晰含义,也不保证模型真正理解程序语义。
7.2 RNN 的优点与局限
| 方面 | 优点 | 局限 |
|---|---|---|
| 序列长度 | 可处理任意长度输入,没有固定上下文窗口 | 实际训练中长距离信息难以保留 |
| 参数规模 | 同一组参数跨时间共享,长度增加不增加参数量 | 重复计算导致并行性差 |
| 信息使用 | 理论上第 \(t\) 步能利用很久以前的信息 | 梯度消失使长期依赖难学 |
| 结构归纳偏置 | 对时间顺序有天然建模能力 | 逐步递推慢,难充分利用现代并行硬件 |
RNN 的一个根本张力是:它用固定大小的隐藏状态压缩任意长历史。对短序列或强局部依赖任务,这种机制有效;对复杂长序列,固定状态容易成为信息瓶颈。
7.3 图像字幕
图像字幕任务把 CNN 和 RNN 结合起来:
- CNN 读取图像,提取视觉特征向量 \(v\)。
- RNN 以
<START>作为初始输入,并接收图像特征作为条件。 - 每一步输出一个词的概率分布。
- 采样或选择一个词,再输入下一步。
- 生成
<END>后停止。
一种简单更新形式是:
\[ h_t = \tanh(W_{xh}x_t + W_{hh}h_{t-1} + W_{ih}v + b_h) \]
其中 \(W_{ih}v\) 把图像信息注入语言生成过程。这样模型可以生成类似“a cat sitting on a suitcase on the floor”的句子。
失败案例也很重要。模型可能把物体识别错,或生成符合语言统计但不符合图像内容的描述。例如把手持物体、动物、运动场景等混淆。这反映出早期 CNN-RNN 图像字幕模型的两类困难:视觉识别错误和语言先验过强。
7.4 其他视觉语言任务
RNN 曾广泛用于多种视觉语言任务:
| 任务 | 基本形式 | 关键挑战 |
|---|---|---|
| VQA(Visual Question Answering) | 图像 + 问题文本 \(\rightarrow\) 答案 | 同时理解视觉内容和问题语义 |
| Visual Dialog | 围绕图像进行多轮问答 | 需要记住对话历史 |
| Vision-Language Navigation | 根据语言指令在环境中移动 | 需要把语言、视觉观察和动作序列结合 |
这些任务都体现了 RNN 的通用性:只要数据可以表示成时间序列,RNN 就能作为编码器、解码器或策略生成器使用。后来 Transformer 在许多场景中取代了 RNN,但这些任务的基本输入输出结构仍然是理解多模态模型的重要基础。
8 多层 RNN、梯度流问题与 LSTM
8.1 多层 RNN
多层 RNN(Multilayer RNN) 在时间维度之外增加深度维度。第 \(l\) 层在时间 \(t\) 的隐藏状态可以依赖同层上一时间步和下一层/上一层的当前时间步表示。
直观上,底层可以处理局部、低级的序列模式,高层可以整合更抽象的信息。多层结构提升表达能力,但也加剧训练难度,因为梯度既要跨时间传播,也要跨层传播。
8.2 Vanilla RNN 的梯度消失与爆炸
Vanilla RNN 的隐藏状态反向传播从 \(h_t\) 到 \(h_{t-1}\) 时,会反复乘以与 \(W_{hh}\) 和激活函数导数相关的矩阵。跨多个时间步时,梯度大致包含连乘项:
\[ \frac{\partial h_T}{\partial h_t} = \prod_{k=t+1}^{T} \frac{\partial h_k}{\partial h_{k-1}} \]
如果暂时忽略非线性,反复乘以同一个矩阵 \(W_{hh}\) 的效果与奇异值有关:
| 条件 | 后果 |
|---|---|
| 最大奇异值 \(>1\) | 梯度可能指数级放大,出现梯度爆炸(exploding gradients) |
| 最大奇异值 \(<1\) | 梯度可能指数级衰减,出现梯度消失(vanishing gradients) |
tanh 或 sigmoid 的导数通常小于等于 \(1\),会进一步加剧梯度消失。因此 Vanilla RNN
很难学习长距离依赖。
8.3 Gradient clipping
梯度爆炸相对容易处理,常用方法是梯度裁剪(gradient clipping)。若梯度范数超过阈值,就按比例缩小:
\[ \tilde{g} = g \cdot \frac{\tau}{\lVert g \rVert} \quad \text{if } \lVert g \rVert > \tau \]
其中 \(\tau\) 是裁剪阈值。梯度裁剪不能解决梯度消失,但能防止参数更新突然过大导致训练崩溃。
8.4 LSTM 的动机
长短期记忆网络(Long Short-Term Memory, LSTM) 被设计来缓解 Vanilla RNN 难以保留长期信息的问题。它在普通隐藏状态之外引入细胞状态(cell state),并通过门控机制控制信息写入、遗忘和输出。
LSTM 的关键直觉是:让某些信息沿着 cell state 以更接近“加法路径”的方式传递,减少每一步都必须经过完整矩阵乘法和非线性压缩的压力。
8.5 LSTM 的四个门
LSTM 通常把上一隐藏状态 \(h_{t-1}\) 和当前输入 \(x_t\) 拼接后,一次线性变换得到四组向量:
\[ \begin{aligned} i_t &= \sigma(W_i [h_{t-1}, x_t] + b_i) \\ f_t &= \sigma(W_f [h_{t-1}, x_t] + b_f) \\ o_t &= \sigma(W_o [h_{t-1}, x_t] + b_o) \\ g_t &= \tanh(W_g [h_{t-1}, x_t] + b_g) \end{aligned} \]
| 门 | 名称 | 作用 |
|---|---|---|
| \(i_t\) | 输入门(input gate) | 决定新候选信息写入多少 |
| \(f_t\) | 遗忘门(forget gate) | 决定旧 cell state 保留多少 |
| \(o_t\) | 输出门(output gate) | 决定 cell state 暴露给隐藏状态多少 |
| \(g_t\) | 候选门 / gate gate | 产生准备写入 cell 的候选内容 |
更新公式为:
\[ c_t = f_t \odot c_{t-1} + i_t \odot g_t \]
\[ h_t = o_t \odot \tanh(c_t) \]
其中 \(\odot\) 表示逐元素乘法。若 \(f_t \approx 1\) 且 \(i_t \approx 0\),则
\[ c_t \approx c_{t-1} \]
信息可以在 cell state 中长期保存。
8.6 LSTM 的梯度流
LSTM 从 \(c_t\) 反向传播到 \(c_{t-1}\) 时,主要经过逐元素乘法 \(f_t\),而不是每一步都乘以完整的 recurrent weight matrix。这使梯度更容易沿 cell state 传播。
这并不意味着 LSTM 完全解决梯度消失或爆炸。若遗忘门长期小于 \(1\),梯度仍会衰减;若其他路径不稳定,也可能有训练问题。但相比 Vanilla RNN,LSTM 提供了更容易学习长期依赖的架构路径。
类比 ResNet:LSTM 的 cell state 像一条较直接的信息通道,ResNet 的残差连接也为梯度提供了更顺畅的传播路径。两者都体现了深层模型训练中的一个重要设计原则:为信息和梯度保留较少阻碍的通路。
9 现代 RNN / 状态空间模型与总结
9.1 现代 RNN 与 State Space Models
现代一些序列模型有时被称为状态空间模型(State Space Models, SSMs),例如 RWKV、Mamba 等。它们延续了“维护隐藏状态并随序列更新”的思想,但在参数化、并行训练、选择性信息传递等方面做了改进。
这些模型常被关注的原因包括:
- 上下文长度理论上不受固定窗口限制。
- 推理时可以按序列长度线性扩展。
- 隐藏状态使增量生成更高效。
- 通过新的门控或状态更新机制改善长距离建模。
Transformer 依靠自注意力直接比较所有 token,表达强但标准复杂度约为 \(O(T^2)\)。状态空间类模型试图在长序列上取得更好的计算扩展性,同时保留足够的表达能力。
9.2 本讲总结
RNN 提供了一种处理序列数据的基本范式:用共享参数反复更新隐藏状态,从而把任意长度历史压缩到状态向量中。它能自然表达 many-to-many、many-to-one、one-to-many 等结构,并曾在语言模型、图像字幕、VQA、视觉导航等任务中广泛使用。
Vanilla RNN 的问题也很突出:递归计算难并行,长距离依赖难学,梯度容易消失或爆炸。BPTT 是训练 RNN 的核心方法,截断 BPTT 是处理长序列的实践折中。LSTM 通过 cell state 和门控机制,为长期信息保留和梯度传播提供更好的路径。现代状态空间模型则可以看作对“带状态序列模型”这一思想的延续和重新设计。
10 复习重点与易混点
10.1 必须掌握的概念
- RNN 的隐藏状态:\(h_t=f_W(h_{t-1},x_t)\),当前状态由过去状态和当前输入共同决定。
- 参数共享:同一组参数在所有时间步重复使用,因此模型可处理不同长度序列。
- 展开计算图:RNN 训练时沿时间展开,变成共享权重的深层网络。
- BPTT:先完整前向,再沿时间反向传播;共享参数的梯度来自所有时间步累加。
- 截断 BPTT:隐藏状态继续前传,但反向传播只回传有限步。
- 梯度消失/爆炸:长时间链上的矩阵连乘导致梯度指数衰减或放大。
- LSTM 门控:输入门、遗忘门、输出门、候选门共同控制 cell state 更新。
- Embedding:one-hot 乘矩阵等价于查表,embedding 是可学习的离散符号向量表示。
10.2 RNN 结构类型对比
| 类型 | 输入 | 输出 | 例子 |
|---|---|---|---|
| one-to-one | 单个输入 | 单个输出 | 普通图像分类 |
| one-to-many | 单个输入 | 序列输出 | 图像字幕 |
| many-to-one | 序列输入 | 单个输出 | 视频动作分类、情感分类 |
| many-to-many,同步 | 序列输入 | 每步输出 | 帧级分类、序列标注 |
| many-to-many,编码-解码 | 序列输入 | 序列输出 | 机器翻译、视频字幕 |
10.3 Vanilla RNN 与 LSTM 对比
| 项目 | Vanilla RNN | LSTM |
|---|---|---|
| 状态 | 只有隐藏状态 \(h_t\) | 隐藏状态 \(h_t\) + 细胞状态 \(c_t\) |
| 更新方式 | 矩阵乘法 + 非线性 | 门控写入、遗忘、输出 |
| 长期依赖 | 难学,易梯度消失 | 更容易保存长期信息 |
| 参数量 | 较少 | 较多,通常约有四组门参数 |
| 训练稳定性 | 较差 | 通常更好,但不是绝对稳定 |
10.4 常见误解
- 误解 1:RNN 一定能记住任意长历史。 结构上可以处理任意长度,但固定维度隐藏状态和梯度消失会限制实际长期记忆能力。
- 误解 2:LSTM 完全解决梯度消失。 LSTM 只是提供更容易的梯度通路,并不保证所有任务上都没有梯度问题。
- 误解 3:截断 BPTT 等于丢弃隐藏状态。 截断的是反向传播范围,隐藏状态仍然可以在前向中继续传递。
- 误解 4:Embedding 是为了让输入更复杂。 Embedding 的核心是把离散符号映射到可学习连续向量;对 one-hot 输入来说,它本质上是查表。
- 误解 5:Dropout 测试时可以直接照常使用训练输出尺度。 测试阶段所有神经元启用,必须通过训练时 inverted dropout 或测试时缩放来匹配期望尺度。
10.5 简短自测题
- 为什么 RNN 可以处理可变长度序列?
- 在 many-to-one 任务中,为什么 Vanilla RNN 容易丢失早期输入信息?
- 完整 BPTT 和截断 BPTT 的主要区别是什么?
- one-hot 向量乘以 embedding 矩阵为什么等价于查表?
- 梯度爆炸为什么可以用 gradient clipping 缓解,而梯度消失更难处理?
- LSTM 中 \(f_t=1\)、\(i_t=0\) 时,cell state 会发生什么?
- 图像字幕模型中,CNN 特征如何影响 RNN 的词生成过程?
10.6 一句话抓住本讲
RNN 用共享参数反复更新隐藏状态来处理序列;BPTT 负责训练这种时间展开的计算图;Vanilla RNN 的长距离依赖受梯度流限制,而 LSTM 和现代状态空间模型都可以看作对“如何更好地传递状态与梯度”的改进。