大模型LLMs组件系列:深入浅出解析注意力Attention机制

💡 注意力机制萌芽于上世纪80年代,最早是CNN视觉检测领域的技术,灵感来源于生物注意力,目的是从纷繁的信息中提取高价值信息,舍弃低价值信息。2014年,注意力机制被用于NLP领域,并于2017年开始作为Transformer架构的核心机制几乎一统NLP领域方法论。当前绝大多数大模型使用的Attention机制执行的是缩放点积注意力(Scaled Dot-Product Attention, SDPA),基本原理是对任意输入X,使其与Q、K、V三个投影矩阵相乘得到三个对应的隐状态向量,然后将Q与K状态向量进行点积后除以缩放因子得到注意力评分/权重。最后用这个权重与值向量点积得到输出。 网上将Transformer架构的文章数量众多而良莠不齐,常常因信息超载而令人劳神。本篇的目的在于使用尽可能少的公式的同时深入浅出解析Transformer架构的机制,并回答一些常见问题。

1 简单self-Attention:注意力机制的原理与优势

1.1 最简单、单Query的、无Batch_size维度的Attention机制图解

简单attention
如上图,假设我们的输入的batch_size=1、seq_len=3的token序列input_ids在经过embedding,前归一化层,位置编码后,以input_embed张量的形式进入Attention机制。在这个假设中,input_embed.shape=torch.Size([3, 8]),分别对应token序列长度和embedding空间维数hidden_size(直观起见没有batch_size维度),简化的代码如下。

inputs = "你好啊"
input_ids = tokenizer( inputs )               # torch.tensor( [8, 4, 7] )
input_embed = self.embedding( input_ids )     # torch.shape = torch.Size([3, 8])
'''
在经过前归一化层、位置编码后,input_embed = tensor([ 
  [ 0.23, 0.81, 0.45, 0.33, 0.44, 0.12, 0.17, 0.88 ],
  [ 0.90, 0.78, 0.32, 0.55, 0.28, 0.31, 0.49, 0.73 ],
  [ 0.12, 0.31, 0.56, 0.71, 0.02, 0.80, 0.34, 0.36 ],
])
'''

注意力机制的核心就是让所有token都用自己的状态向量与包括自己在内的所有token进行点积,相互打分。所以让input_embed中的三个长度为8的张量两两相乘?不行,太简单了,还会使得结果方阵对角线下一半是镜像。那么怎么办?

答:先用两个形状一样但值不一样的可学习权重矩阵对input_embed进行投影变换,再两两相乘,这下不仅在Attention机制中加入了神经网络权重参数,还解决了input_embed自乘带来的镜像问题。这两个权重矩阵就是投影矩阵q_projk_proj,为了消除量纲,还需要除以一个基于head_dim的缩放因子,简化代码如下:

self.q_proj = torch.nn.Linear( hidden_size, head_dim )
self.k_proj = torch.nn.Linear( hidden_size, head_dim )
# d_q和d_k分别是两个投影变换矩阵的输出维度,相等,均为head_dim,因为两者的输出结果要进行点积。再上图例子中,两者值都等于6。
query_states = self.q_proj( input_embed )
key_states = self.k_proj( input_embed )
attn_weights = torch.matmul(query_states, key_states.transpose(0, 2)) / math.sqrt(self.head_dim)
# attn_weights.shape = torch.Size([3, 3])

这样就够了吗?当然不行,这里的运算结果将是一个seq_len*seq_len的评分方阵,输入token的隐藏状态不见了,那么怎么办?

答:要想再次获得token的状态信息,最直接的方式是再用这个评分矩阵乘以输入input_embed,理论上也可以这样做,只不过是效果如何的问题。Transformer机制给出的方案是再来一个投影矩阵v_projinput_embed进行变换,然后再用上述评分矩阵乘以这个变换结果,不论如何,在计算效率允许的范围内,加一个可学习层总共是利大于弊的吧?代码简化如下:

self.v_proj = torch.nn.Linear( hidden_size, d_v )
# d_v和head_dim(d_q和d_k)可以不一样,上图中就设置为4,但很多大模型都设为一样,可能是为了计算方便。
value_states = self.v_proj( input_embed )
attn_weights = nn.functional.softmax(attn_weights, dim=-1)
attn_output = torch.matmul(attn_weights, value_states
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值