门控循环网络:LSTM 与 GRU#
学习目标与记号#
准确解释门控、LSTM 细胞状态与遗忘门;
根据公式和张量维度分析 GRU 更新门,并识别常见实现错误;
用
Python实现或验证长期依赖;在其他条件相同的情况下,只改变一个需要研究的因素进行比较,并根据实验结果分析该因素可能带来的影响。
本节沿用统一记号:普通小写字母表示标量,粗体小写字母表示向量,粗体大写字母表示矩阵或高阶张量;层编号写作上标 \([l]\),样本或时间编号写作下标;转置写作 \(\trans\)。数学公式中的单个向量默认都是列向量;需要表示行向量时,必须显式写出转置符号。批量程序若使用 \(m\times d\) 的矩阵同时存放 \(m\) 个列向量,则统一写为 \(\bU=[\bu_1\trans;\ldots;\bu_B\trans]\)。正文与练习中的程序都应同时检查数值结果和数组维度。本节练习的参考答案见 LSTM 与 GRU 答案。
门控结构针对 普通 RNN 的梯度消失与爆炸;门参数仍通过 随时间后向传播 学习。
Vanilla RNN 每一步都把旧状态经过权重矩阵和非线性重新变换,长期梯度容易在雅可比连乘中衰减或放大。LSTM 和 GRU 引入值域在 \((0,1)\) 的门,使用逐元素加性路径决定保留多少旧信息、写入多少新信息。它们改善了长期依赖学习,但并不从数学上保证梯度永不消失或爆炸。
长短期记忆网络(LSTM)#
与普通 RNN 主要维护隐藏状态不同,LSTM 在每个时间步同时维护 细胞状态 (cell state) \(\bc_t\) 和隐藏状态 \(\bh_t\)。细胞状态是 LSTM 在处理到第 \(t\) 个输入后得到的内部记忆向量,用于把此前保留下来的信息继续传向后面的时间步。例如,在处理一段文字时,模型可以通过细胞状态保留前文中有助于理解后文的信息。这里的“记忆”是一种便于理解的说法,并不表示细胞状态的每个分量都具有预先规定的具体含义。
细胞状态不是训练过程中直接学习的一组固定参数。它会随着输入序列和时间步发生变化,是模型前向传播产生的中间结果。开始处理一条序列时,初始细胞状态 \(\bc_0\in\mathbb{R}^{d_h}\) 和初始隐藏状态 \(\bh_0\in\mathbb{R}^{d_h}\) 通常都设为全 0 向量,也可以在特定模型中设为需要学习的初始向量。
细胞状态与隐藏状态的区别
细胞状态 \(\bc_t\) 主要承担内部记忆的传递,其更新由“保留多少旧信息”和“写入多少新信息”共同决定。隐藏状态 \(\bh_t\) 是当前时间步对外提供的表示:它既会参与下一个时间步各个门的计算,也可以传给下一层网络或最终的预测模块。LSTM 会把 \(\bc_t\) 经过 \(\tanh\) 函数变换,再由输出门选择其中有多少信息进入 \(\bh_t\)。因此,这两个向量相互关联,但作用并不相同。
令当前输入 \(\bx_t\in\mathbb{R}^{d_x}\)、旧隐藏状态 \(\bh_{t-1}\in\mathbb{R}^{d_h}\) 和旧细胞状态 \(\bc_{t-1}\in\mathbb{R}^{d_h}\) 均为列向量。这里,\(\bc_{t-1}\) 表示处理当前输入 \(\bx_t\) 之前保存的内部记忆。把当前输入和旧隐藏状态沿竖直方向拼接为列向量
其中 \(\bW_i,\bW_f,\bW_o,\bW_c\in\mathbb{R}^{d_h\times(d_x+d_h)}\),\(\bb_i,\bb_f,\bb_o,\bb_c\in\mathbb{R}^{d_h}\) 均为列向量。一种紧凑写法为
输入门 \(\bi_t\)、遗忘门 \(\boldsymbol{f}_t\)、输出门 \(\bo_t\)、候选细胞状态 \(\widetilde{\bc}_t\)、细胞状态 \(\bc_t\) 和隐藏状态 \(\bh_t\) 都属于 \(\mathbb{R}^{d_h}\),并且都按列向量理解。候选细胞状态 \(\widetilde{\bc}_t\) 表示当前时间步准备写入的候选新信息,它还不是更新后的细胞状态;只有经过输入门筛选并与保留下来的旧细胞状态相加后,才得到新的 \(\bc_t\)。
图 26 LSTM 单元的基本结构#
遗忘门控制旧细胞状态的保留比例,输入门控制候选信息的写入比例,输出门决定细胞状态中的哪些信息用于形成隐藏状态。门值通常只是接近 0 或 1;对于有限输入,sigmoid 函数不会精确达到端点。
LSTM 的关键是状态更新含有加法路径。若暂时忽略门本身对旧状态的间接依赖,直接路径满足
当一段时间内相关遗忘门接近 1 时,梯度可以沿细胞状态较完整地传播;若门持续接近 0,模型会主动遗忘,对应梯度也会衰减。实际的完整雅可比矩阵还包含门和隐藏状态之间的依赖,因此不能把上式视为全部梯度。
门控循环单元(GRU)#
GRU 不再单独维护 LSTM 中的细胞状态,而是只保留隐藏状态。仍令 \(\bx_t\in\mathbb{R}^{d_x}\) 和 \(\bh_{t-1}\in\mathbb{R}^{d_h}\) 为列向量,并取 \(\bW_z,\bW_r,\bW_h\in\mathbb{R}^{d_h\times d_x}\)、\(\bU_z,\bU_r,\bU_h\in\mathbb{R}^{d_h\times d_h}\),以及列向量 \(\bb_z,\bb_r,\bb_h\in\mathbb{R}^{d_h}\)。常用的一组约定是
更新门 \(\bz_t\)、重置门 \(\br_t\)、候选状态 \(\widetilde{\bh}_t\) 和新隐藏状态 \(\bh_t\) 都属于 \(\mathbb{R}^{d_h}\),并且都按列向量理解。
图 27 GRU 单元的基本结构#
这里 \(\bz_t\) 越接近 1,旧状态保留越多;\(\br_t\) 控制旧状态参与候选状态的程度。有些文献把更新门定义成“新信息的权重”,于是最后一式中的 \(\bz_t\) 与 \(\bone-\bz_t\) 对调。还有实现把 reset 门放在线性变换之后。阅读公式或对接框架权重时必须先确认约定,不能只比较变量名称。
门控为何改善梯度传播#
把更新门和候选状态局部近似为给定量,可将 GRU 更新简化为
其中 \(\bD_z=\operatorname{diag}(\bz_t)\)。若候选状态对旧状态的局部雅可比是 \(\bJ_{\widetilde h}\),则
当某些 \(z_{t,k}\) 接近 1 时,相应维度出现近似恒等路径,使状态和梯度能够跨过该时间步;当门接近 0 时,模型更多采用候选状态。完整求导还包括门对 \(\bh_{t-1}\) 的导数,上式只用于说明加性通路。
LSTM 与 GRU 的选择#
若输入维度为 \(d_x\)、隐藏维度为 \(d_h\),忽略输出头,LSTM 的参数量约为
GRU 的参数量约为 \(3d_h(d_x+d_h+1)\),因此在隐藏维度相同时,GRU 的参数通常更少。参数更少不保证预测更准确,LSTM 也不保证在所有需要长期信息的任务上都更好。比较两种模型时,应使用相同的数据划分和训练量,并采用相同的评价指标及其计算方法;除了预测结果,还应同时比较运行时间与内存。
备注
深层或双向 LSTM/GRU 是在上述单元之外增加层方向和堆叠维度。双向模型会使用未来输入,不可直接用于严格因果预测;多层模型还需要明确每一层初始状态的维度。
Shiny 交互演示:LSTM 与 GRU 的门控作用
交互页面严格采用正文中的 LSTM 与 GRU 约定,允许改变门偏置并观察门值、状态和局部直接路径乘积。图中 LSTM 的 \(\prod_t f_t\) 与 GRU 的 \(\prod_t z_t\) 只表示门所形成的直接路径;完整梯度还包含门、LSTM 的候选细胞状态和 GRU 的候选隐藏状态对旧状态的导数。
核心推导与实现核验#
核心关系
推导路径。 LSTM 用遗忘门缩放旧细胞状态、输入门缩放候选内容;加法路径使信息可以在时间上选择性保留,而不是每步完全重写。
关键条件
沿细胞状态直接路径有 \(\partial\bc_t/\partial\bc_{t-1}=\operatorname{diag}(\boldsymbol{f}_t)\);当遗忘门接近 1 时,该路径梯度可较好保留,但其他路径与门饱和仍会影响训练。
数据规模
若每个时刻的输入列向量含 \(d_x\) 个数、隐藏状态列向量含 \(d_h\) 个数,那么 LSTM 的细胞状态、候选细胞状态和各个门,以及 GRU 的候选隐藏状态和各个门,都是含 \(d_h\) 个数的列向量。把当前输入与旧隐藏状态沿竖直方向拼接后,列向量 \(\bu_t\) 含 \(d_x+d_h\) 个数,因此 LSTM 每个门的权重矩阵大小为 \(d_h\times(d_x+d_h)\)。
常见误区
“门控彻底解决梯度消失”并不成立;使用 sigmoid 函数的门本身可能进入饱和区,长路径仍需合适初始化、归一化和优化。
动手检查
对长度为 2 或 3 的序列,把手写 LSTM/GRU 前向与 自动微分结果 对照;再改变序列长度,绘制初始时刻梯度范数以验证长期传播趋势。
数值稳定性与规模
训练长序列时,应检查所有参数的梯度合在一起后是否过大。如果超过预设上限,可以在确认后向传播公式和程序实现正确后,使用 全局范数裁剪 按相同比例缩小所有参数的梯度。LSTM 和 GRU 的门值需要通过 sigmoid 函数计算,应优先使用 PyTorch 等程序库提供的数值稳定实现,避免直接计算指数时出现数值过大。采用截断 BPTT 时,后一片段可以继续使用前一片段最后的隐藏状态,但应切断两段之间的梯度联系,使梯度不再传回更早的片段。
本节小结#
门控让状态保留与更新成为数据依赖的选择。
LSTM 的加法细胞路径改善但不保证长期梯度。
GRU 与 LSTM 的比较必须控制参数量和训练预算。
综合练习#
程序题应固定随机种子、写出维度断言并报告运行环境。全部参考答案见 LSTM 与 GRU 答案。
LSTM 单步计算。 对一维 LSTM,已知 \(c_{t-1}=2\)、\(f_t=0.8\)、\(i_t=0.25\)、\(\widetilde c_t=-0.4\)、\(o_t=0.5\)。计算 \(c_t\) 和 \(h_t\),并分别说明三个门在这一步中的作用。
细胞状态梯度。 暂时忽略各门对旧状态的间接依赖,推导 \(\partial\bc_t/\partial\bc_{t-k}\)。若连续三步的遗忘门在某一维上依次为 \(0.9,0.8,0.5\),计算该维直接路径的梯度系数,并说明“LSTM 彻底解决梯度消失”为何不成立。
GRU 更新计算。 按正文约定,令一维 GRU 的 \(z_t=0.75\)、\(h_{t-1}=2\)、\(\widetilde h_t=-0.4\)。计算 \(h_t\);再在候选状态局部不依赖旧状态的近似下,计算 \(\partial h_t/\partial h_{t-1}\),解释更新门接近 1 时的含义。
参数量比较。 忽略输出头,对 \(d_x=5\)、\(d_h=4\) 的 Vanilla RNN、GRU 和 LSTM 分别计算参数量。使用正文的门数约定,写出一般公式,并说明为什么同一隐藏维度下的参数量比较不等同于同一容量或同一计算预算比较。
门控单元实现。 从零实现批量 LSTM 单步和 GRU 单步函数。数学公式中的输入和状态均为列向量;若程序用 \(m\times d\) 的矩阵存放一批列向量 \(\bu_1,\ldots,\bu_B\),则统一写为 \(\bU=[\bu_1\trans;\ldots;\bu_B\trans]\),其第 \(i\) 行为 \(\bu_i\trans\)。输入、旧状态和所有门的维度必须显式检查;函数分别返回 LSTM 的门值、候选细胞状态、细胞状态和隐藏状态,以及 GRU 的门值、候选隐藏状态和隐藏状态,以便逐项核验,并用第 1、3 题的一维数值构造回归测试。
框架对齐核验。 将手写单元的参数复制到
torch.nn.LSTMCell和torch.nn.GRUCell,确认框架的门排列、GRU reset 门位置和更新门约定后,比较前向结果以及对输入、旧状态和参数的梯度。给出绝对、相对误差标准,并测试批量大小大于 1 的情形。长期梯度性质测试。 构造零输入序列,分别控制 LSTM 遗忘门和 GRU 更新门接近 \(0.1\)、\(0.5\) 与 \(0.9\)。记录末状态对初始状态的梯度,并与直接路径乘积的理论趋势比较;同时测试极端门输入不会产生
NaN,并说明完整梯度为何可能偏离近似值。RNN、GRU 与 LSTM。 在延迟复制或序列首位记忆任务上比较三种单层模型。固定训练/验证/测试数据、随机种子集合、输入表示、训练更新次数和每次更新的样本数;先使用相同隐藏维度,再补充参数量接近的比较。报告预测准确率、训练时间、固定批量预测时间、峰值内存、参数量和循环单元调用次数。
参数量匹配的 GRU 与 LSTM。 选择不同隐藏维度,使 GRU 与 LSTM 的总可训练参数量尽可能接近,在同一序列分类数据上比较。固定数据划分、随机种子集合、优化器、更新次数、输入长度和评价代码;报告预测质量、训练时间、固定批量预测时间、峰值内存、实际参数量与每步主要乘加量。
依赖长度与学习率。 对 GRU 和 LSTM 分别改变依赖长度与学习率,研究模型何时能够学会长期关系。固定各长度下的数据生成规则、训练/验证/测试规模、随机种子集合、最大更新次数和早停规则;报告预测质量、训练时间、固定批量预测时间、峰值内存、梯度范数和实际更新次数,不能只展示每种模型最有利的学习率。