尽管如前一节所述,简单的循环神经网络(RNN)通过维护一个隐藏状态来处理序列,但在处理更长序列时会遇到显著困难。这些局限性是开发LSTM、GRU等更复杂架构,并最终催生Transformer的主要推动力。其主要问题源于训练时梯度在网络中经过多个时间步的传播方式。梯度消失问题训练RNN通常涉及时间反向传播(BPTT)。该算法将RNN沿序列长度展开并应用标准反向传播。为了计算损失函数相对于序列早期参数(例如,影响隐藏状态 $h_k$ 的权重)的梯度,链式法则要求将对应循环状态转换的多个雅可比矩阵相乘。设 $L$ 为序列上的总损失。损失相对于早期隐藏状态 $h_k$ 的梯度取决于所有后续状态 $h_t$ ($t > k$)的梯度: $$ \frac{\partial L}{\partial h_k} = \sum_{t=k+1}^{T} \frac{\partial L_t}{\partial h_t} \frac{\partial h_t}{\partial h_k} $$ 其中 $T$ 是序列长度,$L_t$ 是时间步 $t$ 的损失。项 $\frac{\partial h_t}{\partial h_k}$ 表示 $h_k$ 对 $h_t$ 的影响。这种影响通过链式计算状态转换的雅可比矩阵得到: $$ \frac{\partial h_t}{\partial h_k} = \frac{\partial h_t}{\partial h_{t-1}} \frac{\partial h_{t-1}}{\partial h_{t-2}} \dots \frac{\partial h_{k+1}}{\partial h_k} $$ 每个雅可比矩阵 $\frac{\partial h_i}{\partial h_{i-1}}$ 都依赖于循环权重矩阵 $W_{hh}$ 和激活函数的导数(例如,$\tanh'$)。如果这些雅可比矩阵的幅值(特别是奇异值)持续小于1,它们的乘积会随着距离 $t-k$ 的增加呈指数级减小。digraph BPTT { rankdir=TB; node [shape=box, style=rounded, fontname="helvetica", fontsize=9]; edge [fontname="helvetica"]; subgraph cluster_0 { label = "前向传播(时间步)"; style=filled; color="#e9ecef"; fontsize=11; node [fillcolor=white]; x_k -> h_k -> loss_k; h_k -> h_kplus1 [label=" W_hh", fontsize=9;]; x_kplus1 -> h_kplus1 -> loss_kplus1; h_kplus1 -> h_dots [label=" W_hh", fontsize=9;]; x_dots -> h_dots -> loss_dots; h_dots -> h_T [label=" W_hh", fontsize=9;]; x_T -> h_T -> loss_T; } subgraph cluster_1 { label = "梯度流(BPTT)"; style=filled; color="#e9ecef"; fontsize=9; rankdir=RL; node [fillcolor=white]; grad_L_T -> grad_h_T; grad_h_T -> grad_h_dots [label=" × (∂h_T/∂h_{...})", fontsize=9;]; grad_h_dots -> grad_h_kplus1 [label=" × (...) "]; grad_h_kplus1 -> grad_h_k [label=" × (∂h_{k+1}/∂h_k)", fontsize=9;]; grad_h_k [label="∂L/∂h_k", color="#f03e3e", style=filled, fillcolor="#ffc9c9"]; grad_h_T [label="∂L/∂h_T"]; loss_T -> grad_L_T [style=dashed, color="#868e96"]; loss_k -> grad_h_k [style=dashed, color="#868e96"]; loss_kplus1 -> grad_h_kplus1 [style=dashed, color="#868e96"]; loss_dots -> grad_h_dots [style=dashed, color="#868e96"]; label_mult [label="雅可比矩阵的\n重复乘法", shape=plaintext, fontsize=10]; grad_h_T -> label_mult [style=invis]; label_mult -> grad_h_k [style=invis]; } loss_k [shape=ellipse, label="Loss_k", fillcolor="#d0bfff"]; loss_kplus1 [shape=ellipse, label="Loss_{k+1}", fillcolor="#d0bfff"]; loss_dots [shape=ellipse, label="...", fillcolor="#d0bfff"]; loss_T [shape=ellipse, label="Loss_T", fillcolor="#d0bfff"]; x_k [label="x_k"]; h_k [label="h_k"]; x_kplus1 [label="x_{k+1}"]; h_kplus1 [label="h_{k+1}"]; x_dots [label="..."]; h_dots [label="..."]; x_T [label="x_T"]; h_T [label="h_T"]; }时间反向传播(BPTT)示意图。后续时间步(如 $T$)的梯度必须通过循环连接(受 $W_{hh}$ 影响)反向流动,以更新影响早期状态(如 $h_k$)的参数。在此反向传播过程中,雅可比矩阵的重复相乘是梯度消失/爆炸问题的根源。这种现象被称为梯度消失问题。结果是,来自后续时间步的误差信号变得过于微弱,无法有效更新负责获取早期信息的权重。实际上,RNN难以学习数据中的长距离依赖关系。模型很难连接序列中相隔多个时间步的事件或信息。梯度爆炸问题反之,如果雅可比矩阵 $\frac{\partial h_i}{\partial h_{i-1}}$ 的幅值持续大于1,它们的乘积会呈指数级增大。这会导致梯度爆炸问题。当梯度爆炸时,权重更新会变得过大,可能导致模型的参数发散至无穷大或NaN(非数值)。这会使训练过程不稳定,常导致损失函数突然急剧增加或训练完全失败。梯度爆炸问题通常更容易发现,有时可以通过梯度裁剪(当梯度范数超过一定阈值时将其按比例缩小)等方法来减轻,但裁剪更多是一种权宜之计,而非根本解决方法。它能阻止灾难性发散,但并未从根本上解决在长时序上传播有效梯度信号的问题,这正是梯度消失问题所针对的主要局限性。import torch import torch.nn as nn # 假设模型参数为'params',已计算出'loss' # PyTorch中的梯度裁剪示例 # 1. 计算梯度 # loss.backward() # 2. 定义最大梯度范数阈值 max_grad_norm = 1.0 # 3. 就地裁剪梯度 nn.utils.clip_grad_norm_(params, max_grad_norm) # 4. 应用优化器步骤 # optimizer.step()PyTorch中演示梯度裁剪的简单示例。计算梯度后,如果总范数超过 max_grad_norm,clip_grad_norm_ 会按比例缩小梯度。对性能的影响这些梯度问题严重限制了简单RNN在需要对长序列建模或获取跨越多个时间步的依赖关系的任务上的实际效果。例如:机器翻译: 翻译长句时,其含义可能依赖于相隔较远的词语。文档摘要: 理解长篇文章的主旨需要整合跨段落的信息。语言建模: 预测下一个词通常依赖于文本中很早之前建立的上下文。无法可靠学习这些长距离依赖关系意味着,与旨在解决这些梯度传播问题的模型相比,简单RNN通常表现不佳。这促成了LSTM和GRU的出现,它们包含了专门设计用于更好控制信息和梯度随时间流动的门控机制,我们将在后续章节中看到这一点。