zoukankan      html  css  js  c++  java
  • transformer中自注意力和多头注意力的pytorch实现

    首先是注意力公式:

    其计算图:

    代码:

    def attention(query, key, value, mask=None, dropout=None): 
    # query, key, value的形状类似于(30, 8, 10, 64), (30, 8, 11, 64), 
    #(30, 8, 11, 64),例如30是batch.size,即当前batch中有多少一个序列;
    # 8=head.num,注意力头的个数;
    # 10=目标序列中词的个数,64是每个词对应的向量表示;
    # 11=源语言序列传过来的memory中,当前序列的词的个数,
    # 64是每个词对应的向量表示。
    # 类似于,这里假定query来自target language sequence;
    # key和value都来自source language sequence.
      "Compute 'Scaled Dot Product Attention'" 
      d_k = query.size(-1) # 64=d_k
      scores = torch.matmul(query, key.transpose(-2, -1)) / 
        math.sqrt(d_k) # 先是(30,8,10,64)和(30, 8, 64, 11)相乘,
        #(注意是最后两个维度相乘)得到(30,8,10,11),
        #代表10个目标语言序列中每个词和11个源语言序列的分别的“亲密度”。
        #然后除以sqrt(d_k)=8,防止过大的亲密度。
        #这里的scores的shape是(30, 8, 10, 11)
      if mask is not None: 
        scores = scores.masked_fill(mask == 0, -1e9) 
        #使用mask,对已经计算好的scores,按照mask矩阵,填-1e9,
        #然后在下一步计算softmax的时候,被设置成-1e9的数对应的值~0,被忽视
      p_attn = F.softmax(scores, dim = -1) 
        #对scores的最后一个维度执行softmax,得到的还是一个tensor, 
        #(30, 8, 10, 11)
      if dropout is not None: 
        p_attn = dropout(p_attn) #执行一次dropout
      return torch.matmul(p_attn, value), p_attn
    #返回的第一项,是(30,8,10, 11)乘以(最后两个维度相乘)
    #value=(30,8,11,64),得到的tensor是(30,8,10,64),
    #和query的最初的形状一样。另外,返回p_attn,形状为(30,8,10,11). 
    #注意,这里返回p_attn主要是用来可视化显示多头注意力机制。

    多头注意力:

    class MultiHeadedAttention(nn.Module): 
      def __init__(self, h, d_model, dropout=0.1): 
        # h=8, d_model=512
        "Take in model size and number of heads." 
        super(MultiHeadedAttention, self).__init__() 
        assert d_model % h == 0 # We assume d_v always equals d_k 512%8=0
        self.d_k = d_model // h # d_k=512//8=64
        self.h = h #8
        self.linears = clones(nn.Linear(d_model, d_model), 4) 
        #定义四个Linear networks, 每个的大小是(512, 512)的,
        #每个Linear network里面有两类可训练参数,Weights,
        #其大小为512*512,以及biases,其大小为512=d_model。
    
        self.attn = None 
        self.dropout = nn.Dropout(p=dropout)
      def forward(self, query, key, value, mask=None): 
       # 注意,输入query的形状类似于(30, 10, 512),
       # key.size() ~ (30, 11, 512), 
       #以及value.size() ~ (30, 11, 512)
        
        if mask is not None: # Same mask applied to all h heads. 
          mask = mask.unsqueeze(1) # mask下回细细分解。
        nbatches = query.size(0) #e.g., nbatches=30
        # 1) Do all the linear projections in batch from 
        #d_model => h x d_k 
        query, key, value = [l(x).view(nbatches, -1, self.h, self.d_k)
          .transpose(1, 2) for l, x in 
          zip(self.linears, (query, key, value))] 
          # 这里是前三个Linear Networks的具体应用,
          #例如query=(30,10, 512) -> Linear network -> (30, 10, 512) 
          #-> view -> (30,10, 8, 64) -> transpose(1,2) -> (30, 8, 10, 64)
          #,其他的key和value也是类似地,
          #从(30, 11, 512) -> (30, 8, 11, 64)。
        # 2) Apply attention on all the projected vectors in batch. 
        x, self.attn = attention(query, key, value, mask=mask, 
          dropout=self.dropout) 
          #调用上面定义好的attention函数,输出的x形状为(30, 8, 10, 64);
          #attn的形状为(30, 8, 10=target.seq.len, 11=src.seq.len)
        # 3) "Concat" using a view and apply a final linear. 
        x = x.transpose(1, 2).contiguous().
          view(nbatches, -1, self.h * self.d_k) 
          # x ~ (30, 8, 10, 64) -> transpose(1,2) -> 
          #(30, 10, 8, 64) -> contiguous() and view -> 
          #(30, 10, 8*64) = (30, 10, 512)
    return self.linears[-1](x) 
    #执行第四个Linear network,把(30, 10, 512)经过一次linear network,
    #得到(30, 10, 512).

    摘自:https://zhuanlan.zhihu.com/p/107889011 

  • 相关阅读:
    过滤指定目录下指定后缀名文件
    Oracle 存储过程创建表
    编码测试之控制条件筛选心得——AE二次开发篇
    log4j配置祥解 (转)
    手机浏览器的viewport(视觉窗口)
    git 常用技巧
    添加eclipse的字体样式
    如何检测android 文件签名成功(转帖)
    javascript 解析json
    身份证正则表达式
  • 原文地址:https://www.cnblogs.com/xiximayou/p/13343856.html
Copyright © 2011-2022 走看看