如何在pytorch中的seq2seq模型中进行批处理?

时间:2018-03-14 16:48:32

标签: 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]

但是现在我有从这个向量解码它的问题。如何在同一批次中处理不同长度序列的解码?我试图谷歌它并发现this,但它们似乎没有解决问题。我想为整个批处理逐个元素,但是我有问题要传递初始隐藏状态,假设编码器中的那些状态为[batch_size, hidden_layer_dimension],而解码器中的那些将是形状[1, hidden_layer_dimension]

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

2 个答案:

答案 0 :(得分:2)

你没有遗漏任何东西。我可以帮助你,因为我使用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

您可以看到完整的源代码here。该应用程序用于在给定当前Web搜索查询的情况下预测用户的下一个Web搜索查询。

回答您的问题

  

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

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

我在上面的例子中使用了masking技术来实现相同的目标。

此外,您绝对正确:您需要逐个元素地解码迷你批次。初始解码器状态[batch_size, hidden_layer_dimension]也没问题。您只需要在尺寸0处展开它,使其成为[1, batch_size, hidden_layer_dimension]

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

答案 1 :(得分:0)

可能有点老,但我目前正面临着类似的问题:

我将数据分批馈入seq2seq模型,但是当解码器从

预测到一个单词时
decoder_input = torch.tensor([[self.SOS_token] * self.batch_size], device=device)
# decoder_input.size() = ([1,2])
for di in range(target_length):
    decoder_output, decoder_hidden, decoder_attention = self.decoder(decoder_input, decoder_hidden, encoder_outputs)
    topv, topi = decoder_output.data.topk(1)
    decoder_input = topi.squeeze().detach()
    # decoder_input.size() = ([1])
    outputs[di] = decoder_output
    if decoder_input.item() == self.EOS_token:
        break

例如,如果我使用batch_size = 2我有2个SOS_tokens,但是随后我仅得到1个单词作为预测,由于该模型需要不同的大小,因此无法通过模型进行计算。我可以像SOS_token一样乘以它吗? 最好的