尽管这是一个典型的用例,但我找不到一个简单明了的指南,来说明在PyTorch中通过RNN处理填充的迷你批次时,计算损失的标准方法是什么。
我认为一个标准的流程可能是这样的:
-
PyTorch的RNN期望一个形状为(max_seq_len, batch_size, emb_size)的填充批次张量
-
因此,我们可以给嵌入层一个这样的张量:
tensor([[1, 1],[2, 2],[3, 9]])
9是填充索引。批次大小为2。嵌入层会将其转换为形状为(max_seq_len, batch_size, emb_size)的张量。批次中的序列按降序排列,所以我们可以打包它。
-
我们应用pack_padded_sequence,应用RNN,最后应用pad_packed_sequence。此时我们有形状为(max_seq_len, batch_size, hidden_size)的张量
-
现在我们对结果应用线性输出层,并假设应用了log_softmax。最后我们得到一个形状为(max_seq_len, batch_size, linear_out_size)的批次分数张量
从这里开始,我应该如何计算损失,将填充部分掩码掉(使用任意目标)?
回答:
我认为PyTorch聊 bot教程对你可能有指导意义。
基本思路是,你计算有效输出值的掩码(填充部分无效),然后用这个掩码仅计算这些值的损失。
请查看教程页面上的outputVar
和maskNLLLoss
方法。为了方便起见,我在这里复制了代码,但你真的需要在所有代码的上下文中查看它。
# 返回填充的目标序列张量、填充掩码和最大目标长度def outputVar(l, voc): indexes_batch = [indexesFromSentence(voc, sentence) for sentence in l] max_target_len = max([len(indexes) for indexes in indexes_batch]) padList = zeroPadding(indexes_batch) mask = binaryMatrix(padList) mask = torch.BoolTensor(mask) padVar = torch.LongTensor(padList) return padVar, mask, max_target_len
def maskNLLLoss(inp, target, mask): nTotal = mask.sum() crossEntropy = -torch.log(torch.gather(inp, 1, target.view(-1, 1)).squeeze(1)) loss = crossEntropy.masked_select(mask).mean() loss = loss.to(device) return loss, nTotal.item()