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

GPa*_*olo 5 python neural-network lstm recurrent-neural-network pytorch

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

[batch_size,sequence_lengths,encoding_dimension]

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

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

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

形状的输出[batch_size, sequence_lengths, hidden_layer_dimension]

形状的隐藏状态[batch_size, hidden_layer_dimension]

细胞状态,形状[batch_size, hidden_layer_dimension]

现在,从输出中,我为每个序列获取最后一个相关元素,即沿着sequence_lengths维度的元素与序列的最后一个非填充元素相对应。因此,我得到的最终输出是定型的[batch_size, hidden_layer_dimension]

但是现在我有从这个向量解码它的问题。如何处理同一批中不同长度的序列的解码?我试图用Google搜索它,发现了这个问题,但是他们似乎并没有解决这个问题。我本来打算对整个批处理逐个元素进行处理,但是我遇到了传递初始隐藏状态的问题,因为来自编码器的状态是形状[batch_size, hidden_layer_dimension],而来自解码器的状态是形状[1, hidden_layer_dimension]

我想念什么吗?谢谢您的帮助!

Was*_*mad 3

你没有错过任何东西。我可以帮助您,因为我已经使用 PyTorch 开发了多个序列到序列应用程序。我在下面给你一个简单的例子。

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
Run Code Online (Sandbox Code Playgroud)

您可以在此处查看完整的源代码。该应用程序是关于在给定当前网络搜索查询的情况下预测用户的下一个网络搜索查询。


您的问题的回答者

如何处理同一批次中不同长度序列的解码?

您有填充序列,因此您可以考虑所有序列的长度相同。但是当你计算损失时,你需要忽略那些使用masking填充的项的损失。

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

另外,您在这一点上是绝对正确的:您需要逐个元素地解码小批量。初始解码器状态[batch_size, hidden_layer_dimension]也很好。你只需要在维度 0 处解压它,使其成为[1, batch_size, hidden_layer_dimension]

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