论文地址:https://arxiv.org/pdf/1706.03762.pdf

声明

本文完全照搬自一位博主 Jay Alammar 的讲解,仅学习和后期拓展使用,若有侵权,本人将立马删除,在此表达忠诚的感谢,能把复杂的东西讲的井井有条 原文地址:The Illustrated Transformer
并在后面罗列了更多其他博主有趣生动的讲解,想要了解 Transformer 的更多细节,强烈推荐看一下!

了解了 Transformer 不看下 BERT,你问问自己怎么回事? 深度学习(二)BERT模型及其一系列衍生模型

从宏观的视角开始

首先将这个模型看成是一个黑箱操作。在机器翻译中,就是输入一种语言,输出另一种语言。

那么拆开这个黑箱,我们可以看到它是由编码组件、解码组件和它们之间的连接组成。

编码组件部分由一堆编码器(encoder)构成(论文中是将6个编码器叠在一起 —— 数字6没有什么神奇之处,你也可以尝试其他数字)。解码组件部分也是由相同数量(与编码器对应)的解码器(decoder)组成的。

所有的编码器在结构上都是相同的,但它们没有共享参数。每个解码器都可以分解成两个子层。

从编码器输入的句子首先会经过一个自注意力(self-attention)层,这层帮助编码器在对每个单词编码时关注输入句子的其他单词。我们将在稍后的文章中更深入地研究自注意力。

自注意力层的输出会传递到前馈(feed-forward)神经网络中。每个位置的单词对应的前馈神经网络都完全一样(译注:另一种解读就是一层窗口为一个单词的一维卷积神经网络)。

解码器中也有编码器的自注意力(self-attention)层和前馈(feed-forward)层。除此之外,这两个层之间还有一个注意力层,用来关注输入句子的相关部分(和 seq2seq 模型 的注意力作用相似)。

将张量引入图景

我们已经了解了模型的主要部分,接下来我们看一下各种向量或张量(译注:张量概念是矢量概念的推广,可以简单理解矢量是一阶张量、矩阵是二阶张量。)是怎样在模型的不同部分中,将输入转化为输出的。

像大部分NLP应用一样,我们首先将每个输入单词通过词嵌入算法转换为词向量。

图片英文翻译:每个单词都被嵌入为 512 维的向量,我们用这些简单的方框来表示这些向量。

词嵌入过程只发生在最底层的编码器中。所有的编码器都有一个相同的特点,即它们接收一个向量列表,列表中的每个向量大小为 512 维。在底层(最开始)编码器中它就是词向量,但是在其他编码器中,它就是下一层编码器的输出(也是一个向量列表)。向量列表大小是我们可以设置的超参数,一般是我们训练集中最长句子的长度。

将输入序列进行词嵌入之后,每个单词都会流经编码器中的两个子层。

接下来我们看看Transformer的一个核心特性,在这里输入序列中每个位置的单词都有自己独特的路径流入编码器。在自注意力层中,这些路径之间存在依赖关系。而前馈(feed-forward)层没有这些依赖关系。因此在前馈(feed-forward)层时可以并行执行各种路径。

然后我们将以一个更短的句子为例,看看编码器的每个子层中发生了什么。

现在我们开始“编码”

如上述已经提到的,一个编码器接收向量列表作为输入,接着将向量列表中的向量传递到自注意力层进行处理,然后传递到前馈神经网络层中,将输出结果传递到下一个编码器中。

输入序列的每个单词都经过自编码过程。然后,他们各自通过前向传播神经网络——完全相同的网络,而每个向量都分别通过它。

从宏观视角看自注意力机制

不要被我用自注意力这个词弄迷糊了,好像每个人都应该熟悉这个概念。其实我之也没有见过这个概念,直到读到《Attention is All You Need》这篇论文时才恍然大悟。让我们精炼一下它的工作原理。

例如,下列句子是我们想要翻译的输入句子:

”The animal didn’t cross the street because it was too tired”

这个 “it” 在这个句子是指什么呢?它指的是 street 还是这个 animal 呢?这对于人来说是一个简单的问题,但是对于算法则不是。

当模型处理这个单词 “it” 的时候,自注意力机制会允许 “it” 与 “animal” 建立联系。

随着模型处理输入序列的每个单词,自注意力会关注整个输入序列的所有单词,帮助模型对本单词更好地进行编码。

如果你熟悉 RNN(循环神经网络),回忆一下它是如何维持隐藏层的。RNN 会将它已经处理过的前面的所有单词/向量的表示与它正在处理的当前单词/向量结合起来。而自注意力机制会将所有相关单词的理解融入到我们正在处理的单词中。

图片英文翻译:当我们在编码器 #5(栈中最上层编码器)中编码 “it” 这个单词的时,注意力机制的部分会去关注 “The Animal”,将它的表示的一部分编入“it” 的编码中。

从微观视角看自注意力机制

首先我们了解一下如何使用向量来计算自注意力,然后来看它实怎样用矩阵来实现。

计算自注意力的第一步就是从每个编码器的输入向量(每个单词的词向量)中生成三个向量。也就是说对于每个单词,我们创造一个查询向量(Queries)、一个键向量(Keys)和一个值向量(Values)。这三个向量是通过词嵌入与三个权重矩阵相乘后创建的。

可以发现这些新向量在维度上比词嵌入向量更低。他们的维度是 64,而词嵌入和编码器的输入/输出向量的维度是 512. 但实际上不强求维度更小,这只是一种基于架构上的选择,它可以使多头注意力(multiheaded attention)的大部分计算保持不变。

图片英文翻译: X1 与 WQ 权重矩阵相乘得到 q1, 就是与这个单词相关的查询向量。最终使得输入序列的每个单词的创建一个查询向量(Queries)、一个键向量(Keys)和一个值向量(Values)。

什么是查询向量、键向量和值向量向量?

它们都是有助于计算和理解注意力机制的抽象概念。请继续阅读下文的内容,你就会知道每个向量在计算注意力机制中到底扮演什么样的角色。

计算自注意力的第二步是计算得分。假设我们在为这个例子中的第一个词 “Thinking” 计算自注意力向量,我们需要拿输入句子中的每个单词对 “Thinking” 打分。这些分数决定了在编码单词 “Thinking” 的过程中有多重视句子的其它部分。

这些分数是通过打分单词(所有输入句子的单词)的键向量与 “Thinking” 的查询向量相点积来计算的。所以如果我们是处理位置最靠前的词的自注意力的话,第一个分数是 q1 和 k1 的点积,第二个分数是 q1 和 k2 的点积。(这里我们就假设只有两个单词的句子)

第三步和第四步是将分数除以 8 (8 是论文中使用的键向量的维数 64 的平方根,这会让梯度更稳定。这里也可以使用其它值,8 只是默认值),然后通过 softmax 传递结果。softmax 的作用是使所有单词的分数归一化,得到的分数都是正值且和为1。


这个 softmax 分数决定了每个单词对编码当下位置(“Thinking”)的贡献。显然,已经在这个位置上的单词将获得最高的 softmax 分数,但有时关注另一个与当前单词相关的单词也会有帮助。

第五步是将每个值向量乘以 softmax 分数 (这是为了准备之后将它们求和)。这里的直觉是希望关注语义上相关的单词,并弱化不相关的单词(例如,让它们乘以 0.001 这样的小数)。

第六步是对加权值向量求和(译注:自注意力的另一种解释就是在编码某个单词时,就是将所有单词的表示(值向量)进行加权求和,而权重是通过该词的表示(键向量)与被编码词表示(查询向量)的点积并通过 softmax 得到。),然后即得到自注意力层在该位置的输出(在我们的例子中是对于第一个单词)。

这样自自注意力的计算就完成了。得到的向量就可以传给前馈神经网络。然而实际中,这些计算是以矩阵形式完成的,以便算得更快。那我们接下来就看看如何用矩阵实现的。

通过矩阵运算实现自注意力机制

第一步是计算查询矩阵、键矩阵和值矩阵。为此,我们将将输入句子的词嵌入装进矩阵X中,将其乘以我们训练的权重矩阵(WQ,WK,WV)。

图片英文翻译:x 矩阵中的每一行对应于输入句子中的一个单词。我们再次看到词嵌入向量 ( 512,或图中的 4 个格子)和 q/k/v 向量( 64,或图中的 3 个格子)的大小差异。

最后,由于我们处理的是矩阵,我们可以将步骤2到步骤6合并为一个公式来计算自注意力层的输出。

图片英文翻译:自注意力机制在矩阵中的计算形式

大战多头怪

通过增加一种叫做 “多头” 注意力(“multi-headed” attention)的机制,论文进一步完善了自注意力层,并在两方面提高了注意力层的性能:

1.它扩展了模型专注于不同位置的能力。 在上面的例子中,虽然每个编码都在 z1 中有或多或少的体现,但是它可能被实际的单词本身所支配。如果我们翻译一个句子,比如“The animal didn’t cross the street because it was too tired”,我们会想知道 “it” 指的是哪个词,这时模型的 “多头” 注意机制会起到作用。

2.它给出了注意力层的多个“表示子空间”(representation subspaces)。接下来我们将看到,对于“多头”注意机制,我们有多个查询/键/值权重矩阵集(Transformer使用八个注意力头,因此我们对于每个编码器/解码器有八个矩阵集合)。这些集合中的每一个都是随机初始化的,在训练之后,每个集合都被用来将输入词嵌入(或来自较低编码器/解码器的向量)投影到不同的表示子空间中。

图片英文翻译:在“多头”注意机制下,我们为每个头保持独立的查询/键/值权重矩阵,从而产生不同的查询/键/值矩阵。和之前一样,我们拿 X 乘以WQ/WK/WV矩阵来产生查询/键/值矩阵。

如果我们做与上述相同的自注意力计算,只需八次不同的权重矩阵运算,我们就会得到八个不同的Z矩阵。


这给我们带来了一点挑战。前馈层不需要8个矩阵,它只需要一个矩阵(由每一个单词的表示向量组成)。所以我们需要一种方法把这八个矩阵压缩成一个矩阵。那该怎么做?其实可以直接把这些矩阵拼接在一起,然后用一个附加的权重矩阵 WO 与它们相乘。

这几乎就是多头自注意力的全部。这确实有好多矩阵,我们试着把它们集中在一个图片中,这样可以一眼看清。

既然我们已经摸到了注意力机制的这么多“头”,那么让我们重温之前的例子,看看我们在例句中编码 “it” 一词时,不同的注意力 “头” 集中在哪里:

图片英文翻译:当我们编码 “it” 一词时,一个注意力头集中在 “animal” 上,而另一个则集中在 “tired” 上,从某种意义上说,模型对 “it” 一词的表达在某种程度上是 “animal” 和 “tired” 的代表。

然而,如果我们把所有的 attention 都加到图示里,事情就更难解释了:

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

到目前为止,我们对模型的描述缺少了一种理解输入单词顺序的方法。

为了解决这个问题,Transformer为每个输入的词嵌入添加了一个向量。这些向量遵循模型学习到的特定模式,这有助于确定每个单词的位置,或序列中不同单词之间的距离。这里的直觉是,将位置向量添加到词嵌入中使得它们在接下来的运算中,能够更好地表达的词与词之间的距离。

图片英文翻译:为了让模型得到有意义的句子,通过添加位置编码向量 -- 值遵循特定的模式

如果我们假设词嵌入的维数为4,则实际的位置编码如下

图片英文翻译:尺寸为4的迷你词嵌入位置编码实例

这个模式会是什么样子?

在下图中,每一行对应一个词向量的位置编码,所以第一行对应着输入序列的第一个词。每行包含512个值,每个值介于 1 和 -1 之间。我们已经对它们进行了颜色编码,所以图案是可见的。

图片英文翻译:20字(行)的位置编码实例,词嵌入大小为512(列)。你可以看到它从中间分裂成两半。这是因为左半部分的值由一个函数(使用正弦)生成,而右半部分由另一个函数(使用余弦)生成。然后将它们拼在一起而得到每一个位置编码向量。

原始论文里描述了位置编码的公式(第3.5节)。你可以在 get_timing_signal_1d()中看到生成位置编码的代码。这不是唯一可能的位置编码方法。然而,它的优点是能够扩展到未知的序列长度(例如,当我们训练出的模型需要翻译远比训练集里的句子更长的句子时)。

2020年7月更新:上面显示的位置编码来自 Transformer 的 transformer2transformer 实现。文中给出的方法略有不同,它不是直接将两个信号串联起来,而是将两个信号交织起来。下图显示了它的样子。

残差模块

在继续进行下去之前,我们需要提到一个编码器架构中的细节:在每个编码器中的每个子层(自注意力、前馈网络)的周围都有一个残差连接,并且都跟随着一个 “Layer-Normalization” 步骤。

如果我们去可视化这些向量以及这个和自注意力相关联的 Layer-Normalization 操作,那么看起来就像下面这张图描述一样:


解码器的子层也是这样样的。如果我们想象一个2 层编码-解码结构的 transformer,它看起来会像下面这张图一样:

解码组件

既然我们已经谈到了大部分编码器的概念,那么我们基本上也就知道解码器是如何工作的了。但最好还是看看解码器的细节。

编码器通过处理输入序列开启工作。顶端编码器的输出之后会变转化为一个包含向量 K(键向量)和 V(值向量)的注意力向量集 。这些向量将被每个解码器用于自身的“编码-解码注意力层”,而这些层可以帮助解码器关注输入序列哪些位置合适:

图片英文翻译:在完成编码阶段后,则开始解码阶段。解码阶段的每个步骤都会输出一个输出序列(在这个例子里,是英语翻译的句子)的元素

接下来的步骤重复了这个过程,直到到达一个特殊的终止符号,它表示 transforme r的解码器已经完成了它的输出。每个步骤的输出在下一个时间步被提供给底端解码器,并且就像编码器之前做的那样,这些解码器会输出它们的解码结果 。另外,就像我们对编码器的输入所做的那样,我们会嵌入并添加位置编码给那些解码器,来表示每个单词的位置。

而那些解码器中的自注意力层表现的模式与编码器不同:在解码器中,自注意力层只被允许处理输出序列中更靠前的那些位置。在 softmax 步骤前,它会把后面的位置给隐去(把它们设为-inf)。

这个“编码-解码注意力层”工作方式基本就像多头自注意力层一样,只不过它是通过在它下面的层来创造查询矩阵,并且从编码器的输出中取得键/值矩阵。

最终的线性变换和Softmax层

解码组件最后会输出一个实数向量。我们如何把浮点数变成一个单词?这便是线性变换层要做的工作,它之后就是Softmax 层。

线性变换层是一个简单的全连接神经网络,它可以把解码组件产生的向量投射到一个比它大得多的、被称作对数几率(logits)的向量里。

不妨假设我们的模型从训练集中学习一万个不同的英语单词(我们模型的“输出词表”)。因此对数几率向量为一万个单元格长度的向量——每个单元格对应某一个单词的分数。

接下来的Softmax 层便会把那些分数变成概率(都为正数、上限 1.0)。概率最高的单元格被选中,并且它对应的单词被作为这个时间步的输出。

图片英文翻译:这张图片从底部以解码器组件产生的输出向量开始。之后它会转化出一个输出单词。

训练部分总结

既然我们已经过了一遍完整的 transformer 的前向传播过程,那我们就可以直观感受一下它的训练过程。

在训练过程中,一个未经训练的模型会通过一个完全一样的前向传播。但因为我们用有标记的训练集来训练它,所以我们可以用它的输出去与真实的输出做比较。

为了把这个流程可视化,不妨假设我们的输出词汇仅仅包含六个单词:“a”, “am”, “I”, “thanks”, “student”以及 “< eos >”(end of sentence的缩写形式)。

图片英文翻译:我们模型的输出词表在我们训练之前的预处理流程中就被设定好。

一旦我们定义了我们的输出词表,我们可以使用一个相同宽度的向量来表示我们词汇表中的每一个单词。这也被认为是一个 one-hot 编码。所以,我们可以用下面这个向量来表示单词“am”:

图片英文翻译:例子:对我们输出词表的one-hot 编码

接下来我们讨论模型的损失函数——这是我们用来在训练过程中优化的标准。通过它可以训练得到一个结果尽量准确的模型。

损失函数

比如说我们正在训练模型,现在是第一步,一个简单的例子——把 “merci” 翻译为 “thanks”。

这意味着我们想要一个表示单词“thanks”概率分布的输出。但是因为这个模型还没被训练好,所以不太可能现在就出现这个结果。

图片英文翻译:因为模型的参数(权重)都被随机的生成,(未经训练的)模型产生的概率分布在每个单元格/单词里都赋予了随机的数值。我们可以用真实的输出来比较它,然后用反向传播算法来略微调整所有模型的权重,生成更接近结果的输出。

你会如何比较两个概率分布呢?我们可以简单地用其中一个减去另一个。更多细节请参考交叉熵和KL散度。

但注意到这是一个过于简化的例子。更现实的情况是处理一个句子。例如,输入“je suis étudiant”并期望输出是“I am a student”。那我们就希望我们的模型能够成功地在这些情况下输出概率分布:

每个概率分布被一个以词表大小(我们的例子里是6,但现实情况通常是 3000 或 10000)为宽度的向量所代表。

第一个概率分布在与“I”关联的单元格有最高的概率

第二个概率分布在与“am”关联的单元格有最高的概率

以此类推,第五个输出的分布表示“”关联的单元格有最高的概率

图片英文翻译:依据例子训练模型得到的目标概率分布

在一个足够大的数据集上充分训练后,我们希望模型输出的概率分布看起来像这个样子:

图片英文翻译:我们期望训练过后,模型会输出正确的翻译。当然如果这段话完全来自训练集,它并不是一个很好的评估指标(参考:交叉验证)。注意到每个位置(词)都得到了一点概率,即使它不太可能成为那个时间步的输出——这是softmax的一个很有用的性质,它可以帮助模型训练。

因为这个模型一次只产生一个输出,不妨假设这个模型只选择概率最高的单词,并把剩下的词抛弃。这是其中一种方法(叫贪心解码)。另一个完成这个任务的方法是留住概率最靠高的两个单词(例如I和a),那么在下一步里,跑模型两次:其中一次假设第一个位置输出是单词“I”,而另一次假设第一个位置输出是单词“me”,并且无论哪个版本产生更少的误差,都保留概率最高的两个翻译结果。然后我们为第二和第三个位置重复这一步骤。这个方法被称作集束搜索(beam search)。在我们的例子中,集束宽度是 2(因为保留了 2 个集束的结果,如第一和第二个位置),并且最终也返回两个集束的结果(top_beams也是 2)。这些都是可以提前设定的参数。

总结

自注意力机制的计算过程

1.将输入单词转化成嵌入向量;

2.根据嵌入向量得到 q,k,v 三个向量;

3.为每个向量计算一个 score:score = q . k ;

4.为了梯度的稳定,Transformer 使用了 score 归一化,即除以 dk\sqrt {dk}dk​;

5.对 score 施以 softmax 激活函数;

6.softmax 点乘 Value 值 v,得到加权的每个输入向量的评分 v;

7.相加之后得到最终的输出结果z

Reference

1.Visualizing A Neural Machine Translation Model (Mechanics of Seq2seq Models With Attention)

2.The Illustrated Transformer 强推 ☆☆☆

3.This post is all you need(上卷)——层层剥开Transformer 强推 ☆☆☆

4.第四周(2)自注意力机制(Self-Attention)

5.Transformer 代码讲解 强推 ☆☆☆

6. 各注意力机制详解

深度学习(一)最通俗易懂的 Transformer相关推荐

  1. 深入理解深度学习——GPT(Generative Pre-Trained Transformer):GPT-3与Few-shot Learning

    分类目录:<深入理解深度学习>总目录 相关文章: · GPT(Generative Pre-Trained Transformer):基础知识 · GPT(Generative Pre-T ...

  2. 【深度学习】这千层transformer让我目瞪口呆

    作者:十方 见过几层的mlp,也见过几十层的GoogleNet,还见过上百层的Resnet,上千层的transformer见过没!这篇论文<DeepNet: Scaling Transforme ...

  3. 【深度学习】去掉softmax后Transformer会更好吗?复旦华为诺亚提出SOFT:轻松搞定线性近似...

    作者丨happy  编辑丨极市平台 导读 本文介绍了复旦大学&华为诺亚提出的一种新颖的softmax-free的Transformer-SOFT.所提SOFT显著改善了现有ViT方案的计算效率 ...

  4. 【深度学习】搞懂 Vision Transformer 原理和代码,看这篇技术综述就够了

    作者丨科技猛兽 编辑丨极市平台 导读 本文对Vision Transformer的原理和代码进行了非常全面详细的解读,一切从Self-attention开始.Transformer的实现和代码以及Tr ...

  5. 【深度学习】超越ConvNeXt!Transformer 风格的卷积网络视觉基线模型Conv2Former

    作者丨科技猛兽 编辑丨极市平台 导读 本文提出一种卷积调制模块,利用卷积来建立关系,这比注意力机制在处理高分辨率图像时更高效,称为 Conv2Former.作者在 ImageNet 分类.目标检测和语 ...

  6. 序列处理的深度学习框架——从RNN到Transformer

    目录 Chapter9 序列处理的深度学习框架 9.1 回顾语言模型 (Language Models Revisited) 9.2 循环神经网络 (Recurrent Neural Networks ...

  7. 深入理解深度学习——GPT(Generative Pre-Trained Transformer):基础知识

    分类目录:<深入理解深度学习>总目录 相关文章: · GPT(Generative Pre-Trained Transformer):基础知识 · GPT(Generative Pre-T ...

  8. 【深度学习】特征提取器:Transformer,Rnn,CNN之间的相互比较

    在辞旧迎新的时刻,大家都在忙着回顾过去一年的成绩(或者在灶台前含泪数锅),并对2019做着规划,当然也有不少朋友执行力和工作效率比较高,直接把2018年初制定的计划拷贝一下,就能在3秒钟内完成2019 ...

  9. 深入理解深度学习——GPT(Generative Pre-Trained Transformer):GPT-2与Zero-shot Learning

    分类目录:<深入理解深度学习>总目录 相关文章: · GPT(Generative Pre-Trained Transformer):基础知识 · GPT(Generative Pre-T ...

  10. [深度学习论文笔记]A Volumetric Transformer for Accurate 3D Tumor Segmentation

    A Volumetric Transformer for Accurate 3D Tumor Segmentation 用于精确三维肿瘤分割的体积Transformer Published: 2021 ...

最新文章

  1. Lampiao靶机渗透测试
  2. Python3中对Dict的内存优化
  3. 零基础可以学好UI设计吗
  4. linux c printf 不能输出
  5. redis 安装-配置
  6. bash漏洞修补, CVE-2014-6271
  7. superobject内存泄漏
  8. 前端学习(3302):类组件父组件和子组件createRef
  9. Oracle的直方图试验
  10. jenkins + maven + nexus + [ svn 或 GitLab 或 GitHub ]
  11. 小语种翻译软件,谷歌api接口批量翻译软件
  12. ibm 的java实现_IBM Java Toolbox for 实现IBM i 消息通信
  13. 为什么宿醉那么缺水_坚决应对云的宿醉
  14. 在线购物系统分析类图
  15. 改造Android手机为,便携式linux服务器,跑tomcat
  16. 三角测量(triangulation)
  17. 区块链技术开发构建大数据征信闭环
  18. 数据治理:企业数据治理蓝图
  19. 微信小程序云开发获取用户openid
  20. Python实现“淘宝自动发货机器人”

热门文章

  1. 通过jad/mc/redefine命令,在docker容器中实现动态更新代码的功能:
  2. Hyperledger Fabric基础介绍
  3. wincc中c语言都是英文版,WINCC画面的中英文语言切换
  4. 常用电子元器件应用要点及识别方法,知识点很专业!
  5. 开源项目——小Q聊天机器人V1.2
  6. hive大数据量实现分页查询,java结合mybatis及多数据源(hive、MySQL)
  7. PPT文件不能编辑如何解决?
  8. 无法获取链接服务器 (null) 的 OLE DB 访问接口 SQLNCLI10 的架构行集
  9. vue2编译报错Class constructor VuexModule cannot be invoked without ‘new‘
  10. 【Pygame小游戏】这款“打地鼠”小游戏要火了(来来来)