注意:本文引用自专业人工智能社区Venus AI
更多AI知识请参考原站 ([www.aideeplearning.cn])
算法简介
Transformer架构于2017年6月推出。最初的研究重点是自然语言处理领域的翻译任务。随后,几个具有影响力的模型被引入,包括:
(1)2018年6月:GPT,第一个预训练的Transformer模型,用于微调各种自然语言处理任务,并获得了SOTA模型效果。
(2)2018年10月:BERT,另一个大型预训练模型,旨在生成更好的句子摘要。
(3)2019年2月:GPT-2,GPT的改进版本(模型更大),由于道德问题没有立即公开发布。
(4)2019年10月:DistilBERT,BERT的精简版,计算速度提高60%,内存减少40%,并且仍保留BERT 97%的性能。
(5)2020年5月:GPT-3,GPT-2改进版本(模型更大),能够在各种任务上表现良好而无需微调(称为零样本学习)。
(6)2021年6月:VIT的发布成为了Transformer进军计算机视觉领域的里程碑。证明了Transformer算法同样适用于计算机视觉。让其逐渐成为两个领域大一统的框架。同年,微软研究院发表在ICCV上的一篇文章Swin Transformer引发视觉领域的又一次碰撞。该论文一经发表就已在多项视觉任务中霸榜。甚至动摇了卷积神经网络在计算机视觉领域的地位。
(7)2023年1月:OpenAI公司发布了聊天机器人ChatGPT,以高质量高水平高逻辑性的信息回复风靡全球,成为各行各业人士的咨询小管家,再一次的对各行业带来了一次技术革新,尤其是咨询行业。
算法概况
Transformer是典型的Seq2Seq模型,关于Seq2Seq模型详见https://www.aideeplearning.cn/seq2seq%e6%a8%a1%e5%9e%8b%ef%bc%88sequence-to-sequence%ef%bc%89/。该模型主要由两个块组成,如图1所示。
左边是编码器(encoder):负责把输入信息编码成特征向量的算法组件,输入信息根据不同的任务而不同,可是是文本也可以是图像。注意,文本和图像本身不能直接输入编码器,必须把它们向量化(Embedding)成向量才能送入编码器。
右边是解码器(decoder):解码器是服务于生成任务的,如果是判别任务,可以没有解码器结构。解码器需要对编码器的输出结果进行翻译和解释,生成想要的目标序列。因为最初的Transformer是用于生成任务,所以是编码器-解码器架构。
注意,不管是编码器还是解码器都可以独立或联合使用,具体取决于任务,以文本信息为例:
(1)仅使用编码器的模型:适用于需要理解输入的任务,例如句子情感取向分类和命名实体识别等。
(2)仅使用解码器的模型:适用于文本生成等生成任务。
(3)使用编码器-解码器的模型:适用于需要输入的生成任务,例如语言翻译或摘要提取等任务。
自注意力层
Transformer模型中最关键部分就是自注意力(Self-Attention)机制,正如 Transformer 的论文的标题是“Attention Is All You Need”!以文本问题为例来讲解这个机制。在处理文本问题时,自注意力机制会告诉模型在处理每个单词的表示时,特别关注在句子中传递给它的某些单词,并或多或少地忽略其它单词。简单来说,就是给句子中不同单词分配不同的权重。这是符合常理的,因为一句话中的每个单词重要程度是不一样的,从语法角度说,主谓宾语比其它句子成分更重要,self-attention机制就是模型尝试学习句子成分重要程度的方法。自注意力机制权重的计算过程如下。
计算自注意力的第一步是为编码器的每个输入向量(在本例中是每个词的特征向量)创建三个新向量。它们分别被称为查询向量(Queries)、键向量(Keys)和值向量(Values),简称q, k, v向量,如图2所示。这些向量是通过与在训练过程中训练的三个权重矩阵WQ,WK,WV相乘而创建的。这个过程在模型实现时非常简单,通过神经网络层的映射即可得到。请注意,这些新向量的维度一般小于输入向量。例如,在原论文中新向量的维度是64维,而输入向量的512维,这可以一定程度上节省后续的计算开销。
查询向量、键向量和值向量是为计算和思考注意力机制而抽象出的概念,或者说是我们对模型的学习期望。因为这三个新向量在刚创建时是随机初始化的,没有特殊含义,是经过模型训练分别得到了类似查询、回复、存值等向量功能,一个词向量可以通过它们与其它词向量进行互动来建模词与词之间的相关性。在读者阅读完接下来的全部计算过程之后,就会明白它们名字的由来。
计算自注意力的第二步是计算一个相关性分数(score)。假设正在计算本例中第一个词“Thinking”的相关性分数。这个分数就决定着“Thinking”这个词在某句话中与其它词的关联程度,所以“Thinking”这个词要与其它所有词都计算一个分数。
分数是通过查询向量与正在评分的相应单词的键向量的点积计算得出的。点积的公式:a×b=|a|×|b|×cosθ。其意义就是比较两个向量的相关程度,相关性越高,分数越大,如图3所示。
第三步和第四步是将相关性分数除以8(8是论文中使用的查询向量维度的平方根,即根号下64等于8)。这会使模型训练时的梯度更稳定。然后通过Softmax函数映射出最后的结果。Softmax函数可以对分数进行归一化处理,使它们都为正且加起来为1,计算过程如图4所示。
Softmax映射后的分数决定了每个词在句子中某个位置的重要性。显然,当词语处于该位置时,其Softmax分数最高。因为它的查询向量、键向量和值向量都是来源于这个词本身的,具体来说q, k, v向量是当前词向量经过神经网络层映射得到的,是比较相近的三个向量,所以q, k的点积结果就大,表示该词与自身是最相关的。但是,这个词是存在于一个句子中的,因此它与其它词之间应该也存在一个相关性,但这个相关性肯定没有与自身的相关性大。所以,当前词的查询向量和别的单词的键向量做点积时,结果就会相对较小。这时的结果表示该词与别的单词的相关性。值得注意的是自注意力机制中“自”的含义是:q, k, v向量都是来源于当前词本身的,是“自己”通过神经网络层映射得到的。
第五步是将每个值向量乘以对应的Softmax分数,目的是做每个单词重要程度的重新分配。最后是加权值向量求和的操作。这会在该位置产生自注意层的输出,例如,词“Thinking”经过自注意力层处理后的输出为 output=0.88v1+0.12v2,即当前这句话经过自注意力层处理后,词“Thinking”的含义包含了88%的自身含义和12%的下一词”Machines”的含义,这样处理就体现了文本上下文的关系。当前这句话中的其它词也要做相同的处理,如图5所示。
自注意力计算到此为止。生成的向量是可以发送到前馈神经网络的向量。然而,在实际的实现中,会将输入向量打包成矩阵,以矩阵形式完成此计算,以便更快地在计算机中计算处理,如图6所示。
其公式表示如下:
其中, Q、K 和 V 是输入矩阵, 分别代表查询矩阵、键矩阵和值矩阵, dk 是向量维度。上述公式的作用是通过对 Q 和 K 的相似度进行加权, 来得到对应于输入的 V 的加权和。
具体来说,这个公式分为三个步骤:
(1)计算 Q 和 K 之间的相似度,即 。
(2)由于 Q 和 K 的维度可能很大, 因此需要将其除以来缩放。这有助于避免在 Softmax 计算时出现梯度消失或梯度爆炸的问题。
(3) 对相似度矩阵进行 Softmax 操作, 得到每个查询向量与所有键向量的权重分布。然后, 将这些权重与值矩阵 V 相乘并相加, 得到自注意力机制的输出矩阵。
多头自注意力层
该论文通过添加一种称为“多头注意力”(Multi-heads self-attention)的机制进一步细化了自注意力层。对于多头注意力,其中有多组查询向量、键向量和值向量,这里把一组q, k, v称之为一个头,Transformer原论文中使用八个注意力头。每组注意力头都是可训练的,经过训练可以扩展模型关注不同位置的能力。
举一个形象的类比:把注意力头类比成小学生,那么多个小学生在学习过程中会形成不同的思维模式,对同样的问题会产生不同的理解。这就是为什么要使用多头的原因,就是希望模型可以从不同的角度思考输入信息,如图7所示。
但是,多头注意力机制也给带来了一个问题。如果使用八个头,经过多头注意力机制后会得到8个输出,但是,实际上只需要一个输出结果。所以需要一种方法将这八个输出压缩成一个矩阵,方法也很简单,将它们乘以一个额外的权重矩阵即可。这个操作可以通过一个神经网络层的映射完成,如图8所示。
编码器结构
每个编码器中的自注意力层周围都有一个残差连接,然后是层归一化步骤。归一化的输出再通过前馈网络(Feed Forward Network,FFN)进行映射,以进行进一步处理。前馈网络本质上就是几层神经网络层,其中间采用ReLU激活函数,两层之间采用残差连接。编码器的结构如图9所示。
其中,残差连接可以帮助梯度的反向传播,让模型更快更好地收敛。层归一化用于稳定网络,减轻深度学习模型数值传递不稳定的问题。
至于Transformer中的FNN是一个MLP,它在自注意力机制之后对序列中的每个向量单独应用。FNN 起到两个主要作用:
(1)引入非线性:虽然自注意力机制能捕捉序列中不同位置的向量之间的依赖关系,但它本质上是线性的。通过引入FNN层,Transformer可以学习到输入序列的非线性表示,这有助于模型捕捉更复杂的模式和结构。
(2)局部特征整合:FNN层是一个MLP,对序列中每个位置的向量独立作用。这意味着它可以学习到局部特征并整合这些特征,以形成更丰富的特征表示。这种局部特征整合与自注意力机制中的全局依赖关系形成互补,有助于提高模型性能。换句话说,自注意力机制学习的是向量之间的关系,而FNN学习的是每个向量本身更好的特征表示。
FNN层由两个全连接层和一个非线性激活函数(如 ReLU 或 GELU)组成。假设有一个输入向量x,那么FNN层的计算过程如下:
其中 W1,b1,W2 和 b2 是需要学习的权重矩阵和偏置向量, ReLU 是激活函数。
以上这些加起来就是编码器的结构组成,可以将编码器堆叠N次以进一步编码信息,其中每一层都有机会学习不同的注意力表示,因此有可能提高Transformer网络的预测能力。最后,在文本信息送进编码器之前,往往需要对输入信息添加位置编码。这是因为 Transformer模型中的自注意力机制是一种全局操作,它在计算时并不考虑输入序列中元素的顺序。然而,在自然语言处理任务中,单词之间的顺序是非常重要的。为了让Transformer 能够捕捉到这种顺序信息,需要为输入添加位置编码。
位置编码是一种表示序列中每个位置信息的向量。位置编码的维度与输入向量的维度相同,因此可以将它们逐元素相加,从而保留位置信息。位置编码可以是固定的(如基于正弦和余弦函数的编码),也可以是可学习的(通过训练得到的向量)。在原始的Transformer 论文中,使用了一种基于正弦和余弦函数的固定位置编码。对于一个给定位置pos和编码维度i,位置编码的计算公式如下:
其中, PE(i,2k) 和 PE(i,2k+1) 是位置编码矩阵中第 i 行第 2k 和 2k+1 列的值, d 是输入向量的维度。通过上述公式可以为输入序列中的每个位置生成一个位置编码向量, 该向量具有一定的模式,能够表示该位置在序列中的位置信息。
为了将位置编码添加到输入序列中,可以将输入序列中的每个词语向量与对应位置编码向量相加,得到包含位置信息的输入向量。如图10所示,这样Transformer模型就可以更好地处理输入序列中的信息,从而提高模型的性能。
如果这些位置编码是可学习的,那么在模型刚开始训练时,会进行随机初始化,我们期望模型能自己通过学习,找到词语之间的位置相关性。就像期望模型可以通过学习,让词向量映射生成的三个新向量q, k, v可以分别执行查询、回复、存值等向量功能一样。
解码器结构
解码器的工作是生成文本序列。解码器具有与编码器类似的子层。它有两个多头注意层、一个前馈网络、残差连接以及每个子层之后的层归一化。这些子层的行为类似于编码器中的层,这里不再做重复赘述。解码器的顶层是一个充当分类器的线性层和一个用于获取单词概率的 Softmax 所覆盖。编解码器完整的结构如下图所示。注意,在一些判别任务中可能没有解码器结构,编码器-解码器结构经常存在于一些生成任务中。
以“我是学生”→ “i am a student” 的语言翻译任务为例,如图12所示。
图12模型由编码器和解码器两部分组成,是典型的Seq2Seq模型,详见编码器-解码器章节。其中编码器是用于提取当前要翻译序列的语义信息,作为解码器的输入。在解码器中,需要根据解码器之前时刻已经生成的部分目标序列和编码器的输出来预测下一个词语。Transformer解码器的详细介绍如下:
位置编码
解码器的输入也需要进行位置编码,与编码器中的方法相同。
自注意力层
与编码器类似,解码器中也包含了多个自注意力层,结合之前已经生成的部分目标序列,用于在当前时间步骤预测下一个词。
编码器-解码器注意力层
在解码器中,还需要使用一个编码器-解码器注意力层来对编码器的输出进行加权求和。该层的输入包括当前解码器的自注意力层输出和编码器的输出。具体来说,我们将当前解码器的自注意力层输出作为查询向量q,这表示解码器当前正在尝试生成的元素,需要哪些信息来进行准确预测。编码器的输出作为键向量k和值向量v,这为解码器提供了一个关于输入序列的丰富上下文。然后使用self-attention公式来计算注意力分布。通过将解码器的查询与编码器的键和值结合,模型可以决定在生成每个新元素时,应该给予编码器输出中的哪些部分更多的重视。这种方法使得解码器在生成输出时不仅考虑到它自己之前生成的内容,而且还考虑到整个输入序列的内容。这对于生成与输入密切相关的准确和连贯的输出至关重要。尤其是类似于机器翻译的语言任务中。
前馈神经网络
与编码器类似,解码器中也包含了前馈神经网络,用于在生成序列的过程中增强模型的表达能力。
需要注意的是,在解码器中会使用遮掩机制。Transformer 中的掩码(masking)机制用于防止模型在处理序列时访问不应该访问的信息。掩码机制在自注意力计算过程中起作用,主要有两种类型:填充掩码(padding mask)和序列掩码(sequence mask,也称为查找掩码或解码器掩码)。
1)填充掩码
在自然语言处理任务中,为了将不同长度的句子输入到模型中,通常需要对较短的句子进行填充,使其与最长句子的长度相同。填充通常使用特殊的符号(如<pad>)表示。
填充掩码的目的是在自注意力计算过程中忽略这些填充位置。这样做是因为这些填充符号实际上并不携带任何有意义的信息,我们不希望它们影响其它单词之间的注意力权重计算。填充掩码通过将填充位置对应的注意力logits设置为一个非常大的负数来实现。这样,在应用 Softmax 函数时,填充位置对应的注意力权重会接近于零。
2)序列掩码
序列掩码主要应用于Transformer 解码器。在自回归生成任务中(如机器翻译、文本摘要等),解码器需要逐步生成输出序列。在每个时间步,解码器只能访问到当前及之前的单词,而不能访问未来的单词。这样做是为了确保模型在生成过程中不会“偷看”未来的信息,从而遵循真实的生成场景。假设模型目前处理的序列只有两个token作为输入,并且我们正在观察第二个token。在这种情况下,最后两个token被屏蔽,其对应的权重分数也将是0。
序列掩码通过在注意力logits矩阵中添加一个下三角矩阵(其上三角部分为负无穷)来实现。这样,在应用Softmax函数时,当前位置之后的单词对应的注意力权重将接近于零。这使得解码器在每个时间步只能关注当前及之前的单词。我们举个实例,想象一个由四个单词组成的序列(例如“robot must obey orders”)。我们先可视化其注意力分数的计算:
相乘之后,我们使用注意掩蔽矩阵。它将我们想要屏蔽的单元格设置为 -无穷大或一个非常大的负数(例如 GPT2 中的 -10 亿):
然后,在每一行上应用 softmax 会产生我们用于自注意力的实际分数:
这个分数表的含义如下:
- 当模型处理数据集中的第一个示例(第 1 行)时,该示例仅包含一个单词(“机器人”),其 100% 的注意力将集中在该单词上。
- 当模型处理数据集中的第二个示例(第 2 行)时,其中包含单词(“机器人必须”),当模型处理单词“必须”时,48% 的注意力将集中在“机器人”上,而 52% 的注意力将集中在“机器人”上。 %的注意力将集中在“必须”上。
- 等等
总结一下,掩码机制在Transformer模型中具有重要作用。填充掩码用于忽略填充符号的影响,而序列掩码确保解码器在生成过程中遵循自回归原则。掩码的存在使得模型在处理序列时更加稳定、可靠。
线性顶层和 Softmax 层
Transformer的线性顶层和Softmax层是用于生成目标序列中每个位置上的词语概率分布的关键组件,详细介绍如下。
在Transformer的解码器中,使用自注意力和编码器-解码器注意力生成了当前时间步骤的特征向量。这些特征向量包含了所有上下文信息,并且对应于目标序列中的每个位置。为了将这些特征向量转换为目标序列上的词语概率分布,需要添加一个线性顶层。
具体来说,可以使用一个全连接层(线性变换)来将特征向量映射到一个维度为词表大小的向量。该向量包含了每个词语的得分,可以使用Softmax函数将这些得分转换为概率分布,即目标序列中每个位置上的词语概率分布。
Softmax函数是一个常用的激活函数,可以将任意实数向量转换为概率分布。在Transformer中,使用Softmax函数将线性顶层输出的向量转换为目标序列中每个位置上的词语概率分布。
具体来说,对于每个位置,将线性顶层输出的向量作为Softmax函数的输入,得到一个大小为词表大小的概率分布向量。该概率分布向量表示了在给定上下文信息和之前已经生成的目标序列时,目标序列中每个位置上每个词语的概率。
总的来说,Transformer的线性顶层和Softmax层是用于将解码器中生成的特征向量转换为目标序列上的词语概率分布的关键组件。在Transformer中,使用一个全连接层将特征向量映射到一个大小为词表大小的向量,然后使用Softmax函数将其转换为概率分布。这样就可以预测目标序列中每个位置上的词语,并且可以使用这些预测来计算损失函数,从而训练模型。
以上就是Transformer的计算机制。Transformer利用注意力机制可以做出更好的预测。之前的循环神经网络试图实现类似的事情,但因为它们受到短期记忆的影响,效果不如Transformer算法好,特别是当编码或生成较长序列。因为Transformer架构在计算注意力时的计算范围是全局的,即查询向量和其它所有的键向量都要做点积来计算相关性,因此Transformer可以捕获长距离的信息依赖,理论上这个距离是可以覆盖当前处理的整句话的。
输入数据的向量化
最后补充一些输入数据向量化(Input Embedding)的知识。所谓文档信息的向量化,就是将信息数值化,从而便于进行建模分析,自然语言处理面临的文本数据往往是非结构化杂乱无章的文本数据,而机器学习算法处理的数据往往是固定长度的输入和输出。因而机器学习并不能直接处理原始的文本数据。必须把文本数据转换成数字,比如向量。
词嵌入(Word Embedding)是一种将自然语言中的词语映射到低维向量空间中的技术。它是自然语言处理领域中的重要技术之一,被广泛应用于文本分类、机器翻译、问答系统等任务中。NLP中常见的几种词嵌入方法如下。
基于计数的方法
基于计数的方法通过统计每个词语在语料库中的出现次数,计算不同词语之间的共现关系。常见的基于计数的方法有LSA和HAL。
LSA(Latent Semantic Analysis)是一种基于奇异值分解(SVD)的方法,将词语表示为在语料库中的共现矩阵的主题向量。LSA的主要思想是:相似的词语在语料库中通常会出现在相似的上下文中。因此,将词语表示为在语料库中的共现矩阵的主题向量,可以捕捉到它们之间的语义关系。
HAL(Hyperspace Analogue to Language)是一种基于点间相似性的方法,通过在高维向量空间中建立词语之间的关联关系,将词语表示为向量。HAL的主要思想是:通过在高维向量空间中表示每个词语,可以捕捉到它们之间的语义关系。在HAL中,每个词语表示为在高维向量空间中的一个点,词语之间的相似度可以通过计算它们在向量空间中的距离来得到。
神经网络方法
神经网络方法通过训练神经网络,将词语映射到低维向量空间中。其中,Word2Vec是最为常见的神经网络方法之一。
Word2Vec有两种模型:CBOW和Skip-gram。CBOW(Continuous Bag-of-Words)模型通过上下文预测当前词语,而Skip-gram模型则通过当前词语预测上下文。这些模型通过训练大量的文本数据,得到了每个词语的词向量,可以用于后续的NLP任务中。
预训练模型
预训练模型通过预先训练大规模文本语料库,学习出通用的语义信息,然后将这些模型迁移到特定的NLP任务中。其中,BERT和GPT是目前最为流行的预训练模型之一。它们都是基于Transformer模型的,通过对大规模文本语料库进行无监督的预训练,学习出词语的词向量表示和文本序列的上下文信息。
总的来说,词嵌入是将自然语言中的词语映射到低维向量空间中的技术。常见的词嵌入方法包括基于计数的方法、神经网络方法和预训练模型。这些方法可以用于提取文本中的语义信息,并为后续的NLP任务提供有用的特征表示。
相关资源
- 自注意力机制详解
- Transformer算法讲解
- OpenAI 的GPT2博文讲解
- OpenAI 的GPT3博文讲解
- Bert,ELMo大语言模型
- 除了 GPT2 之外,还可以查看Hugging Face的pytorch-transformers库,它实现了 BERT、Transformer-XL、XLNet 等前沿 Transformer 模型。
- 本博文的插图来自于Jay Alammar的博客,在此给予真诚的感谢。