批处理如何在 pytorch 的 seq2seq 模型中工作?

How does batching work in a seq2seq model in pytorch?

我正在尝试在 Pytorch 中实现一个 seq2seq 模型,但我在批处理方面遇到了一些问题。 例如我有一批数据,其尺寸为

[batch_size, sequence_lengths, encoding_dimension]

批次中每个示例的序列长度不同。

现在,我设法通过将批处理中的每个元素填充到最长序列的长度来完成编码部分。

这样,如果我将一个形状与上述相同的批次作为我的网络的输入,我将得到以下输出:

output, of shape [batch_size, sequence_lengths, hidden_layer_dimension]

hidden state, of shape [batch_size, hidden_layer_dimension]

cell state, of shape [batch_size, hidden_layer_dimension]

现在,从 输出 ,我为每个序列取最后一个相关元素,即对应于最后一个非填充元素的 sequence_lengths 维度上的元素的顺序。因此我得到的最终输出的形状是 [batch_size, hidden_layer_dimension].

但是现在我遇到了从这个向量解码它的问题。如何处理同一批中不同长度序列的解码?我尝试 google 它并找到 this,但他们似乎没有解决问题。我想对整个批次逐个元素地做,但是我有传递初始隐藏状态的问题,因为来自编码器的形状是 [batch_size, hidden_layer_dimension],而来自解码器的是形状 [1, hidden_layer_dimension].

我错过了什么吗?感谢您的帮助!

你没有遗漏任何东西。我可以帮助你,因为我已经使用 PyTorch 开发了几个 sequence-to-sequence 应用程序。我在下面给你一个简单的例子。

class Seq2Seq(nn.Module):
    """A Seq2seq network trained on predicting the next query."""

    def __init__(self, dictionary, embedding_index, args):
        super(Seq2Seq, self).__init__()

        self.config = args
        self.num_directions = 2 if self.config.bidirection else 1

        self.embedding = EmbeddingLayer(len(dictionary), self.config)
        self.embedding.init_embedding_weights(dictionary, embedding_index, self.config.emsize)

        self.encoder = Encoder(self.config.emsize, self.config.nhid_enc, self.config.bidirection, self.config)
        self.decoder = Decoder(self.config.emsize, self.config.nhid_enc * self.num_directions, len(dictionary),
                                   self.config)

    @staticmethod
    def compute_decoding_loss(logits, target, seq_idx, length):
        losses = -torch.gather(logits, dim=1, index=target.unsqueeze(1)).squeeze()
        mask = helper.mask(length, seq_idx)  # mask: batch x 1
        losses = losses * mask.float()
        num_non_zero_elem = torch.nonzero(mask.data).size()
        if not num_non_zero_elem:
        return losses.sum(), 0 if not num_non_zero_elem else losses.sum(), num_non_zero_elem[0]

    def forward(self, q1_var, q1_len, q2_var, q2_len):
        # encode the query
        embedded_q1 = self.embedding(q1_var)
        encoded_q1, hidden = self.encoder(embedded_q1, q1_len)

        if self.config.bidirection:
            if self.config.model == 'LSTM':
                h_t, c_t = hidden[0][-2:], hidden[1][-2:]
                decoder_hidden = torch.cat((h_t[0].unsqueeze(0), h_t[1].unsqueeze(0)), 2), torch.cat(
                    (c_t[0].unsqueeze(0), c_t[1].unsqueeze(0)), 2)
            else:
                h_t = hidden[0][-2:]
                decoder_hidden = torch.cat((h_t[0].unsqueeze(0), h_t[1].unsqueeze(0)), 2)
        else:
            if self.config.model == 'LSTM':
                decoder_hidden = hidden[0][-1], hidden[1][-1]
            else:
                decoder_hidden = hidden[-1]

        decoding_loss, total_local_decoding_loss_element = 0, 0
        for idx in range(q2_var.size(1) - 1):
            input_variable = q2_var[:, idx]
            embedded_decoder_input = self.embedding(input_variable).unsqueeze(1)
            decoder_output, decoder_hidden = self.decoder(embedded_decoder_input, decoder_hidden)
            local_loss, num_local_loss = self.compute_decoding_loss(decoder_output, q2_var[:, idx + 1], idx, q2_len)
            decoding_loss += local_loss
            total_local_decoding_loss_element += num_local_loss

        if total_local_decoding_loss_element > 0:
            decoding_loss = decoding_loss / total_local_decoding_loss_element

        return decoding_loss

可以看到完整的源码here。此应用程序是关于根据当前 web-search 查询预测用户的下一个 web-search 查询。


您问题的回答者

How do I handle a decoding of sequences of different lengths in the same batch?

你有填充序列,所以你可以认为所有序列的长度相同。但是当您计算损失时,您需要使用 masking.

忽略那些填充项的损失

我在上面的示例中使用了 masking 技术来实现相同的效果。

此外,您完全正确:您需要逐个元素解码 mini-batches。初始解码器状态 [batch_size, hidden_layer_dimension] 也很好。您只需要在维度 0 处取消压缩,使其成为 [1, batch_size, hidden_layer_dimension].

请注意,您不需要遍历批处理中的每个示例,您可以一次执行整个批处理,但您需要遍历序列的元素。