Pytorch学习记录-torchtext和Pytorch的实例对比(Transformer)

Pytorch学习记录-torchtext和Pytorch的实例对比

0. PyTorch Seq2Seq项目介绍

在完成基本的torchtext之后,找到了这个教程,《基于Pytorch和torchtext来理解和实现seq2seq模型》。
这个项目主要包括了6个子项目

  1. 使用神经网络训练Seq2Seq
  2. 使用RNN encoder-decoder训练短语表示用于统计机器翻译
  3. 使用共同学习完成NMT的构建和翻译
  4. 打包填充序列、掩码和推理
  5. 卷积Seq2Seq
  6. Transformer

结束Transformer之后隔了两天没有学习,这两天对几个模型进行对比和总结吧,在完成前三个模型的时候曾经写过一个总结,今天主要是看一下六个模型的变化以及实现。关键是实现,用了15天,但是模型实现部分只能看懂一般Seq2Seq……

7. 总结,从一般Seq2Seq到Transformer

六个模型都是Seq2Seq,都包含有Encoder和Decoder两部分,只是模型核心不同,并且在层与层或是Encoder与Decoder之间不断加新东西分别是:LSTM->多层GRU->Attention->PadMaskAttention->CNN->Transformer

  • 1和2是一般Seq2Seq,分别使用了LSTM和它的变体GRU
  • 3和4是对Attention的补充,增加了打包、填充、掩码
  • 5是使用CNN
  • 6是all-attention,什么高端的都给你用上

7.5 模型5、6对比

<center class="half">
<img src="https://upload-images.jianshu.io/upload_images/14340919-3cd19da0da351933.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width="600"/><img src="https://upload-images.jianshu.io/upload_images/14340919-9e7d518eea914b5c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width="600"/>
</center>

最后一个模型了,对比一下,最大的区别是Transformer是全attention结构,抛弃了RNN和CNN,在之前张俊林的分析中已经说过在“语义特征提取能力、长距离特征捕获能力、任务综合特征抽取能力、并行计算能力及运行效率”等方面,均要优于CNN和RNN。在长文本处理方面,Transforme-XL解决的就是输入特别长的问题。基于Pytorch的项目可以看看这个
整体结构还是有一些相似点,CNNSeq2Seq的Encoder和Decoder采用的是层叠卷积抽取特征,使用GLU作为激活函数,中间attention使用点乘的方式,最后用残差连接,把attention计算的权重与输入序列相乘,加入到decoder的输出中输出输出序列。而Transformer在Encoder和Decoder中使用multi head self-attention机制代替CNN。在对输入序列进行对齐中使用的是padding mask和sequence mask。mask掩码。最后使用层归一和残差连接结束。在Encoder和Decoder连接处使用multi head context-attention。
照例,来实现一下

class Encoder(nn.Module):
    def __init__(self, input_dim, hid_dim, n_layers, n_heads, pf_dim, encoder_layer,
                self_attention, positionwise_feedfoward, dropout, device):
        super(Encoder, self).__init__()
        
        self.input_dim=input_dim
        self.hid_dim=hid_dim
        self.n_layers=n_layers
        self.n_heads=n_heads
        self.pf_dim=pf_dim
        self.encoder_layer=encoder_layer
        self.self_attention=self_attention
        self.positionwise_feedforward=positionwise_feedforward
        self.dropout=dropout
        self.device=device
        
        self.tok_embedding=nn.Embedding(input_dim, emb_dim)
        # word embedding的定义nn.Embedding(1000, hid_dim),这里的1000表示有1000个词,hid_dim表示维度,其实也就是一个1000xhid_dim的矩阵。
        self.pos_embedding=nn.Embedding(1000, hid_dim)
        
        self.layers=nn.ModuleList([encoder_layer(hid_dim,n_heads,pf_dim,self_attention,
                                                positionwise_feedforward,dropout,device) 
                                   for _ in range(n_layers)])
        
        self.do=nn.Dropout(dropout)
        self.scale=torch.sqrt(torch.FloatTensor([hid_dim])).to(device)
        
    # 除了src还增加了src_mask,就是使用padding mask进行处理
    def forward(self, src, src_mask):
        pos=torch.arange(0, src.shape[1]).unsqueeze(0).repeat(src.shape[0]).to(self.device)
        # 进行scale之后相加
        src=self.do(self.tok_embedding(src)*self.scale)+self.pos_embedding(pos)
        for layer in self.layers:
            src=layer(src,src_mask)
        return src

# 层归一处理
class EncoderLayer(nn.Module):
    def __init__(self, hid_dim, n_heads, pf_dim, self_attention, postionwise_feedforward,dropout,device):
        super(EncoderLayer,self).__init__()
        
        self.ln=nn.LayerNorm(hid_dim)
        self.sa=self_attention(hid_dim,n_heads,dropout,device)
        self.pf=postionwise_feedforward(hid_dim, pf_dim,dropout)
        self.do=nn.Dropout(dropout)
        
    def forward(self, src, src_mask):
        src=self.ln(src+self.do(self.sa(src,src,src,src_mask)))
        src=self.ln(src+self.do(self.pf(src)))
        return src

# self-attention
class SelfAttention(nn.Module):
    def __init__(self, hid_dim, n_heads, dropout, device):
        super(SelfAttention,self).__init__()
        
        self.hid_dim=hid_dim
        self.n_heads=n_heads
        
        assert hid_dim%n_heads==0
        
        self.w_q=nn.Linear(hid_dim,hid_dim)
        self.w_k=nn.Linear(hid_dim, hid_dim)
        self.w_v=nn.Linear(hid_dim, hid_dim)
        
        self.fc=nn.Linear(hid_dim,hid_dim)
        self.do=nn.Dropout(dropout)
        
        self.scale=torch.sqrt(torch.FloatTensor([hid_dim//n_heads])).to(device) 
    def forward(self, query, key, value, mask=None):
        bsz=query.shape[0]
        #query = key = value [batch size, sent len, hid dim]
        
        Q=self.w_q(query)
        K=self.w_k(key)
        V=self.w_v(value)
        #Q, K, V = [batch size, sent len, hid dim]
        
        Q = Q.view(bsz, -1, self.n_heads, self.hid_dim // self.n_heads).permute(0, 2, 1, 3)
        K = K.view(bsz, -1, self.n_heads, self.hid_dim // self.n_heads).permute(0, 2, 1, 3)
        V = V.view(bsz, -1, self.n_heads, self.hid_dim // self.n_heads).permute(0, 2, 1, 3)
        #Q, K, V = [batch size, n heads, sent len, hid dim // n heads]
        
        # 实现attentionQ*K^T/D
        energy=torch.matmul(Q,K.permute(0,1,3,2))/self.scale
        #energy = [batch size, n heads, sent len, sent len]
        
        if mask is not None:
            energy=energy.masked_fill(mask==0, -1e10)
        # 实现softmax部分
        attention=self.do(F.softmax(energy, dim=-1))
        #attention = [batch size, n heads, sent len, sent len]
        
        x=torch.matmul(attention,V)
        #x = [batch size, n heads, sent len, hid dim // n heads]
        x=x.permute(0,2,1,3).contiguous()
        #x = [batch size, sent len, n heads, hid dim // n heads]
        
        x=x.view(bsz, -1, self.n_heads*(self.hid_dim//self.n_heads))
        #x = [batch size, src sent len, hid dim]
        
        x=self.fc(x)
        
        return x

class PositionwiseFeedforward(nn.Module):
    def __init__(self, hid_dim, pf_dim, dropout):
        super(PositionwiseFeedforward,self).__init__()
        
        self.hid_dim=hid_dim
        self.pf_dim=pf_dim
        
        self.fc_1=nn.Conv1d(hid_dim,pf_dim,1)
        self.fc_2=nn.Conv1d(pf_dim, hid_dim, 1)
        
        self.do=nn.Dropout(dropout)
        
    def forward(self,x):
        #x = [batch size, sent len, hid dim]
        x = x.permute(0, 2, 1)
        #x = [batch size, hid dim, sent len]
        
        x = self.do(F.relu(self.fc_1(x)))
        #x = [batch size, ff dim, sent len]
        
        x = self.fc_2(x)
        #x = [batch size, hid dim, sent len]
        
        x = x.permute(0, 2, 1)
        #x = [batch size, sent len, hid dim]
        
        return x

class Decoder(nn.Module):
    def __init__(self, output_dim, hid_dim,n_layers,n_heads,pf_dim,decoder_layer,self_attention,positionwise_feedforward,dropout,device):
        super(Decoder,self).__init__()
        
        self.output_dim=output_dim
        self.hid_dim=hid_dim
        self.n_layers=n_layers
        self.n_heads = n_heads
        self.pf_dim = pf_dim
        self.decoder_layer = decoder_layer
        self.self_attention = self_attention
        self.positionwise_feedforward = positionwise_feedforward
        self.dropout = dropout
        self.device = device
        
        self.tok_embedding=nn.Embedding(output_dim, hid_dim)
        self.pos_embedding=nn.Embedding(1000,hid_dim)
        
        self.layers=nn.ModuleList([decoder_layer(hid_dim,n_heads,pf_dim,self_attention,positionwise_feedforward,dropout,device) for _ in range(n_layers)])
        self.fc=nn.Linear(hid_dim, output_dim)
        self.do=nn.Dropout(dropout)
        self.scale=torch.sqrt(torch.FloatTensor([hid_dim])).to(device)
        
    def forward(self, trg, src, trg_mask, src_mask):
        #trg = [batch_size, trg sent len]
        #src = [batch_size, src sent len]
        #trg_mask = [batch size, trg sent len]
        #src_mask = [batch size, src sent len]
        pos=torch.arange(0, trg.shape[1]).unsqueeze(0).repeat(trg.shape[0], 1).to(self.device)  
        trg=self.do((self.tok_embedding(trg)*self.scale)+self.pos_embedding(pos))
        
        for layer in self.layers:
            trg=layer(trg,src,trg_mask,src_mask)
        
        return self.fc(trg)

# Decoder的层归一,可以看到trg和src的mask,在下面Seq2Seq部分具体实现的时候是有区别的
class DecoderLayer(nn.Module):
    def __init__(self, hid_dim, n_heads, pf_dim, self_attention, positionwise_feedforward, dropout, device):
        super().__init__()
        
        self.ln = nn.LayerNorm(hid_dim)
        self.sa = self_attention(hid_dim, n_heads, dropout, device)
        self.ea = self_attention(hid_dim, n_heads, dropout, device)
        self.pf = positionwise_feedforward(hid_dim, pf_dim, dropout)
        self.do = nn.Dropout(dropout)
        
    def forward(self, trg, src, trg_mask, src_mask):
        
        #trg = [batch size, trg sent len, hid dim]
        #src = [batch size, src sent len, hid dim]
        #trg_mask = [batch size, trg sent len]
        #src_mask = [batch size, src sent len]
                
        trg = self.ln(trg + self.do(self.sa(trg, trg, trg, trg_mask)))
        trg = self.ln(trg + self.do(self.ea(trg, src, src, src_mask)))
        trg = self.ln(trg + self.do(self.pf(trg)))
        
        return trg

class Seq2Seq(nn.Module):
    def __init__(self, encoder, decoder, pad_idx, device):
        super().__init__()
        
        self.encoder = encoder
        self.decoder = decoder
        self.pad_idx = pad_idx
        self.device = device
        
    # mask机制 
    def make_masks(self, src, trg):
        
        #src = [batch size, src sent len]
        #trg = [batch size, trg sent len]
        
        src_mask = (src != self.pad_idx).unsqueeze(1).unsqueeze(2)
        trg_pad_mask = (trg != self.pad_idx).unsqueeze(1).unsqueeze(3)

        trg_len = trg.shape[1]
        
        trg_sub_mask = torch.tril(torch.ones((trg_len, trg_len), dtype=torch.uint8, device=self.device))
        trg_mask = trg_pad_mask & trg_sub_mask
        
        return src_mask, trg_mask
    
    def forward(self, src, trg):
        #src = [batch size, src sent len]
        #trg = [batch size, trg sent len]
                
        src_mask, trg_mask = self.make_masks(src, trg)
        enc_src = self.encoder(src, src_mask)
        #enc_src = [batch size, src sent len, hid dim]
                
        out = self.decoder(trg, enc_src, trg_mask, src_mask)
        #out = [batch size, trg sent len, output dim]
        
        return out

这部分就算是结束了,用了16天完成6个模型,学习了torchtext,环境从本地换到了Colab,Transformer模型也跑通了。
但是,这都是别人的东西,怎么才能消化成自己的呢?
接下来的目标是2017年的论文《Deep Context Model for Grammatical Error Correction》,这条路好孤单啊……没有组会没有团队,自己一个人玩……

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容