Paper Reading:你真的了解Transformer了吗?

本文深入解析Transformer模型,探讨其在自然语言处理任务中的应用,包括自注意力机制、多头机制及位置编码等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

你真的了解Transformer了吗?

1. 背景

为什么要看这篇文章呢?LZ最近在接触NLP的相关算法,按照最近大火的BERT来看,Transformer其实就是这个算法的一个核心,所以本着先把论文读厚的过程,还是得把这块详细的理解清楚,除了知识的输入阶段,还要再强行输出一遍,这样才能比较好的掌握对应的知识。

主要参考:

  1. http://nlp.seas.harvard.edu/2018/04/03/attention.html
  2. https://jalammar.github.io/illustrated-transformer/
  3. 原始Paper:Attention is All You Need

2. 理论知识

2.1 宏观角度

首先,我们可以把一个模型看成是一个黑箱子的状态,那么在一个机器翻译应用中,这个黑箱子的输入是一种语言的句子,然后经过这个黑箱子的处理,用另外一种语言输出对应的翻译。那么具体示例如下:

在这里插入图片描述
直接扩展一下,在我们通常使用的例如goole翻译,百度翻译就是这种类似的技术
在这里插入图片描述

我们揭开这个黑箱子,来看下这个黑箱子下的结构,可以从下图中看到两部分,一个是编码部分,另一个是解码部分,并且编码后的信息会传递给解码部分。

在这里插入图片描述
我们再进一步看一下编码部分和解码部分的内部情况吧。

由下图可以看出,编码部分是由一系列编码器堆叠而成(论文中显示的使用六个编码器,但实际上在BERT中Base版本使用了12个编码器,在large版本中使用了24个编码器),与编码器相对应的也存在相同数量的解码器(通常情况下编码器的数量和解码器的数量是一致的)

在这里插入图片描述

那么每个编码器中是什么结构呢?实际上每个编码器的结构都是一致的(但是他们不是权值共享的),每个编码器又可以分成两个子层:自注意力层和前向神经网络这两部分。

在这里插入图片描述
编码器的输入首先会流经自注意力层,这一层是帮助编码器在编码特定单词时可以和句子中其他的词进行联系,后续文章中会详细说明。自注意力层的输出会通过前馈神经网络,完全相同的前馈神经网络独立应用在每个位置。

解码器也包含这两层,但是他们之间是一个注意力层,帮助解码器专注于输入句子的相关部分(类似于注意力在Seq2seq模型中的作用)

在这里插入图片描述
用图像来表示张量

目前为止,我们已经看到了模型的主要部分,现在我们来看看各种向量/张量,看一下我们的输入是怎么经过对应的模块来得到模型的输出结果的。

与 NLP 应用程序中的一般情况一样,我们首先使用以下方法将每个输入词转换为向量

在这里插入图片描述
如果像了解词向量这方面的信息,可以看一下这篇论文

在这里插入图片描述
这里lz可以简单介绍一下什么是word embedding,以中文为例,小时候我们都用过新华字典,通过对应的字,我们可以找到对应字的解释,近义词,反义词,常用句子等,word embedding就可以看作是另外一种新华字典,对应每个字,都有对应维度的向量 0.1 , 0.00003 , − 0.00001... 0.1, 0.00003, -0.00001 ... 0.1,0.00003,0.00001...类似这种编码信息,而这些数字代表什么呢?其实也可以看作是这个字的近义词反义词特征,常用句子等一些原来用文字进行表示的信息转换成了连续的数组编码。这样其实在高维空间中,意思相近的词或者字可能会距离比较近,而意思不相干的词或者字的embedding距离会比较远一些。这个只是一个比较通俗的理解啦!

同样,按照上述的情况,我们会有一个Embedding字典,通过字典查询到对应的embedding,如果没有对应的字,就是UNKNOWN,一般可能就是随机初始化一个符合高斯分布的embedding。

embedding一般都是在最底层,也就是最开始的编码器中使用,(这点其实是可以类比图像的,一般比较原始的图像都是输入底层的网络来进行特征的提取的),比较抽象的来讲,所有的编码其都是接受一个向量列表,每个向量的大小都是512维,唯一的区别可能是底部的编码器的输入是我们刚刚提到的词向量,而后续的编码器就是就是接收前一个编码器的输出。

其中我们提到了,列表的大小是怎么确定的呢?训练过图像的小伙伴应该都知道在训练的时候我们一个batch一个batch的进行训练的,那么输入的图像的尺寸都是一致的,这样才能concat到一起。那么对于不一样长的句子,我们要怎么处理呢?

首先我们会设置一个输入的最大长度,如果超过这个长度的文本会进行截断,当然也可以有其他操作例如滑动窗口之类的方式,其他的短的句子会按照最长的句子的部分进行pad,当然如果最长的句子长度是100,那么短句子pad到100就可以了。

又跑远了,快回来,我们还是继续来看transformer里面的encoder部分,下图展示的是最底层的encoder的状态,可以看到输入是word embedding,每个embedding经过自注意力层会产生对应的z1向量,然后再传入到feed forward层

在这里插入图片描述

在上图中,我们可以看到Transformer的一个关键属性,即每个位置的单词在编码其中都流经其对应的路径,在自注意力层中这个路径之间会存在相互依赖的关系,在后续会介绍词与词之间到底是怎么联系的。

然而,从图中我们还是可以看到前馈层并没有这些依赖关系,因此各种路径可以在流经前馈层时并行执行。

接下来我们将使用更短的句子,来仔细的看下编码器的每个子层中发生的情况

2.2 编码器的内部

正如我们已经提到的,编码器接收一个向量列表作为输入。 它通过将这些向量传递到一个自注意力层,然后传递到一个前馈神经网络,最后将输出传送到下一个编码器来处理这个列表。

在这里插入图片描述
每个位置的单词都经过一个自注意力层,然后再经过一个前馈神经网络

2.3 宏观上来看自注意力

哈哈,原文中说“Don’t be fooled by me throwing around the word “self-attention” like it’s a concept everyone should be familiar with.”其实好像之前CV中都会说是注意力机制,但和这里的自注意力机制还是不同的,小伙伴们要注意区分哈。
这不,到这篇文章,我们就可以好好理解一下自注意力机制了

假设我们有下面的句子,作为输入,然后我们想得到下面输入句子的翻译
“The animal didn’t cross the street because it was too tired”,皮一下,我们来看下goole翻译

在这里插入图片描述
在这里插入图片描述
怎么说呢,感觉百度的好像更加准确一些,因为增加了对动物的单复数区分?

实际上对于算法来说,中间有一个问题是:“it”到底指动物还是指街道呢?对于小伙伴来说肯定是一个非常简单的问题,但是对于算法也许就不那么简单了,并且从位置上来说,可以看到“it”实际上距离“animal”会更加远一些。

实际上,当模型处理“it”这个词时,self-attention 会把“it”和“animal”联系起来。当模型处理每个单词(输入序列中的每个位置)时,自注意力会让它查看输入序列中的其他位置来寻找有助于对该单词进行更好编码的线索。

相信了解过RNN的小伙伴,都知道RNN是如何把之前的词的信息传递给之后的单词,或者换句话说就是增加词与词之间联系,可以RNN一个比较大的缺点就是词的位置如果相差很远的,词与词之间的关系就会被弱化,这也是RNN面对长句的一个弊端。那么自注意力其实就是Transformer用来理解我们当前处理词和其他词之间的联系。

在这里插入图片描述
小伙伴如果想自己玩一下这个功能,可以在这个链接上自己尝试下https://colab.research.google.com/github/tensorflow/tensor2tensor/blob/master/tensor2tensor/notebooks/hello_t2t.ipynb#scrollTo=OJKU36QAfqOC
在这里插入图片描述

2.4 仔细看看到底什么是自注意力

敲黑板啦, 重头戏来了!

让我们首先看看如何使用向量计算自注意力,然后继续看看它是如何实际实现的——使用矩阵。

计算自注意力的第一步是从每个编码器的输入向量(在本例中为每个词的嵌入)创建三个向量。 因此,对于每个单词,我们创建一个 Query 向量、一个 Key 向量和一个 Value 向量。 这些向量是通过将嵌入乘以我们在训练过程中训练的三个矩阵来创建的。

请注意,这些新向量的维度比嵌入向量小。 它们的维数是 64,而嵌入和编码器输入/输出向量的维数是 512,我们使用注意力多头机制机制,这样就可以保持一致了。

在这里插入图片描述
看到上面的图,小伙伴们肯定会有疑惑,“query”,“key”和“value”这些向量到底是什么呢?

简单来说,他们是可以用计算和思考注意力的抽象特征,我们可以通过下面的阅读来明白注意力到底是怎么计算的。

计算self-attention的第二步是计算一个score。 假设我们正在计算本例中第一个单词“Thinking”的自注意力。 我们需要根据这个词对输入句子的每个词进行评分。 当我们在某个位置对单词进行编码时,分数决定了将多少注意力放在输入句子的其他部分上。

得分是通过将查询向量与我们正在评分的各个单词的键向量进行点积来计算的。 因此,如果我们正在处理位置 #1 中单词的自注意力,第一个分数将是 q1 和 k1 的点积。 第二个分数是 q1 和 k2 的点积。具体的我们可以看下图操作

在这里插入图片描述
第三步和第四步是将上一步得到的分数除以 8(论文中使用的关键向量维度的平方根,也就是64的平方根。这会导致梯度更稳定。这里可能还有其他可能的值,但8是默认的值。),然后通过 softmax 。 Softmax 将分数归一化,因此它们都是正数并且加起来为1。

在这里插入图片描述
从这个 softmax分数我们看出来,比如在第一个位置上,“Thinking”这个词肯定是本身自己的关联度最高,那么经过softmax之后,就会得到一个比较高的分数,换句话说是比较高的联系度。而其他位置与这个词的联系度也可以通过这个分数来体现出来。

第五步是将每个值向量乘以 softmax 分数(准备将它们相加)。 这里的直觉是保持我们想要关注的单词的值不变,并选择性的忽略不相关的单词(例如,通过将它们乘以像 0.001 这样的小数字)。

第六步是对加权值向量求和。 这会在这个位置(对于第一个词)产生自注意力层的输出。

在这里插入图片描述
自注意力计算到此结束。 结果向量也就是上图中z1、z2,是我们可以传送到前馈神经网络的向量。 然而,在实际实现中,为了更快的处理,这个计算通常是以矩阵形式完成的。我们再来以矩阵的形式理解一下自注意力的流程。

自注意力的矩阵计算(其实就是向量的扩充版本)

为了加深记忆,咱们再以矩阵的形式理解一下对应的流程:

  • 第一步是计算查询、键和值矩阵。通过将我们的词嵌入写成到一个矩阵 X 中,并将其乘以我们训练过的权重矩阵(WQ、WK、WV)来实现。

在这里插入图片描述
X矩阵中的每一行其实都是对应输入词的embedding。

然后,我们把上面的向量的2-6步合并成如下一步:
在这里插入图片描述

2.5 多头机制

在论文中,通过添加一种称为“多头”注意力的机制进一步细化了自注意力层。具体是通过两种方式来提高注意力层的性能:

  • 多头的机制扩展了模型专注于不同位置的能力。在上面的例子中, z1 包含一点点其他编码,但它可能由实际单词本身主导。如果我们翻译成为“动物没有过马路,因为它太累了”这样的句子其实感官上也是很有用,但是我们还是想知道“它”指的是哪个词。

  • 多头的机制为注意力层提供了多个“表示子空间”。正如我们接下来将看到的,对于多头注意力,我们不仅有一个,而且还有多组查询/键/值权重矩阵(在论文中,Transformer有八个注意力头,所以我们最终为每个编码器/解码器提供了八组向量),这些集合中的权重每一个都是随机初始化的。然后,在训练之后,每组用于将输入嵌入(或来自较低编码器/解码器的向量)投影到不同的表示子空间中。
    在这里插入图片描述

如果我们进行上面描述的自注意力计算方式,那么只需使用不同的权重矩阵进行8次不同的计算,我们最终会得到8个不同的Z矩阵

在这里插入图片描述
但是多头机制也会产生一个问题,就是作为前馈网络的输入,它还是希望可以接收到一个矩阵,而不是八个头对应的八个矩阵,相信小伙伴们肯定想到了对应的措施,就是把对应的八个矩阵concat起来,然后再通过一个矩阵来进行融合,就得到了下面的那张图
在这里插入图片描述

我们把整个流程总结起来,就是下面这张图啦

在这里插入图片描述

2.6 使用位置编码表示序列的顺序

从上面的计算流程来看,这个算法其实对单词在句子中的位置并不感知,也就是随意调换单词的位置对实际网络推断的影响并没有很大,但是我们在讲述一个句子的时候,词的顺序不同,表达的意思也是不同的,以中文为例,如果把牙和刷这两个词顺序相调换,意思是不一样的。

为了解决这个问题,我们在做输入的时候,就不能只把word embedding的信息传入到编码器中,而是还需要在初始情况下加上对应的位置信息编码

在这里插入图片描述
假设我们的word embedding的长度为4, 那么对应的位置的embedding的长度也为4

在这里插入图片描述
小伙伴可能又有疑惑了,到底位置的embedding到底是什么样子的呢?

在下图中,每一行对应一个向量的一个位置编码。 因此,第一行是我们添加到输入序列中第一个词的嵌入中的向量。 每行包含 512 个值——每个值都在1到-1之间。我们对它们进行了颜色编码,因此图案是可见的。

在这里插入图片描述
在论文中的具体编码方式如下:
在这里插入图片描述

当然也可以使用其他的位置编码方式,但是位置编码也需要注意几个问题,就是它的范围要尽量是一定的,并且针对每个位置具有特定的特征,同时不能增加很多的计算开销

2.7 残差连接

在继续之前我们需要提及的编码器架构中的一个细节是,每个编码器中的每个子层(self-attention,ffnn)周围都有一个残差连接,然后是层归一化步骤。
在这里插入图片描述
残差连接也是增加了信息的传递,这边要注意的是会有一个层归一化操作,也许有小伙伴们会问:

  • 为什么不用BN层?
    相信小伙伴们肯定记得我们是要对输入的句子进行组batch的,那么这种操作会对短句子进行padding,那么对于bn计算的时候会产生大量无效的计算,并且计算得到的结果也并不是很好的反映数据的分布状态,所以这里就步进行BN层的操作了

  • 为什么要用LN层?

因为网络结构中会存在一系列的残差操作,这些操作其实会对数据的分布产生一定的影响,并且保证层与层之间的分布比较稳定,所以就采用了LR层

未完待续。。。

这文章写的太长了,欲知后事如何,且听下回分解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值