Получение последнего элемента каждой последовательности из упакованной последовательности

#indexing #deep-learning #pytorch #tensor #zero-padding

#индексирование #глубокое обучение #pytorch #тензор #заполнение нулем

Вопрос:

Я пытаюсь поместить упакованную и дополненную последовательность через 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 .`

Я мог бы перебрать все это и построить новый тензор, содержащий нужные мне элементы, но я надеялся на встроенный подход, который использовал бы преимущества некоторой умной математики. Я боюсь, что ручной цикл и сборка приведут к очень низкой производительности.

Ответ №1:

Вместо двух последних операций last_seq_idxs и last_seq_items вы могли бы просто выполнить last_seq_items=output[torch.arange(4), input_sizes-1] .

Я не думаю, index_select что поступаю правильно. Он выберет весь пакет по переданному вами индексу, и, следовательно, ваш размер вывода равен [4,4,12].

Комментарии:

1. Спасибо. Это действительно выглядит более прямолинейно, чем мой подход.

Ответ №2:

Более подробная альтернатива ответу Уманга Гупты:

 # ...
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)
  

Комментарии:

1. Я думаю, что это было бы медленнее, не так ли?

2. @UmangGupta О да, я определенно так думаю, поскольку ваш — это всего лишь фрагмент, тогда как мой подход требует итерации и конкатенации. Я опубликовал свой в иллюстративных целях. Но именно ваш должен быть использован.