Pytorch 解码器总是预测相同的令牌

Pytorch 解码器总是预测相同的令牌,pytorch,recurrent-neural-network,encoder-decoder,Pytorch,Recurrent Neural Network,Encoder Decoder,我有下面的机器翻译解码器,经过几个步骤后,它只能预测EOS标记。由于这个原因,在一个虚拟的、微小的数据集上过度拟合是不可能的,所以代码中似乎有一个很大的错误 解码器( (嵌入):嵌入( (word_嵌入):嵌入(30002768,padding_idx=3) (LayerNorm):LayerNorm((768,),eps=1e-05,elementwise_affine=True) (辍学):辍学(p=0.5,原地=假) ) (ffn1):FFN( (密集):线性(输入特征=768,输出特征=

我有下面的机器翻译解码器,经过几个步骤后,它只能预测EOS标记。由于这个原因,在一个虚拟的、微小的数据集上过度拟合是不可能的,所以代码中似乎有一个很大的错误

解码器(
(嵌入):嵌入(
(word_嵌入):嵌入(30002768,padding_idx=3)
(LayerNorm):LayerNorm((768,),eps=1e-05,elementwise_affine=True)
(辍学):辍学(p=0.5,原地=假)
)
(ffn1):FFN(
(密集):线性(输入特征=768,输出特征=512,偏差=False)
(layernorm):layernorm((512,),eps=1e-05,elementwise_affine=True)
(辍学):辍学(p=0.5,原地=假)
(激活):格鲁()
)
(rnn):GRU(512,512,批处理_first=True,双向=True)
(ffn2):FFN(
(密集):线性(输入特征=1024,输出特征=512,偏差=False)
(layernorm):layernorm((512,),eps=1e-05,elementwise_affine=True)
(辍学):辍学(p=0.5,原地=假)
(激活):格鲁()
)
(选择器):顺序(
(0):线性(输入特征=512,输出特征=30002,偏差=真)
(1) :LogSoftmax(尺寸=-1)
)
)
转发相对简单(请看我在那里做了什么?):将输入ID传递给嵌入和FFN,然后在RNN中使用该表示,并将给定的
sembedding
作为初始隐藏状态。通过另一个FFN传递输出并执行softmax。返回RNN的登录和最后隐藏状态。在下一步中,将这些隐藏状态用作新的隐藏状态,并将最高预测标记用作新输入

def-forward(自身、输入标识、符号):
嵌入=自嵌入(输入\u ID)
输出=self.ffn1(嵌入式)
输出,隐藏=self.rnn(输出,隐藏)
输出=self.ffn2(输出)
logits=self.selector(输出)
返回登录,隐藏
sembedding
是RNN的初始隐藏状态。这与编码器deocder架构类似,只是在这里我们不训练编码器,但我们可以访问预训练的编码器表示

在我的训练循环中,我以一个SOS令牌开始每一批,并将每个预测最高的令牌输入下一步,直到达到目标。我也在教师强制培训之间随机交换

def步骤(自身、批次、教师强制比例=0.5):
批次大小,目标长度=批次[“输入ID”].size()[:2]
#初始化第一个解码器输入woth SOS(BOS)令牌
解码器\u输入=torch.tensor([[self.tokenizer.bos\u token\u id]]*批量大小).to(self.device)
批处理[“输入标识”]=批处理[“输入标识”].to(self.device)
#Init first decoder hidden_state:如果RNN是双向的,则嵌入一个零秒
解码器_hidden=torch.stack((批处理[“sembedding”],
torch.ZERO(*批次[“sembedding”].size())
).to(self.model.num_directions==2时的self.device)\
else批处理[“sembedding”]。取消查询(0)。到(self.device)
损耗=火炬张量([0.])到(自身设备)
使用教师强制=随机。随机()
#包含预测词和正确词的元组
代币=[]
对于范围内的i(目标长度):
#覆盖以前的解码器。\u隐藏
输出,解码器隐藏=自身模型(解码器输入,解码器隐藏)
batch_correct_id=batch[“input_id”][:,i]
#NLLLoss计算预测类(bs x类)和该词的正确类之间的损失_
#设置为忽略填充索引
损失+=自身标准(输出[:,0,:],批处理\u正确\u ID)
batch\u predicted\u id=output.topk(1).index.squence(1).detach()
#如果使用教师培训:使用当前正确单词进行下一次预测
#否则不要使用教师培训:美国当前预测用于下一次预测
解码器\u输入=批处理\u更正\u ID。如果使用\u教师\u强制其他批处理\u预测\u ID,则取消查询(1)
返回loss,loss.item()/target\u len
我还将在每个步骤后剪裁渐变:

clip\u grad\u norm\u(self.model.parameters(),1.0)
起初,随后的预测已经相对一致,但经过几次迭代后,变化会稍微大一些。但是所有的预测很快就会变成其他的词(但总是相同的),最终变成EOS标记(编辑:在将激活更改为ReLU后,另一个标记总是被预测的-它看起来像一个随机标记,总是被重复)。请注意,这已经发生在80个步骤之后(批次大小128)

我发现RNN返回的隐藏状态包含很多零。我不确定这是否是问题所在,但似乎可能有关联

张量([[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,-7.8125e-03],
[-0.0000e+00,--1.0610e-314,0.0000e+00,…,0.0000e+00,
0.0000e+00,0.0000e+00],
[0.0000e+00,0.0000e+00,0.0000e+00,…,0.0000e+00,
-0.0000e+00,1.0610e-314]]],device='cuda:0',dtype=torch.float64,
grad_fn=)
我不知道可能出了什么问题,尽管我怀疑问题出在我的
步骤上,而不是模型上。我已经尝试过使用学习速率,禁用某些层(LayerForm、dropout、
ffn2
),使用预训练嵌入并冻结或解冻它们,以及禁用教师强制,使用双向和单向GRU。最终结果总是一样的


如果你有什么建议,那将非常有帮助。我在谷歌上搜索了很多关于神经网络的东西,总是预测同一个项目,我尝试了我能找到的所有建议。任何新的,无论多么疯狂,都欢迎

在我的例子中,问题似乎是初始隐藏状态的
dtype
是双精度的