从打包序列中获取每个序列的最后一项
Get each sequence's last item from packed sequence
我正在尝试通过 GRU 放置一个打包和填充的序列,并检索每个序列的最后一项的输出。当然,我指的不是 -1
项目,而是实际的最后一个未填充的项目。我们预先知道序列的长度,因此应该很容易为每个序列提取 length-1
项。
我尝试了以下方法
import torch
from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence
# Data
input = torch.Tensor([[[0., 0., 0.],
[1., 0., 1.],
[1., 1., 0.],
[1., 0., 1.],
[1., 0., 1.],
[1., 1., 0.]],
[[1., 1., 0.],
[0., 1., 0.],
[0., 0., 0.],
[0., 1., 0.],
[0., 0., 0.],
[0., 0., 0.]],
[[0., 0., 0.],
[1., 0., 0.],
[1., 1., 1.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]],
[[1., 1., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]]])
lengths = [6, 4, 3, 1]
p = pack_padded_sequence(input, lengths, batch_first=True)
# Forward
gru = torch.nn.GRU(3, 12, batch_first=True)
packed_output, gru_h = gru(p)
# Unpack
output, input_sizes = pad_packed_sequence(packed_output, batch_first=True)
last_seq_idxs = torch.LongTensor([x-1 for x in input_sizes])
last_seq_items = torch.index_select(output, 1, last_seq_idxs)
print(last_seq_items.size())
# torch.Size([4, 4, 12])
但是形状不是我所期望的。我原本希望得到 4x12
,即 last item of each individual sequence x hidden
。`
我可以遍历整个事情,并构建一个包含我需要的项目的新张量,但我希望有一种内置的方法可以利用一些智能数学。我担心手动循环和构建会导致性能非常差。
除了最后两个操作 last_seq_idxs
和 last_seq_items
你可以只做 last_seq_items=output[torch.arange(4), input_sizes-1]
。
我认为 index_select
做的事情不对。它将 select 整个批次位于您传递的索引处,因此您的输出大小为 [4,4,12]。
Umang Gupta 回答的更详细的替代方法:
# ...
output, input_sizes = pad_packed_sequence(packed_output, batch_first=True)
# One per sequence, with its last actual node extracted, and unsqueezed
last_seq = [output[e, i-1, :].unsqueeze(0) for e, i in enumerate(input_sizes)]
# Merge them together all sequences together to get batch
last_seq = torch.cat(last_seq, dim=0)
我正在尝试通过 GRU 放置一个打包和填充的序列,并检索每个序列的最后一项的输出。当然,我指的不是 -1
项目,而是实际的最后一个未填充的项目。我们预先知道序列的长度,因此应该很容易为每个序列提取 length-1
项。
我尝试了以下方法
import torch
from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence
# Data
input = torch.Tensor([[[0., 0., 0.],
[1., 0., 1.],
[1., 1., 0.],
[1., 0., 1.],
[1., 0., 1.],
[1., 1., 0.]],
[[1., 1., 0.],
[0., 1., 0.],
[0., 0., 0.],
[0., 1., 0.],
[0., 0., 0.],
[0., 0., 0.]],
[[0., 0., 0.],
[1., 0., 0.],
[1., 1., 1.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]],
[[1., 1., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]]])
lengths = [6, 4, 3, 1]
p = pack_padded_sequence(input, lengths, batch_first=True)
# Forward
gru = torch.nn.GRU(3, 12, batch_first=True)
packed_output, gru_h = gru(p)
# Unpack
output, input_sizes = pad_packed_sequence(packed_output, batch_first=True)
last_seq_idxs = torch.LongTensor([x-1 for x in input_sizes])
last_seq_items = torch.index_select(output, 1, last_seq_idxs)
print(last_seq_items.size())
# torch.Size([4, 4, 12])
但是形状不是我所期望的。我原本希望得到 4x12
,即 last item of each individual sequence x hidden
。`
我可以遍历整个事情,并构建一个包含我需要的项目的新张量,但我希望有一种内置的方法可以利用一些智能数学。我担心手动循环和构建会导致性能非常差。
除了最后两个操作 last_seq_idxs
和 last_seq_items
你可以只做 last_seq_items=output[torch.arange(4), input_sizes-1]
。
我认为 index_select
做的事情不对。它将 select 整个批次位于您传递的索引处,因此您的输出大小为 [4,4,12]。
Umang Gupta 回答的更详细的替代方法:
# ...
output, input_sizes = pad_packed_sequence(packed_output, batch_first=True)
# One per sequence, with its last actual node extracted, and unsqueezed
last_seq = [output[e, i-1, :].unsqueeze(0) for e, i in enumerate(input_sizes)]
# Merge them together all sequences together to get batch
last_seq = torch.cat(last_seq, dim=0)