如何在PyTorch中正确实现批量输入LSTM网络?

时间:2017-09-24 07:27:03

标签: pytorch

PyTorch的{​​{3}}似乎为递归神经网络的可变长度输入提供PackedSequence。但是,我发现正确使用它有点困难。

使用pad_packed_sequence恢复由pack_padded_sequence提供的RNN图层的输出,我们得到T x B x N张量outputs,其中T是最大值时间步长,B是批量大小,N是隐藏的大小。我发现对于批处理中的短序列,后续输出将全为零。

以下是我的问题。

  1. 对于单个输出任务,其中一个将需要所有序列的最后一个输出,简单outputs[-1]将给出错误的结果,因为此张量包含许多短序列的零。需要按序列长度构建索引以获取所有序列的单个最后输出。有没有更简单的方法呢?
  2. 对于多输出任务(例如seq2seq),通常会添加一个线性图层N x O并将批量输出T x B x O重新整形为TB x O并使用true计算交叉熵损失目标TB(通常是语言模型中的整数)。在这种情况下,批量输出中的这些零是否重要?

2 个答案:

答案 0 :(得分:8)

问题1 - 最后一个时间步长

这是我用来获取最后一个时间步的输出的代码。我不知道是否有更简单的解决方案。如果是的话,我想知道。我按照此discussion抓取了last_timestep方法的相关代码段。这是我的前锋。

class BaselineRNN(nn.Module):
    def __init__(self, **kwargs):
        ...

    def last_timestep(self, unpacked, lengths):
        # Index of the last output for each sequence.
        idx = (lengths - 1).view(-1, 1).expand(unpacked.size(0),
                                               unpacked.size(2)).unsqueeze(1)
        return unpacked.gather(1, idx).squeeze()

    def forward(self, x, lengths):
        embs = self.embedding(x)

        # pack the batch
        packed = pack_padded_sequence(embs, list(lengths.data),
                                      batch_first=True)

        out_packed, (h, c) = self.rnn(packed)

        out_unpacked, _ = pad_packed_sequence(out_packed, batch_first=True)

        # get the outputs from the last *non-masked* timestep for each sentence
        last_outputs = self.last_timestep(out_unpacked, lengths)

        # project to the classes using a linear layer
        logits = self.linear(last_outputs)

        return logits

问题2 - 蒙面交叉熵损失

是的,默认情况下,零填充时间步长(目标)很重要。但是,它很容易掩盖它们。您有两种选择,具体取决于您使用的PyTorch版本。

  1. PyTorch 0.2.0:现在,pytorch支持使用ignore_index参数直接在CrossEntropyLoss中屏蔽。例如,在语言建模或seq2seq中,我添加零填充,我掩盖零填充的单词(目标),就像这样:

    loss_function = nn.CrossEntropyLoss(ignore_index = 0)

  2. PyTorch 0.1.12及更早版本:在较旧版本的PyTorch中,不支持屏蔽,因此您必须实施自己的解决方法。我使用的解决方案是masked_cross_entropy.pyjihunchoi。您可能也对此discussion感兴趣。

答案 1 :(得分:0)

几天前,我发现this method使用索引编制一个线来完成同一任务。

我首先要处理我的数据集([batch size, sequence length, features]),所以对我来说:

unpacked_out = unpacked_out[np.arange(unpacked_out.shape[0]), lengths - 1, :]

其中unpacked_outtorch.nn.utils.rnn.pad_packed_sequence的输出。

我将其与方法described here进行了比较,该方法看起来类似于Christos Baziotis在上面使用的last_timestep()方法(也建议使用here),结果与我相同情况。