LSTM 与 GRU:门控机制解决长依赖

FreeGuideOnline 最新 2026-06-16

LSTM 与 GRU:门控机制如何解决长时依赖

在自然语言处理、时间序列预测等序列建模任务中,我们常常需要捕捉时间步之间相隔较远的依赖关系。例如,生成一段文字时,可能需要“回忆”前文很远处提到的人物或事件。经典的循环神经网络(RNN)在面对这种“长时依赖”时显得力不从心,而 长短期记忆网络(LSTM)门控循环单元(GRU) 正是为了解决这一痛点而设计的。本教程将带你直观理解它们的工作原理、结构差异,并通过代码快速上手。


为什么简单 RNN 会“遗忘”遥远的信息?

简单 RNN 的核心是一个反复使用的隐藏状态 $h_t$:

$$ h_t = \tanh(W_{xh} x_t + W_{hh} h_{t-1} + b_h) $$

在反向传播时,梯度需要通过时间步连乘多个权重矩阵 $W_{hh}$。如果 $W_{hh}$ 的特征值大于 1,梯度会指数级爆炸;如果小于 1,则会指数级衰减到零。梯度消失是导致长时依赖难以学习的主因——遥远的输入对当前状态的影响微乎其微,网络“记不住”太久以前的事。

LSTM 和 GRU 通过巧妙的 门控机制 主动控制信息的流动,让网络可以选择性地保留或遗忘信息,从而为梯度提供一条相对稳定的“高速公路”。


LSTM:三重门控的记忆单元

LSTM 在普通 RNN 的基础上引入了一个细胞状态(cell state) $C_t$,它像一条传送带贯穿全部时间步,信息可以几乎没有衰减地在上面流动。三个门结构负责向细胞状态添加或移除信息。

LSTM 的核心组件

  • 细胞状态 $C_t$:长期记忆的载体,线性变换为主,梯度容易传递。
  • 隐藏状态 $h_t$:短期记忆,作为当前时间步的输出,并参与下一时间步的计算。
  • 遗忘门 $f_t$:决定要从细胞状态中丢弃哪些旧信息。
  • 输入门 $i_t$候选值 $\tilde{C}_t$:决定要添加哪些新信息到细胞状态。
  • 输出门 $o_t$:决定将细胞状态的哪些部分输出为隐藏状态。

逐步拆解 LSTM 的运算

  1. 遗忘门——选择性“忘记” 通过拼接当前输入 $x_t$ 和上一隐藏状态 $h_{t-1}$,经过 sigmoid 函数输出一个 0~1 之间的向量,与上一个细胞状态 $C_{t-1}$ 逐元素相乘。接近 0 表示“完全忘记”,接近 1 表示“完全保留”。

    $$ f_t = \sigma(W_f \cdot [h_{t-1}, x_t] + b_f) $$

  2. 输入门——选择性“记忆” 由两部分协作:

    • 输入门 $i_t$ 决定哪些值需要更新。
    • 候选细胞状态 $\tilde{C}_t$ 生成新的候选信息(使用 $\tanh$ 激活,输出值在 -1~1 之间)。

    $$ 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) $$

  3. 更新细胞状态 旧状态乘以遗忘门丢弃一部分信息,再加上输入门控制的新信息:

    $$ C_t = f_t \odot C_{t-1} + i_t \odot \tilde{C}_t $$

    这一步是 LSTM 的关键:遗忘门和输入门的线性组合让细胞状态的更新变得平滑,梯度在反向传播时可以直接通过 $C_t$ 流向 $C_{t-1}$,极大缓解了梯度消失。

  4. 输出门——选择性“展示” 输出门控制当前细胞状态的哪些部分会被输出为隐藏状态 $h_t$。

    $$ o_t = \sigma(W_o \cdot [h_{t-1}, x_t] + b_o) $$ $$ h_t = o_t \odot \tanh(C_t) $$

    $h_t$ 被用于当前时间步的预测,以及传入下一时间步。

小结:LSTM 通过细胞状态作为长期记忆,用三个门精确控制信息的遗忘、写入和读取,从而能够有效地捕捉数百步以外的依赖关系。


GRU:更精简的门控循环单元

GRU 可以看作是 LSTM 的一种高效变体,它将遗忘门和输入门合并成一个更新门,并将细胞状态与隐藏状态合并。因此结构更简单,参数更少,训练通常更快,在很多任务上能达到与 LSTM 相当的性能。

GRU 的两个门

  1. 更新门 $z_t$ 决定多少过去的信息需要保留,多少新的信息需要写入。类似于 LSTM 中遗忘门和输入门的组合作用。

    $$ z_t = \sigma(W_z \cdot [h_{t-1}, x_t] + b_z) $$

  2. 重置门 $r_t$ 决定如何将新的输入与过去的记忆结合。当 $r_t$ 接近 0 时,模型会“忘记”之前的状态,只关注当前输入,就像是在阅读一个新句子的开头。

    $$ r_t = \sigma(W_r \cdot [h_{t-1}, x_t] + b_r) $$

GRU 的状态更新

  • 候选隐藏状态 $\tilde{h}_t$ 将经过重置门筛选后的 $h_{t-1}$ 与当前输入 $x_t$ 结合,生成新信息的候选值。

    $$ \tilde{h}t = \tanh(W_h \cdot [r_t \odot h{t-1}, x_t] + b_h) $$

  • 最终隐藏状态 $h_t$ 更新门 $z_t$ 在线性插值中决定:是保留更多旧状态 $h_{t-1}$,还是加入更多新候选状态 $\tilde{h}_t$。

    $$ h_t = (1 - z_t) \odot h_{t-1} + z_t \odot \tilde{h}_t $$

    这与 LSTM 的 $C_t$ 更新形式类似,但 GRU 没有独立的细胞状态,所有记忆都放在同一个隐藏状态中。当 $z_t$ 接近 1 时,旧信息几乎完全被替换;当 $z_t$ 接近 0 时,旧信息几乎完整地传递下去。

GRU 与 LSTM 的直观对比

特性 LSTM GRU
记忆结构 细胞状态 + 隐藏状态 只有隐藏状态
门数量 3 个(遗忘、输入、输出) 2 个(更新、重置)
控制遗忘与输入的机制 单独的门分别控制 更新门统一控制1-z_tz_t
参数数量 较多(4 组权重矩阵) 较少(3 组权重矩阵)
训练速度 相对较慢 更快
实际表现 功能强大,更灵活 在许多中等规模任务上与 LSTM 持平

一般情况下,如果数据量很大、任务复杂,优先考虑 LSTM;如果希望模型更简洁、训练更快,GRU 是很好的起点。两者都可以有效地缓解长时依赖问题。


代码示例:用 Keras 构建 LSTM 和 GRU

以下示例展示如何用 TensorFlow/Keras 快速搭建一个简单的序列分类模型。我们以 IMDB 影评情感分类为例(序列长度 500,词嵌入维度 32)。

import tensorflow as tf
from tensorflow.keras.datasets import imdb
from tensorflow.keras.preprocessing import sequence

# 参数设置
max_features = 10000   # 词汇表大小
maxlen = 500           # 截断/填充长度
embedding_dim = 32

# 加载数据
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=max_features)
x_train = sequence.pad_sequences(x_train, maxlen=maxlen)
x_test = sequence.pad_sequences(x_test, maxlen=maxlen)

# 构建 LSTM 模型
model_lstm = tf.keras.Sequential([
    tf.keras.layers.Embedding(max_features, embedding_dim, input_length=maxlen),
    tf.keras.layers.LSTM(64, dropout=0.2, recurrent_dropout=0.2),
    tf.keras.layers.Dense(1, activation='sigmoid')
])

model_lstm.compile(optimizer='adam',
                   loss='binary_crossentropy',
                   metrics=['accuracy'])
model_lstm.summary()

# 训练(仅示意,可适当减少 epoch 快速验证)
history_lstm = model_lstm.fit(x_train, y_train,
                              batch_size=128,
                              epochs=3,
                              validation_split=0.2)

若要将模型换成 GRU,只需将 LSTM 层替换为 GRU 层即可,其他代码完全相同:

model_gru = tf.keras.Sequential([
    tf.keras.layers.Embedding(max_features, embedding_dim, input_length=maxlen),
    tf.keras.layers.GRU(64, dropout=0.2, recurrent_dropout=0.2),
    tf.keras.layers.Dense(1, activation='sigmoid')
])

model_gru.compile(optimizer='adam',
                  loss='binary_crossentropy',
                  metrics=['accuracy'])
model_gru.fit(x_train, y_train,
              batch_size=128,
              epochs=3,
              validation_split=0.2)

小贴士:在实际项目中,你还可以堆叠多层 LSTM/GRU,或添加 Bidirectional 包装器捕获上下文信息。


总结与关键要点

  • 长时依赖问题 源于梯度消失,简单 RNN 难以学习间隔较远的依赖关系。
  • LSTM 使用细胞状态和三个门(遗忘门、输入门、输出门)精确控制信息的增删,为梯度提供顺畅通路。
  • GRU 将遗忘门与输入门融合为更新门,并合并细胞状态与隐藏状态,结构更精简,在许多场景下效果相当。
  • 两者的核心思想都是通过 0~1 之间的门控信号线性调节信息流,从而选择性地保留历史信息,解决长期记忆难题。
  • 实践中可根据任务规模、训练资源在 LSTM 和 GRU 之间灵活选择。

掌握了 LSTM 和 GRU 后,你就可以自信地应对各种序列建模挑战了。下一步,不妨尝试将它们应用于你手头的文本、音频或时间序列数据,感受门控机制的实际威力。