我需要澄清一下如何使用torch.nn模块的不同组件正确准备批量培训的输入.具体来说,我正在寻找为seq2seq模型创建编码器 - 解码器网络.
假设我有一个包含这三层的模块,按顺序:
nn.Embeddingnn.LSTMnn.Linearnn.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的hidden_size或??)
输出: batch_size xoutput_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))?
lay*_*yog 32
您对大多数概念的理解是准确的,但是,这里和那里有一些缺失点.
你有嵌入输出的形状(batch_size, seq_len, embedding_size).现在,您可以通过多种方式将其传递给LSTM.
*您可以直接将其传递给LSTM,如果LSTM接受输入为batch_first.所以,在创建你的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使用批处理作为辅助运行速度要快得多.
您正在将嵌入输出直接输入到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)
Run Code Online (Sandbox Code Playgroud)
展开文档
现在,您可以按照上面的说明进行操作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)
Run Code Online (Sandbox Code Playgroud)
现在,一旦你准备好你的lstm_inputacc.根据您的需要,您可以将lstm称为
lstm_outs, (h_t, h_c) = lstm(lstm_input, (h_t, h_c))
Run Code Online (Sandbox Code Playgroud)
这里,(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)
Run Code Online (Sandbox Code Playgroud)
现在,你lstm_outs将成型(max_seq_len - context_size + 1, batch_size, lstm_size).现在,您可以根据需要提取lstm的中间输出.
请记住,在每个批处理的大小之后,解压缩的输出将具有0,这只是填充以匹配最大序列的长度(始终是第一个序列的长度,因为我们将输入从最大值排序到最小值).
另请注意,h_t将始终等于每个批输出的最后一个元素.
现在,如果你只想使用lstm的输出,你可以直接h_t输入你的线性层,它会起作用.但是,如果你想使用中间输出,那么,你需要弄清楚,你将如何将它输入线性层(通过一些注意网络或一些池).您不希望将完整序列输入到线性图层,因为不同的序列将具有不同的长度,并且您无法修复线性图层的输入大小.是的,你需要转换lstm的输出才能进一步使用(再次你不能在这里使用视图).
结束注意:我有目的地留下了一些观点,例如使用双向循环单元格,在展开时使用步长,以及连接注意力,因为它们会变得非常麻烦并且超出了本答案的范围.
| 归档时间: |
|
| 查看次数: |
7601 次 |
| 最近记录: |