参考:
- 《神经网络与深度学习》 第8章 p209
- 《[动手学习深度学习](10. 注意力机制 — 动手学深度学习 2.0.0-beta0 documentation (d2l.ai))》第10、15章
- 注意力机制:通过自上而下的信息选择机制来过滤掉大量的无关信息
- 记忆机制:通过自上而下的信息选择机制来过滤掉大量的无关 信息
- 自上而下的有意识的注意力,称为聚焦式注意力(Focus Attention). 聚焦式注意力也常称 为选择性注意力(Se�lective Attention). 聚焦式注意力是指有预定目的、依赖任务的,主动有意识地聚焦于某一对象的注 意力.
- 自下而上的无意识的注意力,称为基于显著性的注意力。比如最大汇聚(Max Pooling)、门控 (Gating)机制
一般神经网络中的注意力都是前者,我们现在需要关注的是后者。
在注意力机制的背景下,我们将自主性提示称为查询(query)。 给定任何查询,注意力机制通过注意力汇聚(attention pooling) 将选择引导至感官输入(sensory inputs,例如中间特征表示)。 在注意力机制中,这些感官输入被称为值(value)。 更通俗的解释,每个值都与一个键(key)配对, 这可以想象为感官输入的非自主提示。我们可以设计注意力汇聚, 以便给定的查询(自主性提示)可以与键(非自主性提示)进行匹配, 这将引导得出最匹配的值(感官输入)。
个人理解就是把没有包含自主性提示的全连接输入或者是max pooling这种通过注意力汇聚将某些神经元的权重变大,从而相当于是有了自主性的提示。而这种自足性的提示来源于查询这一操作。有点类似于boostding的给分错的类别增加权重的思想?或者理解为用查询给整个系统加入了规则?往后再看看。
非参数注意力汇聚可以表示为如下的形式: $$ f(x)=\sum_{i=1}^{n} \frac{K\left(x-x_{i}\right)}{\sum_{j=1}^{n} K\left(x-x_{j}\right)} y_{i} $$ 说白了就是一个函数,然后用x与xi的关系当做一个权重,也就是所谓的核函数。一般这种核可以选择线性核或者高斯核。
带参数的注意力机制可以说给核加一个权重然后再去回归训练,训练出来的结果会更加不平滑并且注意力热力图更加集中。
我们使用高斯核来对查询和键之间的关系建模。 高斯核指数部分视为注意力评分函数(attention scoring function), 简称评分函数(scoring function), 然后把这个函数的输出结果输入到softmax函数中进行运算。 通过上述步骤,我们将得到与键对应的值的概率分布(即注意力权重)。 最后,注意力汇聚的输出就是基于这些注意力权重的值的加权和。
看到了别人的一个理解,意思是训练数据是key,测试数据是query,正确性存疑。没问题,然后值就是每个key在经过注意力机制之后的权重。
下面说几种不同的注意力机制以满足不同的NLP的任务的需求:
某些文本序列被填充了没有意义的特殊词元,为了仅将有意义的词元作为值来获取注意力汇聚, 我们可以指定一个有效序列长度(即词元的个数), 以便在计算softmax时过滤掉超出指定范围的位置。 通过这种方式,我们可以在下面的masked_softmax
函数中 实现这样的掩蔽softmax操作(masked softmax operation), 其中任何超出有效长度的位置都被掩蔽并置为0。
我的理解就是因为文本的词数是不固定的,所以key长会变化,所以需要能够解决不等长输入的注意力机制。
查询和键是不同长度的矢量。一般来说, 当查询和键是不同长度的矢量时, 我们可以使用加性注意力作为评分函数。给定 查询
#@save
class AdditiveAttention(nn.Module):
"""加性注意力"""
def __init__(self, key_size, query_size, num_hiddens, dropout, **kwargs):
super(AdditiveAttention, self).__init__(**kwargs)
self.W_k = nn.Linear(key_size, num_hiddens, bias=False)
self.W_q = nn.Linear(query_size, num_hiddens, bias=False)
self.w_v = nn.Linear(num_hiddens, 1, bias=False)
self.dropout = nn.Dropout(dropout)
def forward(self, queries, keys, values, valid_lens):
queries, keys = self.W_q(queries), self.W_k(keys)
# 在维度扩展后,
# queries的形状:(batch_size,查询的个数,1,num_hidden)
# key的形状:(batch_size,1,“键-值”对的个数,num_hiddens)
# 使用广播方式进行求和
features = queries.unsqueeze(2) + keys.unsqueeze(1)
features = torch.tanh(features)
# self.w_v仅有一个输出,因此从形状中移除最后那个维度。
# scores的形状:(batch_size,查询的个数,“键-值”对的个数)
scores = self.w_v(features).squeeze(-1)
self.attention_weights = masked_softmax(scores, valid_lens)
# values的形状:(batch_size,“键-值”对的个数,值的维度)
return torch.bmm(self.dropout(self.attention_weights), values)
查询、键和值的形状为(批量大小,步数或词元序列长度,特征大小), 实际输出为:
query:(2,3,20)
keys:(2,10,2)
values:(2,10,4)
valid_lens:([2, 6])
如下是输出结果:
queries.shape: torch.Size([2, 3, 8]) keys.shape: torch.Size([2, 10, 8])
queries.unsqueeze(2).shape: torch.Size([2, 3, 1, 8])
keys.unsqueeze(1).shape: torch.Size([2, 1, 10, 8])
features.shape: torch.Size([2, 3, 10, 8])
scores.shape: torch.Size([2, 3, 10])
attention_weights.shape torch.Size([2, 3, 10])
out.shape: torch.Size([2, 3, 4])
- 经过线性层将query和key的特征约束到同样的hidden size=8。
- 增加维数从而让query和key可以通过广播的形式相加
- 经过线性层加权特征
- 通过mask_softmax对attention_weights进行操作,维数不变但是有些值已经被用0填充
- dropout在验证的时候是不会有影响的,但是在训练的时候会随机丢弃值。下面是不加上mask_softmax以及在train模式下输出的结果。当dropout取0.9的时候可以看到有大量的值被丢弃。
- torch.bmm()是一种矩阵乘法模式。
If input is a
参考的动手学习深度学习第15章。用人话来讲一下注意力机制,看公式实在是看吐了,真的是看不懂。我们不要管注意力机制是什么,我们只做一个对其内容的翻译。
注意力机制有三步构成:对齐,比较,聚合。
对齐是基于语言的相似性,将一个文本序列和另外一个序列进行对齐。
步骤:
- 将前提与假设文本分别输入多层感知机。
- 计算前提与假设之间每个词的注意力权重。
- 利用注意力矩阵分别对前提和假设中的词元进行对齐。对齐的方式为软对齐,即用softmax进行计算。需要定义出attention类来计算出两种对齐。
将一个序列中的词元与与该词元软对齐的另一个序列进行比较。之前不是对齐了么,那相当于就是有了一个序列对于另外一个序列的一个注意力的权重的表达。现在通过把这个注意力矩阵和序列本身的每一个词元进行对比。
-
求和比较向量。
-
求和的结果提供给感知机以获得逻辑关系的分类结果。
整体步骤如下:
def forward(self, X):
premises, hypotheses = X
A = self.embedding(premises)
B = self.embedding(hypotheses)
beta, alpha = self.attend(A, B)
V_A, V_B = self.compare(A, B, beta, alpha)
Y_hat = self.aggregate(V_A, V_B)
return Y_hat