1. 注意力的构造函数

1.1 <key, value> , query

key :代表从原始输入x 中, 从事物自身本来所固有的某一个属性(或多个属性上)上提取出来的特征, 这些特征可以抽象的表示原始输入;

value: 从原始输入中, 进行映射,在另外一个特征维度上来表征输入;

query: (即可以是人脑的意识作用下, 也可以是机器学习出来的)提炼出来的一种属性特征;

1.2 注意力层的输出

EMSA 注意力_注意力的评价函数

上图的步骤:

  1. 使用 query 的属性与 key 的属性,通过注意力的评价函数, 得到 key 与 query 之间的注意力分数;
  2. 注意分数通过softmax 便得到注意力权重;
  3. 将注意力权重 作用到各个 value 上,得到最终注意力层的输出;

即最终该注意力层的输出: 是注意力权重与各个vaule 值的加权和得到;

1.3 注意力层数学描述

用数学语言描述,假设有一个查询 EMSA 注意力_权重_02
EMSA 注意力_词元_03 个“键-值”对 EMSA 注意力_权重_04,
其中 EMSA 注意力_构造函数_05

则,注意力层的输出函数就被表示成值的加权和:

EMSA 注意力_注意力的评价函数_06

其中注意力权重,

  1. 先通过注意力评分函数将查询和键 (EMSA 注意力_EMSA 注意力_07)两个向量映射成标量,
  2. 再经过softmax运算得到的。

EMSA 注意力_EMSA 注意力_08

正如我们所看到的,选择不同的注意力评分函数会导致不同的注意力汇聚操作。

在本节中,我们将介绍两个流行的评分函数,稍后将用他们来实现更复杂的注意力机制。

2. 带有mask 的softmax 运算;

正如上面提到的,softmax操作用于输出一个概率分布作为注意力权重。

在某些情况下,并非所有的值都应该被纳入到注意力汇聚中。 例如,为了在 9.5节中高效处理小批量数据集, 某些文本序列被填充了没有意义的特殊词元。

为了仅将有意义的词元作为值来获取注意力汇聚, 我们可以指定一个有效序列长度(即词元的个数), 以便在计算softmax时过滤掉超出指定范围的位置。

通过这种方式,我们可以在下面的masked_softmax函数中 实现这样的掩蔽softmax操作(masked softmax operation), 其中任何超出有效长度的位置都被掩蔽并置为0。

具体的实现:
便是 通过在最后一个轴 上 使用 掩蔽查过有效长度的数字来实现;

3. 加性注意力

一般来说,当查询和键是不同长度的矢量时, 我们可以使用加性注意力作为评分函数。 给定查询EMSA 注意力_注意力的评价函数_09
和 键EMSA 注意力_构造函数_10, 加性注意力(additive attention)的评分函数为:

EMSA 注意力_构造函数_11

其中可学习的参数是 EMSA 注意力_注意力的评价函数_12 ,EMSA 注意力_注意力的评价函数_13, EMSA 注意力_EMSA 注意力_14,

如 前面图中所示, 将查询和键连结起来后输入到一个多层感知机(MLP)中, 感知机包含一个隐藏层,其隐藏单元数是一个超参数。 通过使用作为激活函数,并且禁用偏置项。

下面我们来实现加性注意力。

#@save
class AdditiveAttention(tf.keras.layers.Layer):
    """Additiveattention."""
    def __init__(self, key_size, query_size, num_hiddens, dropout, **kwargs):
        super().__init__(**kwargs)
        self.W_k = tf.keras.layers.Dense(num_hiddens, use_bias=False)
        self.W_q = tf.keras.layers.Dense(num_hiddens, use_bias=False)
        self.w_v = tf.keras.layers.Dense(1, use_bias=False)
        self.dropout = tf.keras.layers.Dropout(dropout)

    def call(self, queries, keys, values, valid_lens, **kwargs):
        queries, keys = self.W_q(queries), self.W_k(keys)
        # 在维度扩展后,
        # queries的形状:(batch_size,查询的个数,1,num_hidden)
        # key的形状:(batch_size,1,“键-值”对的个数,num_hiddens)
        # 使用广播方式进行求和
        features = tf.expand_dims(queries, axis=2) + tf.expand_dims(
            keys, axis=1)
        features = tf.nn.tanh(features)
        # self.w_v仅有一个输出,因此从形状中移除最后那个维度。
        # scores的形状:(batch_size,查询的个数,“键-值”对的个数)
        scores = tf.squeeze(self.w_v(features), axis=-1)
        self.attention_weights = masked_softmax(scores, valid_lens)
        # values的形状:(batch_size,“键-值”对的个数,值的维度)
        return tf.matmul(self.dropout(
            self.attention_weights, **kwargs), values)

4. 缩放点积注意力

使用点积可以得到计算效率更高的评分函数, 但是点积操作要求查询和键具有相同的长度 d。 假设查询和键的所有元素都是独立的随机变量, 并且都满足零均值和单位方差, 那么两个向量的点积的均值为0,方差为EMSA 注意力_构造函数_15

为确保无论向量长度如何, 点积的方差在不考虑向量长度的情况下仍然是1, 我们将点积除以EMSA 注意力_EMSA 注意力_16, 则缩放点积注意力(scaled dot-product attention)评分函数为:
EMSA 注意力_EMSA 注意力_17

在实践中,我们通常从小批量的角度来考虑提高效率, 例如基于个查询和个键-值对计算注意力, 其中查询和键的长度为,值的长度为。

查询 EMSA 注意力_权重_18、 键 EMSA 注意力_构造函数_19和 值EMSA 注意力_权重_20的缩放点积注意力是:

EMSA 注意力_词元_21

在下面的缩放点积注意力的实现中,我们使用了暂退法进行模型正则化。

#@save
class DotProductAttention(tf.keras.layers.Layer):
    """Scaleddotproductattention."""
    def __init__(self, dropout, **kwargs):
        super().__init__(**kwargs)
        self.dropout = tf.keras.layers.Dropout(dropout)

    # queries的形状:(batch_size,查询的个数,d)
    # keys的形状:(batch_size,“键-值”对的个数,d)
    # values的形状:(batch_size,“键-值”对的个数,值的维度)
    # valid_lens的形状:(batch_size,)或者(batch_size,查询的个数)
    def call(self, queries, keys, values, valid_lens, **kwargs):
        d = queries.shape[-1]
        scores = tf.matmul(queries, keys, transpose_b=True)/tf.math.sqrt(
            tf.cast(d, dtype=tf.float32))
        self.attention_weights = masked_softmax(scores, valid_lens)
        return tf.matmul(self.dropout(self.attention_weights, **kwargs), values)

5. 小结

  • 选择不同的注意力评价函数在注意力层中带来不同的注意力操作。
  • 当查询和键是不同长度的矢量时,可以使用可加性注意力评分函数。
  • 当它们的长度相同时,使用缩放的“点-积”注意力评分函数的计算效率更高。