注意力机制笔记
一、课程内容
1-注意力机制介绍
2-注意力机制的实现方法
3-注意力机制的优缺点
二、为什么提出注意力机制
引入Attention的原因1: 1、在 Attention 机制引入之前,有一个问题大家一直很苦恼:长距离的信息会被弱化,就好像记忆能力弱的人,记不住过去的事情是一样的。
引入Attention的原因2: 参数少、速度快、效果好
最初场景问题: 文本翻译问题
问题1: 如果翻译的句子很长很复杂,比如直接一篇文章输进去,模型的计算量很大,并且模型的准确率下降严重
问题2: 没有考虑词与词之间的相关性,导致翻译效果比较差
三、什么是注意力机制
3.1 实用例子解释Attention:
通俗理解注意力机制原理与本质:Q(query)、K(key)、V(value)
- 第一步【查询】:打开京东网站,我们输入查询(query,Q),比如“笔记本”
- 第二步【计算相似性】:京东后台拿到这个查询Q,会用这个查询Q去和后台的所有商品的关键字(或者title)(key, K)一一来对比,找到物品和我们查询的相似性(或者说物品对应的相似性的权重),相似性越高,越可能推送给我们
- 第三步【得到价值】:这个时候我们还需要考虑物品的价值(value, V),这个V不是指物品值几块钱,而是这个物品在算法中的价值。如果商家给了京东广告费,或者商品物美价廉,评论好,点赞高等,那么算法就越有可能把物品排在前面推送给我们
- 第四步【计算带权重的价值】:我们拿刚刚得到的相似性,乘以物品在算法中的价值V,计算结果就是每件物品的最后带有相似性权重的价值,京东最后的算法就是返回这个带权重的价值,也就是把排序好的商品推送给我们
- **【总结】**:这是个典型的注意力过程,它推送在最前面给我们展示的商品,肯定是它最希望获得我们注意力的商品。当然,京东内部的算法肯定不是这样的,但是他们本质原理一样,都是基于注意力,并且我们看到的现象也是一样的。
3.2 Attention概念
“注意力机制”实际上就是想将人的感知方式、注意力的行为应用在机器上,让机器学会去感知数据中的重要和不重要的部分。
3.3 注意力机制分类:
1、软注意力: 注意力权重值分布在0-1之间,关注所有的词汇,但是不同词汇根据权重大小关注的程度不一样。
2、硬注意力: 注意力权重值是0或者1,只关注哪些重要的部分,忽略次要的部分
3、自注意力: 通过输入项内部的"表决"来决定应该关注哪些输入项.
3.4 注意力计算步骤(常见三步走战略)
Attention计算构成元素:
query(Q)、key(K)、value(V)
(计算过程的普遍性表达):
第一步: query和key进行相似度计算,得到attention_score
第二步: 对attention_score进行softmax归一化得到权重值,压缩数值到0-1之间
第三步: 对上一步的权重值和value进行融合,得到具有权重信息的新value
基本步骤
第一步: 根据注意力计算规则, 对Q,K,V进行相应的计算.
第二步: 根据第一步采用的计算方法, 如果是拼接方法,则需要将Q与第一步的计算结果再进行拼接, 如果是转置点积, 一般是自注意力, Q与V相同, 则不需要进行与Q的拼接.
第三步: 最后为了使整个attention机制按照指定尺寸输出, 使用线性层作用在第二步的结果上做一个线性变换, 得到最终对Q的注意力表示.
四、注意力计算规则
计算规则前提:
必须有指定的数据: Q、K、V;当输入的Q=K=V时(或者Q\K\V来自于同一个X), 称作自注意力计算规则;当Q、K、V不相等时称为一般注意力计算规则
三种规则方法:
第一种方法: 将Q和K进行纵轴拼接,然后经过线性变换,再经过Softmax进行处理得到权重,最后和V进行相乘
第二种方法: 将Q和K进行纵轴拼接,接着经过一次线性变化,然后进过tanh激活函数处理,再进行sum求和,再经过softmax进行处理得到权重,最后和V进行张量的乘法
第三种方法: 将Q和K的转置进行点乘,然后除以一个缩放系数,再经过softmax进行处理得到权重,最后和V进行张量的乘法
五、注意力机制的作用
概念:
在解码器端的注意力机制: 能够根据模型目标有效的聚焦编码器的输出结果, 当其作为解码器的输入时提升效果. 改善以往编码器输出是单一定长张量, 无法存储过多信息的情况.
在编码器端的注意力机制: 主要解决表征问题, 相当于特征提取过程, 得到输入的注意力表示. 一般使用自注意力(self-attention).
注意力机制实现步骤(深度学习中):
第一步: 按照注意力规则,对Q、K、V进行注意力的计算
第二步: 如果第一步是拼接操作,需要将Q和第一步计算的结果进行再次拼接,如果是点乘运算,Q和K、V相等,一般属于自注意力,不需要拼接
第三步: 我们需要将第二步的结果,进行线性变化,按照指定输出维度进行结果的表示
代码实现:
class MyAtt(nn.Module):
# 32 32 32 64 32
def __init__(self, query_size, key_size, value_size1, value_size2, output_size):
super(MyAtt, self).__init__()
self.query_size = query_size
self.key_size = key_size
self.value_size1 = value_size1
self.value_size2 = value_size2
self.output_size = output_size
# 线性层1 注意力权重分布
self.attn = nn.Linear(self.query_size + self.key_size, self.value_size1)
# 线性层2 注意力结果表示按照指定维度输出层 self.attn_combine
self.attn_combine = nn.Linear(self.query_size+self.value_size2, output_size)
def forward(self, Q, K, V):
# 1 求查询张量q的注意力权重分布, attn_weights[1,32]
# [1,1,32],[1,1,32]--> [1,32],[1,32]->[1,64]
# [1,64] --> [1,32]
# tmp1 = torch.cat( (Q[0], K[0]), dim=1)
# tmp2 = self.attn(tmp1)
# tmp3 = F.softmax(tmp2, dim=1)
attn_weights = F.softmax( self.attn(torch.cat( (Q[0], K[0]), dim=-1)), dim=-1)
# 2 求查询张量q的结果表示 bmm运算, attn_applied[1,1,64]
# [1,1,32] * [1,32,64] ---> [1,1,64]
attn_applied = torch.bmm(attn_weights.unsqueeze(0), V)
# 3 q 与 attn_applied 融合,再按照指定维度输出 output[1,1,64]
# 3-1 q与结果表示拼接 [1,32],[1,64] ---> [1,96]
output = torch.cat((Q[0], attn_applied[0]), dim=-1)
# 3-2 shape [1,96] ---> [1,32]
output = self.attn_combine(output).unsqueeze(0)
# 4 返回注意力结果表示output:[1,1,32], 注意力权重分布attn_weights:[1,32]
return output, attn_weights