循环神经网络 RNN 与 LSTM/GRU
本教程共 60 篇 · 第 22 篇 · 更新于 2026-08-17 · 约 3 分钟阅读
本节目标:搞懂循环神经网络(RNN)处理序列的思路,理解 LSTM 和 GRU 的门控机制解决了什么问题,并会用
nn.RNN、nn.LSTM、nn.GRU跑通最小示例。
前面学的全连接网络和 CNN,输入都是一次性看完的:一张图进去,一个结果出来。但如果输入是一段话、一首歌、一条股价曲线呢?顺序很重要,前面的信息会影响后面的理解。
序列数据的难题
读这句话:「我今天出门忘带伞,结果下午被淋成落汤鸡。」读到「落汤鸡」,你得记得前面「忘带伞」这个原因。
这类数据叫序列数据(Sequential Data):文本、语音、股票走势、传感器读数都是。它们有两个特点:顺序有意义,长度不固定。
全连接网络处理序列很尴尬:它没有记忆,每个输入独立处理,前一个词的信息传不到后一个词。把整句话打包成一个大向量?句子长短不一,没法打包。
RNN:边读边记笔记
循环神经网络(Recurrent Neural Network,RNN)的核心是引入隐状态(Hidden State)。你可以把它想成一张草稿纸:每读一个词,就在纸上记一点摘要;读下一个词时,带着这张纸一起读。
用公式说:当前隐状态 h_t 由两部分决定,当前输入 x_t 和上一步的隐状态 h_{t-1},再经过激活函数。
h_t = tanh(W_ih · x_t + W_hh · h_{t-1} + b)
这里有个关键点:每个时间步用的是同一套权重。所以 RNN 的参数量和序列长度无关,序列再长也是这些参数。
RNN 的两个毛病
理想很丰满,现实有两点麻烦。
- 梯度消失:序列一长,反向传播时梯度一路连乘,越乘越小,远处的信息学不到。
- 梯度爆炸:反过来,权重矩阵的放大效应让梯度越滚越大,训练直接崩掉。
梯度爆炸可以用梯度裁剪(Gradient Clipping)压住,梯度消失则催生了 LSTM 和 GRU。
LSTM:带传送带的记忆
长短期记忆网络(Long Short-Term Memory,LSTM)给 RNN 加了一条「传送带」:细胞状态(Cell State)。信息在传送带上流动,只做少量加减,不容易丢。
传送带上装三个门,输出的都是 0 到 1 之间的数,控制信息进出:
- 遗忘门(Forget Gate):决定旧记忆丢多少。
- 输入门(Input Gate):决定新信息记多少。
- 输出门(Output Gate):决定此刻输出什么。
所以 LSTM 有两个状态在时间步之间传递:细胞状态 c 管长期记忆,隐状态 h 管当前输出。这就像整理笔记:先划掉过时的,再写上新的,最后决定今天讲什么。
GRU:更轻量的版本
门控循环单元(Gated Recurrent Unit,GRU)是 LSTM 的简化版。它把遗忘门和输入门合并成一个更新门,取消独立的细胞状态,只剩两个门。
结构简化,参数自然少:大约只有 LSTM 的 75%,训练更快。小数据量下,表现经常和 LSTM 差不多。
PyTorch 里怎么用
nn.RNN、nn.LSTM、nn.GRU 三个模块接口几乎一样,核心参数就三个:input_size(每步输入的维度)、hidden_size(隐状态维度)、num_layers(层数)。
输入形状是最容易搞错的地方。推荐全程用 batch_first=True,形状就是 (batch, seq_len, input_size),和直觉一致。
import torch
import torch.nn as nn
lstm = nn.LSTM(input_size=32, hidden_size=64, batch_first=True)
x = torch.randn(4, 10, 32) # batch=4,序列长 10,每步 32 维
output, (h_n, c_n) = lstm(x)
print(output.shape) # torch.Size([4, 10, 64]) 每个时间步的输出
print(h_n.shape) # torch.Size([1, 4, 64]) 最后一步的隐状态
print(c_n.shape) # torch.Size([1, 4, 64]) 最后一步的细胞状态
注意返回值:output 是所有时间步的输出,h_n 是最后一个时间步的隐状态,LSTM 还多一个 c_n。做分类时通常取最后一步的输出:
last = output[:, -1, :] # (4, 64)
fc = nn.Linear(64, 5) # 接一个分类头
logits = fc(last)
print(logits.shape) # torch.Size([4, 5])
Tip训练 RNN 建议加梯度裁剪,防止梯度爆炸:
nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
LSTM 还是 GRU?
| 场景 | 建议 |
|---|---|
| 数据少、资源有限 | GRU,参数少、训练快 |
| 序列长、依赖远 | LSTM,细胞状态擅长长期记忆 |
| 快速做实验 | 先 GRU 跑通,再换 LSTM 对比 |
| 数据量大、追求极致 | 考虑下一章的 Transformer |
本章记住一句话:RNN 靠隐状态记笔记,LSTM 用三个门管住一条传送带,GRU 是它的轻量版。序列建模的大门,就从这三个模块打开。