You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyTorch中双向GRU输出与最后层隐藏状态为何不一致?

PyTorch双向GRU中Output与Hidden状态的差异解析

问题复现代码

import torch
from torch import nn

input_ids = torch.randn((1,4,3))
gru = nn.GRU(input_size=3, hidden_size=5, bidirectional=True, num_layers=1, batch_first=True)
o, h = gru(input_ids)

print(o[:,-1,:]) # 获取最后一层隐藏状态的最后时间步
print(torch.cat([h[0::2, :, :], h[1::2,:,:]], dim=-1)) # 拼接正向与反向隐藏状态 

输出结果:

tensor([[-0.2308,  0.0597, -0.4346, -0.3713,  0.2811,  0.2582, -0.0627,  0.3114,
         -0.3813,  0.0590]], grad_fn=<SliceBackward0>)

tensor([[[-0.2308,  0.0597, -0.4346, -0.3713,  0.2811,  0.3513, -0.0135,
           0.1894, -0.4211,  0.0058]]], grad_fn=<CatBackward0>)

核心差异原因

单层单向GRU中,output的最后一个时间步结果和hidden状态完全一致,这是因为单向GRU的最终隐藏状态就是序列最后一个时间步的输出特征。但双向GRU的逻辑完全不同:

双向GRU包含正向、反向两个独立的GRU分支:

  • 正向GRU按序列顺序(t0→t1→t2→t3)处理输入,最终隐藏状态h[0]是处理完最后一个时间步t3后的状态,对应output最后时间步的前半部分特征;
  • 反向GRU按序列逆序(t3→t2→t1→t0)处理输入,最终隐藏状态h[1]是处理完第一个时间步t0后的状态,而output最后时间步的后半部分特征,是反向GRU处理t3时的输出(即反向GRU的第一个时间步输出)。

这就是代码中两组结果不一致的核心原因——你拼接的反向状态和output最后时间步的反向状态,根本不是同一个时间步的输出。

反向传播的差异

反向传播没有额外特殊处理,梯度计算完全基于各自的计算图:

  • output[:,-1,:]的梯度对应序列最后一个时间步的正向+反向输出节点的梯度;
  • h的梯度对应正向GRU最终状态、反向GRU最终状态节点的梯度。
    由于前向传播时两者代表的状态本身不同,反向传播的梯度自然也会不同,但都是符合GRU计算逻辑的正确结果。

官方社区的解释

双向RNN的设计逻辑可总结为:

正向隐藏状态是处理完序列最后一个元素后的状态,反向隐藏状态是处理完序列第一个元素后的状态(因为反向RNN逆序处理序列)。输出的第t个时间步是正向RNN在t步的输出与反向RNN在t步的输出的拼接(其中反向RNN在t步的输出是处理原序列第L-1-t个元素后的输出)。


内容的提问来源于stack exchange,提问作者jupyter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 07:04:53