资讯动态

从RNN到LSTM与GRU:循环神经网络的原理、差异与选型指南

发布时间:2026/10/20 9:39:44 来源:尧图企业网站定制
第一次用全连接网络处理影评情感分类时我遇到一件特别憋屈的事把句子切成一堆词向量拼在一起喂给模型短句效果还行一旦句子超过二十个词准确率就肉眼可见地往下掉。当时我一度以为是数据清洗不到位反复调预处理、换分词器折腾了一周也没明显改善。后来才意识到问题不在数据而在网络结构本身缺乏时间维度——它不知道词语之间的先后顺序意味着什么。这就是循环神经网络RNN及其两个经典改进——LSTM和GRU——存在的意义。这篇文章不是教科书式的公式罗列我会从为什么普通网络搞不定序列问题讲起把三个模型的机制、差异、使用场景和实战中的坑一次说透。无论你是刚入门深度学习的学生还是已经在项目中试图用序列模型解决实际问题的工程师这篇文章都能帮你建立一套清晰的选型思路。1. 从记忆说起为什么普通网络搞不定序列问题1.1 文本、语音与时间序列的共同点做深度学习这几年我越来越觉得理解一个模型最好的方式不是先看公式而是先看它到底想解决什么。RNN这一族模型要解决的是我们平时接触最多的那一类数据有先后顺序的数据。一句话是词的序列一段语音是采样点的序列股票价格、传感器读数、心电图信号全是时间序列。这类数据的核心特点就一个上下文相关。同一个词放在不同句子里含义完全不同同样一个数值出现在上升趋势里和出现在下降趋势里代表的意义截然相反。处理这类数据模型必须拥有记忆——在读到当前位置时能知道前面发生了什么。更麻烦的是这个前面的长度是不固定的。今天的天气跟前一天有关跟前三十天可能也有关系一段话里的代词可能往前跨越几十个词才能找到指代对象。这种变长依赖关系是全连接网络和卷积网络天然不擅长的——它们的输入长度是固定的感受野是有限的即使堆出很大的窗口也只是在暴力地记住位置而不是真正理解顺序。1.2 滑动窗口为什么治标不治本可能有人会说那我把句子用滑动窗口切一下每次取前n个词作为上下文不就行了吗我最早也是这么干的用一个大小为5的窗口去预测下一个词。效果确实有但很快撞上两个天花板。第一是窗口大小很难定。取小了长距离依赖直接被切断取大了输入维度膨胀训练数据量根本喂不饱而且窗口边缘的信息几乎被模型忽略了——全连接网络对输入位置是敏感的窗口里第1个词和第5个词被当成完全独立的特征它们的相对关系要靠网络自己硬学。第二是窗口完全不共享逻辑。同一个词出现在第1个位置和第3个位置网络要用两组不同的权重去处理它。这等于让模型从零学习同一个词在不同位置的含义是相通的数据利用率极低。打个比方你让一个实习生读句子规定他每次只看前五个字而且读每个位置的字要动用完全不同的理解规则——他能学好才怪。所以问题的本质是我们需要的不是更大的窗口而是一种能随序列长度自然伸缩、且在时间上共享参数的结构。这也正是循环神经网络的核心思想。1.3 循环结构的本质参数共享与状态传递RNN的思路总结起来就八个字参数共享状态传递。它不去看一个固定大小的窗口而是维护一个隐藏状态hidden state每读入一个新输入就把当前输入和上一时刻的隐藏状态一起融合产生新的隐藏状态。这个隐藏状态本质上就是网络对到目前为止发生了什么的压缩记忆。这样做的好处非常明显。首先处理长度不同的序列时网络的参数量不会变其次处理每个时间步使用的权重是完全相同的模型学到的规律可以被复用到任意位置。换句话说RNN把时间上的平移不变性直接设计进了结构里就像卷积网络把空间上的平移不变性设计进结构里一样。这种设计在直觉上很像人的阅读理解你读一句话不是把每个字存下来而是读完之后脑子里留一个大意读到结尾时你依靠的是这个不断更新的大意而不是逐字回看。RNN的隐藏状态就是这个大意。2. RNN的运作机制与它的阿喀琉斯之踵2.1 展开图视角把循环看成一串共享权重的深网络RNN的公式非常简洁核心就一条h_t tanh(W_hh * h_{t-1} W_xh * x_t b) y_t softmax(W_hy * h_t b_y)x_t是第t步的输入向量h_{t-1}是上一步的隐藏状态经过一个线性变换加tanh激活就得到新的隐藏状态h_t然后h_t再经过一个输出层得到当前步的预测。这里有两个权重矩阵W_hh负责处理历史信息W_xh负责处理当前输入隐藏状态的维度是超参数一般叫hidden_size。如果你把循环按时间展开就会得到一个深度等于序列长度的前馈网络而且每一层的权重是共享的。这个展开视角非常重要因为RNN的反向传播就是在这个展开图上做的也就是常说的BPTTBackpropagation Through Time。BPTT先按时间顺序做一遍前向把每一步的隐藏状态都存下来然后从最后一步开始把损失对每一步的梯度逐步往回传。往回传的每一步梯度都要经过权重矩阵和tanh导数序列越长乘积项越多隐患就在这里。2.2 前向传播与反向传播的计算细节让我们把反向传播的路径看清楚。假设序列长度为T损失L对第t步隐藏状态的梯度要一路传回第1步。每往回传一步就多乘一个雅可比矩阵这个矩阵由两部分构成一个是tanh的导数对角矩阵另一个是W_hh的转置。这里有两个因素决定了梯度的大小。第一个是tanh的导数——tanh函数在两端的导数趋近于0一旦隐藏状态的绝对值偏大梯度就会被严重打折第二个是W_hh本身的谱半径也就是它的最大奇异值。如果每一步都让梯度乘以一个大于1的数累积T步之后梯度会指数爆炸如果每一步都乘以一个小于1的数累积T步之后梯度会指数衰减到几乎为0。这就是梯度消失和梯度爆炸在数学上的来源。它不是某个参数、某个初始值的问题而是连乘结构带来的必然结果。用一句话总结RNN天然对长序列不友好因为它的梯度路径是一条乘法链而乘法链的稳定性极难控制。就算你把初始化做得再好序列一长反馈信号照样在传递过程中散失。2.3 梯度消失与爆炸的直觉理解想直观理解这件事可以想象你在一座很长的山谷里喊一句话让声音从谷头传到谷尾。每传一段距离声音都会衰减一点传到最后一个字时基本听不见——这就是梯度消失相当于远方的信息对当前的决策完全失去了影响。而梯度爆炸则像你在山谷里对着岩壁喊回声不断叠加最后变成轰鸣——梯度数值疯涨训练直接发散。这两个问题在实际训练中的表现完全不同。梯度爆炸容易发现loss突然变成NaN、权重数值剧烈震荡用梯度裁剪就能压住。梯度消失则隐蔽得多loss下降得很慢甚至停滞模型看起来在学但远距离信息根本没有进入参数更新——你如果打印出每一步的梯度范数就会发现靠近序列开头的权重对应梯度几乎全是0。我在早期训练文本生成模型时踩过这个坑。一个两层的RNN序列长度从20加到50之后损失从还算正常的下降变成纹丝不动。当时我先怀疑学习率设置反复调小都没用后来把梯度范数打印出来才发现靠近序列开头的权重更新量比靠近结尾的小了三四个数量级。这就是典型的梯度消失它不是优化算法能解决的问题是结构设计的问题。2.4 梯度问题在实践中的具体表现再补充一个容易忽略的现象梯度消失不是均匀消失而是越靠前越严重。RNN在长序列上的表现本质上是近因主导——最后的输入对输出的影响最大开头的输入几乎被忘光。这导致一个很尴尬的后果RNN生成的文本往往在局部读起来通顺但过了一段之后就跑题因为它压根不记得开头说了什么。另一个常见的现象是反向传播的梯度在时间步之间震荡。即使你做了梯度裁剪隐藏状态的更新依然可能忽大忽小训练曲线像锯齿一样。我后来养成了一个习惯把每步梯度的L2范数打印出来观察同时检查前向传播时隐藏状态的绝对值分布如果大部分值都接近1说明tanh已经普遍处于饱和区这时候就该考虑调整初始化方式或者加层归一化LayerNorm。这些细节光看论文是学不到的。3. LSTM给网络装一个长期记忆抽屉3.1 细胞状态与三个门的结构拆解LSTM的全称是Long Short-Term Memory1997年提出。它的核心理念是与其让信息在隐藏状态里被反复压缩、反复非线性变换不如专门设置一条传送带——细胞状态cell state——让信息可以几乎无损地流过整个序列。为了控制这条传送带上写什么、删什么、读什么LSTM引入了三个门遗忘门forget gate、输入门input gate、输出门output gate。每个门本质上就是一个sigmoid激活的线性层输出值在0到1之间再和待控制的信息做逐元素相乘。sigmoid的输出在这里可以理解为打开程度0代表彻底关闭1代表全量放行。遗忘门决定上一步的细胞状态有多少要保留f_t sigmoid(W_f · [h_{t-1}, x_t] b_f)。输入门决定当前输入和新候选状态有多少要被写入i_t sigmoid(W_i · [h_{t-1}, x_t] b_i)。候选状态用tanh生成g_t tanh(W_g · [h_{t-1}, x_t] b_g)。然后细胞状态更新为C_t f_t * C_{t-1} i_t * g_t。最后输出门根据新的细胞状态决定暴露多少给隐藏状态o_t sigmoid(W_o · [h_{t-1}, x_t] b_o)h_t o_t * tanh(C_t)。3.2 遗忘门、输入门、输出门的直觉化解读把这三个门对应到人的记忆行为上理解起来会顺很多。遗忘门像大脑的清理机制当你进入一个新场景旧场景的临时信息就该丢弃否则两个场景的记忆混在一起会让判断混乱。输入门像写入机制眼前的信息不是都要记只有重要的才写进长期记忆。输出门像表达机制你的记忆库里有大量信息但当前这个时刻该输出什么需要按需取用。举个例子文本生成时预测下一个词。如果模型刚处理完我昨天去了这句话下一步要预测北京还是上海它需要记住的是地点类信息这个大方向而不是把整个记忆库全倒出来。输出门的职责就是从细胞状态里挑选出与当前预测最相关的部分。这种隔离设计是LSTM最聪明的地方。隐藏状态h_t依然像RNN那样每个时刻都被改写但细胞状态C_t是一条相对独立的高速通道遗忘门和输入门只对它做选择性修改。信息在C_t上流动时不需要经过反复的tanh压缩梯度可以沿着这条通道直接传回很远的前方——这就是LSTM缓解梯度消失的关键机理。3.3 为什么LSTM能缓解梯度消失稍微从数学角度解释一下细胞状态为什么是梯度高速公路。C_t f_t * C_{t-1} i_t * g_t当遗忘门f_t接近1时C_t对C_{t-1}的导数接近1。也就是说反向传播时梯度可以乘以一个接近1的系数直接穿过多步既不会被tanh的饱和区吃掉也不会因为权重矩阵的连乘而指数级缩小。这就是LSTM相对RNN的核心优势它不是完全消灭了梯度消失而是给了梯度一条绕过非线性的路径。当模型需要记住长期信息时遗忘门会学到接近1的值把那条通路打开需要清空时遗忘门学到接近0的值把通路关掉。这种可学习的开关机制让网络自己决定哪些信息值得穿越长距离。需要强调的是LSTM并不是万能的。如果任务确实要求每一步都剧烈改动细胞状态梯度依然可能消散。但相比RNNLSTM把长程记忆这个能力从几乎不可能变成了可以通过学习实现。这就是它二十多年经久不衰的根本原因也是理解后来所有序列模型的一把钥匙。3.4 一个完整的LSTM前向计算示例用一个具体例子走一遍流程。假设每个词向量x_t是4维隐藏状态维度是3初始h_0和C_0都设为零向量。第一步拼接h_0和x_1得到一个7维向量。遗忘门对这个拼接向量做线性变换加sigmoid得到3个0到1之间的值输入门同样得到3个值候选状态用tanh压到-1到1之间。然后C_1 f_1 * C_0 i_1 * g_1由于C_0是零向量这一步其实就是把候选状态按输入门的打开程度写入。h_1 o_1 * tanh(C_1)。第二步C_0变成了C_1遗忘门开始决定上一轮记忆保留多少输入门决定新信息写入多少就这样一步步滚动下去。这种边读边写、按需遗忘的机制让LSTM在处理长句子时不会像RNN那样早期信息被层层稀释。对比参数量也很直观。以输入维度d、隐藏维度h为例RNN的循环部分参数量大约是h*(dh)hLSTM因为有4组权重遗忘门、输入门、候选状态、输出门参数量大约是4h(dh)4*h差不多是RNN的4倍。这是结构带来的成本用更多参数换更长记忆你要心里有数。4. GRU把三个门砍到两个之后4.1 更新门与重置门的设计逻辑GRUGated Recurrent Unit是2014年提出的可以看成LSTM的精简版。它的设计目标很明确保持门控机制带来的长程记忆能力同时减少参数、简化结构。GRU只有两个门——更新门update gate和重置门reset gate而且没有独立的细胞状态隐藏状态本身就承担了记忆载体的角色。重置门的作用是决定过去的隐藏状态有多少需要被遗忘以便更好地处理当前输入。更新门的作用是决定新的隐藏状态里旧状态和新候选状态各占多大比例。具体公式如下r_t sigmoid(W_r · [h_{t-1}, x_t] b_r) z_t sigmoid(W_z · [h_{t-1}, x_t] b_z) h_t (1 - z_t) * h_{t-1} z_t * tanh(W_h · [r_t * h_{t-1}, x_t] b_h)注意最后一行更新门做了一个软插值新的隐藏状态是旧隐藏状态和新候选状态的加权平均权重由z_t控制。这种设计很优雅——它把LSTM里遗忘门和输入门的分工合并成了一个新旧比例的旋钮。z_t接近1时模型倾向于保留旧记忆、几乎不吸收新信息z_t接近0时模型倾向于重置记忆、更多地吸收当前输入。4.2 公式对比LSTM与GRU的差异到底在哪把两者放在一起对比会看得更清楚。LSTM有三个门和一条独立的细胞状态信息的写入和遗忘是分开控制的输出时还要经过输出门和tanh的二次加工。GRU只有两个门没有细胞状态隐藏状态本身就是记忆更新门同时管理遗忘旧信息和写入新信息两个动作而且因为(1 - z_t)的存在这两个动作被强制成互补关系——忘得越多新信息进得越猛。这种差异带来一个有趣的工程特性GRU的参数更少同等隐藏维度下大约比LSTM少四分之一到三分之一。在数据量不太大时GRU往往更容易训练、更不容易过拟合。在不少基准任务上GRU的效果和LSTM非常接近甚至偶尔更好尤其在小数据集和短序列场景下。从梯度角度看GRU同样保留了梯度高速公路更新门z_t接近1时h_t对h_{t-1}的导数接近1梯度可以沿这条路径远距离传播。所以GRU缓解梯度消失的能力并不比LSTM差太多但结构更简单、计算资源需求更少。可以说GRU是在长程记忆能力和结构精简之间找到了一个相当好的平衡点。4.3 参数量与计算量的实测对比我习惯用PyTorch在相同配置下跑对比词向量维度128隐藏维度128单层循环网络序列长度截断到50。RNN的循环部分参数量约3.3万LSTM约13.1万GRU约9.8万。训练到相同验证精度时GRU的收敛速度往往最快LSTM略慢但很稳RNN在小规模任务上也能凑合可一旦序列变长或语义依赖变复杂就开始掉链子。计算时间上LSTM因为多了几个门矩阵乘法单步前向大约比GRU慢20%到30%。在大规模序列数据、显存受限的场景下这个差距是实打实的成本。所以我后来在做工程项目的序列编码环节时默认选择往往是GRU只有任务确实需要更精细的门控分工时才换回LSTM。5. 实战选型RNN、LSTM、GRU到底该用哪个5.1 不同场景下的选择依据先给出一句大实话论文里常见的RNN效果差、LSTM好是特定任务下的经验规律不是普适真理。实际选型要看的不是名气而是任务的数据量、序列长度、依赖距离和可解释性要求。如果序列很短比如10步以内、信息依赖简单RNN完全够用参数量最小、训练最快这个阶段LSTM和GRU的优势发挥不出来反而因为参数多需要更多数据。如果序列长度中等、依赖距离在几十步内GRU是性价比最高的选择。如果任务对记忆的精细控制有明确需求像语言建模、机器翻译这类需要同时处理记住主语信息和丢弃当前时态细节的高复杂度任务LSTM仍然是稳妥之选。另外要考虑部署环境。LSTM和GRU的推理速度差异在CPU上会被放大移动端或嵌入式场景里参数量和计算量就是生命线。我做过一个端侧关键词识别的小项目用GRU替换LSTM之后模型体积小了一半推理时延大约降了三分之一准确率只掉了不到一个点——这种取舍在真实产品里非常典型。5.2 相同条件下三者的效果对比记录放一份我自己跑过的对比记录在这里。任务是对产品评论做情感二分类训练集约5万条序列长度截断到100三者使用相同的词向量初始化、Adam优化器和学习率1e-3模型参数量隐藏维度128验证准确率收敛速度训练曲线稳定性RNN约3.3万84%左右较快后期loss持续震荡LSTM约13.1万89.5%较慢很平顺GRU约9.8万89.1%最快较平顺另一个让我印象深刻的实验是把序列长度从100拉到200。RNN的准确率直接掉到80%以下而LSTM和GRU只降了1到2个百分点。这个对比非常直观地说明了门控机制对长程依赖的承载力。不过也要说清楚当我换成更强的编码方式双向循环再加注意力之后LSTM和GRU之间的差距基本就消失了。两者的上限更多由任务和数据决定而不是由结构决定。5.3 训练技巧与踩坑经验这几个网络虽然经典但训练起来有几个坑值得专门讲。第一初始化很重要。RNN系模型对参数初始化非常敏感推荐使用正交初始化或较小的均匀初始化。我见过不少LSTM不收敛的案例最后发现只是初始化范围太大导致隐藏状态一开始就饱和。第二梯度裁剪一定要做clip_grad_norm_的阈值设置在1到5之间。虽然LSTM和GRU比RNN稳健但长序列训练时梯度爆炸依然可能出现。第三如果序列特别长可以考虑截断反向传播truncated BPTT把长序列切成若干固定长度的块每个块只回传块内梯度。这会牺牲一部分长程依赖但能显著提升训练稳定性。另外一个容易被忽略的细节是输入特征尺度。词向量的范数如果差异过大会干扰门控sigmoid的输入分布让门一开始就偏向0或1导致记忆行为固化。我习惯对词向量做L2归一化或者用LayerNorm稳定分布这个小改动在很多任务上比调学习率还管用。# 训练循环中必加的一段 torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm2.0) # 观察梯度是否消失的常用手段 total_norm 0.0 for p in model.parameters(): if p.grad is not None: total_norm p.grad.norm().item() ** 2 print(fgrad norm: {total_norm ** 0.5:.4f})5.4 超越RNN注意力机制和Transformer之后还要不要学这些最后必须回答一个绕不开的问题现在Transformer几乎接管了一切序列建模任务很多主流预训练模型的基础结构里根本没有循环单元。那RNN、LSTM、GRU是不是就过时了我的看法是恰恰相反理解它们反而能帮你更容易理解Transformer。Transformer里的自注意力机制本质上和LSTM做的是同一件事——让信息在序列的长距离之间流动。区别在于LSTM是顺序处理、用门控决定丢哪些信息自注意力是并行处理、用注意力权重决定哪些位置需要交互。你只要把LSTM的遗忘门想象成学出来的注意力权重把细胞状态想象成残差连接上的信息累积就会发现两者的设计哲学高度相似。事实上Transformer里的残差连接和LayerNorm很多设计思路都延续了RNN时代的实践经验。在工程上RNN家族也远没有退休。时序预测、在线流式处理、低资源设备上的语音识别这些场景对延迟和内存的要求往往让大模型显得笨重而GRU这类小模型反而能跑得又快又好。我这两年做端侧项目兜兜转转又用回了GRU——不是因为它新而是因为它在这个场景下就是最顺手的选择。所以我的建议是不要因为它们老就跳过也不要因为新就盲目追随。把RNN、LSTM、GRU的机制吃透你收获的不只是三个模型而是一套理解序列建模问题的思维方式。这套思维方式在你后续学习注意力机制、理解各种新架构时会反复被用到。我到现在看一篇新论文还是会先把它的结构和LSTM做对比——这个习惯帮我省下了大量理解成本。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑