我需要明确如何使用 torch.nn
模块的不同组件正确准备批量训练的输入 . 具体来说,我正在寻找为seq2seq模型创建编码器 - 解码器网络 .
假设我有一个包含这三层的模块,按顺序:
-
nn.Embedding
-
nn.LSTM
-
nn.Linear
nn.Embedding
Input: batch_size * seq_length
Output: batch_size * seq_length * embedding_dimension
我在这里没有任何问题,我只想明确输入和输出的预期形状 .
nn.LSTM
Input: seq_length * batch_size * input_size
(在这种情况下为 embedding_dimension
)
Output: seq_length * batch_size * hidden_size
last_hidden_state: batch_size * hidden_size
last_cell_state: batch_size * hidden_size
要使用 Embedding
图层的输出作为 LSTM
图层的输入,我需要转置轴1和2 .
我在网上找到的许多例子都有类似_843239的内容,但这让我很困惑 . 该视图如何确保同一批次的元素保留在同一批次中?当 len(sentence)
和 self.batch
尺寸相同时会发生什么?
nn.Linear
Input: batch_size
x input_size
(在这种情况下LSTM的hidden_size或??)
Output: batch_size
x output_size
如果我只需要 last_hidden_state
的 LSTM
,那么我可以将它作为 nn.Linear
的输入 .
但是,如果我想使用输出(其中包含所有中间隐藏状态),那么我需要将 nn.Linear
的输入大小更改为 seq_length * hidden_size
并使用输出作为 Linear
模块的输入我需要转置输出的轴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的单词,您将始终一次给它一个单词 . 但是,这不是我们一直想要的 . 因此,您需要扩展上下文大小 . 这可以按如下方式完成 -
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变量 .
5.现在,在通过嵌入和创建适当的上下文大小输入传递上述变量之后,您需要按如下方式打包您的序列 -
了解LSTM的输出
现在,一旦你准备好
lstm_input
acc . 根据您的需要,您可以将lstm称为这里,需要提供
(h_t, h_c)
作为初始隐藏状态,它将输出最终的隐藏状态 . 您可以看到,为什么需要打包可变长度序列,否则LSTM也将运行非必需的填充字 .现在,
lstm_outs
将是一个打包序列,它是每一步的lstm输出,(h_t, h_c)
分别是最终输出和最终单元状态 .h_t
和h_c
的形状为(batch_size, lstm_size)
. 您可以直接使用这些进一步输入,但是如果您想使用中间体输出也需要先打开lstm_outs
,如下所示现在,你的
lstm_outs
将成形(max_seq_len - context_size + 1, batch_size, lstm_size)
. 现在,您可以根据需要提取lstm的中间输出 .将lstm连接到线性
现在,如果你只想使用lstm的输出,你可以直接将
h_t
提供给你的线性层,它会起作用 . 但是,如果你想使用中间输出,那么,你需要弄清楚,你将如何将其输入到线性层(通过一些注意网络或一些池) . 您不希望将完整序列输入到线性层,因为不同的序列将具有不同的长度,并且您无法修复线性层的输入大小 . 是的,你需要转换lstm的输出才能进一步使用(再次你不能在这里使用视图) .