NLP预训练模型【4】 – 注意力机制

1. 什么是 $ Attention $ 机制

在” $ seq2seq $ ”一节⾥,解码器在各个时间步依赖相同的背景变量来获取输⼊序列信息。当编码器为循环神经网络时,背景变量来自它最终时间步的隐藏状态。

现在,让我们再次思考那一节提到的翻译例⼦:输⼊为英语序列“They”“are”“watching”“.”,输出为法语序列“Ils”“regardent”“.”。不难想到,解码器在⽣成输出序列中的每一个词时可能只需利用输⼊序列某一部分的信息。例如,在输出序列的时间步1,解码器可以主要依赖“They”“are”的信息来⽣成“Ils”,在时间步2则主要使用来自“watching”的编码信息⽣成“regardent”,最后在时间步3则直接映射句号“.”。这看上去就像是在解码器的每一时间步对输⼊序列中不同时间步的表征或编码信息分配不同的注意力一样。这也是注意力机制的由来。

仍然以循环神经网络为例,注意力机制通过对编码器所有时间步的隐藏状态做加权平均来得到背景变量。解码器在每一时间步调整这些权重,即注意力权重,从而能够在不同时间步分别关注输⼊序列中的不同部分并编码进相应时间步的背景变量。

在注意力机制中,解码器的每一时间步将使用可变的背景变量。记 $ c_{t^′} $ 是解码器在时间步 $ t^′ $ 的背景变量,那么解码器在该时间步的隐藏状态可以改写为:

st=g(yt1,ct,st1)s_{t^{\prime}}=g\left(y_{t^{\prime}-1}, c_{t^{\prime}}, s_{t^{\prime}-1}\right)

这⾥的关键是如何计算背景变量 $ c_{t^′} $ , 如何利用它来更新隐藏状态 $ s_{t^′} $ 。下⾯将分别描述这两个关键点。

2. 编解码器中的 $ Attention $

2.1 计算背景变量

我们先描述第一个关键点,即计算背景变量。下图描绘了注意力机制如何为解码器在时间步2计算背景变量。

  • 函数 $ a $ 根据解码器在时间步1的隐藏状态和编码器在各个时间步的隐藏状态计算 $ softmax $ 运算的输⼊。
  • $ softmax $ 运算输出概率分布并对编码器各个时间步的隐藏状态做加权平均,从而得到背景变量。

图1 Attention机制示意图

令编码器在时间步 $ t $ 的隐藏状态为 $ h_t $ ,且总时间步数为 $ T $ 。那么解码器在时间步 $ t′ $ 的背景变量为所有编码器隐藏状态的加权平均:

ct=t=1Tαtthtc_{t^{\prime}}=\sum_{t=1}^{T} \alpha_{t^{\prime} t} h_{t}

背景变量的向量化计算技巧

我们还可以对注意力机制采用更⾼效的⽮量化计算。我们先定义,在上⾯的例⼦中,查询项为解码器的隐藏状态,键项和值项均为编码器的隐藏状态。
广义上,注意力机制的输⼊包括查询项以及一一对应的键项和值项,其中值项是需要加权平均的一组项。在加权平均中,值项的权重来自查询项以及与该值项对应的键项的计算。

让我们考虑一个常⻅的简单情形,即编码器和解码器的隐藏单元个数均为 $ h $ ,且函数 $ a(s, h)=s^{T} h $ 。假设我们希望根据解码器单个隐藏状态 $ s_{t′−1} $ 和编码器所有隐藏状态 $ h_t $ , $ t = 1, . . . , T $ 来计算背景向量 $ c_{t′} $ 。我们可以将查询项矩阵 $ Q $ 设为 $ s_{t’-1}^T $ ,并令键项矩阵 $ K $ 和 值项矩阵 $ V $ 相同且第 $ t $ 行均为 $ h_t^T $ 。此时,我们只需要通过⽮量化计算:

softmax(QKT)Vsoftmax(QK^T)V

即可算出转置后的背景向量 $ c_{t’}^T $ 。当查询项矩阵 $ Q $ 的行数为 $ n $ 时,上式将得到 $ n $ 行的输出矩阵。输出矩阵与查询项矩阵在相同行上一一对应。

2.2 更新隐藏状态

现在我们描述第二个关键点,即更新隐藏状态。以门控循环单元(GRU)为例,在解码器中我们可以对门控循环单元中门控循环单元的设计稍作修改,从而变换上一时间步 $ t^′−1 $ 的输出 $ y_{t^′−1} $ 、隐藏状态 $ s_{t^′−1} $ 和当前时间步 $ t^′ $ 的含注意力机制的背景变量 $ c_{t^′} $ 。解码器在时间步 tt’ 的隐藏状态为:

st=ztst1+(1zt)s~ts_{t^{\prime}}=z_{t^{\prime}} \odot s_{t^{\prime}-1}+\left(1-z_{t^{\prime}}\right) \odot \tilde{s}_{t^{\prime}}

其中的重置门、更新门和候选隐藏状态分别为:

rt=σ(Wyryt1+Wsrst1+Wcrct+br)zt=σ(Wyzyt1+Wszst1+Wczct+bz)s~t=tanh(Wysyt1+Wss(st1rt)+Wcsct+bs)\begin{equation} \begin{aligned} r_{t^{\prime}}&=\sigma\left(W_{y r} y_{t^{\prime}-1}+W_{s r} s_{t^{\prime}-1}+W_{c r} c_{t^{\prime}}+b_{r}\right) \\\\ z_{t^{\prime}}&=\sigma\left(W_{y z} y_{t^{\prime}-1}+W_{s z} s_{t^{\prime}-1}+W_{c z} c_{t^{\prime}}+b_{z}\right) \\\\ \tilde{s}_{t^{\prime}}&=\tanh \left(W_{y s} y_{t^{\prime}-1}+W_{s s}\left(s_{t^{\prime}-1} \odot r_{t^{\prime}}\right)+W_{c s} c_{t^{\prime}}+b_{s}\right) \end{aligned} \end{equation}

其中含下标的 $ W $ 和 $ b $ 分别为门控循环单元的权重参数和偏差参数。

3. $ Attention $ 本质

3.1 用机器翻译说明 $ Attention $

本节先以机器翻译作为例子讲解最常见的 $ Attention $ Soft 模型的基本原理,之后抛离 $ Encoder $ - $ Decoder $ 框架抽象出注意力机制的本质思想。

如果拿机器翻译来解释这个 $ Encoder $ - $ Decoder $ 框架更好理解,比如输入的是英文句子:Tom chase Jerry, $ Encoder $ - $ Decoder $ 框架逐步生成中文单词:“汤姆”,“追逐”,“杰瑞”。

在翻译“杰瑞”这个中文单词的时候,模型里面的每个英文单词对于翻译目标单词“杰瑞”贡献是相同的,很明显这里不太合理,显然“Jerry”对于翻译成“杰瑞”更重要,但是模型是无法体现这一点的,这就是为何说它没有引入注意力的原因。

没有引入注意力的模型在输入句子比较短的时候问题不大,但是如果输入句子比较长,此时所有语义完全通过一个中间语义向量来表示,单词自身的信息已经消失,可想而知会丢失很多细节信息,这也是为何要引入注意力模型的重要原因。

上面的例子中,如果引入 $ Attention $ 模型的话,应该在翻译“杰瑞”的时候,体现出英文单词对于翻译当前中文单词不同的影响程度,比如给出类似下面一个概率分布值:

(Tom,0.3)(Chase,0.2) (Jerry,0.5)

**每个英文单词的概率代表了翻译当前单词“杰瑞”时,注意力分配模型分配给不同英文单词的注意力大小。**这对于正确翻译目标语单词肯定是有帮助的,因为引入了新的信息。

同理,目标句子中的每个单词都应该学会其对应的源语句子中单词的注意力分配概率信息。这意味着在生成每个单词 $ y_i $ 的时候,原先都是相同的中间语义表示 $ C $ 会被替换成根据当前生成单词而不断变化的 $ C_i $ 。理解 $ Attention $ 模型的关键就是这里,即由固定的中间语义表示 $ C $ 换成了根据当前输出单词来调整成加入注意力模型的变化的 $ C_i $ 。增加了注意力模型的 $ Encoder $ - $ Decoder $ 框架理解起来如下图所示。

图2 Attention机制的简化示意图

每个 $ C_i $ 可能对应着不同的源语句子单词的注意力分配概率分布,比如对于上面的英汉翻译来说,其对应的信息可能如下:

C汤姆=g(0.6f2(tom),0.2f2(chase),0.2f2(jerry))C追逐=g(0.2f2(tom),0.7f2( chase),0.1f2(jerry))C杰瑞=g(0.3f2( tom ),0.2f2(chase ),0.5f2(jerry))\begin{equation}| \begin{aligned} C_{\text{汤姆}}&=g(0.6 * f2(\text{tom}), 0.2 * f2( \text{chase}), 0.2 * f2( \text{jerry} )) \\\\ C_{\text{追逐}}&=g(0.2 * f2( \text{tom} ), 0.7 * f2( \text{ chase} ), 0.1 * f2( \text{jerry})) \\\\ C_{\text{杰瑞}}&=g(0.3 * f2( \text{ tom } ), 0.2 * f2(\text{chase } ), 0.5 * f2( \text{jerry})) \end{aligned} \end{equation}

其中, $ f2 $ 函数代表 $ Encoder $ 对输入英文单词的某种变换函数(编码),比如如果 $ Encoder $ 是用的RNN模型的话,这个 $ f2 $ 函数的结果往往是某个时刻输入 $ x_i $ 后隐层节点的状态值; $ g $ 代表 $ Encoder $ 根据单词的中间表示合成整个句子中间语义表示的变换函数,一般的做法中, $ g $ 函数就是对构成元素加权求和,即下列公式:

Ci=j=1LxaijhjC_{i}=\sum_{j=1}^{L_{x}} a_{i j} h_{j}

其中, $ L_x $ 代表输入句子 $ Source $ 的长度, $ a_{ij} $ 代表在 $ Target $ 输出第 $ i $ 个单词时 $ Source $ 输入句子中第 $ j $ 个单词的注意力分配系数,而 $ h_j $ 则是 $ Source $ 输入句子中第 $ j $ 个单词的语义编码。假设下标 $ i $ 就是上面例子所说的“ 汤姆” ,那么 $ L_x $ 就是3, $ h_1=f(“Tom”) $ , $ h_2=f(“Chase”) $ , $ h_3=f(“Jerry”) $ 分别是输入句子每个单词的语义编码,对应的注意力模型权值则分别是 $ 0.6,0.2,0.2 $ ,所以 $ g $ 函数本质上就是个加权求和函数。

图3 注意力的分配及加权平均

3.2 注意力分配概率计算

这里还有一个问题:生成目标句子某个单词,比如“汤姆”的时候,如何知道 $ Attention $ 模型所需要的输入句子单词注意力分配概率分布值呢?就是说“汤姆”对应的输入句子 $ Source $ 中各个单词的概率分布: $ (Tom,0.6) $ $ (Chase,0.2) $ $ (Jerry,0.2) $ 是如何得到的呢?

对于采用RNN的 $ Decoder $ 来说,在时刻 $ i $ ,如果要生成单词 $ Y_i $ ,我们是可以知道 $ Target $ 在生成 $ Y_i $ 之前的时刻 $ i-1 $ 时,隐层节点 $ i-1 $ 时刻的输出隐状态值 $ H_{i-1} $ 的,而我们的目的是要计算生成 $ Y_i $ 时输入句子中的单词 “Tom”、 “Chase”、 “Jerry” 对 $ Y_i $ 来说的注意力分配概率分布,那么可以用 $ Target $ 输出句子 $ i-1 $ 时刻的隐层节点状态 $ H_{i-1} $ 去一一和输入句子 $ Source $ 中每个单词对应的RNN隐层节点状态 $ h_j $ 进行对比,即通过函数 $ F(h_j,H_{i-1}) $ 来获得目标单词 $ y_i $ 和每个输入单词对应的对齐可能性,这个 $ F $ 函数在不同论文里可能会采取不同的方法,然后函数 $ F $ 的输出经过 $ Softmax $ 进行归一化就得到了符合概率分布取值区间的注意力分配概率分布数值。

图4 注意力分配概率计算过程示意图

3.3 $ Attention $ 的物理含义

一般在自然语言处理应用里会把 $ Attention $ 模型看作是输出 $ Target $ 句子中某个单词和输入 $ Source $ 句子每个单词的对齐模型,这是非常有道理的。

**目标句子生成的每个单词对应输入句子单词的概率分布可以理解为输入句子单词和这个目标生成单词的对齐概率,**这在机器翻译语境下是非常直观的:传统的统计机器翻译一般在做的过程中会专门有一个短语对齐的步骤,而注意力模型其实起的是相同的作用。

如果把 $ Attention $ 机制从上文讲述例子中的 $ Encoder $ - $ Decoder $ 框架中剥离,并进一步做抽象,可以更容易看懂 $ Attention $ 机制的本质思想。

图5 通过Qurey和Key的比较计算注意力权重

我们可以这样来看待 $ Attention $ 机制(参考上图):将 $ Source $ 中的构成元素想象成是由一系列的 $ <Key,Value> $ 数据对构成,此时给定 $ Target $ 中的某个元素 $ Query $ ,通过计算 $ Query $ 和各个 $ Key $ 的相似性或者相关性,得到每个 $ Key $ 对应 $ Value $ 的权重系数,然后对 $ Value $ 进行加权求和,即得到了最终的 $ Attention $ 数值。所以本质上 $ Attention $ 机制是对 $ Source $ 中元素的 $ Value $ 值进行加权求和,而 $ Query $ 和 $ Key $ 用来计算对应 $ Value $ 的权重系数。即可以将其本质思想改写为如下公式:

Attention(Query,Source)=i=1LxSimilarity(Query,keyi)ValueiAttention (Query,Source )=\sum_{i=1}^{L_{x}} Similarity \left(\right. Query, key \left._{i}\right) * Value _{i}

其中, $ L_x=|| Source || $ 代表 $ Source $ 的长度,公式含义即如上所述。上文所举的机器翻译的例子里,因为在计算 $ Attention $ 的过程中, $ Source $ 中的 $ Key $ 和 $ Value $ 合二为一,指向的是同一个东西,也即输入句子中每个单词对应的语义编码,所以可能不容易看出这种能够体现本质思想的结构。

从概念上理解,把 $ Attention $ 理解为“从大量信息中有选择地筛选出少量重要信息并聚焦到这些重要信息上,忽略大多不重要的信息”,这种思路仍然成立。聚焦的过程体现在权重系数的计算上,权重越大越聚焦于其对应的 $ Value $ 值上,即权重代表了信息的重要性,而 $ Value $ 是其对应的信息。

从上图可以引出另外一种理解:也可以将 $ Attention $ 机制看作一种软寻址, $ Source $ 可以看作存储器内存储的内容,元素由地址 $ Key $ 和值 $ Value $ 组成,当前有个 $ Key=Query $ 的查询,目的是取出存储器中对应的 $ Value $ 值,即 $ Attention $ 数值。通过 $ Query $ 和存储器内元素 $ Key $ 的地址进行相似性比较来寻址,之所以说是软寻址,指的不像一般寻址只从存储内容里面找出一条内容,而是可能从每个 $ Key $ 地址都会取出内容,取出内容的重要性根据 $ Query $ 和 $ Key $ 的相似性来决定,之后对 $ Value $ 进行加权求和,这样就可以取出最终的 $ Value $ 值,也即 $ Attention $ 值。所以不少研究人员将 $ Attention $ 机制看作软寻址的一种特例,这也是非常有道理的。

至于 $ Attention $ 机制的具体计算过程,如果对目前大多数方法进行抽象的话,可以将其归纳为两个过程:

第一个过程是根据 $ Query $ 和 $ Key $ 计算权重系数。又可以细分为两个阶段:第一个阶段根据 $ Query $ 和 $ Key $ 计算两者的相似性或者相关性;第二个阶段对第一阶段的原始分值进行归一化处理。 第二个过程根据权重系数对 $ Value $ 进行加权求和。这样,可以将 $ Attention $ 的计算过程抽象为如下图展示的三个阶段:

图6 注意力计算的三阶段示意图

在第一个阶段,可以引入不同的函数和计算机制,根据 $ Query $ 和某个 $ Key_i $ ,计算两者的相似性或者相关性,最常见的方法包括:求两者的向量点积、求两者的向量余弦(Cosine)相似性或者通过再引入额外的神经网络来求值,即如下方式:

第一阶段产生的分值根据具体产生的方法不同其数值取值范围也不一样,第二阶段引入类似 $ SoftMax $ 的计算方式对第一阶段的得分进行数值转换,一方面可以进行归一化,将原始计算分值整理成所有元素权重之和为1的概率分布;另一方面也可以通过SoftMax的内在机制更加突出重要元素的权重。即一般采用如下公式计算:

第二阶段的计算结果 $ a_i $ 即为 $ value_i $ 对应的权重系数,然后进行加权求和即可得到 $ Attention $ 数值:

通过如上三个阶段的计算,即可求出针对 $ Query $ 的 $ Attention $ 数值,目前绝大多数具体的注意力机制计算方法都符合上述的三阶段抽象计算过程。

4. $ Self- Attention $ 模型

$ Self Attention $ 也经常被称为 $ intra\quad Attention $ (内部 $ Attention $ ),最近一年也获得了比较广泛的使用,比如Google最新的机器翻译模型内部大量采用了 $ Self \quad Attention $ 模型。

在一般任务的 $ Encoder $ - $ Decoder $ 框架中,输入 $ Source $ 和输出 $ Target $ 内容是不一样的,比如对于英-中机器翻译来说, $ Source $ 是英文句子, $ Target $ 是对应的翻译出的中文句子, $ Attention $ 机制发生在 $ Target $ 的元素 $ Query $ 和 $ Source $ 中的所有元素之间。**而 $ Self \quad Attention $ 顾名思义,指的不是 $ Target $ 和 $ Source $ 之间的 $ Attention $ 机制,而是 $ Source $ 内部元素之间或者 $ Target $ 内部元素之间发生的 $ Attention $ 机制,也可以理解为 $ Target $ = $ Source $ 这种特殊情况下的注意力计算机制。**其具体计算过程是一样的,只是计算对象发生了变化而已,所以此处不再赘述其计算过程细节。

如果是常规的 $ Target $ 不等于 $ Source $ 情形下的注意力计算,其物理含义正如上文所讲,比如对于机器翻译来说,本质上是目标语单词和源语单词之间的一种单词对齐机制。那么如果是 $ Self Attention $ 机制,一个很自然的问题是:通过 $ Self Attention $ 到底学到了哪些规律或者抽取出了哪些特征呢?或者说引入 $ Self Attention $ 有什么增益或者好处呢?我们仍然以机器翻译中的 $ Self Attention $ 来说明,下图可视化地表示 $ Self Attention $ 在同一个英语句子内单词间产生的联系。

图7 自注意力学习中不同位置词之间的相关性

图8 利用相关性处理指代关系

从上述两张图可以看出, $ Self Attention $ 可以捕获同一个句子中单词之间的一些句法特征(比如第一张图中展示的有一定距离的短语结构)或者语义特征(比如第二张图展示的its的指代对象Law)。
很明显,引入 $ Self \quad Attention $ 后会更容易捕获句子中长距离的相互依赖的特征,因为如果是 $ RNN $ 或者 $ LSTM $ ,需要依次序序列计算,对于远距离的相互依赖的特征,要经过若干时间步步骤的信息累积才能将两者联系起来,而距离越远,有效捕获的可能性越小。
但是 $ Self \quad Attention $ 在计算过程中会直接将句子中任意两个单词的联系通过一个计算步骤直接联系起来,所以远距离依赖特征之间的距离被极大缩短,有利于有效地利用这些特征。除此外, $ Self \quad Attention $ 对于增加计算的并行性也有直接帮助作用。这是为何 $ Self \quad Attention $ 逐渐被广泛使用的主要原因。

5 $ Attention $ 模型的应用

$ Attention $ 机制在深度学习的各种应用领域都有广泛的使用场景。上文主要以自然语言处理中的机器翻译任务作为例子,下面分别再从图像处理领域和语音识别选择典型应用实例来对其应用做简单说明。

图9 图片-描述任务的Encoder-Decoder框架

图片描述( $ Image-Caption $ )是一种典型的图文结合的深度学习应用,输入一张图片,人工智能系统输出一句描述句子,语义等价地描述图片所示内容。很明显这种应用场景也可以使用 $ Encoder-Decoder $ 框架来解决任务目标,此时 $ Encoder $ 输入部分是一张图片,一般会用 $ CNN $ 来对图片进行特征抽取, $ Decoder $ 部分使用 $ RNN $ 或者 $ LSTM $ 来输出自然语言句子(参考上图)。

此时如果加入 $ Attention $ 机制能够明显改善系统输出效果, $ Attention $ 模型在这里起到了类似人类视觉选择性注意的机制,在输出某个实体单词的时候会将注意力焦点聚焦在图片中相应的区域上。下图给出了根据给定图片生成句子“A person is standing on a beach with a surfboard.”过程时每个单词对应图片中的注意力聚焦区域。

图10 图片生成句子中每个单词时的注意力聚焦区域

下图给出了另外四个例子形象地展示了这种过程,每个例子上方左侧是输入的原图,下方句子是人工智能系统自动产生的描述语句,上方右侧图展示了当AI系统产生语句中划横线单词的时候,对应图片中聚焦的位置区域。比如当输出单词dog的时候,AI系统会将注意力更多地分配给图片中小狗对应的位置。

图11 图像描述任务中Attention机制的聚焦作用

图12 语音识别中音频序列和输出字符之间的Attention

语音识别的任务目标是将语音流信号转换成文字,所以也是 $ Encoder-Decoder $ 的典型应用场景。 $ Encoder $ 部分的 $ Source $ 输入是语音流信号, $ Decoder $ 部分输出语音对应的字符串流。
图16可视化地展示了在Encoder-Decoder框架中加入Attention机制后,当用户用语音说句子 how much would a woodchuck chuck 时,输入部分的声音特征信号和输出字符之间的注意力分配概率分布情况,颜色越深代表分配到的注意力概率越高。从图中可以看出,在这个场景下,Attention机制起到了将输出字符和输入语音信号进行对齐的功能。
上述内容仅仅选取了不同AI领域的几个典型Attention机制应用实例,Encoder-Decoder加Attention架构由于其卓越的实际效果,目前在深度学习领域里得到了广泛的使用,了解并熟练使用这一架构对于解决实际问题会有极大帮助。

6 发展

本质上,注意力机制能够为表征中较有价值的部分分配较多的计算资源。这个有趣的想法自提出后得到了快速发展,特别是启发了依靠注意力机制来编码输⼊序列并解码出输出序列的变换器(Transformer)模型的设计。变换器抛弃了卷积神经网络和循环神经网络的架构。它在计算效率上比基于循环神经网络的“编码器—解码器”模型通常更具明显优势。

含注意力机制的变换器的编码结构在后来的BERT预训练模型中得以应用并令后者大放异彩:微调后的模型在多达11项自然语言处理任务中取得了当时最先进的结果。不久后,同样是基于变换器设计的GPT-2模型于新收集的语料数据集预训练后,在7个未参与训练的语言模型数据集上均取得了当时最先进的结果。除了自然语言处理领域,注意力机制还被广泛用于图像分类、自动图像描述、唇语解读以及语音识别。

参考文献

动手学深度学习

注意力机制的基本思想和实现原理


作者:@mantchs

GitHub:https://github.com/NLP-LOVE/ML-NLP