RNN循环神经网络(一)

循环神经网络(RNN)是深度学习领域处理序列数据的基础架构,广泛应用于自然语言处理、语音识别、时间序列预测等任务。本文从零开始,深入剖析RNN的核心机制、参数共享原理以及前向传播过程,详细讲解梯度消失问题的数学本质,并系统介绍LSTM如何通过门控机制有效解决长距离依赖。此外,还将探讨双向RNN、深度RNN、GRU等高级架构变体的设计思想与实际应用场景,帮助读者全面掌握RNN及其演进历程。

1. 为什么需要序列模型?

1.1 传统神经网络的局限

传统的神经网络(如多层感知机 MLP)假设输入和输出是相互独立的,但在处理文本、语音或时间序列数据时,这个假设并不成立。序列数据具有两个关键特征,使得传统神经网络难以处理:

1.2 序列模型的核心特征

顺序依赖性(Sequential Dependency)

输入元素的顺序对最终的语义至关重要。例如:

  • “我不喜欢吃饭” vs “我喜欢不吃饭” - 虽然包含相同的字,但由于顺序不同,意义完全不同
  • “张三打了李四” vs “李四打了张三” - 主语和宾语的位置决定了动作的方向

可变长度(Variable Length)

输入和输出的长度往往不固定且相互独立:

  • 机器翻译:英文 “How are you?” (3个词) → 中文 “你好吗?” (3个字)
  • 情感分析:任意长度的评论 → 单个情感标签(正面/负面)
  • 文本生成:一个主题词 → 完整的段落或文章

1.3 典型应用场景

应用领域输入输出示例
语音识别音频波形序列文本序列语音 → “今天天气不错”
情感分析文本序列分类标签“这部电影太棒了” → 正面
机器翻译源语言序列目标语言序列“Hello” → “你好”
股票预测历史价格序列未来价格[p1, p2, …, pn] → pn+1
命名实体识别文本序列标注序列“马云在杭州” → [人名, 介词, 地名]

2. 文本数据的数值化表示

2.1 为什么需要数值化?

计算机只能处理数字,因此需要将文本转换为数值表示。这个过程称为词向量化(Word Embedding)编码(Encoding)

2.2 字典映射(Dictionary Mapping)

基本原理

建立一个包含所有可能词汇的字典(词汇表),为每个词分配唯一的整数索引。

示例

vocabulary = {
    "hello": 0,
    "world": 1,
    "deep": 2,
    "learning": 3,
    "is": 4,
    "awesome": 5
}

sentence = "deep learning is awesome"
encoded = [2, 3, 4, 5]  # 转换为索引序列

2.3 One-Hot编码

原理说明

将每个词表示为一个高维稀疏向量。假设词汇表大小为 $V$ ,则每个词是一个长度为 $V$ 的向量,只在对应位置为1,其余位置为0。

数学表示

对于词汇表大小 $V=10000$,如果 “learning” 的索引是 3:

learning = [0, 0, 0, 1, 0, 0, ..., 0]  # 第3位为1,其余为0
            ↑______________________↑
          位置0                  位置9999

完整流程

  1. 分词:将句子拆分为单词
  2. 建立词典:统计所有唯一词汇,建立映射关系
  3. 编码:将每个词转换为One-Hot向量
  4. 序列输入:将向量序列 $[x^{1}, x^{2}, …, x^{T}]$ 送入模型

局限性

  • 维度过高(通常10,000-100,000维)
  • 无法表达词与词之间的语义相似性
  • 存储和计算效率低

改进方案:使用词嵌入(Word Embedding),如Word2Vec、GloVe,将词映射到低维稠密向量空间(通常100-300维)。

3. RNN的核心机制

3.1 什么是"记忆"?

RNN的本质是带记忆的神经网络。与传统神经网络不同,RNN通过隐藏状态(Hidden State) $h^{(t)}$ 将历史信息传递到未来时间步。

3.2 前向传播过程

在时间步 $t$,RNN接收两个输入:

  1. 当前输入 $x^{(t)}$:当前时刻的数据(如当前单词)
  2. 历史状态 $h^{(t-1)}$:上一时刻的隐藏状态(包含历史信息)

计算公式

$$ \begin{align} h^{(t)} &= \tanh(W_{hh} h^{(t-1)} + W_{xh} x^{(t)} + b_h) \\ y^{(t)} &= \text{softmax}(W_{hy} h^{(t)} + b_y) \end{align} $$

其中:

  • $W_{hh}$:隐藏层到隐藏层的权重矩阵(记忆传递)
  • $W_{xh}$:输入层到隐藏层的权重矩阵(当前输入)
  • $W_{hy}$:隐藏层到输出层的权重矩阵(生成输出)
  • $b_h, b_y$:偏置项
  • $\tanh$:激活函数,将值压缩到 $[-1, 1]$ 范围
  • $\text{softmax}$:用于多分类任务的输出层

3.3 参数共享机制

关键特性:所有时间步共享相同的权重参数 $W_{hh}, W_{xh}, W_{hy}$

优势

  1. 参数数量固定:与序列长度无关,避免参数爆炸
  2. 泛化能力强:学到的模式可应用于不同位置
  3. 计算效率高:减少训练参数

示例

处理句子 “I love deep learning” 时:

# 伪代码
h0 = zeros(hidden_size)  # 初始隐藏状态

h1 = tanh(Whh @ h0 + Wxh @ x1 + bh)  # 处理 "I"
h2 = tanh(Whh @ h1 + Wxh @ x2 + bh)  # 处理 "love"(使用相同的Whh和Wxh)
h3 = tanh(Whh @ h2 + Wxh @ x3 + bh)  # 处理 "deep"
h4 = tanh(Whh @ h3 + Wxh @ x4 + bh)  # 处理 "learning"

3.4 损失函数与训练

单步损失

对于时间步 $t$,如果真实标签是 $\hat{y}^{(t)}$,预测值是 $y^{(t)}$,则:

$$ \mathcal{L}^{(t)} = -\sum_{k} \hat{y}_k^{(t)} \log(y_k^{(t)}) $$ (交叉熵损失,用于分类任务)

总体损失

整个序列的损失是所有时间步损失的累加: $$ \mathcal{L} = \sum_{t=1}^{T} \mathcal{L}^{(t)} $$

反向传播

RNN使用时间反向传播算法(BPTT, Backpropagation Through Time)

  1. 从最后时间步开始,向前计算梯度
  2. 梯度沿着时间轴反向流动
  3. 累积所有时间步的梯度更新参数

4. RNN的四种架构

根据输入序列长度 $T_x$ 和输出序列长度 $T_y$ 的关系,RNN有四种典型架构:

4.1 多对多(同步,$T_x = T_y$)

结构特点

每个输入时间步对应一个输出时间步。

应用场景

  • 命名实体识别(NER):识别句子中的人名、地名
输入: "张三在北京工作"
输出: [人名, 介词, 地名, 动词]
  • 词性标注(POS Tagging)
  • 视频帧标注:为每一帧打标签

网络结构图

x1 → [RNN] → y1
x2 → [RNN] → y2
x3 → [RNN] → y3

4.2 多对一($T_x > 1, T_y = 1$)

结构特点

整个输入序列产生一个输出。

应用场景

  • 情感分析
输入: "这部电影的剧情很精彩,演员表现也很出色"
输出: 正面(1个标签)
  • 文本分类:新闻分类、垃圾邮件检测
  • 序列异常检测

网络结构图

x1 → [RNN]
x2 → [RNN]
x3 → [RNN] → y(只在最后输出)

4.3 一对多($T_x = 1, T_y > 1$)

结构特点

一个输入产生多个输出。

应用场景

  • 音乐生成:给定一个音符,生成完整旋律
  • 图像描述生成:一张图片 → 描述句子
输入: [猫的图片]
输出: "一只橘猫坐在窗台上"(多个词)
  • 故事/诗歌创作:给定主题词,生成完整内容

网络结构图

x → [RNN] → y1
    [RNN] → y2
    [RNN] → y3

4.4 多对多(异步,$T_x \neq T_y$)

结构特点

输入和输出序列长度不同,通常先编码再解码。

应用场景

  • 机器翻译
输入(英文3词): "How are you"
输出(中文5字): "你好吗朋友"
  • 对话系统:问题 → 回答
  • 文本摘要:长文本 → 简短摘要

网络结构(Encoder-Decoder)

编码器阶段:
x1 → [RNN]
x2 → [RNN]
x3 → [RNN] → 上下文向量 c

解码器阶段:
c → [RNN] → y1
    [RNN] → y2
    [RNN] → y3
    [RNN] → y4
    [RNN] → y5

5. RNN的致命缺陷:梯度消失问题

5.1 问题描述

普通RNN在处理长序列时存在严重的 梯度消失(Vanishing Gradient) 问题:

现象:前面时间步的信息在传递到后面时逐渐衰减,导致长距离依赖关系无法学习。

数学原因

在反向传播时,梯度需要连乘多个 $W_{hh}$: $$ \frac{\partial \mathcal{L}}{\partial h^{(1)}} = \frac{\partial \mathcal{L}}{\partial h^{(T)}} \cdot \prod_{t=2}^{T} \frac{\partial h^{(t)}}{\partial h^{(t-1)}} $$

如果 $\frac{\partial h^{(t)}}{\partial h^{(t-1)}} < 1$(通常由于激活函数导数 $< 1$),连乘后梯度趋近于0。

5.2 实际案例

语法一致性问题

考虑以下两个句子:

句子A: The cat, which ate the fish yesterday, was hungry.
句子B: The cats, which ate the fish yesterday, were hungry.

模型需要:

  1. 记住开头的主语是单数(cat)还是复数(cats)
  2. 在很多词之后,正确预测动词形式(was/were)

问题:如果中间插入的内容很长(如20-30个词),普通RNN会"忘记"主语的单复数信息,导致预测错误。

5.3 梯度爆炸问题

与梯度消失相反,如果 $\frac{\partial h^{(t)}}{\partial h^{(t-1)}} > 1$,连乘后梯度会指数级增长,导致梯度爆炸(Exploding Gradient)

解决方案:梯度裁剪(Gradient Clipping)

if gradient.norm() > threshold:
    gradient = gradient * (threshold / gradient.norm())

6. LSTM:长短期记忆网络

6.1 核心思想

LSTM(Long Short-Term Memory)通过引入记忆细胞(Memory Cell) $c^{(t)}$ 和门控机制(Gate Mechanism),解决了长距离依赖问题。

关键对比

特性普通RNNLSTM
状态变量隐藏状态 $h$隐藏状态 $h$ + 记忆细胞 $c$
信息传递直接传递,容易衰减通过"传送带"机制,信息保持能力强
长距离依赖难以学习(梯度消失)可以有效学习

6.2 LSTM的三个门

LSTM通过三个门来精确控制信息的流动:

1. 遗忘门(Forget Gate)

功能:决定从记忆细胞中丢弃哪些信息

$$ f^{(t)} = \sigma(W_f \cdot [h^{(t-1)}, x^{(t)}] + b_f) $$

其中 $\sigma$ 是sigmoid函数,输出范围 $[0, 1]$:

  • $f^{(t)} = 0$:完全遗忘
  • $f^{(t)} = 1$:完全保留

示例:当遇到新主语时,遗忘旧主语的单复数信息。

2. 输入门(Input Gate)

功能:决定将哪些新信息存储到记忆细胞

$$ \begin{align} i^{(t)} &= \sigma(W_i \cdot [h^{(t-1)}, x^{(t)}] + b_i) \\ \tilde{c}^{(t)} &= \tanh(W_c \cdot [h^{(t-1)}, x^{(t)}] + b_c) \end{align} $$

  • $i^{(t)}$:输入门控信号(0-1之间)
  • $\tilde{c}^{(t)}$:候选值(新信息内容)

示例:记录新主语是单数还是复数。

3. 输出门(Output Gate)

功能:决定从记忆细胞中输出什么信息

$$ \begin{align} o^{(t)} &= \sigma(W_o \cdot [h^{(t-1)}, x^{(t)}] + b_o) \\ h^{(t)} &= o^{(t)} \cdot \tanh(c^{(t)}) \end{align} $$

示例:根据记忆的主语信息,决定输出 “was” 还是 “were”。

6.3 记忆细胞更新

完整更新过程 $$ c^{(t)} = f^{(t)} \odot c^{(t-1)} + i^{(t)} \odot \tilde{c}^{(t)} $$

其中 $\odot$ 表示逐元素乘法(element-wise multiplication)。

直观理解

新记忆 = (遗忘门 × 旧记忆) + (输入门 × 新信息)

传送带比喻

记忆细胞 $c$ 就像一条传送带:

  • 信息可以沿着传送带不受阻碍地传递(避免梯度消失)
  • 门控机制可以精确地向传送带添加或移除信息
  • 这种机制使得信息可以跨越很多时间步保持不变

6.4 LSTM的优势

  1. 缓解梯度消失:记忆细胞的加法更新(而非乘法)保持梯度流动
  2. 选择性记忆:通过门控机制学习什么信息重要、什么可以遗忘
  3. 长距离依赖:可以有效处理100+时间步的长序列

实验对比

任务类型普通RNN准确率LSTM准确率
短序列(<10步)85%87%
中序列(10-30步)65%82%
长序列(>50步)40%78%

7. 高级架构变体

7.1 双向RNN(Bidirectional RNN, BRNN)

动机

单向RNN只能利用历史信息,无法看到未来。但在某些任务中,上下文信息都很重要。

典型案例

判断 “The courses are taught by Flare Zhao” 中 “Flare” 是否为人名:

  • 仅看前文 “The courses are taught by”:无法确定
  • 看到后文 “Zhao”(典型姓氏):可以确认 “Flare Zhao” 是人名

架构设计

同时训练两个RNN:

  1. 前向RNN(Forward):从左到右读取序列
  2. 后向RNN(Backward):从右到左读取序列

应用场景

  • 命名实体识别(NER)
  • 词性标注(POS Tagging)
  • 填空题(完形填空)

局限性

  • 计算成本翻倍
  • 不适用于实时任务(需要等待完整序列)

7.2 深度RNN(Deep RNN, DRNN)

动机

增加网络深度以学习更复杂的表示。

架构设计

垂直堆叠多个RNN层:

第3层: h1(3) → h2(3) → h3(3) → ... → output
         ↑       ↑       ↑
第2层: h1(2) → h2(2) → h3(2) → ...
         ↑       ↑       ↑
第1层: h1(1) → h2(1) → h3(1) → ...
         ↑       ↑       ↑
输入:   x1      x2      x3

更新公式

对于第 $l$ 层: $$ h^{(t)[l]} = g(W^{[l]} h^{(t)[l-1]} + U^{[l]} h^{(t-1)[l]} + b^{[l]}) $$

实践建议

  • 通常2-4层即可(过深容易过拟合)
  • 可以在最后添加全连接层进行最终预测
  • 使用残差连接(Residual Connection)缓解梯度消失

7.3 GRU(门控循环单元)

简化版的LSTM

GRU(Gated Recurrent Unit)将LSTM的三个门简化为两个:

  1. 更新门(Update Gate):结合了遗忘门和输入门
  2. 重置门(Reset Gate):控制前一时刻信息的使用程度

优势

  • 参数更少(训练更快)
  • 性能与LSTM相当

何时使用GRU

  • 数据量较小
  • 计算资源有限
  • 需要快速实验

8. 实践建议与最佳实践

8.1 选择合适的架构

任务类型推荐架构理由
情感分类多对一 LSTM/GRU需要整体语义理解
机器翻译多对多(Encoder-Decoder)+ 注意力长度不一致且需要对齐
命名实体识别双向LSTM + CRF需要上下文信息
文本生成一对多 LSTM + Attention需要长程依赖

8.2 超参数调优

关键超参数

  • 隐藏层大小:128-512(视任务复杂度)
  • 层数:1-3层(过深易过拟合)
  • 学习率:0.001-0.01(使用Adam优化器)
  • Dropout:0.2-0.5(防止过拟合)
  • 梯度裁剪阈值:5-10

8.3 常见问题与解决方案

问题1:训练速度慢

  • 使用GRU代替LSTM
  • 减少序列长度(截断或采样)
  • 使用更小的batch size
  • 考虑使用Transformer架构(并行计算)

问题2:过拟合

  • 增加Dropout比例
  • 使用L2正则化
  • 数据增强(同义词替换、回译等)
  • 早停(Early Stopping)

问题3:梯度消失仍然存在

  • 使用残差连接(Residual Connection)
  • 尝试Layer Normalization
  • 减少序列长度
  • 使用注意力机制(Attention)

9. 总结与展望

9.1 核心要点回顾

  1. RNN的本质:通过隐藏状态传递信息,处理序列数据
  2. 参数共享:所有时间步使用相同权重,提高泛化能力
  3. 梯度消失:普通RNN的致命缺陷,限制长距离依赖学习
  4. LSTM/GRU:通过门控机制有效缓解梯度消失
  5. 架构选择:根据输入输出关系选择合适的RNN类型

9.2 RNN的局限性

尽管LSTM/GRU改进了普通RNN,但仍存在问题:

  • 顺序计算:无法并行化,训练速度慢
  • 长序列仍有瓶颈:100步以上效果下降
  • 上下文向量瓶颈:Encoder-Decoder结构中,整个输入需压缩到固定维度

9.3 现代替代方案

Transformer(2017年提出)已在大多数NLP任务中取代RNN:

  • 并行计算:通过自注意力机制实现
  • 长距离依赖:直接建模任意位置间的关系
  • 可扩展性:支持超大规模预训练(GPT、BERT等)

何时仍使用RNN

  • 实时流式任务(语音识别)
  • 资源受限场景(边缘设备)
  • 时间序列预测(股票、传感器数据)

10. 其他参考资料

经典论文

  1. Hochreiter & Schmidhuber (1997): “Long Short-Term Memory” - LSTM原始论文
  2. Cho et al. (2014): “Learning Phrase Representations using RNN Encoder-Decoder” - GRU提出
  3. Vaswani et al. (2017): “Attention is All You Need” - Transformer架构

实践教程

开源项目