您对大多数概念的理解是准确的,但是,这里和那里都存在一些缺失点。
将嵌入接口连接到 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 + 1 和embedding_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_t 和 h_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 的输出以供进一步使用(同样,您不能在此处使用视图)。
结束说明:我特意留下了一些要点,例如使用双向循环单元,在展开时使用步长以及接口注意力,因为它们可能会变得非常麻烦并且超出了此答案的范围。