简单循环神经网络(RNN)内部的核心数学运算,即跨时间步重复进行的矩阵乘法,直接导致了梯度消失和梯度爆炸问题。训练深度循环网络变得不稳定,使得捕获序列中相距较远元素之间的依赖关系变得困难。长短期记忆(LSTM)网络由Hochreiter和Schmidhuber于1997年提出,专门设计用于通过一个更精巧的内部结构来解决这些问题,该结构内含门控机制。LSTM的核心创新是除了隐藏状态($h_t$)之外,还引入了单元状态($C_t$)。可以将单元状态想象成一条信息高速公路或记忆传送带。它贯穿整个序列,只有轻微的线性交互。信息可以被添加到单元状态中或从其中移除,这些操作由称为门的结构精确地调控。这些门由一个sigmoid神经网络层和一个逐点乘法操作组成。sigmoid层输出0到1之间的数字,用于描述每个分量应该通过多少。值为1表示“让所有信息通过”,而值为0表示“不让任何信息通过”。一个LSTM单元通常包含三个这样的门,以保护和控制单元状态。让我们在特定时间步 $t$ 查看每个门,考虑当前输入 $x_t$、前一个隐藏状态 $h_{t-1}$ 和前一个单元状态 $C_{t-1}$。遗忘门 ($f_t$)第一步是决定我们要从单元状态中丢弃哪些信息。这个决定由遗忘门做出。它查看 $h_{t-1}$ 和 $x_t$,并为单元状态 $C_{t-1}$ 中的每个数字输出一个介于0和1之间的值。$$ f_t = \sigma(W_f [h_{t-1}, x_t] + b_f) $$这里,$[h_{t-1}, x_t]$ 表示前一个隐藏状态和当前输入向量的拼接。$W_f$ 代表遗忘门的权重矩阵,$b_f$ 代表其偏置向量。sigmoid函数 $\sigma$ 将输出压缩到 [0, 1] 的范围。接近0的值表示忘记 $C_{t-1}$ 中相应的信息,而接近1的值表示保留它。输入门 ($i_t$) 和候选值 ($\tilde{C}_t$)接下来,我们需要决定将哪些新信息存储到单元状态中。这包括两个部分:输入门 ($i_t$) 是另一个sigmoid层,它决定我们将更新哪些值。一个 tanh 层创建新的候选值向量 $\tilde{C}_t$,这些值可以被添加到状态中。$$ i_t = \sigma(W_i [h_{t-1}, x_t] + b_i) $$$$ \tilde{C}t = \tanh(W_C [h{t-1}, x_t] + b_C) $$类似于遗忘门,$W_i, b_i, W_C, b_C$ 是在训练过程中学到的权重矩阵和偏置向量。tanh 函数输出介于-1和1之间的值,代表对单元状态的潜在更新(正向或负向)。现在,我们将旧的单元状态 $C_{t-1}$ 更新为新的单元状态 $C_t$。我们将旧状态乘以 $f_t$,忘记了我们之前决定忘记的内容。然后我们加上 $i_t * \tilde{C}_t$。这是新的候选值,根据我们决定更新每个状态值的程度进行缩放。$$ C_t = f_t * C_{t-1} + i_t * \tilde{C}_t $$符号 $*$ 表示逐元素乘法。输出门 ($o_t$)最后,我们需要决定输出什么。这个输出将基于我们的单元状态,但会是一个经过筛选的版本。首先,我们运行一个sigmoid层,它决定我们将输出单元状态的哪些部分。$$ o_t = \sigma(W_o [h_{t-1}, x_t] + b_o) $$然后,我们将单元状态 $C_t$ 通过 tanh (将值推至-1和1之间)并将其乘以sigmoid门 $o_t$ 的输出,这样我们只输出了我们决定要输出的部分。这个结果就是新的隐藏状态 $h_t$。$$ h_t = o_t * \tanh(C_t) $$这个 $h_t$ 被传递到下一个时间步,也可以作为当前时间步LSTM单元的输出进行预测。digraph G { rankdir="LR"; node [shape=box, style=filled, fontname="Arial"]; splines=true; subgraph cluster_lstm { label = "LSTM单元 (时间 t)"; bgcolor="#e9ecef"; node [fillcolor="#a5d8ff"]; // Blue for states/inputs/outputs xt [label="x_t"]; ht_1 [label="h_{t-1}"]; Ct_1 [label="C_{t-1}"]; ht [label="h_t"]; Ct [label="C_t"]; // 门 (橙色/黄色) node [fillcolor="#ffe066"]; forget_gate [label=<σ<SUB><FONT POINT-SIZE="8">f</FONT></SUB>>, shape=ellipse]; input_gate [label=<σ<SUB><FONT POINT-SIZE="8">i</FONT></SUB>>, shape=ellipse]; output_gate [label=<σ<SUB><FONT POINT-SIZE="8">o</FONT></SUB>>, shape=ellipse]; candidate_values [label=<tanh<SUB><FONT POINT-SIZE="8">C</FONT></SUB>>, shape=ellipse]; // 运算 (灰色) node [fillcolor="#ced4da", shape=circle, width=0.3, label="*"]; mul_f_Ct1; mul_i_Ctilda; mul_o_tanhCt; node [fillcolor="#ced4da", shape=circle, width=0.3, label="+"]; add_Ct; node [fillcolor="#ced4da", shape=circle, width=0.3, label="tanh"]; tanh_Ct; // 连接 edge [color="#495057"]; ht_1 -> forget_gate [label="[h,x]"]; xt -> forget_gate; ht_1 -> input_gate [label="[h,x]"]; xt -> input_gate; ht_1 -> candidate_values [label="[h,x]"]; xt -> candidate_values; ht_1 -> output_gate [label="[h,x]"]; xt -> output_gate; Ct_1 -> mul_f_Ct1; forget_gate -> mul_f_Ct1; mul_f_Ct1 -> add_Ct [color="#0ca678"]; // 单元状态路径绿色 input_gate -> mul_i_Ctilda; candidate_values -> mul_i_Ctilda; mul_i_Ctilda -> add_Ct [color="#0ca678"]; // 单元状态路径绿色 add_Ct -> Ct [color="#0ca678"]; // 单元状态路径绿色 Ct -> tanh_Ct [color="#0ca678"]; // 单元状态路径绿色 Ct_1 -> Ct [style=dotted, arrowhead=none, color="#0ca678"]; // 指示时间流向 tanh_Ct -> mul_o_tanhCt; output_gate -> mul_o_tanhCt; mul_o_tanhCt -> ht; // 传递状态 ht_1 -> ht [style=invis]; // 确保布局使ht_1和ht大致对齐 Ct_1 -> Ct [style=invis]; // 确保布局使Ct_1和Ct大致对齐 } }LSTM单元的内部结构。门(sigmoid $\sigma$)控制信息进出单元状态 ($C_t$) 的流动,由绿色路径表示。隐藏状态 ($h_t$) 是单元状态的筛选版本。门控如何缓解梯度消失问题主要的发现是单元状态的加性交互。新信息被添加到单元状态中(通过 $i_t * \tilde{C}_t$),旧信息被移除(通过乘以 $f_t$),而不像简单RNN那样通过矩阵乘法和非线性操作重复转换。遗忘门允许单元状态在需要时长时间保留信息(通过将 $f_t$ 设置接近1)。这种结构创建了梯度可以反向传播而不迅速消失的路径。门学习控制这种流动,根据上下文打开或关闭对单元状态的访问。如果遗忘门大部分是打开的 ($f_t \approx 1$) 并且输入门大部分是关闭的 ($i_t \approx 0$),单元状态可以将其信息在许多时间步内大致不变地传递,从而保留梯度。尽管LSTM代表了显著的进展,并使得许多以前对简单RNN来说难以处理的序列建模任务取得了进展,但它们并不是一个完美的解决方案。它们仍然按顺序处理信息,限制了训练和推理过程中的并行化。此外,尽管它们在捕获更长依赖方面远优于简单RNN,但它们在处理数千时间步中存在细微依赖的极长序列时仍然可能遇到困难。门控机制的复杂性也增加了相比简单模型的计算开销。这些未解决的问题为Transformer等架构的出现创造了条件,该架构完全放弃了循环。