PyTorch的{{3}}似乎为递归神经网络的可变长度输入提供PackedSequence
。但是,我发现正确使用它有点困难。
使用pad_packed_sequence
恢复由pack_padded_sequence
提供的RNN图层的输出,我们得到T x B x N
张量outputs
,其中T
是最大值时间步长,B
是批量大小,N
是隐藏的大小。我发现对于批处理中的短序列,后续输出将全为零。
以下是我的问题。
outputs[-1]
将给出错误的结果,因为此张量包含许多短序列的零。需要按序列长度构建索引以获取所有序列的单个最后输出。有没有更简单的方法呢?N x O
并将批量输出T x B x O
重新整形为TB x O
并使用true计算交叉熵损失目标TB
(通常是语言模型中的整数)。在这种情况下,批量输出中的这些零是否重要?答案 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版本。
PyTorch 0.2.0:现在,pytorch支持使用ignore_index
参数直接在CrossEntropyLoss中屏蔽。例如,在语言建模或seq2seq中,我添加零填充,我掩盖零填充的单词(目标),就像这样:
loss_function = nn.CrossEntropyLoss(ignore_index = 0)
PyTorch 0.1.12及更早版本:在较旧版本的PyTorch中,不支持屏蔽,因此您必须实施自己的解决方法。我使用的解决方案是masked_cross_entropy.py,jihunchoi。您可能也对此discussion感兴趣。
答案 1 :(得分:0)
几天前,我发现this method使用索引编制一个线来完成同一任务。
我首先要处理我的数据集([batch size, sequence length, features]
),所以对我来说:
unpacked_out = unpacked_out[np.arange(unpacked_out.shape[0]), lengths - 1, :]
其中unpacked_out
是torch.nn.utils.rnn.pad_packed_sequence
的输出。
我将其与方法described here进行了比较,该方法看起来类似于Christos Baziotis在上面使用的last_timestep()
方法(也建议使用here),结果与我相同情况。