循环神经网络 (RNN):处理序列数据的逻辑
📂 所属阶段:第二阶段 — 深度学习与序列模型(进阶篇)
🔗 相关章节:PyTorch 基础 · 长短时记忆网络 LSTM/GRU
1. 为什么需要 RNN?
1.1 传统神经网络的局限
传统的全连接网络(Dense)和卷积网络(CNN),本质上都是独立处理每个输入样本的。无论我们把文本里的词怎么排列,只要词频差不多,模型输出的特征向量就可能非常相似。
文本序列:“这部电影 难看” vs “这部电影 好看”。
传统网络只会统计「电影」「这」「部」这些词,甚至可能把两个句子的特征向量做得几乎一样,完全没法区分「好看」和「难看」的语义差异!
这种“只看词频、不看顺序”的方式,在面对自然语言、语音信号、股票价格这类带有明显先后顺序的序列数据时,显得力不从心。
1.2 RNN 的核心突破:加入「记忆」
循环神经网络(Recurrent Neural Network,简称 RNN)的名字里就藏着它的秘密——循环复用同一个神经元单元,同时引入了一个叫隐藏状态(Hidden State)的东西,用来保存“之前看到的信息”。简单说,RNN 在读取序列时,每一步都会把当前输入与上一步留下的“记忆”融合,生成新的记忆和输出。
为了更直观理解,可以把 RNN 沿着时间步(序列中的每个元素)展开:
每个时间步的处理逻辑都一样:
- 接收当前输入
x_t和上一步的隐藏状态h_{t-1} - 经过相同的 RNN 单元,生成新的隐藏状态
h_t和当前输出y_t
RNN 单元内部的工作很简洁:先将当前输入和前一步隐藏状态分别乘以各自的权重矩阵,再加一个偏置,最后通过双曲正切(tanh)激活函数将数值压缩到 (-1, 1) 之间,得到新的隐藏状态。整个过程完全一样,无论序列多长都复用同一套参数,所以 RNN 天然能够处理任意长度的序列。
2. RNN 的致命问题
虽然 RNN 解决了“独立处理”的问题,但它有两个天然的硬伤,导致长序列任务里基本不再使用原生 RNN。
2.1 梯度消失与爆炸
训练神经网络的核心是反向传播:从输出层逐步往回计算每个参数对最终损失的影响,然后更新参数。RNN 的特殊之处在于,参数在时间步上是被重复使用的,因此反向传播时,梯度会沿着时间步连乘多次(连乘的次数等于序列长度)。
- 梯度消失:如果连乘的梯度值普遍小于 1,那么经过很多次相乘后,梯度会越来越小,趋近于 0。这意味着模型几乎无法学习到很久以前的信息对当前的影响。
- 梯度爆炸:如果梯度值大于 1,经过多次相乘后,梯度会指数级增长,趋近于无穷大,导致训练时的损失值(Loss)直接变成
NaN,训练崩溃。
“我出生在中国……(中间夹杂了 1000 个完全不相关的字)……我会说___”
原生 RNN 大概率会把第一个分句的「中国」忘得一干二净,很难填出「中文」!
梯度爆炸可以通过梯度裁剪(Gradient Clipping)来缓解:设定一个阈值,当梯度超过该阈值时就强行按比例缩小。但梯度消失问题对于原生 RNN 几乎是无法修复的,这也直接催生了 LSTM 和 GRU 这些改进模型。
2.2 其他不足
除了梯度问题,原生 RNN 还有一些小瑕疵:
- 串行计算,效率受限:每一个时间步的计算都必须等待上一步完成,没法像 CNN 那样大规模并行,训练速度慢。
- 对初始状态敏感:初始隐藏状态(通常是全零向量)的选择会影响早期时间步的学习效果。
3. PyTorch RNN 快速实现
虽然原生 RNN 不常用,但它是理解 LSTM、GRU 的基础。下面我们用 PyTorch 实现简单的文本分类器,体会一下 RNN 的使用方式。
3.1 单向 RNN 文本分类器
下面的代码构建了一个单向 RNN,用于情感二分类。输入是句子的词索引(token id)序列,输出是正/负类的 logits。
3.2 双向 RNN 文本分类器
有时候理解一个词不仅要看前面说了什么,还要结合后面的上下文。比如“我今天很__,因为没吃到火锅”,横线处的情绪显然和后面的“没吃到火锅”负相关。
双向 RNN(Bidirectional RNN)同时训练两个方向的结构:
- 前向 RNN:从左到右处理序列;
- 后向 RNN:从右到左处理序列。
最后把两个方向最后时刻的隐藏状态拼接起来,作为对整个序列的表示。
4. 小结与速查
4.1 核心知识点回顾
- RNN 的作用:为序列数据引入“记忆”,解决传统网络“只看词频、忽略位置”的问题。
- 展开图理解:按时间步展开后,每个时间步复用同一个单元,输入为当前词和上一步记忆。
- 致命问题:
- 梯度消失,导致捕获不到长距离依赖;
- 梯度爆炸,导致训练不稳定。
- 改进方向:LSTM / GRU 专门解决梯度消失;梯度裁剪专门解决梯度爆炸。
4.2 PyTorch RNN 速查
原生 RNN 在长序列任务(长文本分类、机器翻译等)中表现很差,实际项目中请直接使用 LSTM 或 GRU。下一篇文章我们就来深入解析 LSTM。
🔗 扩展阅读

