image.png

摘要:在 Transformer 成为主流之前,RNN、LSTM、GRU 是处理文本、语音、时间序列的核心模型。它们的基本思想是按顺序读取输入,用隐藏状态把过去的信息传到未来。RNN 简洁但难以学习长距离依赖,容易梯度消失或爆炸;LSTM 通过遗忘门、输入门、输出门和 cell state 管理记忆,让长期信息更容易保留;GRU 则用更简洁的门控结构做类似事情。本文从序列建模问题讲起,解释 RNN 公式、BPTT、长距离依赖、LSTM 门控和 NumPy 单步实现,最后说明为什么 Transformer 取代了 RNN/LSTM 成为大模型基础。

前置知识:链式法则,反向传播,词嵌入
阅读时间:约 65 分钟
代码环境:Python 3.10+,numpy >= 1.24,torch >= 2.0

入门导读:先抓住主线

语言是有顺序的。模型看到一句话时,不能只知道有哪些词,还要知道它们按什么顺序出现。

RNN 的做法很直接:

读第 1 个 token -> 更新隐藏状态
读第 2 个 token -> 基于上一个隐藏状态继续更新
读第 3 个 token -> 继续更新

隐藏状态 h_t 像一份不断更新的记忆,里面压缩了到当前位置为止的历史信息。

LSTM 的改进是:不要让所有信息都挤在一个普通隐藏状态里,而是用门控机制决定:

哪些旧信息要忘掉
哪些新信息要写入
当前要输出多少记忆

读完先达到这个程度就够了

  • 能解释序列建模为什么需要记忆;
  • 能看懂 RNN 的递推公式;
  • 能理解 BPTT 为什么会导致长距离梯度问题;
  • 能说清 LSTM 遗忘门、输入门、输出门分别做什么;
  • 能读懂一个简化 LSTM 单步代码;
  • 能理解 Transformer 为什么在大模型时代取代 RNN/LSTM。

带着这 3 个问题读

  1. RNN 的隐藏状态为什么既是优势也是瓶颈?
  2. LSTM 的 cell state 为什么有助于长期记忆?
  3. Transformer 取代 RNN/LSTM 的关键原因是效果,还是并行和规模化?

一、什么是序列建模

序列建模处理的是有顺序的数据。
image.png

常见序列包括:

  • 文本:一句话、一个段落、一段代码;
  • 语音:按时间排列的声学特征;
  • 时间序列:股票价格、传感器数据;
  • 用户行为:点击、购买、浏览记录;
  • 生物序列:DNA、蛋白质序列。

序列的特点是:当前位置的含义依赖前后文。

例如:

我昨天买了一台苹果电脑,它很轻。

“它”指什么,需要记住前面出现过“苹果电脑”。

早期神经网络处理序列,一个自然想法是:按顺序读,用一个状态保存历史。这就是 RNN 的基本思想。


二、RNN:把过去压进隐藏状态

RNN 全称 Recurrent Neural Network,循环神经网络。

在时间步 t,输入是当前 token 向量 x_t,上一步隐藏状态是 h_{t-1},输出当前隐藏状态 h_t

公式:

h t = t a n h ( W x x t + W h h t − 1 + b ) h_t = tanh(W_x x_t + W_h h_{t-1} + b) ht=tanh(Wxxt+Whht1+b)

其中:

  • x_t 是当前输入;
  • h_{t-1} 是过去信息;
  • h_t 是更新后的记忆;
  • W_xW_hb 是可训练参数。

用代码写一个 RNN 单步:

import numpy as np

input_dim = 4
hidden_dim = 6

x_t = np.random.randn(input_dim)
h_prev = np.zeros(hidden_dim)

W_x = np.random.randn(input_dim, hidden_dim) * 0.1
W_h = np.random.randn(hidden_dim, hidden_dim) * 0.1
b = np.zeros(hidden_dim)

h_t = np.tanh(x_t @ W_x + h_prev @ W_h + b)
print(h_t.shape)

处理完整序列时,就是循环调用这个单步:

seq_len = 5
xs = np.random.randn(seq_len, input_dim)
h = np.zeros(hidden_dim)

states = []
for t in range(seq_len):
    h = np.tanh(xs[t] @ W_x + h @ W_h + b)
    states.append(h)

print(len(states), states[-1].shape)

最后一个隐藏状态可以用于分类,每个时间步隐藏状态可以用于序列标注或语言建模。


三、RNN 如何做语言模型

如果用 RNN 做语言模型,可以在每个时间步根据 h_t 预测下一个 token。

输入:我 -> 预测 喜欢
输入:喜欢 -> 预测 机器
输入:机器 -> 预测 学习

公式:

o t = h t W o + b o o_t = h_t W_o + b_o ot=htWo+bo

p t = s o f t m a x ( o t ) p_t = softmax(o_t) pt=softmax(ot)

训练目标是让真实下一个 token 的概率更高。

这和后来的 GPT 目标有相似之处:都是从左到右预测下一个 token。区别在于,RNN 用隐藏状态压缩历史,Transformer 用 self-attention 直接访问上下文。


四、BPTT:沿时间展开反向传播

RNN 参数在每个时间步共享。训练时需要把整个序列展开,然后反向传播。

这叫 Backpropagation Through Time,简称 BPTT。

展开后可以想象成:

h0 -> h1 -> h2 -> h3 -> h4 -> loss

image.png

虽然每一步用的是同一组参数,但反向传播要沿时间链条传回去。

问题来了:如果序列很长,梯度要穿过很多时间步。

梯度连续相乘后,可能出现:

  • 梯度消失:越往前越小,早期 token 学不到;
  • 梯度爆炸:越往前越大,训练不稳定。

这就是 RNN 处理长距离依赖困难的重要原因。


五、RNN 的长距离依赖问题

看一个句子:

我在巴黎住了很多年,虽然后来搬去了伦敦,但我仍然能说流利的法语。

要预测“法语”,模型需要记住很早之前的“巴黎”。
image.png

RNN 必须把“巴黎”信息压进隐藏状态,然后一步步传到后面。中间经过很多 token,信息可能被覆盖或衰减。

这就是隐藏状态的瓶颈:

所有历史信息 -> 一个固定维度 h_t

无论前文多长,RNN 都要压进同一个向量。长文本、复杂依赖、多实体关系都会让这个压缩变得困难。

LSTM 的目标就是缓解这个问题。


六、LSTM:用门控管理记忆

LSTM 全称 Long Short-Term Memory。

它在普通隐藏状态 h_t 之外,引入了 cell state c_t。你可以把 c_t 理解成一条更稳定的长期记忆通道。

LSTM 有三个主要门:

  • 遗忘门:决定旧记忆保留多少;
  • 输入门:决定新信息写入多少;
  • 输出门:决定当前暴露多少记忆给隐藏状态。

整体直觉:

c_{t-1} -> 遗忘一部分 -> 加入新候选记忆 -> c_t
c_t -> 输出门控制 -> h_t

相比普通 RNN,LSTM 不需要每一步都完全重写记忆,而是可以让重要信息沿 cell state 传得更远。


七、遗忘门:哪些旧信息该丢掉

遗忘门公式:

f t = σ ( W f [ h t − 1 , x t ] + b f ) f_t = \sigma(W_f [h_{t-1}, x_t] + b_f) ft=σ(Wf[ht1,xt]+bf)

f_t 的每个元素在 0 到 1 之间。

  • 接近 1:对应记忆保留;
  • 接近 0:对应记忆忘掉。

旧 cell state 会乘以遗忘门:

f t ⊙ c t − 1 f_t \odot c_{t-1} ftct1

这里 ⊙ \odot 表示逐元素相乘。

遗忘门让模型可以选择性丢掉不再重要的信息。例如对话换了主题,某些旧上下文就可以降低权重。


八、输入门:哪些新信息该写入

输入门控制新信息写入多少。

输入门:

i t = σ ( W i [ h t − 1 , x t ] + b i ) i_t = \sigma(W_i [h_{t-1}, x_t] + b_i) it=σ(Wi[ht1,xt]+bi)

候选记忆:

c ~ t = t a n h ( W c [ h t − 1 , x t ] + b c ) \tilde{c}_t = tanh(W_c [h_{t-1}, x_t] + b_c) c~t=tanh(Wc[ht1,xt]+bc)

写入部分:

i t ⊙ c ~ t i_t \odot \tilde{c}_t itc~t

然后更新 cell state:

c t = f t ⊙ c t − 1 + i t ⊙ c ~ t c_t = f_t \odot c_{t-1} + i_t \odot \tilde{c}_t ct=ftct1+itc~t

这条公式是 LSTM 的核心。旧记忆不是被完全覆盖,而是和新记忆按门控比例组合。


九、输出门:当前暴露多少记忆

输出门决定 cell state 里有多少信息输出成 hidden state。

公式:

o t = σ ( W o [ h t − 1 , x t ] + b o ) o_t = \sigma(W_o [h_{t-1}, x_t] + b_o) ot=σ(Wo[ht1,xt]+bo)

h t = o t ⊙ t a n h ( c t ) h_t = o_t \odot tanh(c_t) ht=ottanh(ct)

c_t 是内部长期记忆,h_t 是当前时间步对外提供的表示。

这让 LSTM 可以保留某些长期信息,但不一定每一步都全部暴露出来。


十、用 NumPy 写一个简化 LSTM 单步

下面实现一个 LSTM 单步,帮助你把公式和代码对上。

import numpy as np


def sigmoid(x):
    return 1 / (1 + np.exp(-x))

input_dim = 4
hidden_dim = 6

x_t = np.random.randn(input_dim)
h_prev = np.zeros(hidden_dim)
c_prev = np.zeros(hidden_dim)

concat_dim = input_dim + hidden_dim
# 注意:下面所有 W_* 权重矩阵都是 (concat_dim, hidden_dim) 的形状。
# 因为 combined = [h_prev, x_t](先 h 后 x),所以权重矩阵在维度上的排布约定是
#   前 hidden_dim 行对应 h_prev 的权重,后 input_dim 行对应 x_t 的权重。
# 如果你把 combined 改成 [x_t, h_prev],那 W_* 的分块顺序也要跟着换,
# 否则前向计算逻辑上是错的。PyTorch/TF 里对应约定各不相同,读别人代码时留意这一点。
W_f = np.random.randn(concat_dim, hidden_dim) * 0.1
W_i = np.random.randn(concat_dim, hidden_dim) * 0.1
W_c = np.random.randn(concat_dim, hidden_dim) * 0.1
W_o = np.random.randn(concat_dim, hidden_dim) * 0.1

b_f = np.zeros(hidden_dim)
b_i = np.zeros(hidden_dim)
b_c = np.zeros(hidden_dim)
b_o = np.zeros(hidden_dim)

# 拼接顺序:先 h_prev 后 x_t,与上面权重矩阵的约定一致
combined = np.concatenate([h_prev, x_t])

f_t = sigmoid(combined @ W_f + b_f)
i_t = sigmoid(combined @ W_i + b_i)
c_candidate = np.tanh(combined @ W_c + b_c)
o_t = sigmoid(combined @ W_o + b_o)

c_t = f_t * c_prev + i_t * c_candidate
h_t = o_t * np.tanh(c_t)

print("c_t:", c_t.shape)
print("h_t:", h_t.shape)

真实 LSTM 会把四个门的矩阵合并成一个大矩阵,提高计算效率。但逻辑仍然是这几个门。


十一、GRU:更简洁的门控 RNN

GRU 全称 Gated Recurrent Unit,是比 LSTM 更简洁的门控 RNN。

它常用两个门:

  • 更新门 update gate;
  • 重置门 reset gate。

GRU 没有单独的 cell state,而是直接在 hidden state 上做门控。

相比 LSTM:

模型结构优点局限
RNN一个隐藏状态简单长距离依赖差
LSTMcell state + 三个门长期记忆更强参数更多,计算复杂
GRU两个门比 LSTM 简洁表达和任务表现视情况而定

GRU 在一些中小规模序列任务里效果很好,训练也比 LSTM 简单一些。


十二、为什么 Transformer 取代 RNN/LSTM

Transformer 取代 RNN/LSTM,不只是因为模型效果更好,还因为它更适合规模化。

1. 并行性

RNN 必须按时间顺序计算:

h1 -> h2 -> h3 -> h4

后一步依赖前一步,很难在序列维度并行。

Transformer 的 self-attention 可以在同一层同时处理所有 token,更适合 GPU/TPU。

2. 长距离依赖

RNN 要把远处信息一步步传过来。

Transformer 里,任意两个 token 可以通过 attention 直接交互。

3. 训练稳定和规模化

Transformer 结构规整,矩阵乘法占主导,容易堆大模型、做分布式训练和硬件优化。

4. 表示能力

Self-attention 可以动态选择关注哪些 token,不必把所有历史压进一个固定隐藏状态。

当然,Transformer 的代价是 attention 复杂度随序列长度平方增长。这也是后面长上下文技术要解决的问题。


十三、RNN/LSTM 还值得学吗

值得。

原因有三点。

第一,它们是理解序列建模历史的关键。你会更清楚 Transformer 解决了什么问题。

第二,RNN/LSTM 在某些场景仍然有用。比如低延迟流式处理、小型时间序列、边缘设备、数据量较小的序列任务。

第三,LSTM 的门控思想仍然影响现代模型。你在 GLU、SwiGLU、门控残差、MoE router 等结构里,都能看到“选择性通过信息”的影子。

所以学习 RNN/LSTM 不是为了回到旧时代,而是为了理解序列模型的核心矛盾:记忆、依赖、并行和计算成本。


十四、用 PyTorch 快速对比 RNN/LSTM

PyTorch 提供了现成模块。

import torch
import torch.nn as nn

batch, seq_len, input_dim, hidden_dim = 2, 5, 8, 16
x = torch.randn(batch, seq_len, input_dim)

rnn = nn.RNN(input_dim, hidden_dim, batch_first=True)
lstm = nn.LSTM(input_dim, hidden_dim, batch_first=True)

y_rnn, h_rnn = rnn(x)
y_lstm, (h_lstm, c_lstm) = lstm(x)

print("RNN output:", y_rnn.shape)
print("RNN h:", h_rnn.shape)
print("LSTM output:", y_lstm.shape)
print("LSTM h:", h_lstm.shape)
print("LSTM c:", c_lstm.shape)

输出:

output: [batch, seq_len, hidden_dim]
h: [num_layers, batch, hidden_dim]
c: [num_layers, batch, hidden_dim]

LSTM 多了 c,这就是 cell state。


十五、常见误区

误区 1:RNN 已经过时,所以没必要理解。
RNN/LSTM 是理解序列模型、长距离依赖和 Transformer 优势的重要基础。

误区 2:LSTM 完全解决了长距离依赖。
LSTM 缓解了问题,但长序列、大规模并行和复杂全局依赖仍然困难。

误区 3:隐藏状态能完美记住所有历史。
隐藏状态维度固定,历史越长,压缩压力越大。

误区 4:Transformer 只是更大的 RNN。
不是。Transformer 用 self-attention 让 token 直接交互,信息流方式完全不同。

误区 5:LSTM 的门控公式必须死记硬背。
第一遍更重要的是理解门控的作用:忘掉旧信息、写入新信息、控制输出。

误区 6:RNN 不能做语言模型。
RNN 当然可以做语言模型,只是大规模训练和长距离建模上不如 Transformer 路线。


十六、你应该记住的最小心智模型

RNN:

h_t = f(x_t, h_{t-1})
把过去压进隐藏状态

LSTM:

遗忘门:旧记忆保留多少
输入门:新记忆写入多少
输出门:当前暴露多少
cell state:长期记忆通道

Transformer 相比 RNN/LSTM:

不再一步步传递隐藏状态
而是让 token 通过 attention 直接读取上下文
更适合并行和规模化训练

总结

RNN 用隐藏状态按顺序传递历史信息,是早期序列建模的核心方法。它结构简单,但长距离依赖难学,反向传播穿过很多时间步时容易梯度消失或爆炸。LSTM 通过 cell state 和遗忘门、输入门、输出门管理记忆,让重要信息更容易跨时间保存。GRU 用更简洁的门控结构实现类似目标。

Transformer 后来取代 RNN/LSTM 成为大模型主流,是因为 self-attention 支持更直接的长距离交互,更适合并行计算和大规模训练。但 RNN/LSTM 仍然值得学习,因为它们清楚展示了序列建模的核心问题:如何记住过去,如何更新记忆,如何让梯度穿过时间。

第一遍记住一句话:RNN/LSTM 用隐藏状态传递记忆,Transformer 用 attention 直接读取上下文。

大模型视角

理解 RNN/LSTM 后,你会更容易看懂 Transformer 的革命性:它不是简单换了一个模块,而是改变了序列信息流。现代大模型能在海量数据上并行训练,很大程度上来自 Transformer 摆脱了 RNN 的逐时间步依赖。

下一篇

残差连接:解决深层网络梯度消失问题 —— RNN/LSTM 让我们看到梯度穿过长链条的困难。下一篇回到深层网络结构,看残差连接如何让信息和梯度更顺畅地穿过很多层。

更多推荐