代码之家  ›  专栏  ›  技术社区  ›  Silpara

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

  •  40
  • Silpara  · 技术社区  · 7 年前

    我需要弄清楚如何使用 torch.nn 单元具体来说,我希望为seq2seq模型创建一个编码器-解码器网络。

    假设我有一个具有以下三层的模块,顺序如下:

    1. nn.Embedding
    2. nn.LSTM
    3. nn.Linear

    nn。嵌入

    输入: 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
    上次\u hidden\u状态: batch_size * hidden_size
    上次\u cell\u状态: 批次大小*隐藏大小

    要使用 Embedding 图层作为 LSTM 层,我需要转置轴1和2。

    我在网上找到的许多例子 x = embeds.view(len(sentence), self.batch_size , -1) ,但这让我困惑。此视图如何确保同一批的元素保留在同一批中?当 len(sentence) self.batch 大小是否相同?

    nn。线性的

    输入: batch_size x个 input_size (在这种情况下,LSTM的隐藏\u大小或??)
    输出: batch\u大小 x个 output_size

    如果我只需要 last_hidden_state 属于 LSTM公司 ,然后我可以将其作为输入 nn。线性的

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

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

    1 回复  |  直到 6 年前
        1
  •  58
  •   layog    7 年前

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

    接口嵌入到LSTM(或任何其他经常性装置)

    将输出嵌入为 (batch_size, seq_len, embedding_size) 。现在,有多种方法可以将其传递给LSTM。
    *您可以将其直接传递给 LSTM 如果 LSTM公司 接受输入为 batch_first 。因此,在创建 LSTM公司 pass参数 batch_first=True
    *或者,您可以以以下形状传递输入 (seq_len, batch_size, embedding_size) 。因此,要将嵌入输出转换为该形状,需要使用 torch.transpose(tensor_name, 0, 1) ,就像你提到的。

    Q、 我在网上看到了许多类似于x=嵌入的示例。视图(len(句子),self。batch\u size,-1),这让我很困惑。
    A、 这是错误的。它会混淆批次,你将试图学习一项毫无希望的学习任务。无论您在哪里看到这一点,都可以告诉作者更改此语句,并改用转置。

    有一种观点支持不使用 第一批\u ,这说明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 + 1 embedding_size (是LSTM的输入大小)现在更改为 context_size * embedding_size

    使用可变序列长度

    批处理中不同实例的输入大小并不总是相同的。例如,您的句子中有些可能有10个单词长,有些可能有15个,有些可能有1000个。所以,你肯定想要可变长度的序列输入到你的循环单位。要做到这一点,在将输入反馈到网络之前,需要执行一些额外的步骤。您可以按照以下步骤操作-
    1、将批次从最大顺序排序到最小顺序。
    2、创建 seq_lengths 定义批次中每个序列长度的数组。(这可以是一个简单的python列表)
    3、填充所有序列,使其与最大序列的长度相等。
    4、创建该批次的LongTensor变量。
    现在,在通过嵌入和创建适当的上下文大小输入传递上述变量之后,您需要按如下方式打包您的序列-

    # 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 根据您的需要,您可以将lstm称为

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

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

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

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

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

    还要注意,对于每个批次输出,h\u t始终等于最后一个元素。

    将lstm连接到线性

    现在,如果只想使用lstm的输出,可以直接馈送 h\U t 到你的线性层,它会工作。但是,如果您还想使用中间输出,那么您需要弄清楚,如何将其输入到线性层(通过一些注意网络或池)。您不想将完整的序列输入到线性层,因为不同的序列具有不同的长度,并且您无法固定线性层的输入大小。是的,您需要转置lstm的输出以进一步使用(同样,您不能在这里使用view)。

    结束语:我特意留下了一些要点,例如使用双向循环单元格、在“展开”中使用步长以及界面注意,因为它们可能会变得非常繁琐,超出了本答案的范围。