美文网首页机器学习100天Machine Learning & Recommendation & NLP & DL
Pytorch学习记录-Seq2Seq模型实现(Decoder部

Pytorch学习记录-Seq2Seq模型实现(Decoder部

作者: 我的昵称违规了 | 来源:发表于2019-04-24 11:48 被阅读6次

    一点点总结
    回过头看看这三天的模型,从一般LSTM Seq2Seq -> GRU Seq2Seq -> 基于注意力机制的 Seq2Seq

    LSTM Seq2Seq.png
    GRU Seq2Seq.png
    基于注意力机制的 Seq2Seq.png

    2. 三个模型的Decoder部分

    Decoder部分和Encoder是对应的,层数、隐藏层、单元数都要对应。

    2.1 LSTM Seq2Seq Decoder

    Decoder只执行一个解码步骤。第一层将从前一个时间步接收隐藏和单元状态,并通过将当前的token 传给LSTM,进一步产生一个新的隐藏和单元状态。
    Decoder的初始隐藏和单元状态是我们的上下文向量,它们是来自同一层的Encoder的最终隐藏和单元状态。接下来将隐藏状态传递给Linear层,预测目标序列下一个标记应该是什么。

    Encoder输入参数:

    • output_dim将要输入到Decoder的one-hot向量,这个和输出词汇大小一致,就是输出字典长度
    • emb_dim嵌入层的维度,这一层将one-hot向量转为密度向量,256
      词嵌入在 pytorch 中只需要调用 torch.nn.Embedding(m, n) 就可以了,m 表示单词的总数目,n 表示词嵌入的维度,是一种降维,相当于是一个大矩阵,矩阵的每一行表示一个单词。
    • hid_dim隐藏和cell的状态维度,512
    • n_layers RNN层数,这里就是2
    • dropout是要使用的丢失量。这是一个防止过度拟合的正则化参数,0.5

    Encoder返回参数:

    • prediction,预测
    • hidden,隐藏状态
    • cell,单元状态
      看一下实现
    class Decoder(nn.Module):
        def __init__(self, output_dim, emb_dim, hid_dim, n_layers, dropout):
            super(Decoder,self).__init__()
            
            self.emb_dim=emb_dim
            self.hid_dim=hid_dim
            self.output_dim=output_dim
            self.n_layers=n_layers
            self.dropout=dropout
            
            self.embedding=nn.Embedding(output_dim,emb_dim)
            self.rnn=nn.LSTM(emb_dim,hid_dim,n_layers,dropout=dropout)
            self.out=nn.Linear(hid_dim,output_dim)
            self.dropout=nn.Dropout(dropout)
        def forward(self, input,hidden,cell):
            # torch.unsqueeze()这个函数主要是对数据维度进行扩充。给指定位置加上维数为一的维度,比如原本有个三行的数据(3),在0的位置加了一维就变成一行三列(1,3)
            input=input.unsqueeze(0)
            embedded=self.dropout(self.embedding(input))
            output, (hidden,cell)=self.rnn(embedded,(hidden,cell))
            prediction=self.out(output.squeeze(0))
            
            #output = [1, batch size, hid dim]
            #hidden = [n layers, batch size, hid dim]
            #cell = [n layers, batch size, hid dim]
            return prediction,hidden ,cell
    

    2.2 GRU Seq2Seq Decoder

    GRU的Seq2Seq和LSTM有很大不同,减少了信息压缩,GRU获取目标token y_t、上一个隐藏状态 s_{t-1},上下文向量z。这里的上下文向量其实就是Encoder的上下文状态,就是隐藏状态s_{t-1}。这里就是输入了两个相同的上下文向量。

    Encoder输入参数:

    • output_dim将要输入到Decoder的one-hot向量,这个和输出词汇大小一致,就是输出字典长度
    • emb_dim嵌入层的维度,这一层将one-hot向量转为密度向量,256
      词嵌入在 pytorch 中只需要调用 torch.nn.Embedding(m, n) 就可以了,m 表示单词的总数目,n 表示词嵌入的维度,是一种降维,相当于是一个大矩阵,矩阵的每一行表示一个单词。
    • hid_dim隐藏和cell的状态维度,512
    • dropout是要使用的丢失量。这是一个防止过度拟合的正则化参数,0.5

    Encoder返回参数:

    • prediction,预测
    • hidden,隐藏状态
      看一下实现
    class Decoder(nn.Module):
        def __init__(self, output_dim,emb_dim,hid_dim,dropout):
            super(Decoder,self).__init__()
            
            self.output_dim=output_dim
            self.emb_dim=emb_dim
            self.hid_dim=hid_dim
            self.dropout=dropout
            
            self.embedding=nn.Embedding(output_dim,emb_dim)
            # 在实现的时候,通过将$y_t$和$z$串联传入GRU,所以输入的维度应该是emb_dim+ hid_dim
            self.rnn=nn.GRU(emb_dim+ hid_dim,hid_dim)
            # linear层输入的是 $y_t, s_t$ 和 $z$串联,而隐藏状态和上下文向量都是$h$维度相同,所以输入的维度是emb_dim+hid_dim*2  
            self.out=nn.Linear(emb_dim+hid_dim*2, output_dim)
            self.dropout=nn.Dropout(dropout)
            
        def forward(self, input, hidden, context):
            input =input.unsqueeze(0)
            embedded=self.dropout(self.embedding(input))
            #embedded = [1, batch size, emb dim]
            
            emb_con = torch.cat((embedded, context), dim = 2)
            #emb_con = [1, batch size, emb dim + hid dim]
            output,hidden=self.rnn(emb_con,hidden)
            
            #input = [batch size]
            #hidden = [n layers * n directions, batch size, hid dim]
            #context = [n layers * n directions, batch size, hid dim]
            
            #在Decoder中层数和方向始终是1,因此hidden和context要做压缩处理:
            #hidden = [1, batch size, hid dim]
            #context = [1, batch size, hid dim]
            output=torch.cat((embedded.squeeze(0), hidden.squeeze(0), context.squeeze(0)), dim = 1)
            
            prediction=self.out(output)
            return prediction,hidden
    

    2.3 Attention Seq2Seq Decoder

    这个是目前为止看到最复杂的,因为增加了attention。

    2.3.1 Attention

    放置在这里,反正以后都要用到attention。

    class Attention(nn.Module):
        def __init__(self, enc_hid_dim, dec_hid_dim):
            super(Attention,self).__init__()
            self.enc_hid_dim=enc_hid_dim
            self.dec_hid_dim=dec_hid_dim
            self.attn=nn.Linear((enc_hid_dim*2)+dec_hid_dim,dec_hid_dim)
            self.v=nn.Parameter(torch.rand(dec_hid_dim))
            
        def forward(self, hidden, encoder_outputs):
            batch_size=encoder_outputs.shape[1]
            src_len=encoder_outputs.shape[0]
            hidden=hidden.unsqueeze(1).repeat(1,src_len,1)
            encoder_outputs=encoder_outputs.permute(1,0,2)
            energy = torch.tanh(self.attn(torch.cat((hidden, encoder_outputs), dim = 2))) 
            energy = energy.permute(0, 2, 1)
            v = self.v.repeat(batch_size, 1).unsqueeze(1)
            attention = torch.bmm(v, energy).squeeze(1)
            return F.softmax(attention, dim=1)
    

    2.3.2 Decoder

    Decoder包括了注意力层,含有上一个隐藏状态s_{t-1},所有Encoder的隐藏状态H,返回注意力向量a_t
    接下来使用注意力向量创建加权源向量功能w_t,含有Encoder隐藏状态的加权和H,并使用注意力向量a_t作为权重。

    class Decoder(nn.Module):
        def __init__(self,output_dim,emb_dim,enc_hid_dim,dec_hid_dim,dropout, attention):
            super(Decoder,self).__init__()
            self.emb_dim = emb_dim
            self.enc_hid_dim = enc_hid_dim
            self.dec_hid_dim = dec_hid_dim
            self.output_dim = output_dim
            self.dropout = dropout
            self.attention = attention
            
            self.embedding=nn.Embedding(output_dim,emb_dim)
            self.rnn=nn.GRU((enc_hid_dim*2)+emb_dim,dec_hid_dim)
            self.out=nn.Linear((enc_hid_dim*2)+dec_hid_dim+emb_dim,output_dim)
            self.dropout=nn.Dropout(dropout)
            
        def forward(self,input,hidden,encoder_outputs):
            input=input.unsqueeze(0)
            embedded=self.dropout(self.embedding(input))
            a=self.attention(hidden,encoder_outputs)
            a=a.unsqueeze(1)
            encoder_outputs=encoder_outputs.permute(1,0,2)
            weighted = torch.bmm(a, encoder_outputs)
            weighted = weighted.permute(1, 0, 2)
            rnn_input = torch.cat((embedded, weighted), dim = 2)
            output, hidden = self.rnn(rnn_input, hidden.unsqueeze(0))
            assert (output == hidden).all()
            
            embedded = embedded.squeeze(0)
            output = output.squeeze(0)
            weighted = weighted.squeeze(0)
            
            output = self.out(torch.cat((output, weighted, embedded), dim = 1))
            
            #output = [bsz, output dim]
            return output, hidden.squeeze(0)                                    
    

    相关文章

      网友评论

        本文标题:Pytorch学习记录-Seq2Seq模型实现(Decoder部

        本文链接:https://www.haomeiwen.com/subject/olnwgqtx.html