Self-Attention&Multi-head-Attention(原理和代码)

文章详细介绍了注意力机制在商品推荐中的应用以及Self-Attention的工作原理,包括如何计算不同token之间的相似度并进行加权求和。进一步,文章讨论了Multi-headAttention的必要性,旨在从多个角度提取特征,通过多个独立的Self-Attention层并行处理信息,最后将结果合并。代码示例展示了如何在Python中实现这两个机制。
摘要由CSDN通过智能技术生成

注意力


所谓的注意力,就是我们在查询某个事物的时候,对事物的某个特征具有一定的偏向性,故会根据此特征去对事物进行相似度匹配。

例如在商品的推荐机制中,系统可以根据用户输入的关键字Query与商品的关键词Key进行相似度匹配attn ,最后利用attn作为商品对于用户关心的权重,再和各商品的重要程度value 进行组合,最终得到商品关于Query的总分。
 图1. 商品推荐机制

图1. 商品推荐机制

Self-Attention


Self-Attention 中的 Self的理解

Self-Attention 最先是在NLP领域被应用,句子中的字token进行Eembedding后需要提取出属于自己的querykeyvalue
在这里插入图片描述

图2. Self-AttentionQ、K、V的含义

通过计算 q 1 q^1 q1 k 1 , k 2 . . . k n k^1,k^2...k^n k1,k2...kn的乘积来计算其他字符相对于token1而言的相似程度。
α i j = q i k j d k (1) \alpha_{ij}=\frac{q^ik^j}{\sqrt{d_k}}\tag{1} αij=dk qikj(1)
α i j \alpha_{ij} αij表示第 j j jtoken对第 i i itoken的的相似度(权重),至于为什么要除以 d k \sqrt{d_k} dk 有以下两个理解。

  • 防止输入softmax的值太大,导致偏导数为0。
  • 除以 d k \sqrt{d_k} dk 可以让 q ∗ k q*k qk的结果满足期望为0,方差为1的分布,类似归一化处理。
    总的Self-Attention的计算流程图如下所示:
    在这里插入图片描述
图3. Self-Attention计算流程图

α = s o f t m a x ( Q K T d k ) (2) \alpha=softmax\left(\frac{QK^T}{\sqrt{d_k}}\right)\tag{2} α=softmax(dk QKT)(2)
其中 Q Q Q K K K 分别表示句子编码后的矩阵,例如有4个token,每个 q q q的维度是1x2的矩阵,则矩阵Q的维度是4x2,规定矩阵K的维度也是4x2。通过上述公式可以得到相似度矩阵 α \alpha α,最后通过下列公式计算得到关于每个token的总分。例如对于token1而言,通过上述公式可以得到token1相对于其他token的相似度归一化矩阵 α 1 = [ α 11 , α 12 , α 13 , α 14 ] \alpha_{1}=\left[\alpha_{11},\alpha_{12},\alpha_{13},\alpha_{14}\right] α1=[α11,α12,α13,α14],再将 α 1 \alpha_1 α1与V矩阵(3)相乘,得到关于token1的分数 b 1 b_1 b1
b 1 = [ V 1 V 2 V 3 V 4 ] ∗ [ α 11 , α 12 , α 13 , α 14 ] (3) b_1=\left[\begin{matrix} V_1\\ V_2\\V_3\\ V_4 \end{matrix} \right]*\left[\alpha_{11},\alpha_{12},\alpha_{13},\alpha_{14}\right]\tag{3} b1= V1V2V3V4 [α11,α12,α13,α14](3)
故总的Self-Attention的计算公式如(4)所示。 B = A t t e n t i o n ( Q , K , V ) = s o f t m a x ( Q K T d k ) V (4) B=Attention\left(Q,K,V \right)=softmax(\frac{QK^T}{\sqrt{d_k}})V\tag{4} B=Attention(Q,K,V)=softmax(dk QKT)V(4)

Self-Attention得到的结果 b 1 , b 2 , b 3 b_1,b_2,b_3 b1,b2,b3 …可以看作是提取出来的features,这些features可以用于下游任务的训练和推理。


代码实现Self-Attention

整体python代码如下:主要就是定义了一个Self-Attention的类,继承nn.Module构建 Q , K , V Q,K,V Q,K,V的矩阵 W W W,维度dim表示输入的字所表示的维度,例如用一个1x2的向量表示一个字,则dim就为2。dk表示 q , k q,k q,k的维度, Q , K Q,K Q,K的维度需要相同。 V V V的维度可以不用相同。
然后我们来看下forward函数,就是Self-Attention的具体计算过程。具体来说就是,通过q,k,v进行self操作,得到自身的 Q , K , V Q,K,V Q,K,V矩阵。attn就是进行 Q , K T Q,K^T Q,KT的点乘,transpose是进行维度的转换,最后再针对最后一维进行softmax操作,即对 α 1 = [ α 11 , α 12 , α 13 , α 14 ] \alpha_{1}=\left[\alpha_{11},\alpha_{12},\alpha_{13},\alpha_{14}\right] α1=[α11,α12,α13,α14]进行softmax操作。最后将归一化的相似度与 V V V相乘得到每个词向量x的总分(feature)。

import torch.nn as nn  
import torch   
  
class Self_Attention(nn.Module):  
	def __init__(self, dim, dk, dv):  
		super(Self_Attention, self).__init__()  
		self.scale = dk ** -0.5  
		self.q = nn.Linear(dim, dk)  
		self.k = nn.Linear(dim, dk)  
		self.v = nn.Linear(dim, dv)  
  
  
	def forward(self, x):  
		q = self.q(x)  
		k = self.k(x)  
		v = self.v(x)  
  
		attn = (q @ k.transpose(-2, -1)) * self.scale  
		attn = attn.softmax(dim=-1)  
		x = attn @ v  
		return x  
		
att = Self_Attention(dim=2, dk=2, dv=3)  
x = torch.rand((1, 4, 2))  
output = att(x)

Multi-head-Attention

为什么需要Multi-head-Attention?

对于为什么需要Multi-head-Attention这个问题,我自己的理解是Sigle-head-Attention中的Q例如是1x2的矩阵,它只关注了某一个点的特征,例如我们在选择购买某件商品的时候,需要综合考虑商品的质量、颜色、风格等特征。故为了**多角度考虑商品的每个特征,需要针对多个不同类别的query计算其相似度。**故引出了Multi-head-Attention。
在这里插入图片描述

图4. single head架构

Multi-head 示意图:
在这里插入图片描述

图5. Multi head 架构

什么是Multi-head-Attention?

通过按照不同类别的关注点来分解 Q Q Q矩阵进行相似度计算,解决提取特征单一化的问题,达到多角度考虑商品的每个特征,更加充分的提取特征。

具体操作如下,对在使用公式(2)计算相似度之前,进行multi-head分解,分别计算每个head(角度)的总分,最后再concat起来,得到总的多角度分析的特征值
在这里插入图片描述

图6. Multi head 结构

计算完 Q , K , V Q,K,V Q,K,V矩阵之后,完成多head(角度)的拆分。每个Head之间,就是一个Single-self-Attention。按照上述公式(4)完成计算。在这里插入图片描述

图7. Mulit head 结构图

最后在进行多头的concat过程中,先按照列拼接,完成多头的组合,再完成行的拼接,完成每一个词向量的特征提取(feature)。
注意:此处按照论文里面的解释,向量 V V V的维度和 Q , K Q,K Q,K相同,都为 d m o d e l d_{model} dmodel,其实是可以不相同的
行列concat完成之后,再通过一个全连接层进行特征的输出。

在这里插入图片描述

图8. Multi head 结构流程图

代码实现Multi-head-Attention

整体python代码如下:总体来说就是定义了一个MultiHeadSelfAttention类,继承nn.Moudel,然后dim_in和self-Attention代码中的dim一样,表示输入的字所表示的维度,例如用一个1x2的向量表示一个字,则dim_in就为2。d_model表示整体 Q , K , V Q,K,V Q,K,V矩阵的维度,接着,对d_model % num_heads进行了一个检测,需要整除。

接着定义 Q , K , V Q,K,V Q,K,V的变换层,维度和self-attention的解释一致,最主要的步骤是在forward函数。首先计算每个head q , k , v q,k,v q,k,v的维度,即nh。例如本例中num_token的值为4,即4个字, d m o d e l d_{model} dmodel的长度为6,head为3,则传入x生成 Q , K , V Q,K,V Q,K,V的维度为[1, 4, 6].通过reshape函数,拆分成多头,每个head的维度是[1, 4, 3, 2],为了方便后续计算attn,需要交换下维度成[1, 3, 4, 2]。然后按照公式(2)计算每个head的相似度,再按照公式(3)得到每个head的特征feature,维度是[1, 3, 4, dv],这里的dv等于2,最后再对multi-head进行拼接成[1, 4, 6]的矩阵,再通过一个全连接层fc完成Multi-head-attention的特征提取。

from math import sqrt
import torch
import torch.nn as nn


class MultiHeadSelfAttention(nn.Module):
    def __init__(self, dim_in, d_model, num_heads=3) -> None:
        super(MultiHeadSelfAttention, self).__init__()

        self.dim_in = dim_in
        self.d_model = d_model
        self.num_heads = num_heads

        assert d_model % num_heads == 0, "d_model must be multiple of num_heads"

        self.linear_q = nn.Linear(dim_in, d_model)
        self.linear_k = nn.Linear(dim_in, d_model)
        # v的层数可以不和q k相同
        self.linear_v = nn.Linear(dim_in, d_model)
        self.scale = 1 / sqrt(d_model // num_heads)

        self.fc = nn.Linear(d_model, d_model)

    def forward(self, x):
        batch, num_token, dim_in = x.shape
        assert dim_in == self.dim_in

        nh = self.num_heads
        dk = self.d_model // nh

        # multi heads attention
        q = self.linear_q(x).reshape(batch, num_token, nh, dk).transpose(1, 2)
        k = self.linear_k(x).reshape(batch, num_token, nh, dk).transpose(1, 2)
        v = self.linear_v(x).reshape(batch, num_token, nh, dk).transpose(1, 2)

        dist = torch.matmul(q, k.transpose(2, 3)) * self.scale
        dist = torch.softmax(dist, dim=-1)

        att = torch.matmul(dist, v)  # shape(batch nh num_token dv)
        # multi heads fusion
        att = att.transpose(1, 2).reshape(batch, num_token, self.d_model)  # shape(batch num_token d_model)

        output = self.fc(att)

        return output


x = torch.rand((1, 4, 2))
multi_head_att = MultiHeadSelfAttention(x.shape[2], 6, 3)
output = multi_head_att(x)

注:此文是作者学习的总结,便于后续回看,转载请联系作者

参考文献


Multi-head attention是一种注意力机制,它在Transformer模型中被引入。它可以看作是多个self-attention的组合,类似于CNN中的多核。不同于循环计算每个头,multi-head attention使用矩阵乘法来实现。它的计算流程可以通过转置和重塑来完成。使用多头注意力机制可以使模型同时关注来自不同表示子空间和不同位置的信息,从而提高模型的表达能力。理解self-attention的本质实际上就是了解multi-head attention结构。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [自注意力(Self-Attention)与Multi-Head Attention机制详解](https://blog.csdn.net/weixin_60737527/article/details/127141542)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Multi-Head Attention的讲解](https://blog.csdn.net/qq_41980734/article/details/120842437)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [详解Transformer中Self-Attention以及Multi-Head Attention](https://blog.csdn.net/qq_37541097/article/details/117691873)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值