Transformer的几个问题

      Transformer是《Attention Is All You Need》提出来的,结构如下所示:

在这里插入图片描述

       讲解Transformer的文章很多,这里不再重复,可以参考文献1文献2

      本文只想讲一些在看Transformer这篇文章时遇到的一些问题,并写下我对这些问题的理解。

问题一:为什么要除以 d k \sqrt{d_k} dk ?

在这里插入图片描述
      当 d k d_k dk增大时,意味着q和k之间的点乘操作会增加,只要 q i k i q_ik_i qiki稍微比其它值大一点,经过softmax之后绝大部分值就会变得很小,接近于0,使得其梯度很小。

      我们假设q和k的分布均服从标准正太分布,即其均值为0,方差为1,当进行点乘操作后:
在这里插入图片描述
      分布变成均值为0,方差为 d k d_k dk【利用均值和方差的性质即可推得】,当 d k d_k dk很大时,意味着q*k的方差就很大,分布会趋于陡峭(分布的方差大,分布就会集中在绝对值大的区域),就会使得softmax()之后使得值出现两极分化的状态。

      做个简单的实验, d k d_k dk分别取5和100时,随机生成 d k d_k dk个服从标准正太分布的q和k,点乘后经过softmax函数,图像如下所示:
      当 d k d_k dk=5时:
dk=5
      当 d k d_k dk=100时:
在这里插入图片描述
      当我把 d k d_k dk=100,q和k点乘之后除以 d k \sqrt{d_k} dk 后,图像变成:
在这里插入图片描述
问题二:multi-head attention的作用

      Attention是将query和key映射到同一高维空间中去计算相似度,而对应的multi-head attention把query和key分成h个小序列分别映射到高维空间的不同子空间中去计算相似度,这样的好处是在两种方法的参数总量保持不变的情况下,Attention在不同子空间有不同的分布,进行concat后使得Attention层信息多样化,增加了Attention的表达能力。
在这里插入图片描述

问题三:positional encoding

      因为Transformer是处理序列问题,没有捕获数据位置的能力,所以需要加上额外的位置信息,这里位置信息可以是绝对位置也可以是相对位置,可以是可训练的位置信息也可以是不可训练的位置信息,在文中作者提出使用sin和cos来表示序列的相对位置信息:
在这里插入图片描述
      其中pos是位置,i是维度。

      sin和cos是周期性的函数,且对任意的x值,函数值都是唯一确定的,当位置 p o s pos pos偏移了k个单位(记为 p o s + k pos+k pos+k), P E p o s + k PE_{pos+k} PEpos+k可以用 P E p o s PE_{pos} PEpos的线性倍数表示。

问题四:Layer Normal和batch Normal

      假定输入为[batch_size,channels,H,W]
在这里插入图片描述

      batch Normal如第一张图所示,是针对小批次(batch_size)中所有样本每一个通道分别做计算均值和方差,然后再进行归一化。batch Normal和batch_size有关系,当batch_size较小时,对每个小批次进行归一化不足以代表整个数据的分布情况,而且计算的均值方差等信息需要额外的存储空间,对于有着固定的深度的DNN和CNN来说比较适合。

      Layer Normal如第二张图所示,是对每一个样本计算均值和方差做归一化,不依赖于batch_size的大小和输入sequence的深度,比较适合RNN。【参考

2023/5/18 再一次学习Transformer时一些代码记录
这部分代码和文字基本参考 https://b23.tv/Vw6IYII

掩码张量

掩码张量一般由0和1组成,代表当前位置被遮掩/不被遮掩。在Transformer中掩码张量主要用在attention上,有些生成的attention张量中的计算有可能是已知了未来的信息而得到的,未来的信息被看到是因为训练时把整个输出结果一次性进行embedding,但是一般decoder不是一次就能产生最终结果,而是需要一次次通过上一次结果综合得到。故未来的信息可能会被提前使用。所以需要掩码张量将未来信息进行遮掩。

# 构建掩码张量函数
def subsequent_mask(size):
    atten_shape=(1,size,size)
    subsequent_mask=np.triu(np.ones(atten_shape),k=1).astype('uint8')
    return torch.from_numpy(1-subsequent_mask)
    
plt.figure(figsize=(5,5))
plt.imshow(subsequent_mask(20)[0])
plt.show()

在这里插入图片描述
上图为掩码张量的一个图例,黄色表示遮挡部分,紫色为可见部分,横坐标为目标词汇的位置,纵坐标为词汇可查看的位置。例如x=5时,y=4,表示在第五个词汇位置上,可以查看的只有前4个词汇。

注意力代码

# 注意力机制
def attention(query,key,value,mask=None,dropout=None):
    '''
    args:
        query
        key
        value
        mask:是否使用掩码
        dropout:dropout函数
    '''
    d_k=query.size(-1)  # 词向量维度,一般设为512
    scores=torch.matmul(query,key.transpose(-2,-1))/math.sqrt(d_k)
    
    if mask is not None:
        # mask维度和scores一样,用于遮掩满足一定条件的scores位置的值,用于掩码注意力机制中
        # mask一般为上三角矩阵,这里的意思是将mask中为0的位置对应的scores位置的值用1e-9进行填充
        scores=scores.masked_fill(mask==0,1e-9)
        
    p_attn=F.softmax(scores)
    if dropout is not None:
        p_attn=dropout(p_attn)
        
    return torch.matmul(p_attn,value),p_attn

Q、K和V的形象解释:

假设给出一段文本,需要使用一些关键词对它进行描述:
key表示提前给的提示信息,value是第一次看到后脑中猜测的信息,qurey表示给出的这段文本。
一般key和value值相同,这种为一般的注意力机制。当key=value=query时,成为自注意力机制。

多头注意力机制

def clone(module,nums):
    return nn.ModuleList([module for _ in range(nums)])


class MultiHeadedAttention(nn.Module):
    def __init__(self,head,embedding_dim,dropout=0.1):
        super(MultiHeadedAttention,self).__init__()
        '''   
        head:多头数目
        embedding_dim:词嵌入维度
        '''
        assert embedding_dim%head == 0 #可以整除
        
        self.d_k=embedding_dim/head  # 每个头分配的词嵌入维度
        self.head=head
        
        self.linears=clone(nn.Linear(embedding_dim,embedding_dim),4)
        self.attn=None
        self.dropout=nn.Dropout(p=dropout)
        
    
    def forward(self,query,key,value,mask=None):
        if mask is not None:
            mask=mask.unsqueeze(1) # 使用unsqueeze扩展维度,表示多头中的第头
        
        bach_size=query.size(0)  #表示有多少样本
        
        # 多头注意力机制
        query,key,value=[model(x).view(bach_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,self.dropout)
        
        x=x.transpose(1,2).contiguous().view(bach_size,-1,self.head*self.d_k)
        
        return self.linears[-1](x)

      以上就是我在看Transformer论文时遇到的问题和自己的一些想法,如果有写的不正确或者表达不清楚的请各位多指教。

猜你喜欢

转载自blog.csdn.net/qq_44846512/article/details/114364559