购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

3.1 注意力机制与模型详解

注意力机制来自人类对事物的观察方式。当我们看一幅图片时,我们并没有看清图片的全部内容,而是将注意力集中在了图片的焦点上。图3-1形象地展示了人类在看到一幅图像时是如何高效分配有限的注意力资源的,其中红色区域表明视觉系统更关注的目标。

图3-1 人类在看到一幅图像时的注意力分配

很明显,对于图3-1所示的场景,人们会把注意力更多地投入近景人物穿着、姿势等各个部位的细节,而远处则更多地关注人物的脸部区域。因此,可以认为这种人脑的注意力模型是一种资源分配模型,在某个特定时刻,你的注意力总是集中在画面中的某个焦点部分,而对其他部分视而不见。这种只关注特定区域的形式被称为“注意力”机制。

3.1.1 注意力机制详解

注意力机制(Attention Mechanism)最早在人类视觉领域提出。在认知科学中,由于信息处理的瓶颈,人类会选择性地关注所有信息的一部分,同时忽略其他可见的信息。上述机制通常被称为注意力机制。人类视网膜不同的部位具有不同程度的信息处理能力,即敏锐度(Acuity),只有视网膜中央凹部位具有最强的敏锐度。为了合理利用有限的视觉信息处理资源,人类需要选择视觉区域中的特定部分,然后集中关注它。例如,人们在阅读时,通常只有少量要被读取的词会被关注和处理。综上,注意力机制主要有两个方面:决定需要关注输入的哪部分,分配有限的信息处理资源给重要的部分(以上内容引用自百度百科,可以帮助读者理解注意力机制)。

2014年,Google Mind发表了 Recurrent Models of Visual Attention ,使注意力机制流行起来。这篇论文采用了RNN模型,并加入了注意力机制来进行图像的分类。

2015年,Bahdanau等在论文 Neural Machine Translation by Jointly Learning to Align and Translate 中,将注意力机制首次应用在NLP领域,其采用Seq2Seq+Attention模型来进行机器翻译,并且得到了效果的提升。

2017年,在Google机器翻译团队发表的 Attention is All You Need 中,完全抛弃了RNN和CNN等网络结构,而仅仅采用注意力机制来进行机器翻译任务,并且取得了很好的效果,注意力机制也成为深度学习中最重要的研究热点。

概括起来说,注意力机制是一种允许模型在处理信息时专注于关键部分,从而忽略不相关信息,并提高处理效率和准确性的机制。它模仿了人类视觉处理信息时选择性关注的特点,通过计算查询向量(Query)、键向量(Key)之间的相似度来确定注意力权重,然后对值向量(Value)进行加权求和,得到最终的输出。

注意力机制的核心思想是在处理输入数据时,并非对所有部分都给予同等的关注,而是根据当前的上下文动态地分配“注意力”,从而突出关键的信息。具体来说,模型通过计算查询向量(Query)、键向量(Key)和值向量(Value)之间的相似度来确定注意力权重,然后对值向量进行加权求和,得到最终的输出。

注意力机制的直观理解可以借鉴人类的生物系统。以我们的视觉处理系统为例,它通常会选择性地聚焦于图像的特定部分,而忽略那些不相关的信息,从而帮助我们更好地感知周围环境。同样,在处理语言、语音或视觉等问题时,输入的某些部分往往比其他部分更具相关性。

深度学习中的注意力模型正是基于这种相关性概念而构建的。通过动态地关注那些有助于有效执行目标任务的输入部分,注意力模型能够提升处理效率。图3-2详细展示了完整的注意力模型计算过程。

图3-2展示了基于注意力机制的完整计算流程。在这个流程中,输入的特征首先会经过线性变换,以获得新的特征表达。随后,我们会计算这些新特征的相似度。在得出相似度之后,我们会根据这些值重新计算权重,并进行加权求和,从而得出最终的注意力输出结果。

图3-2 注意力机制全景

在本节接下来的内容中,我们将分步骤详细阐述这一过程的每一个环节。

3.1.2 自注意力(Self-Attention)机制

自注意力层不仅是本章的重点,也是本书最重要的内容(然而实际上非常简单)。

自注意力机制通常指的是不使用其他额外的信息,仅仅使用自我注意力的形式,通过关注输入数据本身建立自身连接,从而从输入的数据中抽取特征信息。自注意力又称作内部注意力,它在很多任务上都有十分出色的表现,比如阅读理解、视频分割、多模态融合等。

Attention用于计算“相关程度”,例如在翻译过程中,不同的英文对中文的依赖程度不同,Attention通常可以进行如下描述,表示为将Queries( Q )和key-value pairs{ K i , V i | i =1,2,…, m }映射到输出上,其中query、每个key、每个value都是向量,输出是 V 中所有values的加权,其中权重是由query和每个key计算出来的,计算方法分为如下3步。

第一步:自注意力中的query、key和value的线性变换

自注意力机制是进行自我关注从而抽取相关信息的机制。而从具体实现来看,注意力函数的本质可以被描述为一个查询(query)到一系列键值(key-value)对的映射,它们被作为一种抽象的向量,主要用来计算和辅助自注意力,如图3-3所示(更详细的解释在后面)。

图3-3 自注意力中的query、key和value

如图3-3所示,一个字“中”经过Embedding层初始化后,得到一个矩阵向量的表示 X 1 ,之后经过3个不同全连接层重新计算后,得到一个特定维度的向量,即我们看到的 q 1 。而 q 2 的计算过程与 q 1 完全相同,之后依次将 q 1 q 2 连接起来,组成一个新的连接后的二维矩阵 W Q ,被定义成query。

WQ= concat([q1, q2],axis = 0)

实际上,一般的输入是一个经过序号化处理后的序列,例如[0,1,2,3,…]这样的数据形式,经过Embedding层计算后生成一个多维矩阵,再经过3个不同的神经网络层处理后,得到具有相同维度大小的query、key和value向量。而得到的向量均来自同一条输入数据,从而强制模型在后续的步骤中通过计算选择和关注来自同一条数据的重要部分。

Q = W Q X

K = W K X

V = W V X

举例来说,在计算相似度时,一个单词和自己的相似度应该最大,但在计算不同向量的乘积时也可能出现更大的相似度,这种情况是不合理的。因此,通过新向量 q k 相乘来计算相似度,通过模型的训练学习就可以避免这种情况。相比原来一个个词向量相乘的形式,通过构建新向量 q k ,在计算相似度时灵活性更大,效果更好。

第二步:使用query和key进行相似度计算

Attention的目的是找到向量内部的联系,通过来自同一个输入数据的内部关联程度分辨最重要的特征,即使用query和key计算自注意力的值,其过程说明如下。

(1)将query和每个key进行相似度计算得到权重,常用的相似度函数有点积、拼接、感知机等,这里作者使用的是点积计算,如图3-4所示。

图3-4 相似度计算

公式如下:

( q 1 · k 1 ),( q 1 · k 2 ),…

例如,对于句中的每个字特征,将当前字的 Q 与句中所有字的 K 相乘,从而得到一个整体的相似度计算结果。

(2)基于缩放点积操作的softmax函数对这些进行权重转换。

基于缩放点积操作的softmax函数的作用是计算不同输入之间的权重“分数”,又称为权重系数。例如,正在考虑“中”这个字,就用它的 q i 乘以每个位置的 k i ,随后将得分加以处理,再传递给softmax,然后通过softmax计算,其目的是在特征内部进行权重转换。即:

其中, d k 是缩放因子。

这个softmax计算分数决定了每个特征在该特征矩阵中需要关注的程度。相关联的特征将具有相应位置上最高的softmax分数。用这个得分乘以每个value向量,可以增强需要关注部分的值,或者降低对不相关部分的关注度,如图3-5所示。

softmax的分数决定了当前单词在每个句子中每个单词位置的表示程度。很明显,当前单词对应句子中此单词所在位置的softmax的分数最高;但是,有时注意力机制也能关注到此单词外的其他单词。

图3-5 使用softmax进行权重转换

第三步:每个Value向量乘以softmax后进行加权求和

最后一部为累加计算相关向量,为了让模型更灵活,使用点积缩放作为注意力的打分机制得到权重后,与生成的value向量进行计算,然后将其与转换后的权重进行加权求和,从而得到最终的新向量 Z ,即:

即将权重和相应的键值value进行加权求和,从而得到最后的注意力值,其步骤如图3-6所示。

图3-6 加权求和计算

总结自注意力的计算过程,根据输入的query与key计算两者之间的相似性或相关性,之后通过一个softmax来对值进行归一化处理,从而获得注意力权重值,然后对Value进行加权求和,并得到最终的Attention数值。然而在实际的实现过程中,该计算会以矩阵的形式完成,以便更快地处理。自注意力公式如下:

转换成更为通用的矩阵点积的形式来实现,其结构和形式如图3-7所示。

图3-7 矩阵点积

可以看到,在通过点积缩放计算注意力权重后,这些权重被用于对同一输入变换后的value进行加权求和,从而得到一个最终的新的结果向量。

3.1.3 自注意力的代码实现

下面是自注意力的代码实现,实际上通过3.1.2节的讲解,自注意力模型的基本架构其实并不复杂,基本代码如下(仅供演示):

核心代码的实现实际上是非常简单的。读者到这里只需先掌握这些核心代码即可。

换个角度,作者从概念上对注意力机制做个解释。注意力机制可以理解为从大量信息中有选择性地筛选出少量重要信息,并聚焦到这些重要信息上,忽略大部分不重要的信息。这种思路仍然成立。聚焦的过程体现在权重系数的计算上:权重越大,就越聚焦于其对应的Value值,即权重代表了信息的重要性,而权重与Value的点积则是对应的最终信息。

完整的注意力层代码如下。值得注意的是,在实现注意力机制的完整代码中,作者引入了mask部分。这种操作的作用是:在计算过程中,忽略因序列填充(pad)以达成统一长度而产生的掩码计算。通过这种方式,可以更加精确地控制注意力机制的计算过程,确保模型能够聚焦于真正有意义的部分,而不会被填充操作所干扰。关于这一点的具体细节,将在3.1.4节中进行详细介绍。

具体结果请读者自行打印查阅。

3.1.4 ticks和Layer Normalization

3.1.3节的最后,我们基于PyTorch 2.0自定义层的形式编写了注意力模型的代码。与演示代码存在的区别是,实战代码中在这一部分的自注意层中还额外加入了mask值,即掩码层。掩码层的作用就是获取输入序列的“有意义的值”,而忽视本身就是用作填充或补全序列的值。一般用0表示有意义的值,而用1表示填充值(这点并不固定,0和1的意思可以互换)。

[2,3,4,5,5,4,0,0,0] -> [1,1,1,1,1,1,0,0,0]

掩码计算的代码如下:

mask = (x > 0).unsqueeze(1).repeat(1, x.size(1), 1).unsqueeze(1)  # 创建注意力掩码。

此外,计算出的query与key的点积还需要除以一个常数,其作用是缩小点积的值方便进行softmax计算。

这通常被称为ticks,即采用一点点小的技巧使得模型训练能够更加准确和便捷。Layer Normalization函数也是如此。下面我们对其进行详细介绍。

Layer Normalization(层归一化)是一种专门用于对序列进行归一化的技术,其目的是防止字符序列在计算过程中出现数值发散,从而避免对神经网络的拟合过程产生不良影响。在PyTorch 2.0中,Layer Normalization的使用提供了高级API,调用方式如下:

     layer_norm = torch.nn.LayerNorm(normalized_shape, eps=1e-05, elementwise_affine=True,
device=None, dtype=None)函数
     embedding = layer_norm(embedding) #使用layer_norm对输入数据进行处理

图3-8展示了Layer Normalization与Batch Normalization的不同。从图中可以看到,Batch Normalization是对一个batch中不同序列中处于同一位置的数据进行归一化处理,而Layer Normalization是对同一序列中不同位置的数据进行归一化处理。

图3-8 Layer Normalization与Batch Normalization的不同

有兴趣的读者可以展开学习,这里不再过多阐述。具体的使用如下(注意一定要显式声明归一化的维度):

     embedding = torch.rand(size=(5,80,768))
     print(torch.nn.LayerNorm(normalized_shape=[80,768])(embedding).shape)  #显式声明归
一化的维度

3.1.5 多头自注意力

在3.1.2节的最后,我们使用PyTorch 2.0自定义层编写了自注意力模型。从相应的代码中可以看到,除使用自注意力核心模型外,还额外加入了掩码层和点积的除法运算,以及为了整形使用了Layer Normalization。实际上,这些处理都是为了使得整体模型在训练时更加简易和便捷而做出的优化。

聪明的读者应该会发现,前面无论是“掩码”计算、“点积”计算还是使用Layer Normalization,都是在一些细枝末节上的修补,那么有没有可能对注意力模型进行一个较大的结构调整,使其更加适应模型的训练。

本小节将在上述基础上介绍一种更高级的架构,即多头注意力架构。这是在原始自注意力模型的基础上做出的一种重要优化。

多头注意力的结构如图3-9所示,query、key、value首先经过一个线性变换,之后计算相互之间的注意力值。相对于原始自注意力计算方法,注意这里的计算要做 h 次( h 为“头”的数目),其实也就是所谓的多头,每一次计算一个头。而每次query、key、value进行线性变换的参数 W 是不一样的。

图3-9 从单头到多头注意力结构

h 次的缩放点积注意力的计算结果进行拼接,再进行一次线性变换,得到的值作为多头注意力的结果,如图3-10所示。

图3-10 多头注意力的结果合并

可以看到,这样计算得到的多头注意力值的不同之处在于进行了 h 次计算,而不是仅仅计算一次。这样的好处是允许模型在不同的表示子空间中学习到相关的信息,并且相对于单独的注意力模型的计算复杂度,多头模型的计算复杂度大大降低了。拆分多头模型的代码如下:

def splite_tensor(tensor,h_head):
    embedding = elt.Rearrange("b l (h d) -> b l h d",h = h_head)(tensor)
    embedding = elt.Rearrange("b l h d -> b h l d", h=h_head)(embedding)
return embedding

在此基础上,可以对注意力模型进行修正,新的多头注意力层代码如下:

相较单一的注意力模型,多头注意力模型能够简化计算,并且在更多维的空间对数据进行整合。图3-11展示了一个多头注意力模型架构。

图3-11 h 头注意力模型架构

在实际应用中,使用“多头”注意力模型时,可以观察到每个“头”所关注的内容并不一致。有的“头”专注于相邻的序列,而有的“头”则会关注更远处的单词。这种特性使得模型能够根据需要更细致地对特征进行辨别和提取,从而提高模型的准确率。 WjAV6CtFV+KR2qg2gzLcFN3f48OuhmyV2UvXU7ZRajSKy/iL9NYUdgNLe+fbA8MZ

点击中间区域
呼出菜单
上一章
目录
下一章
×