NLP学习笔记08:循环神经网络(RNN)——从基础 RNN 到 LSTM 与 GRU

NLP学习笔记08:循环神经网络(RNN)------从基础 RNN 到 LSTM 与 GRU

作者:Ye Shun

日期:2026-04-17

一、前言

循环神经网络(Recurrent Neural Network,RNN)是一类专门处理序列数据的神经网络。所谓序列数据,指的是数据之间存在先后顺序、上下文依赖或时间关系的数据,例如:

  • 文本序列
  • 语音信号
  • 时间序列
  • DNA 序列

与传统的前馈神经网络不同,RNN 具有一种"记忆"能力。它在处理当前输入时,不只看当前时刻的数据,还会参考之前时刻的隐藏状态(Hidden State),从而建模序列中的上下文依赖关系。

如果把普通神经网络理解为"只看当前这一帧"的模型,那么 RNN 更像是"边读边记"的模型。比如人在读句子时,理解当前词的含义,往往依赖前面已经读过的内容。RNN 模仿的正是这种过程。

这篇笔记将围绕以下几个问题展开:

  1. RNN 的核心思想是什么
  2. 标准 RNN 是如何工作的
  3. 为什么会出现梯度消失和长期依赖问题
  4. LSTM 和 GRU 是如何改进 RNN 的
  5. 双向 RNN 在什么场景下更有优势

二、RNN 的核心思想

RNN 的关键在于"循环连接(Recurrent Connection)"。它在每一个时间步都会把上一步的隐藏状态传递到下一步,因此当前输出不仅依赖当前输入,也依赖之前的上下文。

1. 传统神经网络与 RNN 的区别

传统前馈神经网络

前馈神经网络通常假设每个输入样本彼此独立。例如图像分类中,模型处理某张图片时,不需要依赖上一张图片。

RNN

RNN 不同,它假设数据之间存在顺序依赖。每一步计算时,都会把上一步的隐藏状态带到当前步骤,形成一种"记忆链条"。

因此,RNN 在每个时间步的输入可以理解为:

  • 当前输入 x_t
  • 上一步隐藏状态 h_{t-1}

当前状态又会生成新的隐藏状态 h_t,供后续时间步继续使用。

2. 一个直观类比

可以把 RNN 理解成一个在阅读时会不断做笔记的人。

  • 读到当前词时,他不仅看这个词本身
  • 还会结合前面记下的内容一起理解

例如:

他打开了......

读到"打开了"时,人脑会自动利用前文推测下一个词可能是"门""书""电脑"等。这种对上下文的依赖,就是 RNN 想建模的东西。

三、RNN 的工作机制

标准 RNN 在每个时间步 t 的计算通常包括三步:

  1. 接收当前输入 x_t
  2. 接收前一时刻隐藏状态 h_{t-1}
  3. 计算当前隐藏状态 h_t,并进一步得到输出 y_t

其常见形式为:

text 复制代码
h_t = f(W_hh * h_{t-1} + W_xh * x_t + b_h)
y_t = g(W_hy * h_t + b_y)

其中:

  • x_t:当前输入
  • h_{t-1}:上一时刻隐藏状态
  • h_t:当前隐藏状态
  • y_t:当前输出
  • W_xhW_hhW_hy:权重矩阵
  • fg:激活函数,如 tanhsigmoidsoftmax

1. 一个简单的 RNN 单元示例

下面是一个简化版 RNN 单元实现:

python 复制代码
import numpy as np


class SimpleRNN:
    def __init__(self, input_size, hidden_size):
        self.Wx = np.random.randn(hidden_size, input_size)
        self.Wh = np.random.randn(hidden_size, hidden_size)
        self.b = np.zeros((hidden_size, 1))

    def forward(self, x, h_prev):
        h_next = np.tanh(np.dot(self.Wx, x) + np.dot(self.Wh, h_prev) + self.b)
        return h_next

这个例子已经体现了 RNN 的核心:当前隐藏状态由"当前输入 + 过去状态"共同决定。

2. 参数共享

RNN 的一个重要特点是参数共享。也就是说,同一组权重会在所有时间步重复使用。

这有两个好处:

  • 可以处理任意长度的序列
  • 参数量不会随着序列长度增加而线性膨胀

也正因为这样,RNN 很自然地适合处理文本、语音等变长输入。

四、RNN 的优缺点

1. 优点

RNN 的核心优势在于它天然适合序列建模:

  • 能处理变长序列
  • 能利用历史上下文
  • 参数共享,模型结构统一
  • 理论上可以记住较长的历史信息

在早期 NLP、语音识别和时间序列建模中,RNN 曾经非常重要。

2. 缺点

不过,标准 RNN 也有明显局限:

  • 难以捕捉长期依赖
  • 存在梯度消失和梯度爆炸问题
  • 时间步之间强依赖,难以并行计算
  • 序列较长时训练效率较低

其中最关键的问题,就是长期依赖和梯度传播问题。

五、RNN 的长期依赖问题

虽然标准 RNN 理论上可以记住任意长度的信息,但在实际训练中,它往往只能较好利用较近的上下文,而难以记住很早之前的信息。

1. 梯度消失

在反向传播时,梯度需要跨越很多时间步不断连乘。如果权重和激活函数的组合使得梯度逐步变小,那么越往前的时间步,梯度就越接近 0,模型就很难学到长期依赖。

2. 梯度爆炸

与之相反,如果梯度在反向传播过程中不断放大,就会出现梯度爆炸,训练不稳定甚至发散。

3. 为什么这很重要

例如在句子:

我昨天在书店买的那本关于深度学习的书非常有意思。

如果模型要理解"书非常有意思"中的"书"究竟指的是什么,就需要跨较长距离记住前文信息。标准 RNN 在这种场景下往往表现不佳。

这正是 LSTM 和 GRU 被提出的背景。

六、长短期记忆网络(LSTM)

LSTM(Long Short-Term Memory)是 RNN 的一个重要改进版本,专门设计来缓解长期依赖问题。

它最核心的思想是:不要让隐藏状态在每一步都被完全重写,而是引入更稳定的记忆单元,并用门控机制控制信息流动。

1. LSTM 的核心结构

LSTM 中最关键的组成部分包括:

组件 作用
遗忘门 决定保留多少旧记忆
输入门 决定接收多少新信息
输出门 决定输出多少内部状态
记忆单元 存储长期状态

2. 为什么 LSTM 更强

标准 RNN 每一步都在直接更新隐藏状态,而 LSTM 引入了专门的记忆单元 c_t,让信息可以沿着相对更稳定的路径传播。

这样做的好处是:

  • 可以有选择地记住长期信息
  • 可以有选择地遗忘无关内容
  • 梯度传播路径更稳定

3. LSTM 单元的简化实现

python 复制代码
import numpy as np


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


class LSTMCell:
    def __init__(self, input_size, hidden_size):
        self.hidden_size = hidden_size
        self.W = np.random.randn(4 * hidden_size, input_size + hidden_size)
        self.b = np.zeros((4 * hidden_size, 1))

    def forward(self, x, h_prev, c_prev):
        combined = np.vstack((h_prev, x))
        gates = np.dot(self.W, combined) + self.b

        hs = self.hidden_size
        f_gate = sigmoid(gates[:hs])
        i_gate = sigmoid(gates[hs:2 * hs])
        o_gate = sigmoid(gates[2 * hs:3 * hs])
        c_candidate = np.tanh(gates[3 * hs:])

        c_next = f_gate * c_prev + i_gate * c_candidate
        h_next = o_gate * np.tanh(c_next)

        return h_next, c_next

这个实现虽然简化,但已经体现了 LSTM 的核心思想:通过门控机制控制记忆。

七、门控循环单元(GRU)

GRU(Gated Recurrent Unit)可以看作 LSTM 的简化版本。它在保留门控思想的同时,减少了参数数量,使模型更紧凑。

1. GRU 的核心结构

GRU 主要包含:

组件 作用
更新门 决定保留多少旧信息
重置门 决定如何组合旧信息与当前输入
候选隐藏状态 生成新的候选状态

GRU 没有像 LSTM 那样显式区分隐藏状态和记忆单元,而是把结构做得更简洁。

2. GRU 单元示例

python 复制代码
import numpy as np


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


class GRUCell:
    def __init__(self, input_size, hidden_size):
        self.hidden_size = hidden_size
        self.Wz = np.random.randn(hidden_size, input_size + hidden_size)
        self.Wr = np.random.randn(hidden_size, input_size + hidden_size)
        self.Wh = np.random.randn(hidden_size, input_size + hidden_size)
        self.bz = np.zeros((hidden_size, 1))
        self.br = np.zeros((hidden_size, 1))
        self.bh = np.zeros((hidden_size, 1))

    def forward(self, x, h_prev):
        combined = np.vstack((h_prev, x))
        z = sigmoid(np.dot(self.Wz, combined) + self.bz)
        r = sigmoid(np.dot(self.Wr, combined) + self.br)

        candidate_input = np.vstack((r * h_prev, x))
        h_candidate = np.tanh(np.dot(self.Wh, candidate_input) + self.bh)

        h_next = (1 - z) * h_prev + z * h_candidate
        return h_next

3. GRU vs LSTM

两者可以做一个直观比较:

特性 GRU LSTM
参数数量 较少 较多
训练速度 通常更快 通常更慢
显式记忆单元
门控数量 2 个 3 个
适用场景 小数据或轻量任务常见 长依赖和复杂任务更常见

一般来说:

  • 如果更看重模型简洁和训练速度,可以优先试 GRU
  • 如果更看重长期记忆能力,可以优先试 LSTM

八、双向 RNN(Bi-RNN)

标准 RNN 只能利用"过去到现在"的上下文,但很多任务中,理解当前词时不仅依赖前文,也依赖后文。

例如在命名实体识别中,一个词是不是组织机构,有时需要结合前后词共同判断。

1. 双向 RNN 的思想

双向 RNN 包含两个方向的循环层:

  • 前向层:从左到右处理序列
  • 反向层:从右到左处理序列

最后把两个方向的输出拼接或融合起来,得到更完整的上下文表示。

2. 为什么双向 RNN 有用

它的优势在于:

  • 同时利用过去和未来信息
  • 对词性标注、命名实体识别、语音识别等任务更有效

3. 双向 LSTM 示例

现代应用中,双向结构通常配合 LSTM 或 GRU 使用。例如:

python 复制代码
from tensorflow.keras.layers import Bidirectional, LSTM

model.add(Bidirectional(LSTM(64)))

这表示构建一个双向 LSTM 层,让模型同时读取正向和反向上下文。

九、RNN、LSTM、GRU 的应用场景

虽然近年来 Transformer 已经成为主流,但 RNN 系列模型仍然非常值得理解,因为它们在序列建模史上占据核心位置,也仍然适用于一些场景。

1. 自然语言处理

典型应用包括:

  • 语言建模
  • 文本分类
  • 词性标注
  • 命名实体识别
  • 机器翻译(早期 Seq2Seq)

2. 语音识别

语音信号天然是时间序列,RNN 和 LSTM 在早期语音识别系统中非常常见。

3. 时间序列预测

例如:

  • 股票价格预测
  • 电力负载预测
  • 传感器数据预测

4. 生物信息学

例如 DNA、RNA、蛋白质序列分析等,都会涉及序列建模。

十、RNN 的局限与 Transformer 的兴起

虽然 RNN 在序列建模中非常经典,但它有两个结构性弱点:

1. 难以并行

因为每个时间步都依赖前一个时间步,所以训练和推理往往是串行的。

2. 长距离依赖仍然难

即便 LSTM 和 GRU 已经缓解了问题,但当序列特别长时,依然不如后来的自注意力模型更灵活。

也正因为这样,Transformer 后来逐渐取代 RNN 成为 NLP 中更主流的架构。不过,理解 RNN 仍然非常重要,因为:

  • 它是现代序列模型的重要起点
  • 它帮助我们理解"记忆"和"时序依赖"到底是什么
  • LSTM / GRU 的门控思想对后续很多模型设计都有启发

十一、学习建议

如果你刚开始学神经网络中的序列模型,我建议按下面顺序来:

  1. 先理解标准 RNN 的状态传递机制
  2. 再理解为什么会有梯度消失和长期依赖问题
  3. 接着学习 LSTM 的遗忘门、输入门、输出门
  4. 再学习 GRU 如何做结构简化
  5. 最后再去理解双向结构和 Transformer

这样会更容易把"序列建模"的演化逻辑串起来。

十二、总结

循环神经网络(RNN)是处理序列数据的重要模型,它通过循环连接把历史信息带入当前计算,从而具备建模上下文的能力。

从方法演进来看:

  • 标准 RNN 奠定了序列建模基础
  • LSTM 通过门控机制缓解长期依赖问题
  • GRU 在保留性能的同时简化了结构
  • 双向 RNN 则进一步利用了前后文信息

虽然 Transformer 在很多 NLP 场景中已经成为主流,但 RNN、LSTM 和 GRU 依然是理解深度学习序列建模不可绕过的基础。

相关推荐
教育知暖意2 小时前
广州编程机构选择指南:入门到进阶实用参考
学习
Bluescape2 小时前
韦恩图在线绘图工具
rnn
MegaDataFlowers2 小时前
基于Gitee帮助中心学习Gitee Go
学习·gitee
鱼鳞_2 小时前
Java学习笔记_Day33(高级流)
java·笔记·学习
say_fall2 小时前
深入理解AVL树:平衡调整机制与性能优化实战
开发语言·数据结构·c++·学习
Fanfanaas2 小时前
Linux 进程篇 (四)
linux·运维·服务器·开发语言·c++·学习
2501_944934732 小时前
咨询行业怎样提升自己?
学习
Engineer邓祥浩2 小时前
JVM学习笔记(11) 第四部分 程序编译与代码优化 第10章 前端编译与优化
jvm·笔记·学习
Sss_Ass3 小时前
跟着老师不迷路系列---跟着李述铜老师学习汇编语言之内核寄存器简介
学习·学习方法·汇编语言·李述铜