如何作为打包序列给出的填充序列由pytorch中的RNN处理?

Was*_*mad 10 pytorch

在pytorch中,我们可以给出一个打包序列作为RNN的输入.从官方文档中,RNN的输入可以如下.

input(seq_len,batch,input_size):包含输入序列特征的张量.输入也可以是打包的可变长度序列.

packed = torch.nn.utils.rnn.pack_padded_sequence(embedded, input_lengths)
outputs, hidden = self.rnn(packed, hidden)
outputs, output_lengths = torch.nn.utils.rnn.pad_packed_sequence(outputs)
Run Code Online (Sandbox Code Playgroud)

这里embedded是批量输入的嵌入表示.

我的问题是,如何对RNN中的打包序列进行计算?如何通过打包表示计算批量填充序列的隐藏状态?

Cry*_*ina 2

对于第二个问题:填充序列的隐藏状态将不会被计算。

要回答这是如何发生的,让我们首先看看对我们有什么pack_padded_sequence影响:

from torch.nn.utils.rnn import pad_sequence, pad_packed_sequence, pack_padded_sequence

raw = [ torch.ones(25, 300) / 2, 
        torch.ones(22, 300) / 2.3, 
        torch.ones(15, 300) / 3.2 ]
padded = pad_sequence(raw)  # size: [25, 3, 300]

lengths = torch.as_tensor([25, 22, 15], dtype=torch.int64)
packed = pack_padded_sequence(padded, lengths)
Run Code Online (Sandbox Code Playgroud)

到目前为止,我们随机创建了一个具有不同长度(RNN 中的时间步长)的三个张量,我们首先将它们填充到相同的长度,然后将其打包。现在如果我们运行

print(padded.size())
print(packed.data.size()) # packed.data refers to the "packed" tensor
Run Code Online (Sandbox Code Playgroud)

我们会看到:

torch.Size([25, 3, 300])
torch.Size([62, 300])
Run Code Online (Sandbox Code Playgroud)

显然 62 不是来自 25 * 3。所以所做的pack_padded_sequence只是根据lengths我们传递给的张量保留每个批次条目的有意义的时间步长pack_padded_sequence(即,如果我们将 [25, 25, 25] 传递给它,则packed.data仍然会为 [75, 300],即使原始张量没有改变)。简而言之,rnn 甚至看不到 pack_padded_sequence 的 pad 时间步长

现在让我们看看传递paddedpackedrnn之后有什么区别

rnn = torch.nn.RNN(input_size=300, hidden_size=2)
padded_outp, padded_hn = rnn(padded) # size: [25, 3, 2] / [1, 3, 2]
packed_outp, packed_hn = rnn(packed) # 'PackedSequence' Obj / [1, 3, 2]
undo_packed_outp, _ = pad_packed_sequence(packed_outp)

# return "h_n"
print(padded_hn) # tensor([[[-0.2329, -0.6179], [-0.1158, -0.5430],[ 0.0998, -0.3768]]]) 
print(packed_hn) # tensor([[[-0.2329, -0.6179], [ 0.5622,  0.1288], [ 0.5683,  0.1327]]]

# the output of last timestep (the 25-th timestep)
print(padded_outp[-1]) # tensor([[[-0.2329, -0.6179], [-0.1158, -0.5430],[ 0.0998, -0.3768]]]) 
print(undo_packed_outp.data[-1]) # tensor([[-0.2329, -0.6179], [ 0.0000,  0.0000], [ 0.0000,  0.0000]]
Run Code Online (Sandbox Code Playgroud)

padded_hn和的值packed_hn是不同的,因为 rnn 确实计算了“打包”(PackedSequence 对象)的 pad padded,但也没有计算“打包”(PackedSequence 对象)的 pad,这也可以从最后一个隐藏状态观察到:所有三个批次条目的padded最后隐藏状态都为非零,即使它的长度小于25。但是对于packed,不计算较短数据的最后一个隐藏状态(即 0)

ps另一个观察结果:

print([(undo_packed_outp[:, i, :].sum(-1) != 0).sum() for i in range(3)])
Run Code Online (Sandbox Code Playgroud)

会给我们[tensor(25), tensor(22), tensor(15)],它与我们输入的实际长度对齐。