当前位置:   article > 正文

详细分解Transformer各部件总结_6.transformer模型的核心组件及其作用

6.transformer模型的核心组件及其作用

Transformer的介绍

  • Transformer的优势

    • # 1. Transformer能够利用分布式GPU进行并行训练,提升模型训练效率
      # 2. 在分析预测更长文本时,捕捉间隔较长的语义关联效果更好,
      作用:
      	基于seq2seq架构的transformer模型可以完成NLP领域研究的典型任务,如机器翻译,文本生成等,同时又可以构建预训练语言模型,用于其他任务
      
      • 1
      • 2
      • 3
      • 4
  • Transformer架构

    • 在这里插入图片描述

    • 输入部分

      • 源文本嵌入层及其位置编码器
        • 文本嵌入(Embedding)作用:无论源文本嵌入还是目标文本嵌入,都是为了将文本中词汇的数字表示转变为向量表示,希望在这样的高维空间捕捉词汇之间的关系
        • 位置编码器的作用:在编码器结构中,并没有对词汇位置信息进行处理,因此需要在Embdeding层后加入位置编码器,将词汇位置不同可能会产生不同语义的信息加入到词嵌入张量中,以弥补位置信息的缺失
      • 目标文本嵌入层及其位置编码器
    • 输出部分

      • 线性层
      • softmax层
    • 编码器部分

      • N个编码器层堆叠而成
      • 每个编码器由两个子层连接结构组成
      • 第一个子层链接包含一个多头自注意力层和规范化层及一个残差连接
      • 第二个子层连接结构包含一个前馈全连接子层和规范化层及一个残差连接
    • 解码器部分

      • N个解码器层堆叠而成
      • 每个解码器由三个子层连接结构组成
      • 第一个子层链接包含一个多头自注意力层和规范化层及一个残差连接
      • 第二个子层链接包含一个多头自注意力层和规范化层及一个残差连接
      • 第三个子层连接结构包含一个前馈全连接子层和规范化层及一个残差连接

1. 输入部分

  • 文本词嵌入

    • 作用:对输入文本数值化以方便拟合算法

    • import math
      import torch
      import torch.nn as nn
      class Embeddings(nn.Module):
          def __init(self, d_model, vocab):
              """
              d_model: 词嵌入的维度
              vocab: 词表的大小
              """
              super(Emdedings,self).__init()
              self.lut = nn.Embedding(vocab, d_model)
              self.d_model = d_model
              
          def forward(self, x):
              return self.lut(x) * math.sqrt(self.d_model)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
  • 位置编码器

    • 作用:因为在transformer的编码器结构中,并没有对词汇位置信息的处理,所以需要在embedding层后加入位置编码器,将词汇位置不同可能会产生不同语义的信息加入到词嵌入张量中,以弥补位置信息的缺失

      • class PositionalEncoding(nn.Module):
            """
            d_model: 词嵌入的维度
            dropout: 置0比率
            max_len: 句子最大长度
            """
            def __init__(self, d_model, dropout, max_len=5000):
                super(PositionalEncoding, self).__init__()
                self.dropout = nn.Dropout(p=dropout)
        
                # 初始化一个位置编码矩阵,它是0阵,大小维max_len * d_model
                pe = torch.zeros(max_len, d_model)
        
                # 绝对位置矩阵,词汇的绝对位置就是用它的索引去表示
                position = torch.arange(0, max_len).unsqueeze(1)
        
                # 将位置信息加入到位置编码矩阵中,将绝对位置矩阵有max_len*1变成max_len*d_model, 覆盖原来的初始化位置编码矩阵
                # 先需要一个1*d_model的变换矩阵div_term,其实div_term是一个1*d_model/2的矩阵
                div_term = torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.)/d_model))
        
                # 位置编码数值控制在-1,1之间有助于梯度下降
                pe[:, 0::2] = torch.sin(position*div_term)
                pe[:, 1::2] = torch.cos(position*div_term)
        
                # 现在pe是一个二维矩阵,embedded之前要扩展一个维度
                pe = pe.unsqueeze(0)
        
                # 将位置编码矩阵注册成模型的buffer,对模型效果有帮助,却不是模型结构中的超参数,不需要随着优化步骤进行更新的增益对象
                # 注册之后我们可以在模型保存后重加载时和模型结构与参数一同加载
                self.register_buffer('pe', pe)
        
            def forward(self, x):
                # x.size(1),根据输入句子的长度,截取pe的范围
                print("位置编码:", Variable(self.pe[:, :x.size(1)]).size())
                x = x + Variable(self.pe[:, :x.size(1)], requires_grad=False)
                return self.dropout(x)
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36

2. 编码器部分

  • 多头注意力机制

    • 作用:多头的本质是多个独立的attention计算,作为一个集成的作用,防止过拟合

      • # 注意力机制
        def attention(query, key, value, mask=None, dropout=None):
            # 词嵌入维度大小
            d_k = query.size(-1)
            # 计算注意力矩阵score
            scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k)
            # 判断是否使用掩码张量
            if mask:
                scores = scores.masked_fill(mask == 0, -1e9)
        
            # 对scores进行最后一维进行softmax
            p_atten = F.softmax(scores, dim=-1)
        
            if dropout:
                p_atten = dropout(p_atten)
            return torch.matmul(p_atten, value), p_atten
        
        
        def clone(module, N):
            """
            实现模型深拷贝
            :param module:要拷贝的目标网络层
            :param N: 拷贝数量
            """
            return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])
        
        
        class MultiHeadedAttention(nn.Module):
            """
            多头注意力机制
                head:头数
                embedding_dim: 词嵌入维度
                dropout: 置零率
            """
            def __init__(self, head, embedding_dim, dropout=0.1):
                super(MultiHeadedAttention, self).__init__()
                self.head = head
                self.attn = None
                self.dropout = nn.Dropout(p=dropout)
                # 判断head能否整除词嵌入维度
                assert embedding_dim % head == 0
        
                self.d_k = embedding_dim // head
                # 4表示多头注意力中Q,K,V各需要一个,最后拼接的矩阵还需要一个
                self.linears = clone(nn.Linear(embedding_dim, embedding_dim), 4)
        
            def forward(self, query, key, value, mask=None):
                if mask:
                    mask = mask.unsqueeze(1)
                # 获取batch size大小
                batch_size = query.size(0)
        
                # 多头处理环节, 将embedding.size变为head.size * d_k.size
                # 对二、三维进行转置操作,让句子长度维度和词向量维度能够相邻,这样注意力机制才能找到词义与句子的位置关系
                query, key, value = [model(x).view(batch_size, -1, self.head, self.d_k).transpose(1, 2) for model, x in zip(self.linears, (query, key, value))]
        
                # 接下来传入attention
                x, self.attn = attention(query, key, value, mask=mask, dropout=self.dropout)
        
                # 获得每个头计算结果组成的4维张量
                x = x.transpose(1, 2).contiguous().view(batch_size, -1, self.head*self.d_k)
        
                return self.linears[-1](x)
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43
        • 44
        • 45
        • 46
        • 47
        • 48
        • 49
        • 50
        • 51
        • 52
        • 53
        • 54
        • 55
        • 56
        • 57
        • 58
        • 59
        • 60
        • 61
        • 62
        • 63
  • 前馈全连接层

    • 作用:考虑注意力机制拟合程度不够,故意增加两层网络来增强模型的能力

      • class PositionWiseFeedForward(nn.Module):
            """
            前馈全连接层
                d_model: 词嵌入的维度
                d_ff: 全连接层维度
            """
            def __init__(self, d_model, d_ff, dropout=0.1):
                super(PositionWiseFeedForward, self).__init__()
                self.fc1 = nn.Linear(d_model, d_ff)
                self.fc2 = nn.Linear(d_ff, d_model)
        
                self.dropout = nn.Dropout(p=dropout)
        
            def forward(self, x):
                return self.fc2(self.dropout(F.relu(self.fc1(x))))
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
  • 规范化层

    • 作用:有助于模型收敛,防止过拟合

      • class LayerNorm(nn.Module):
            """
            数据规范化
                features: 词嵌入的维度
            """
            def __init__(self, features, eps=1e-6):
                super(LayerNorm, self).__init__()
                # nn.parameter代表模型参数
                self.a = nn.Parameter(torch.ones(features))
                self.b = nn.Parameter(torch.zeros(features))
        
                self.eps = eps
        
            def forward(self, x):
                mean = x.mean(-1, keepdim=True)
                std = x.std(-1, keepdim=True)
                return self.a*(x-mean) / (std+self.eps) + self.b
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
  • 残差连接

    • 作用:很好解决深度模型退化问题,同时使模型收敛更快,前后向传播更加顺畅

      • class sublayerConnection(nn.Module):
            """
            子层残差连接结构
                size: 词嵌入的维度
            """
            def __init__(self, size, dropout=0.1):
                super(SublayerConnection, self).__init__()
                # 规范化层
                self.norm = LayerNorm(size)
                self.dropout = nn.Dropout(p=dropout)
        
            def forward(self, x, sublayer):
                return x + self.dropout(sublayer(self.norm(x)))
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
  • 编码器层

    • 作用:作为编码器组成部分,完成对输入的特征提取

      • class EncoderLayer(nn.Module):
            """
            编码器
                size: 词嵌入维度
                self_attn: 多头自注意力机制
                feed_forward: 前馈全连接层
                dropout: 置零比例
            """
            def __init__(self, size, self_attn, feed_forward, dropout):
                super(EncoderLayer, self).__init__()
                self.self_attn = self_attn
                self.feed_forward = feed_forward
                self.size = size
        
                # 编码器层有两个残差连接结构
                self.sublayer = clone(SublayerConnection(size, dropout), 2)
        
            def forward(self, x, mask):
                x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, mask))
                return self.sublayer[1](x, self.feed_forward)
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
  • 编码器

    • 作用:对输入的特征提取,由若干个编码层组成

      • class Encoder(nn.Module):
            """
            实现编码器
                encoder_layer: 编码器层
                num_layer: 编码器层的个数
            """
            def __init__(self, encoder_layer, num_layer):
                super(Encoder, self).__init__()
                self.layers = clone(encoder_layer, num_layer)
                self.norm = LayerNorm(encoder_layer.size)
        
            def forward(self, x, mask):
                for layer in self.layers:
                    x = layer(x, mask)
                return self.norm(x)
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15

3. 解码器部分

  • 解码器层

    • 作用:根据给定的输入向目标进行特征提取

      • class DecoderLayer(nn.Module):
            """
            解码器层
                size: 词嵌入的维度大小
                self_attn: 多头自注意力机制
                src_attn: 多头注意力机制
                feed_forward: 前馈神经网络
            """
            def __init__(self, size, self_attn, src_attn, feed_forward, dropout):
                super(DecoderLayer, self).__init__()
                self.size = size
                self.self_attn = self_attn
                self.src_attn = src_attn
                self.feed_forward = feed_forward
                self.dropout = dropout
                self.sublayer = clone(SublayerConnection(size, dropout), 3)
        
            def forward(self, x, memory, source_mask, target_mask):
                """
                x: 上一层输出
                memory: 编码器语义存储变量
                source_mask: 源数据掩码张量, 遮掩住对结果没有意义的字符
                target_mask: 目标数据掩码张量,不希望模型利用后续信息
                """
                m = memory
                x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, target_mask))
                x = self.sublayer[1](x, lambda x: self.src_attn(x, m, m, source_mask))
                return self.sublayer[3](x, self.feed_forward)
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
  • 编码器

    • 作用:提取encoder输出的特征

      • class Decoder(nn.Module):
            """
            解码器
                decoder_layer: 解码器层
                num_layer: 解码器层个数
            """
            def __init__(self, decoder_layer, num_layer):
                super(Decoder, self).__init__()
                self.layers = clone(decoder_layer, num_layer)
                self.norm = LayerNorm(decoder_layer.size)
        
            def forward(self, x, memory, source_mask, target_mask):
                for layer in self.layers:
                    x = layer(x, memory, source_mask, target_mask)
                return self.norm(x)
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15

4. 输出部分

  • 输出结构

    • class Generator(nn.Module):
          """
          输出层结构
              d_model: 词嵌入维度
              vocab_size: 词表大小
          """
          def __init__(self, d_model, vocab_size):
              super(Generator, self).__init__()
              self.project = nn.Linear(d_model, vocab_size)
      
          def forward(self, x):
              return F.log_softmax(self.project(x), dim=-1)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/926626
推荐阅读
相关标签
  

闽ICP备14008679号