如何正确地为 PyTorch 中的嵌入、LSTM 和线性层提供输入?

Posted

技术标签:

【中文标题】如何正确地为 PyTorch 中的嵌入、LSTM 和线性层提供输入?【英文标题】:How to correctly give inputs to Embedding, LSTM and Linear layers in PyTorch? 【发布时间】:2018-09-03 03:32:47 【问题描述】:

我需要了解如何使用torch.nn 模块的不同组件正确准备批量训练的输入。具体来说,我希望为 seq2seq 模型创建一个编码器-解码器网络。

假设我有一个包含这三层的模块,按顺序:

    nn.Embedding nn.LSTM nn.Linear

nn.Embedding

输入: batch_size * seq_length输出: batch_size * seq_length * embedding_dimension

我在这里没有任何问题,我只是想明确输入和输出的预期形状。

nn.LSTM

输入: seq_length * batch_size * input_size(在本例中为embedding_dimension输出: seq_length * batch_size * hidden_sizelast_hidden_​​state: batch_size * hidden_sizelast_cell_state: batch_size * hidden_size

要将Embedding 层的输出用作LSTM 层的输入,我需要转置轴1 和轴2。

我在网上找到的许多示例都类似于x = embeds.view(len(sentence), self.batch_size , -1),但这让我感到困惑。这种观点如何确保同一批次的元素保留在同一批次中?当len(sentence)self.batch 大小相同时会发生什么?

nn.Linear

输入: batch_size x input_size(在这种情况下是 LSTM 的隐藏大小还是??)输出: batch_size x output_size

如果我只需要LSTMlast_hidden_state,那么我可以将其作为输入提供给nn.Linear

但是,如果我想使用 Output(其中还包含所有中间隐藏状态),那么我需要将 nn.Linear 的输入大小更改为 seq_length * hidden_size 并将 Output 作为输入到 Linear 模块 I需要转置输出的轴1和轴2,然后我可以用Output_transposed(batch_size, -1)查看。

我的理解正确吗?如何在张量(tensor.transpose(0, 1)) 中进行这些转置操作?

【问题讨论】:

【参考方案1】:

您对大多数概念的理解是准确的,但是,这里和那里都存在一些缺失点。

将嵌入接口连接到 LSTM(或任何其他循环单元)

您有(batch_size, seq_len, embedding_size) 形状的嵌入输出。现在,您可以通过多种方式将其传递给 LSTM。 * 如果LSTM 接受输入为batch_first,您可以将其直接传递给LSTM。因此,在创建您的 LSTM 时,传递参数 batch_first=True。 * 或者,您可以以(seq_len, batch_size, embedding_size) 的形式传递输入。因此,要将嵌入输出转换为此形状,您需要使用torch.transpose(tensor_name, 0, 1) 转置第一维和第二维,就像您提到的那样。

问。我在网上看到很多例子,这些例子让我很困惑。 答:这是错误的。它会混淆批次,您将尝试学习无望的学习任务。无论你在哪里看到这个,你都可以告诉作者改变这个语句并使用转置来代替。

有一个观点支持不使用batch_first,它指出Nvidia CUDA 提供的底层API 在使用批处理作为辅助时运行速度要快得多。

使用上下文大小

您将嵌入输出直接提供给 LSTM,这会将 LSTM 的输入大小固定为上下文大小 1。这意味着如果您的输入是 LSTM 的单词,您将始终一次给它一个单词。但是,这并不是我们一直想要的。因此,您需要扩展上下文大小。这可以按如下方式完成 -

# Assuming that embeds is the embedding output and context_size is a defined variable
embeds = embeds.unfold(1, context_size, 1)  # Keeping the step size to be 1
embeds = embeds.view(embeds.size(0), embeds.size(1), -1)

Unfold documentation 现在,您可以按照上述方法将其提供给LSTM,只需记住seq_len 现在更改为seq_len - context_size + 1embedding_size(这是LSTM 的输入大小)现在更改为@987654336 @

使用可变序列长度

批次中不同实例的输入大小不会始终相同。例如,您的句子中的一些可能是 10 个字长,一些可能是 15 个,一些可能是 1000 个。所以,您肯定希望可变长度的序列输入到您的循环单元。为此,需要执行一些额外的步骤,然后才能将输入提供给网络。您可以按照以下步骤操作 - 1. 将批次从最大序列到最小序列进行排序。 2. 创建一个seq_lengths 数组,定义批处理中每个序列的长度。 (这可以是一个简单的python列表) 3. 将所有序列填充为与最大序列等长。 4. 创建该批次的 LongTensor 变量。 5. 现在,在通过嵌入传递上述变量并创建适当的上下文大小输入之后,您需要按如下方式打包您的序列 -

# Assuming embeds to be the proper input to the LSTM
lstm_input = nn.utils.rnn.pack_padded_sequence(embeds, [x - context_size + 1 for x in seq_lengths], batch_first=False)

了解 LSTM 的输出

现在,一旦您准备好您的lstm_input acc。根据您的需要,您可以将 lstm 称为

lstm_outs, (h_t, h_c) = lstm(lstm_input, (h_t, h_c))

这里需要提供(h_t, h_c)作为初始隐藏状态,它会输出最终的隐藏状态。您可以看到,为什么需要打包可变长度序列,否则 LSTM 也会运行非必需的填充词。 现在,lstm_outs 将是一个打包序列,它是 lstm 在每一步的输出,(h_t, h_c) 分别是最终输出和最终单元状态。 h_th_c 的形状为 (batch_size, lstm_size)。您可以直接使用这些作为进一步的输入,但如果您还想使用中间输出,则需要先解压缩lstm_outs,如下所示

lstm_outs, _ = nn.utils.rnn.pad_packed_sequence(lstm_outs)

现在,您的lstm_outs 将变成(max_seq_len - context_size + 1, batch_size, lstm_size)。现在,您可以根据需要提取 lstm 的中间输出。

请记住,解压后的输出将在每个批次的大小之后有 0,这只是填充以匹配最大序列的长度(它始终是第一个,因为我们将输入从最大到最小排序)。

另请注意,h_t 将始终等于每个批次输出的最后一个元素。

将 lstm 连接到线性

现在,如果您只想使用 lstm 的输出,您可以直接将 h_t 输入到您的线性层,它会起作用。但是,如果您也想使用中间输出,那么您需要弄清楚,您将如何将其输入到线性层(通过一些注意力网络或一些池化)。您不想将完整的序列输入到线性层,因为不同的序列将具有不同的长度,并且您无法固定线性层的输入大小。是的,您需要转置 lstm 的输出以供进一步使用(同样,您不能在此处使用视图)。

结束说明:我特意留下了一些要点,例如使用双向循环单元,在展开时使用步长以及接口注意力,因为它们可能会变得非常麻烦并且超出了此答案的范围。

【讨论】:

感谢您花时间写出这么棒的答案!我只是想为我补充一点embeds = embeds.contiguous().view(embeds.size(0), embeds.size(1), -1) 是一个有效的方法,否则我得到 input is not contiguous 错误。 伟大而重要的文章。从view 更改为“转置”以准备 rnn 输入的嵌入输出刚刚救了我。我注意到任何大于 1 的批量大小都会显着恶化结果。应该有更多人支持该帖子,因此它的排名高于使用 view 的所有教程:)。 很好的解释。谢谢。有一件事对我来说仍然是可疑的。我们如何直接使用 h_t 到线性层,它将是暗淡的(num_layers*batch_size*lstm_size)。我认为我们只需要使用h_t[-1] 来获取最后一层的输出。对吗?

以上是关于如何正确地为 PyTorch 中的嵌入、LSTM 和线性层提供输入?的主要内容,如果未能解决你的问题,请参考以下文章

pytorch之词嵌入

如何使用 Pytorch 中的截断反向传播(闪电)在很长的序列上运行 LSTM?

如何使用Keras LSTM与word嵌入来预测单词id

如何在 PyTorch 中正确实现 Seq2Seq LSTM 的填充?

如何在 Pytorch LSTM/GRU/RNN 中指定不同的层大小

ccc-pytorch-LSTM