当检查GRU getPage
和h_n
的输出不相同时,我确实从概念上理解LSTM或GRU应该做什么(由于这个问题What's the difference between "hidden" and "output" in PyTorch LSTM?)应该是...
output
它们彼此互为换位...为什么?
答案 0 :(得分:1)
在PyTorch中,您需要记住三件事。
假设您使用的是torch.nn.GRU之类的东西,并且如果您使用它来制作多层RNN,则您正在使用num_layers
参数这样做(而不是自己从头开始在单个图层中构建一个。)
output
将为您提供每个时间步长的网络隐藏层输出,但仅针对最后一层。这在许多应用中很有用,尤其是注意编码器/解码器。 (这些体系结构从所有隐藏的输出中构建了一个“上下文”层,将它们作为一个独立的单元来放置非常有用。)
h_n
仅在最后一个时间步为您提供隐藏层输出,但在所有层中都为您提供。因此,当且仅当您具有单层体系结构时,h_n
是output
的严格子集。否则,output
和h_n
相交,但不是彼此严格的子集。 (在编码器-解码器模型中,您通常会希望它们来自编码器,以便快速启动解码器。)
如果您使用的是双向输出,并且想实际验证h_n
中包含output
的一部分(反之亦然),则需要了解PyTorch在输入和输出组织中的场景。具体来说,它将时间反向输入与时间正向输入连接起来,并将它们一起运行。这是字面上的。这意味着,在时间T处的“正向”输出位于紧靠在时间0处的“反向”输出的output
张量的最终位置;如果您正在寻找时间T的“反向”输出,则它位于第一个位置。
尤其是我第一次玩RNN和GRU时,第三点就使我绝对狂热了大约三个小时。公平地说,这也是为什么将h_n
作为输出提供的原因,因此一旦弄清楚了,就不必再担心了,您只需从返回值中获取正确的内容即可。
答案 1 :(得分:0)
不是移调, 当lstm的层为1时,您会得到rnn_output = hidden [-1]
hidden是每一层每一层的输出,它是特定输入时间步长的2D数组,但是lstm返回所有时间步长,因此层的输出应隐藏[-1]
讨论了批处理为1或输出量和隐藏量需要添加一个时的这种情况
答案 2 :(得分:0)
它们不是完全相同。考虑我们具有以下单向 GRU模型:
import torch.nn as nn
import torch
gru = nn.GRU(input_size = 8, hidden_size = 50, num_layers = 3, batch_first = True)
请确保您仔细观察输入形状。
inp = torch.randn(1024, 112, 8)
out, hn = gru(inp)
当然
torch.equal(out, hn)
False
帮助我了解输出与隐藏状态的最有效方法之一是将hn
视为hn.view(num_layers, num_directions, batch, hidden_size)
其中num_directions = 2
用于双向递归网络(以及另一种方式,即我们的情况)。因此,
hn_conceptual_view = hn.view(3, 1, 1024, 50)
文档说明为(请注意斜体和粗体):
h_n的形状(num_layers * num_directions,batch,hidden_size):张量包含t = seq_len的隐藏状态(即最后一个时间步长)
在我们的例子中,它包含时间步t = 112
的隐藏矢量,其中:
形状的输出(seq_len,批处理,num_directions * hidden_size):张量,包含来自GRU的最后一层的输出特征h_t,每个t的 如果已将torch.nn.utils.rnn.PackedSequence作为输入,则输出也将是打包序列。对于未打包的情况,可以使用output.view(seq_len,batch,num_directions,hidden_size)分隔方向,前进和后退分别是方向0和1。
因此,一个人可以做:
torch.equal(out[:, -1], hn_conceptual_view[-1, 0, :, :])
True
说明:我将out[:, -1]
中所有批次的最后一个序列与hn[-1, 0, :, :]
对于双向 GRU(需要先阅读单向):
gru = nn.GRU(input_size = 8, hidden_size = 50, num_layers = 3, batch_first = True bidirectional = True)
inp = torch.randn(1024, 112, 8)
out, hn = gru(inp)
视图更改为(因为有两个方向):
hn_conceptual_view = hn.view(3, 2, 1024, 50)
如果您尝试使用确切的代码:
torch.equal(out[:, -1], hn_conceptual_view[-1, 0, :, :])
False
说明:这是因为我们甚至在比较错误的形状;
out[:, 0].shape
torch.Size([1024, 100])
hn_conceptual_view[-1, 0, :, :].shape
torch.Size([1024, 50])
请记住,对于双向网络,隐藏状态会在每个时间步长(第一个hidden_state
大小(即out[:, 0,
:50
{{1} })是前向网络的隐藏状态,其他]
大小是向后的(即hidden_state
out[:, 0,
50:
)。然后,转发网络的正确比较是:
]
如果要后向网络和since a backward network processes the sequence from time step n ... 1
的隐藏状态。您比较序列的第一时间步长,但最后一个torch.equal(out[:, -1, :50], hn_conceptual_view[-1, 0, :, :])
True
大小,然后将hidden_state
的方向更改为hn_conceptual_view
:
1
总而言之:
单向:
torch.equal(out[:, -1, :50], hn_conceptual_view[-1, 1, :, :])
True
其中rnn_module = nn.RECURRENT_MODULE(num_layers = X, hidden_state = H, batch_first = True)
inp = torch.rand(B, S, E)
output, hn = rnn_module(inp)
hn_conceptual_view = hn.view(X, 1, B, H)
是GRU或LSTM(在撰写本文时),RECURRENT_MODULE
是批处理大小,B
序列长度和S
嵌入大小
E
同样,我们使用torch.equal(output[:, S, :], hn_conceptual_view[-1, 0, :, :])
True
,因为S
是向前的(即单向),并且最后一个时间步长以序列长度rnn_module
存储。
双向:
S
比较
rnn_module = nn.RECURRENT_MODULE(num_layers = X, hidden_state = H, batch_first = True, bidirectional = True)
inp = torch.rand(B, S, E)
output, hn = rnn_module(inp)
hn_conceptual_view = hn.view(X, 2, B, H)
上面是前向网络比较,我们使用torch.equal(output[:, S, :H], hn_conceptual_view[-1, 0, :, :])
True
,因为前向每个时间步都将其隐藏矢量存储在前:H
个元素中。
对于后向网络:
H
我们将torch.equal(output[:, 0, H:], hn_conceptual_view[-1, 1, :, :])
True
的方向更改为hn_conceptual_view
,以获得反向网络的隐藏矢量。
在所有示例中,我们都使用1
,因为我们只对最后一层感兴趣。