Attention概述及代码实现
Attention机制
参考链接:
- 【NLP】Attention Model(注意力模型)学习总结
- https://nndl.github.io/
- https://github.com/philipperemy/keras-attention-mechanism/tree/0309dbf79da32c0d8d90925660fc4cc7fe53dc8a
- https://blog.csdn.net/uhauha2929/article/details/80733255
核心框架(Key-Value Attention):
将Source中的构成元素想象成是由一系列的<Key,Value>数据对构成,此时给定Target中的某个元素Query,通过计算Query和各个Key的相似性或者相关性,得到每个Key对应Value的权重系数,然后对Value进行加权求和,即得到了最终的Attention数值。所以本质上Attention机制是对Source中元素的Value值进行加权求和,而Query和Key用来计算对应Value的权重系数。
打分函数:
模型 | 公式 |
---|---|
加性模型 | ????(????, ????) = |
点积模型 | ????(????, ????) = |
缩放点击模型 | ????(????, ????) = |
双线性模型 | ????(????, ????) = |
理论上,加性模型和点积模型的复杂度差不多,但是点积模型在实现上可以更好地利用矩阵乘积,从而计算效率更高。当输入向量的维度???? 比较高时,点积模型的值通常有比较大的方差,从而导致Softmax函数的梯度会比较小。因此,缩放点积模型可以较好地解决这个问题。
双线性模型是一种泛化的点积模型。假设公式中???? = ????T????,双线性模型可以写为????(????, ????) = ????T????T???????? = (????????)T(????????),即分别对???? 和???? 进行线性变换后计算点积。相比点积模型,双线性模型在计算相似度时引入了非对称性。
多头注意力
多头注意力(Multi-Head Attention)利用多个查询???? = [????1, ⋯ , ???????? ],来并行地从输入信息中选取多组信息,每个注意力关注输入信息的不同部分。
结构化注意力
在之前介绍中,我们假设所有的输入信息是同等重要的,是一种扁平(Flat)结构,注意力分布实际上是在所有输入信息上的多项分布.但如果输入信息本身具有层次(Hierarchical)结构,比如文本可以分为词、句子、段落、篇章等不同粒度的层次,我们可以使用层次化的注意力来进行更好的信息选择[Yang et al.,2016].此外,还可以假设注意力为上下文相关的二项分布,用一种图模型来构建更复杂的结构化注意力分布[Kim et al., 2017]。
自注意力
如果要建立输入序列之间的长距离依赖关系,可以使用以下两种方法:一种方法是增加网络的层数,通过一个深层网络来获取远距离的信息交互;另一种方法是使用全连接网络.全连接网络是一种非常直接的建模远距离依赖的模型,但是无法处理变长的输入序列.不同的输入长度,其连接权重的大小也是不同的.这时我们就可以利用注意力机制来“动态”地生成不同连接的权重,这就是自注意力模型(Self-Attention Model)。
引入Self Attention后会更容易捕获句子中长距离的相互依赖的特征,因为如果是RNN或者LSTM,需要依次序序列计算,对于远距离的相互依赖的特征,要经过若干时间步步骤的信息累积才能将两者联系起来,而距离越远,有效捕获的可能性越小。
为了提高模型能力,自注意力模型经常采用查询-键-值(Query-Key-Value,QKV)模式,其计算过程如图8.4所示,其中红色字母表示矩阵的维度。
代码实现
- 相似度函数采用的是一层全连接层。全连接层的输出经过softmax**函数计算权重。他对隐层向量的每一维在每个时间步上进行了softmax操作,这里函数的返回值是三维的,也就是说这里只是乘上了权重,但并没有求和。
见GitHub: Many-to-one attention mechanism for Keras - 参考https://colinraffel.com/publications/iclr2016feed.pdf
在softmax之前还加了tanh**函数,而且将输出进行了求和,所以输出是二维的。
自定义Attention层:(若上接 LSTM 则需return_sequences=True
,即需LSTM输出三维向量)
class AttentionLayer(Layer):
def __init__(self, **kwargs):
super(AttentionLayer, self).__init__(** kwargs)
def build(self, input_shape):
assert len(input_shape)==3
# W.shape = (time_steps, time_steps)
self.W = self.add_weight(name='att_weight',
shape=(input_shape[1], input_shape[1]),
initializer='uniform',
trainable=True)
self.b = self.add_weight(name='att_bias',
shape=(input_shape[1],),
initializer='uniform',
trainable=True)
super(AttentionLayer, self).build(input_shape)
def call(self, inputs):
# inputs.shape = (batch_size, time_steps, seq_len)
x = K.permute_dimensions(inputs, (0, 2, 1))
# x.shape = (batch_size, seq_len, time_steps)
a = K.softmax(K.tanh(K.dot(x, self.W) + self.b))
outputs = K.permute_dimensions(a * x, (0, 2, 1))
outputs = K.sum(outputs, axis=1)
return outputs
def compute_output_shape(self, input_shape):
return input_shape[0], input_shape[2]
上一篇: 双端循环队列
下一篇: 构建多基因的系统发育树
推荐阅读