如何正确输入PyTorch中的Embedding,LSTM和Linear层?
lstm
pytorch
6
0

我需要一些清楚的信息,说明如何使用torch.nn模块的不同组件正确准备用于批培训的输入。具体来说,我正在寻找为seq2seq模型创建一个编码器-解码器网络。

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

  1. nn.Embedding
  2. nn.LSTM
  3. 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_size
last_hidden_state: batch_size * hidden_size
last_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的input_size大小或??)
输出: batch_size x output_size

如果我只需要LSTMlast_hidden_state ,则可以将其作为nn.Linear输入。

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

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

参考资料:
Stack Overflow
收藏
评论
共 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)转置第一维和第二维,就像您提到的那样。

问:我在网上看到很多例子,它们使x = embeds.view(len(sentence),self.batch_size,-1)感到困惑。
答:这是错误的。它将混合批次,您将尝试学习无望的学习任务。无论您在哪里看到此内容,都可以告诉作者更改此语句,而改用转置。

有一个论点赞成不使用batch_first ,它指出Nvidia CUDA提供的基础API使用批处理作为次要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)

展开文件
现在,您可以如上所述进行操作以将其馈送到LSTM ,只是要记住seq_len现在更改为seq_len - context_size + 1并且embedding_size (这是LSTM的输入大小)现在更改为context_size * embedding_size

使用可变的序列长度

批次中不同实例的输入大小将始终不会相同。例如,您的某些句子可能长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的输出以进一步使用(同样,您在这里不能使用view)。

结束语:我故意留下了一些要点,例如使用双向递归单元格,使用展开的步长大小以及衔接注意力,因为它们可能变得很麻烦并且将不在此答案的范围内。

收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题

关于我们

常见问题

内容许可

联系我们

@2020 AskGo
京ICP备20001863号