首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PyTorch中Transformer模型的搭建

PyTorch中Transformer模型的搭建

作者头像
带萝卜
发布2020-10-26 14:34:06
2.1K0
发布2020-10-26 14:34:06
举报

PyTorch最近版本更新很快,1.2/1.3/1.4几乎是连着出,其中: 1.3/1.4版本主要是新增并完善了PyTorchMobile移动端部署模块和模型量化模块。 而1.2版中一个重要的更新就是把加入了NLP领域中炙手可热的Transformer模型,这里记录一下PyTorchTransformer模型的用法(代码写于1.2版本,没有在1.3/1.4版本测试)。

1. 简介


也许是为了更方便地搭建BertGPT-2之类的NLP模型,PyTorchTransformer相关的模型分为nn.TransformerEncoderLayernn.TransformerDecoderLayernn.LayerNorm等几个部分。搭建模型的时候不一定都会用到, 比如fastai中的Transformer模型就只用到了encoder部分,没有用到decoder

至于WordEmbeddingPositionEncoding两个部分需要自己另外实现。

WordEmbedding可以直接使用PyTorch自带的nn.Embedding层。

PositionEncoding层的花样就多了,不同的模型下面有不同的PositionEncoding,比如Transformer的原始论文Attention is all you need中使用的是无参数的PositionEncodingBert中使用的是带有学习参数的PositionEncoding

在本文中介绍的是参考Transformer原始论文实现的Sequence2sequence形式的Transformer模型。

2. Sequence2sequence形式的Transformer模型搭建:


2.1 无可学习参数的PositionEncoding层

无参数的PositionEncoding计算速度快,还可以减小整个模型的尺寸,据说在有些任务中,效果与有参数的接近。

class PositionalEncoding(nn.Module):
    def __init__(self, d_model,dropout=0.1, max_len=5000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=dropout)
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0).transpose(0, 1)
        self.register_buffer('pe', pe)

    def forward(self, x):
        x = x + self.pe[:x.size(0), :]
        return self.dropout(x)

2.2 有可学习参数的PositionEncoding层

我曾在一个序列预测任务(非NLP)里面对比过两种PositionEncoding层,发现带有参数的PositionEncoding层效果明显比没有参数的PositionEncoding要好。

带参数的PositionEncoding层的定义更为简单,直接继承一个nn.Embedding,再续上一个dropout就可以了。因为nn.Embedding中包含了一个可以按索引取向量的权重矩阵weight

class LearnedPositionEncoding(nn.Embedding):
    def __init__(self,d_model, dropout = 0.1,max_len = 5000):
        super().__init__(max_len, d_model)
        self.dropout = nn.Dropout(p = dropout)

    def forward(self, x):
        weight = self.weight.data.unsqueeze(1)
        x = x + weight[:x.size(0),:]
        return self.dropout(x)

2.3 Sequence2sequence模型

将embedding、position_encoding、encoder和decoder拼接起来,就可以构成一个完整的sequence2sequence形式的Transformer模型了。

class S2sTransformer(nn.Module):

    def __init__(self,vocab_size,position_enc,d_model = 512,nhead = 8,num_encoder_layers=6,
                 num_decoder_layers=6,dim_feedforward=2048,dropout=0.1):
        super(S2sTransformer,self).__init__()

        # Preprocess
        self.embedding = nn.Embedding(vocab_size,d_model)
        self.pos_encoder_src = position_enc(d_model=512)
        # tgt
        self.pos_encoder_tgt = position_enc(d_model=512)

        # Encoder
        encoder_layer = nn.TransformerEncoderLayer(d_model,nhead,dim_feedforward,dropout)
        encoder_norm = nn.LayerNorm(d_model)
        self.encoder = nn.TransformerEncoder(encoder_layer,num_encoder_layers,encoder_norm)

        # Decoder
        decoder_layer = nn.TransformerDecoderLayer(d_model,nhead,dim_feedforward,dropout)
        decoder_norm = nn.LayerNorm(d_model)
        self.decoder = nn.TransformerDecoder(decoder_layer,num_decoder_layers,decoder_norm)
        self.output_layer = nn.Linear(d_model,vocab_size)

        self._reset_parameters()
        self.d_model = d_model
        self.nhead = nhead


    def forward(self, src,tgt,src_mask = None,tgt_mask = None,
                memory_mask = None,src_key_padding_mask = None,
                tgt_key_padding_mask = None,memory_key_padding_mask = None):

        # word embedding
        src = self.embedding(src)
        tgt = self.embedding(tgt)

        # shape check
        if src.size(1) != tgt.size(1):
            raise RuntimeError("the batch number of src and tgt must be equal")
        if src.size(2) != self.d_model or tgt.size(2) != self.d_model:
            raise RuntimeError("the feature number of src and tgt must be equal to d_model")

        # position encoding
        src = self.pos_encoder_src(src)
        tgt = self.pos_encoder_tgt(tgt)

        memory = self.encoder(src, mask=src_mask, src_key_padding_mask=src_key_padding_mask)
        output = self.decoder(tgt, memory, tgt_mask=tgt_mask, memory_mask=memory_mask,
                              tgt_key_padding_mask=tgt_key_padding_mask,
                              memory_key_padding_mask=memory_key_padding_mask)
        output = self.output_layer(output)
        # return output
        return softmax(output,dim = 2)


    def generate_square_subsequent_mask(self, sz):
        r"""Generate a square mask for the sequence. The masked positions are filled with float('-inf').
            Unmasked positions are filled with float(0.0).
        """
        mask = (torch.triu(torch.ones(sz, sz)) == 1).transpose(0, 1)
        mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))
        return mask

    def _reset_parameters(self):
        r"""Initiate parameters in the transformer model."""

        for p in self.parameters():
            if p.dim() > 1:
                xavier_uniform_(p)

模型搭建好了之后,就可以按照Sequence2sequence的训练方式进行训练了, 唯一需要注意的就是Transformerforward过程是并行的,与基于RNNSequence2sequence模型稍有不同。

训练过程可以参考PyTorch官网提供的chatbot的教程

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 简介
  • 2. Sequence2sequence形式的Transformer模型搭建:
    • 2.1 无可学习参数的PositionEncoding层
      • 2.2 有可学习参数的PositionEncoding层
        • 2.3 Sequence2sequence模型
        相关产品与服务
        NLP 服务
        NLP 服务(Natural Language Process,NLP)深度整合了腾讯内部的 NLP 技术,提供多项智能文本处理和文本生成能力,包括词法分析、相似词召回、词相似度、句子相似度、文本润色、句子纠错、文本补全、句子生成等。满足各行业的文本智能需求。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档