问题描述
我有以下用于机器翻译的解码器,仅需几步便可以预测EOS令牌。因此,不可能在虚拟的微小数据集上过度拟合,因此似乎在代码中存在很大的错误。
Decoder(
(embedding): Embeddings(
(word_embeddings): Embedding(30002,768,padding_idx=3)
(Layernorm): Layernorm((768,),eps=1e-05,elementwise_affine=True)
(dropout): Dropout(p=0.5,inplace=False)
)
(ffn1): FFN(
(dense): Linear(in_features=768,out_features=512,bias=False)
(layernorm): Layernorm((512,inplace=False)
(activation): GELU()
)
(rnn): GRU(512,512,batch_first=True,bidirectional=True)
(ffn2): FFN(
(dense): Linear(in_features=1024,inplace=False)
(activation): GELU()
)
(selector): Sequential(
(0): Linear(in_features=512,out_features=30002,bias=True)
(1): Logsoftmax(dim=-1)
)
)
转发相对简单(请参阅我在那做的事情?):将input_ids传递给嵌入和FFN,然后在RNN中以给定的sembedding
作为初始隐藏状态使用该表示形式。使输出通过另一个FFN并执行softmax。返回RNN的登录信息和最后隐藏状态。下一步,将这些隐藏状态用作新的隐藏状态,并将预测的最高令牌用作新的输入。
def forward(self,input_ids,sembedding):
embedded = self.embedding(input_ids)
output = self.ffn1(embedded)
output,hidden = self.rnn(output,sembedding)
output = self.ffn2(output)
logits = self.selector(output)
return logits,hidden
sembedding
是RNN的初始hidden_state。这仅与编码器解码器体系结构相似,只是在这里我们不训练编码器,但可以访问预训练的编码器表示。
在我的训练循环中,我从每个批次开始使用SOS令牌,并将每个最高预测的令牌馈入下一步,直到达到target_len
。我也在老师的强制训练之间随机交换。
def step(self,batch,teacher_forcing_ratio=0.5):
batch_size,target_len = batch["input_ids"].size()[:2]
# Init first decoder input woth SOS (BOS) token
decoder_input = torch.tensor([[self.tokenizer.bos_token_id]] * batch_size).to(self.device)
batch["input_ids"] = batch["input_ids"].to(self.device)
# Init first decoder hidden_state: one zero'd second embedding in case the RNN is bidirectional
decoder_hidden = torch.stack((batch["sembedding"],torch.zeros(*batch["sembedding"].size()))
).to(self.device) if self.model.num_directions == 2 \
else batch["sembedding"].unsqueeze(0).to(self.device)
loss = torch.tensor([0.]).to(self.device)
use_teacher_forcing = random.random() < teacher_forcing_ratio
# contains tuples of predicted and correct words
tokens = []
for i in range(target_len):
# overwrite prevIoUs decoder_hidden
output,decoder_hidden = self.model(decoder_input,decoder_hidden)
batch_correct_ids = batch["input_ids"][:,i]
# NLLLoss compute loss between predicted classes (bs x classes) and correct classes for _this word_
# set to ignore the padding index
loss += self.criterion(output[:,:],batch_correct_ids)
batch_predicted_ids = output.topk(1).indices.squeeze(1).detach()
# if use teacher training: use current correct word for next prediction
# else do NOT use teacher training: us current predction for next prediction
decoder_input = batch_correct_ids.unsqueeze(1) if use_teacher_forcing else batch_predicted_ids
return loss,loss.item() / target_len
在每个步骤之后,我还会剪切渐变:
clip_grad_norm_(self.model.parameters(),1.0)
起初,随后的预测已经相对相同,但是经过几次迭代后,会有更多的变化。但是相对较快的是,所有预测都变成了其他词(但始终是相同的),最终变成了EOS令牌(编辑:将激活更改为ReLU后,总是会预测另一个令牌-好像总是重复的随机令牌)。请注意,这已经发生了80步(batch_size 128)。
我发现RNN返回的隐藏状态包含很多零。我不确定这是否是问题所在,但似乎可以解决。
tensor([[[ 3.9874e-02,-6.7757e-06,2.6094e-04,...,-1.2708e-17,4.1839e-02,7.8125e-03],[ -7.8125e-03,-2.5341e-02,7.8125e-03,-7.8125e-03,-7.8125e-03],[ -0.0000e+00,-1.0610e-314,0.0000e+00,0.0000e+00],[ 0.0000e+00,-0.0000e+00,1.0610e-314]]],device='cuda:0',dtype=torch.float64,grad_fn=<CudnnRnnBackward>)
我不知道出了什么问题,尽管我怀疑问题出在我的step
而不是模型上。我已经尝试过使用学习速率,禁用某些层(Layernorm,辍学,ffn2
),使用预训练的嵌入并冻结或解冻它们,并使用双向和单向GRU禁用教师强迫。最终结果始终相同。
如果您有任何指针,那将非常有帮助。我用谷歌搜索了很多关于神经网络的东西,它们总是预测相同的项目,并且我尝试了所有可以找到的建议。任何新事物,无论多么疯狂,都欢迎!
解决方法
在我的情况下,问题似乎是初始隐藏状态的dtype
是双精度型,而输入是浮点型。我不太了解为什么这是一个问题,但是将隐藏状态转换为float可以解决此问题。如果您对为什么PyTorch可能会遇到问题有任何直觉,请在评论中,或者最好在the official PyTorch forums上告诉我。
编辑:正如该主题所示,这是PyTorch 1.6中的一个错误,已在1.7中解决,在1.7中,您将收到一条错误消息,该错误消息将希望为您节省调试所有代码的麻烦,而不必找出导致奇怪的原因行为。