动手学深度学习——双向循环神经网络

张开发
2026/4/14 14:37:08 15 分钟阅读

分享文章

动手学深度学习——双向循环神经网络
1. 前言前面我们已经学习了RNNGRULSTM深层循环神经网络到这里循环神经网络这条线已经很清楚了模型沿着时间从前往后处理序列并把前面的信息传到后面。这种方式当然很自然因为很多任务本来就是按时间向前推进的。但新的问题也很快出现了如果当前时刻的理解不仅需要前面的信息也需要后面的信息怎么办例如一句话里一个词的含义有时要结合前后文一起看才更准确。这就引出了这一节的主题双向循环神经网络Bidirectional RNN, BiRNN它的核心思想非常直接不仅从左到右看序列也从右到左看序列。2. 为什么单向 RNN 不够普通 RNN、GRU、LSTM 默认都是单向的。也就是说在时间步t上模型通常只能利用当前输入以及前面时刻的信息这意味着它对未来信息是“看不到”的。但很多任务里仅靠左侧上下文并不总是够用。2.1 文本理解中的例子例如一句话他拿着一个很轻的苹果如果这里只看到“苹果”前面模型可能还不确定这是水果还是苹果公司相关产品如果再结合后文例如“吃了一口”“发布会”“手机”含义就更清楚了。这说明理解当前位置有时需要前后文一起参与。3. 什么是双向循环神经网络双向循环神经网络可以简单理解为在同一个序列上建立两条循环链一条从前往后一条从后往前。也就是说对一个序列x1, x2, x3, ..., xT会同时有前向 RNN从x1走到xT后向 RNN从xT走到x1然后在每个时间步把这两个方向得到的隐藏表示合起来共同作为当前位置的表示。所以双向 RNN 的本质是让每个位置同时拥有左上下文和右上下文信息。4. 双向 RNN 的结构怎么理解假设我们有一个序列x1, x2, x3, x4那么双向 RNN 会有两套隐藏状态。前向隐藏状态H_1^f, H_2^f, H_3^f, H_4^f它们从左到右递推。后向隐藏状态H_1^b, H_2^b, H_3^b, H_4^b它们从右到左递推。在位置t最终表示通常会把两者拼接起来H_t [H_t^f ; H_t^b]这表示H_t^f提供左侧上下文信息H_t^b提供右侧上下文信息于是当前位置的表示更完整了。5. 为什么双向 RNN 更适合理解任务对于很多“理解类任务”双向结构非常有优势。因为这类任务的目标通常是对整个输入序列做分析例如命名实体识别词性标注序列标注文本分类中的句子编码语音识别中的帧标注在这些任务里当前位置的判断通常可以合法地依赖未来信息。因为整个序列在推理时本来就已经完整给出。所以双向 RNN 特别适合输入完整已知目标是更充分理解当前位置或整体序列。6. 双向 RNN 不适合什么任务这点一定要讲清楚。双向 RNN 虽然强但它不是万能的。它不适合那些需要“实时生成”的任务。例如6.1 语言模型语言模型要做的是根据前文预测下一个词这时候你根本不可能提前看到“后文”否则就等于作弊了。6.2 实时在线预测如果任务要求模型边读边输出而未来数据尚未到来双向结构也无法使用。所以双向 RNN 的前提是整个序列在处理时已经完整可见。这也是它和普通单向语言模型的最大使用场景差异。7. 双向 RNN 的核心公式怎么理解如果用更数学一点的形式表示双向 RNN 通常可以写成前向递推H_t^f f(X_t, H_{t-1}^f)后向递推H_t^b f(X_t, H_{t1}^b)然后当前时刻的输出或表示可以写成H_t [H_t^f ; H_t^b]或者再接一个线性层映射成最终输出。这里最关键的是前向链依赖前一个状态后向链依赖后一个状态最后把两个方向合并这样当前位置就拥有了双向上下文。8. 为什么通常是“拼接”而不是“相加”在双向 RNN 中最常见做法是把两个方向的隐藏状态concat也就是拼接起来。例如前向隐藏状态是 256 维后向隐藏状态也是 256 维拼接后就得到512 维为什么更常用拼接因为拼接能最大程度保留两个方向各自的信息。如果直接相加会把两部分混在一起信息区分度没那么强。所以一般默认理解双向 RNN 输出时隐藏维度会翻倍。9. 双向 RNN 和深层 RNN 有什么区别这两个概念非常容易混一定要区分。深层 RNN强调的是层数增加也就是同一时间步上多层循环单元堆叠。双向 RNN强调的是方向增加也就是同一层里既从左往右看也从右往左看。所以它们是两个完全不同的维度深层沿“深度”加层双向沿“方向”加链而且它们还能组合起来形成多层双向 LSTM / 多层双向 GRU这在实际任务里非常常见。10. 双向 RNN 在代码层面怎么实现PyTorch 对双向循环网络也做了很方便的封装。核心参数就是bidirectionalTrue例如lstm_layer nn.LSTM( input_sizevocab_size, hidden_sizenum_hiddens, bidirectionalTrue )这一句就表示用 LSTM 作为循环单元同时建立前向和后向两套链这和前面深层网络里改num_layers的感觉很像也是一个非常直接的接口。11.bidirectionalTrue后最明显的变化是什么最明显的变化有两个。第一输出隐藏维度翻倍如果单向 LSTM 的输出是hidden_size那么双向后就会变成2 * hidden_size因为前向和后向隐藏状态拼接了。第二状态第一维会乘上方向数原来的状态形状一般是(num_layers, batch_size, hidden_size)双向后就会变成(num_layers * 2, batch_size, hidden_size)这里多出来的2就是两个方向。12. 为什么状态第一维变成num_layers * 2因为每一层现在不再只有一个方向的状态而是前向一份后向一份例如单层单向 LSTM(1, batch_size, hidden_size)单层双向 LSTM(2, batch_size, hidden_size)两层双向 LSTM(4, batch_size, hidden_size)因为2 层 × 2 方向 4这点在代码里非常重要后面初始化状态和理解输出都要用到。13. 双向 RNN 的输出为什么更强因为每个时间步的表示更完整。单向 RNN 在位置t的表示大致只能概括我到当前位置之前看到了什么而双向 RNN 在位置t的表示变成我到当前位置之前看到了什么加上我从后往前看到当前位置之后还有什么这让模型更容易 disambiguate消歧义和理解上下文。所以双向结构本质上是在做更充分的上下文编码14. 双向 RNN 在 NLP 中典型适合哪些任务特别适合下面这些任务14.1 序列标注例如词性标注命名实体识别分词因为这些任务对当前位置的判断通常需要前后文共同决定。14.2 编码器比如机器翻译中的编码器经常可以用双向 RNN 来更充分地编码源句子。14.3 句子表示学习如果整个句子已经给定双向 RNN 能更完整提取句子级表示。所以双向 RNN 往往是理解型模块而不是生成型模块15. 为什么双向 RNN 不适合做标准语言模型这一点一定要再强调一遍。语言模型的任务是根据前文预测下一个 token如果你用了双向 RNN那么当前位置的表示已经看到了右侧未来信息。这时候再去预测“下一个词”就不公平了。换句话说双向 RNN 会偷看到答案后面的内容。所以它不适合标准自回归语言模型。这也是为什么 GPT 这类模型本质上都是单向/因果遮罩的。16. 李沐这一节最想让你理解什么这一节的重点不是公式多复杂而是场景意识第一双向 RNN 的价值在于前后文同时利用这会显著增强序列理解能力。第二双向和深层是两个不同维度不要混淆。第三双向特别适合理解任务但不适合标准自回归生成任务。第四代码层面通常只需要加bidirectionalTrue这使得双向循环网络很容易落地。所以这一节本质上是在告诉你循环网络不仅能“记过去”也可以“看未来”。前提是任务允许这样做。17. 双向 RNN 和前面内容怎么接上回头看这条主线会很顺。先有单向 RNN学会沿时间从前往后建模。再有 GRU / LSTM提升单元的记忆管理能力。再有深层循环网络提升层次表示能力。最后引入双向循环网络补上“未来上下文”这一侧信息。所以这一节其实是在循环网络这条线里再打开一个全新维度方向维18. 本节总结这一节我们学习了双向循环神经网络核心内容可以总结为以下几点。18.1 双向 RNN 会同时建立前向和后向两条循环链从而让每个位置同时拥有左右上下文信息。18.2 双向结构特别适合理解类任务例如序列标注、句子编码、编码器建模等。18.3 双向和深层是两个不同维度深层加层数双向加方向18.4 在代码中通常通过bidirectionalTrue开启这是最直接的实现方式。18.5 双向 RNN 不适合标准自回归语言模型因为它会使用未来信息。19. 学习感悟这一节很有意思因为它让“序列建模”第一次摆脱了“只能往前看”的限制。以前我们总觉得序列模型就是一步一步从前往后走但双向 RNN 提醒我们如果任务目标不是实时生成而是充分理解那么未来信息同样值得利用。这其实非常符合人类阅读的直觉。很多时候我们理解一句话里的某个词也会结合前后文一起判断而不是只看左边。从这个角度看双向 RNN 让模型更接近“完整阅读”的方式。

更多文章