Transformer学习笔记

2021/12/14 6:21:35

本文主要是介绍Transformer学习笔记,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

        Transformer 这一概念是在论文Attention is All You Need 中提出,感兴趣的可以通过链接阅读原文。这篇文章主要讲讲我对Transformer这个模型学习的理解。

什么是Transformer?

        Transformer可以理解为一个黑盒,我们将一段序列输入模型,经过Transformer之后,输出另一段序列,这个序列可以是翻译,可以是摘要。而Transformer最重要的创新在于使用注意力机制,注意力机制能够关注输入序列中感兴趣的部分,并分配一个较大的概率给该部分。这部分是怎么完成的呢?其实就是由多个encoder-decoder对输入的序列进行编解码,我们可以看一下简化的encoder-decoder长什么样。

图1.encoder-decoder组件

         encoders部分其实是由6个encode组成(原论文中设置为6),这个设置其实可以有别的尝试,现在应该是有了,因为论文阅读的还没那么快,最新的研究还需要花点时间,后续慢慢补上。当然decoders的数量设置和encoders相同。对encoder-decoder组件展开后我们可以看到下图。

图2.encoder-decoder组件展开图 

        encoder在结构上都是相同的,但它们并不共享权重。每一层又分为两个子层:

图3.transformer的encoder

         encoder的输入首先流经self-attention——该层帮助encoder在编码特定单词时可查看输入句子中的其他单词,也就是上面提到的分配概率。self-attention在稍后的部分进行讲解,这里先把当前模块讲清楚

        self-attention层的输出送到前馈神经网络。decoder同样具有这两层,但它们之间是还多了一个注意力层,帮助decoder专注于输入句子的相关部分,这里考虑了输入的时序。

 图4.transformer的decoder

Transformer是如何对输入进行编码的?

        现在让我们开始看看各种向量/张量,它们是如何在这些组件之间流动,将训练模型的输入转换为输出。在NLP的应用场景中,输入是一个句子或单词,我们首先使用embedding算法将每个输入词转换为一个向量。每个单词都嵌入到一个大小为高维的向量中(通常长度为512),这个向量的大小是我们可以设置的超参数,基本上它是我们训练数据集中最长句子的长度。

        在这里我用简单的向量进行表示,如下图。

  图5.embedding的向量

        这里要注意一下,embedding仅仅发生在最开始输入的地方,也就是第一个encoder之前,此时会将位置编码一同嵌入,这个后面说。在其他encoder中,它的输入将是位于其下方的encoder的输出。

        输入经过embedding的单词后,它们中的每一个元素都会流经encoder的两层中的每一层。

 图6.向量的输入

        图6中,可以看到,每个向量输入encoder时都有其各自的路径,在self-attention 层中这些路径之间存在依赖关系。而前馈神经网络层中并没有这种位置关系。

向量进入encoder之后是如何进行处理的?

        encoder接收向量作为输入后,它通过将这些向量传递到一个self-attention层,然后传递到一个前馈神经网络,然后将输出向上发送到下一个encoder来处理这个向量。

图7.向量的处理路径

        每个位置的单词都经过一个自注意力过程。然后,它们每个都通过一个前馈神经网络(完全相同)。 接下来看看它实际是怎么实现的。

        计算self-attention的第一步是从encoder的每个输入向量(这里的两个单词)创建三个可学习的向量。因此,对于每个单词,我们创建一个 Query 向量、一个 Key 向量和一个 Value 向量。这些向量是通过可训练的三个矩阵来创建的。

图8.向量的创建过程

         将编码后的x1 乘以 WQ 权重矩阵会得到q1,即与该词关联的Query向量。通过将x1与剩下的WK,WV矩阵相乘,这样就为输入的句子中的每个单词创建了一个Query、一个Key 和一个Value 的映射。注意力的概率就是由以上三个向量得来。

注意力是怎么计算的?

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

        这个分数的计算方法是将Query向量与我们正在评分的各个单词的Key向量进行点积(dot product),所以,对于图8,我们正在处理位置 #1 中单词(Thinking)的自注意力,第一个分数将是 q1 和 k1 的点积,第二个分数是 q1 和 k2 的点积。

       图9

        接下来将得到的分数除以8, 为什么是8呢?论文中使用的key向量维度的平方根 (64),这能够产生更具稳定性的梯度,(这里可能还有其他可能的值)。

        然后通过 softmax 层操作得到结果, Softmax 将分数进行归一化,因此它们都是正数并且加之后的和为1。经过softmax之后的这个值就是为这个单词分配的注意力,Thinkin这个词具有最高的 softmax 分数,但是,有时关注与当前词相关的另一个词是有用的。

        接下来就是将softmax得到的值与每个value向量相乘,这里的操作就是保持我们感兴趣的这个词的关注度不变,并降低其他词的关注度。之后对加权后的value向量求和,这里(对于第一个词thinking)会产生self-attention层的输出。

图10

        关于self-attention的计算到这里就结束了,最后将加权后的value(z)向量输入到前馈神经网络中。为了加速计算,上述的过程都是以矩阵进行的,接下来看看他是怎么操作的。

图11.self-attendtion的矩阵计算 

         首先计算Query, Key, 和Value矩阵,将embedding之后的向量,转换到X 中,并将其乘以可训练的权重矩阵(WQ、WK、WV)。X 矩阵中的每一行对应于输入句子中的一个词,实际的长度为512。将图11的过程描述为公式后,可以看下图。

图12

         实际上,论文中的self-attention应用了多头注意力机制,接下来来聊聊什么是多头注意力机制(multi-headed attention)。

什么是多头注意力机制?

多头注意力机制从两个方面提升了模型表现:

1、扩展了模型专注于不同位置的能力

2、它为注意力层提供了多个“representation subspaces”。

        对于多头注意力,模型可以产生有多组Query, Key, 和Value权重矩阵(Transformer使用八个注意力头,所以我们最终为每个encoder-decoder提供了八组)。每一个WQ、WK、WV都是随机初始化的。每个矩阵用于将输入(或来自较前一层encoder-decoder的输出向量)投影到不同的表示子空间中。

图13 

        通过multi-headed attention,我们为每个头维护单独的 Q/K/V 权重矩阵,从而产生不同的 Q/K/V 矩阵。正如图12中的那样,我们将 X 乘以 WQ/WK/WV 矩阵以生成 Q/K/V 矩阵。最后输出的z矩阵使用不同的权重矩阵进行 8 次不同的计算。

 图14

        那么这里还有一个问题,前馈神经网络的权重不需要八个矩阵,它只需要一个矩阵(每个单词一个向量),所以就需要一种方法将这八个压缩成一个矩阵。那么怎么做呢?只需要将矩阵连接起来,然后通过一个额外的权重矩阵 WO 将它们相乘。

 图15

        以上差不多就是多头自注意力的全部内容。让我尝试将它们全部放在一个图中:

图16 多头注意力机制 

        值得注意的是,只有在z0的位置上需要进行embedding操作,其他位置的输入不需要进行embedding。

什么是位置编码?

        模型中缺少了一个非常重要的信息,即单词顺序的表示方法。transformer 为每个输入的embedding添加了位置向量。这些向量遵循模型学习的特定模式,这有助于确定每个单词的位置,或序列中不同单词之间的距离。一旦它们被投影到 Q/K/V 向量中,并且在点积注意力的过程中,这些值添加到embedding向量中,就会在embedding向量之间提供有意义的距离信息。

 图17

        为了让模型了解单词的顺序,在这里添加了位置编码向量,他的设置遵行特定的模式:

         这一模式看起来像什么呢?我们可以用下图进行表示:

图18 

        图18是一个20个词的位置编码向量,其中的每行对应一个向量的位置编码,因此,第一行就是我们添加到输入序列中第一个词的位置编码向量。每行包含 512 个值,每个值都在 1 到 -1 之间。我们可以清楚的看到图18中中间部分的分割,这是因为左半部分的值由一个函数(正弦)生成,右半部分由另一个函数(余弦)生成,即上述公式。然后将它们进行concatenated操作形成每个位置编码向量。

残差组件

        每个encoder中的每个子层(self-attention,ffnn)都有一个残差连接,再进行LN( layer-normalization)。

图19 

        如果我们要可视化与自注意力相关的向量和层操作,它看起来会是这样:

图20 

        当然这也适用于decoder,假设考虑一个由 2 个encoder-decoder组成的 Transformer,那么它会是这样的:

图21 

        到这里encoder的部分差不多都讲完了。接下来是decoder的部分。

什么是decoder?

        encoder首先处理输入序列,然后将最后一层encoder的输出转换为一组注意力向量 K 和 V,他们将输入decoder中的encoder-decoder attention层。解码阶段的每一步都从输出序列中输出一个元素。下图可以直观的展示:

       图22

        之后的步骤重复该过程,直到达到一个特殊符号(结束符),表明transformer的decoder已完成其输出。就像在encoder中对输入所做的一样,我们将位置编码embedding并添加到这些decoder的输入中,表示每个单词的位置。如下图所示:

 图23

decoder中的self attention层与encoder中的self attention层的计算方式略有不同:

1、在decoder中,self attention层只允许关注输出序列中较早的位置。通过在 self-attention 计算中的 softmax 步骤之前屏蔽未来位置(将它们设置为 -inf),由mask multi-headed attention来完成的。

2、Encoder-Decoder Attention层就像multi-headed attention层一样,除了它从它下面的层创建它的查询矩阵,并从encoder的输出中获取key和value矩阵。

图24 

        最后的输出层就是一个普通的Linear layer加一个softmax,得到每个词的置信度。到这里transformer的算法流程就讲完了。



这篇关于Transformer学习笔记的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程