对于Pytorch中的GRU单元,隐藏和输出是否相同?

时间:2019-06-19 23:14:40

标签: machine-learning neural-network pytorch recurrent-neural-network

当检查GRU getPageh_n的输出不相同时,我确实从概念上理解LSTM或GRU应该做什么(由于这个问题What's the difference between "hidden" and "output" in PyTorch LSTM?)应该是...

output

它们彼此互为换位...为什么?

3 个答案:

答案 0 :(得分:1)

在PyTorch中,您需要记住三件事。 假设您使用的是torch.nn.GRU之类的东西,并且如果您使用它来制作多层RNN,则您正在使用num_layers参数这样做(而不是自己从头开始在单个图层中构建一个。)

  1. output将为您提供每个时间步长的网络隐藏层输出,但仅针对最后一层。这在许多应用中很有用,尤其是注意编码器/解码器。 (这些体系结构从所有隐藏的输出中构建了一个“上下文”层,将它们作为一个独立的单元来放置非常有用。)

  2. h_n仅在最后一个时间步为您提供隐藏层输出,但在所有层中都为您提供。因此,当且仅当您具有单层体系结构时,h_noutput的严格子集。否则,outputh_n相交,但不是彼此严格的子集。 (在编码器-解码器模型中,您通常会希望它们来自编码器,以便快速启动解码器。)

  3. 如果您使用的是双向输出,并且想实际验证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,因为我们只对最后一层感兴趣。