背景

在深度学习领域,如果你连Transformer都不知,那就太out了。现如今基于Transformer的模型,如Bert在NLP的下游的很多任务中都达到了sota。而这个Transformer就出自于这篇论文。虽然是2017年发表的,但是已经被称作是非常经典的论文了。下面主要是捡一些主要的来看吧。原文地址:Attention Is All You Need。
当然本文也参考了这些文章:6 - Attention is All You Need,The Illustrated Transformer,没有这些文章我理解的也没有那么快。

介绍

之前的文章中已经介绍了attention,注意力帮助提高神经机器翻译等应用到性能。但传统的基于RNN和attention的神经网络,速度还是慢了点。谷歌根据这个现状提出了Transformer–一种利用注意力提高训练速度的模型,这里的attention就是Self-Attention结构,Transformer一个巨大的优点是:模型在处理序列输入时,可以对整个序列输入进行并行计算,不需要按照时间步循环递归处理输入序列。实验结果,不仅提高了训练速度,效果还也得到了比较大的提升。现在就来看看吧。

1. 基本结构

原文中的模型的结构如下图:

不过我们可以先进行抽象,慢慢细化来看。

我们首先将模型看作一个单独的黑盒子。在机器翻译应用程序中,它将采用一种语言的句子,并输出另一种语言的翻译。


我们再看看下面这种进一步细化的结构,其中包括编码器部分和解码器部分来联结他们。

编码组件是一堆编码器(图中六个堆叠在彼此的顶部–数字六没有什么神奇之处,人们肯定可以尝试其他排列方式)。解码组件是相同数量的解码器的堆砌。

下面再进一步的细化,编码器在结构上都是相同的(但它们不共享权重)。每一层分为两个子层:

编码器的输入首先通过一个self-attention层——一个帮助编码器在编码特定单词时查看输入句子中其他单词的层。

self-attention层的输出反馈给前馈神经网络(Feed Forward Neural Network, FFNN)。需要注意编码器的输入:假设一个序列用 X = w 1 , w 2 , ⋯ , w n X=w_1,w_2,\cdots,w_n X=w1,w2,,wn表示,第一个encoder输入,则是其对应的embedding之后的结果,有: X = x 1 , x 2 ⋯ , x n X=x_1,x_2\cdots, x_n X=x1,x2,xn,其中 x i ∈ R d x_i\in \mathbb{R}^d xiRd,即每个词用d维的向量表示,经过第一个encoder之后的输出则是:经过self-attention和FFNN之后的向量,其维度与 x i x_i xi相同。

解码器也有编码器的两个层,但在这两层之间有一个注意层,帮助解码器关注输入句子的相关部分。

2 张量在模型结构中的使用

现在我们已经看到了模型的主要部分,让我们开始看看各种向量/张量,以及它们如何在这些组件之间流动,从而将经过训练的模型的输入转化为输出。

与NLP应用程序的一般情况一样,我们首先使用词嵌入算法将每个输入字转换为向量。假如将每个词转成词向量的维度为4维,实际的代码维度通常是256或者512等。假设以下图表示:

在输入序列中嵌入单词后,每个单词都会流经编码器的两层。如下:

在这里,我们开始看到Transformer的一个关键属性,即每个位置的字在编码器中通过自己的路径流动。在self-attention层中,这些路径之间存在依赖关系。然而,前馈层没有这些依赖关系,因此在流经前馈层时,可以并行执行各种路径。

接下来,我们将把示例切换到一个较短的句子,并查看编码器的每个子层中发生了什么。

正如已经提到的,编码器接收向量列表作为输入。

它通过将这些向量传递到“self-attention”层,然后传入前馈神经网络,然后将输出向上发送到下一个编码器来处理。

3 详细了解self-attention

让我们首先看看如何使用向量计算self-attention,然后继续看它是如何实际实现的——使用矩阵,深刻理解数据的流向。

计算self-attention的第一步是从编码器的每个输入向量中创建三个向量(在本例中,输入是每个单词的嵌入)。因此,我们为每个单词创建一个查询向量、一个键向量和一个值向量。这些向量是通过将嵌入乘以我们在训练过程中训练的三个矩阵来创建的。

这些新向量的维数小于嵌入向量。它们的维数为64,而嵌入和编码器输入/输出向量的维数为512,当然图中显示的是4。

那么问题来了,query,key,value向量到底是什么呢,使用的意义是什么?提前透漏:他们是三个参数矩阵,是需要学习的。
假如Thinking、Machines这两个单词经过Embedding后得到向量是 x 1 , x 2 x_1,x_2 x1,x2,那么 q 1 = x 1 W Q , q 2 = x 2 W Q q_1=x_1W^Q,q_2=x_2W^Q q1=x1WQ,q2=x2WQ,同理可得 k 1 , k 2 , v 1 , v 2 k_1,k_2,v_1,v_2 k1,k2,v1,v2,attention的计算逻辑:query和key计算attention得分,然后根据attention得分进行加权求和(在seq2seq中的attention是两个语句之间的,现在是计算当前语句内各词语之间的相似得分,然后再使用softmax,所以就起名叫self-attention吗?)。具体如何计算 self-attention如下:

计算self-attention的第二步是计算分数,当然第一步是获取对应的q,k,v向量值了。假设我们正在计算例子中第一个单词“thinking”的self-attention。我们需要根据这个词对输入句子的每个词进行评分。当我们在某个位置对单词进行编码时,分数决定了对输入句子其他部分的关注程度。

分数是通过将各个单词的query vector和key vector 的点积与来评分的。操作事例如下:如果我们处理位置1的单词的self-attention,第一个分数将是q1和k1的点积。第二个分数就是q1和k2的点积了。

第三步和第四步是将分数除以8(论文中使用的key向量维度的平方根64。这会有更稳定的梯度,当然也可以是其他可能的值,但这是默认值),然后通过softmax操作传递结果。Softmax将分数标准化,使其全部为正值,加起来等于1。


此softmax分数确定每个单词在此位置的表达。很明显,这个位置上的单词将具有最高的softmax分数,但有时关注与当前单词相关的另一个单词会很有用。

第五步是将每个value vector乘以softmax分数(准备将它们相加)。这里的直觉是保持我们想要关注的单词的完整值,并忽略不相关的单词(例如,将它们乘以0.001这样的小数字)。

第六步是对加权值向量求和。这将在该位置(对于第一个单词)生成self-attention层的输出,即 z 1 z_1 z1

self-attention计算到此结束。结果向量是我们可以发送到前馈神经网络的向量。然而,在实际实现中,这种计算是以矩阵形式进行的,以加快处理速度。现在我们来看一下,我们已经看到了单词级计算的直觉。在实际的实现过程中使用矩阵计算更加方便。

4.self-attention的矩阵计算

第一步是计算Query、Key和Value 的矩阵。我们通过将Embedding值打包到矩阵X中,并将其乘以我们训练的权重矩阵( W Q W^Q WQW K W^K WKW V W^V WV)来实现这一点。

最后,由于我们处理的是矩阵,我们可以将第二步到第六步浓缩成一个公式来计算self-attention层的输出。

5 多头注意力机制

论文中通过添加一种称为“多头”注意力机制,进一步细化了self-attention层。这从两个方面提高了注意层的性能:

  1. 它扩展了模型关注不同位置的能力。在上面的例子中, z 1 z_1 z1包含了一些其他的编码信息,但是它仅仅被当前词所支配。如果我们翻译一句话,比如“The animal didn’t cross the street because it was too tired”,我们会想知道“it”指的是哪个词,这时多头注意力机制就会有所帮助。
  2. 它为注意力层提供了多个“表示子空间”。正如我们接下来将要看到的,对于多头注意力,我们不仅有一组,而且有多组Query/Key/Value 权重矩阵(Transformer使用八个注意力,因此每个编码器/解码器有八组)。这些集合中的每一个都是随机初始化的。在训练之后,每一组注意力权重( W Q , W K , W V W^Q,W^K,W^V WQ,WK,WV)将输入词嵌入(或来自较低编码器/解码器的向量)投影到不同的表示子空间。


如果我们做上面所述的同样的self-attention计算,只需使用不同的权重矩阵进行八次不同的计算,我们最终得到八个不同的Z矩阵。

前馈层则不需要八个矩阵——它需要一个矩阵(每个单词对应一个向量)。这时就需要一种方法把这八个元素压缩成一个矩阵。然后将它们乘以一个额外的权重矩阵 W O W^O WO进行一次变换。

这几乎就是多头self-attention的全部内容。我意识到这是相当多的矩阵。让我试着把它们放在一个视觉上,这样我们就可以在一个地方看到它们


既然我们已经谈到了多头注意力,那么让我们回顾一下之前的示例,看看在我们的示例语句中对单词“it”进行编码时,不同的注意力头集中在哪里:

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

到目前为止,模型中缺少的一件事是解释输入序列中单词顺序的方法。

为了解决这个问题,Transformer 在每个输入嵌入中添加一个向量。这些向量遵循模型学习的特定模式,这有助于确定每个单词的位置,或序列中不同单词之间的距离。这里的直觉是,将这些值添加到Embedding中可以在Embedding投影到Q/K/V向量和点积注意力间提供嵌入向量之间有意义的距离。


如果我们假设嵌入的维度为4,那么实际的位置编码将如下所示:

论文中位置编码的设计如下:
P E ( p o s , 2 i ) = sin ⁡ ( p o s / 1000 0 2 i / d m o d e l ) P E ( p o s , 2 i + 1 ) = cos ⁡ ( p o s / 1000 0 2 i / d m o d e l ) \mathbf{PE}(pos, 2i) = \sin (pos/10000^{2i/d_{model}})\\ \mathbf{PE}(pos, 2i + 1) = \cos(pos/10000^{2i/d_{model}}) PE(pos,2i)=sin(pos/100002i/dmodel)PE(pos,2i+1)=cos(pos/100002i/dmodel)
其中pos表词的位置, d m o d e l d_{model} dmodel表示位置编码向量维度, i ∈ [ 0 , d m o d e l ) i\in[0, d_{model}) i[0,dmodel)代表位置 d m o d e l d_{model} dmodel维向量的第 i i i维。

7 残差连接

在继续之前我们需要提到的编码器架构中的一个细节是,每个编码器中的每个子层(self-attention,ffnn)在其周围都有一个残差连接,然后是一个层进行归一化步骤。

如果我们要可视化与 self attention 相关的向量和 layer-norm 操作,它看起来像这样:

这也适用于解码器的子层。如果我们想一个由 2 个堆叠编码器和解码器组成的 Transformer,它看起来像这样:

8 解码器

现在我们已经涵盖了编码器方面的大部分概念,我们基本上知道解码器的组件是如何工作的。让我们来看看它们是如何协同工作的。

编码器首先处理输入序列。然后将顶部编码器的输出转换为一组注意向量 K 和 V。这些将由每个解码器在其“编码器-解码器注意力”层中使用,这有助于解码器专注于输入序列中的适当位置:

以下步骤重复该过程,直到出现一个特殊符号,表明转换器解码器已完成其输出。每一步的输出在下一个时间步被馈送到底部的解码器,解码器就像编码器一样冒泡它们的解码结果。就像我们对编码器输入所做的那样,我们将位置编码嵌入并添加到这些解码器输入中,以指示每个单词的位置。

解码器中的自注意力层的操作方式与编码器中的方式略有不同:

  1. 在解码器里,Self Attention 层只允许关注到输出序列中早于当前位置之前的单词。具体做法是:在 Self Attention 分数经过 Softmax 层之前,屏蔽当前位置之后的那些位置(将attention score设置成-inf)。
  2. 解码器 Attention层是使用前一层的输出来构造Query 矩阵,而Key矩阵和 Value矩阵来自于编码器最终的输出。

9 线性层和softmax

解码器堆栈输出一个浮点向量。我们如何把它变成一个词?这是最后一个线性层的工作,后面是一个 Softmax 层。线性层是一个简单的全连接神经网络,它将解码器堆栈产生的向量投影到一个更大的向量中,称为 logits 向量。

假设我们的模型知道从训练数据集中学习到的 10,000 个独特的英语单词(我们模型的“输出词汇”)。这将使 logits 向量有 10,000 个单元格宽——每个单元格对应一个唯一单词的分数。这就是我们如何解释模型的输出,然后是线性层。然后,softmax 层将这些分数转化为概率(全部为正,全部加起来为 1.0)。选择概率最高的单元格,并生成与其关联的单词作为该时间步的输出。

模型训练

在训练期间,未经训练的模型将通过完全相同的前向传递。但是由于我们是在一个带标签的训练数据集上训练它,我们可以将它的输出与实际正确的输出进行比较。

为了可视化,假设我们的输出词汇表只包含六个单词(“a”、“am”、“i”、“thanks”、“student”和“<eos>”(“end of sentence”的缩写))

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

损失函数选取

假设我们正在训练我们的模型。假设这是我们在训练阶段的第一步,我们正在通过一个简单的例子来训练它——将“merci”翻译成“thanks”

这意味着,我们希望输出是一个概率分布,表示“thanks”这个词。但由于这个模型还没有经过训练,所以这还不太可能发生。

那我们要怎么比较两个概率分布呢?:我们可以简单的用两组概率向量的的空间距离作为loss(向量相减,然后求平方和,再开方),当然也可以使用交叉熵(cross-entropy)]和KL 散度(Kullback–Leibler divergence)。

解码

但请注意,这是一个过于简单的示例。更现实的是,我们会使用一个比一个词长的句子。例如——输入:“je suis étudiant”,预期输出:“i am a student”。这真正意味着,我们希望我们的模型能够连续输出概率分布,其中:

  • 每次输出的概率分布都由一个大小为 vocab_size 的向量表示(在示例中为 6,但更实际的是一个数字,例如 30,000 或 50,000)
  • 第一个概率分布在与单词“i”相关的单元格中具有最高概率
  • 第二个概率分布在与单词“am”相关的单元格中具有最高概率
  • 依此类推,直到第五个输出分布指示“”符号,它也有一个来自 10,000 个元素词汇表的单元格与之关联。

    在足够大的数据集上训练模型足够的时间后,我们希望生成的概率分布如下所示:

现在,因为模型一次产生一个输出,我们可以假设模型正在从该概率分布中选择具有最高概率的单词并丢弃其余的单词。这是一种方法(称为贪婪解码),除此之外还有一种叫做beam search:每个时间步保留k个最高概率的输出词,然后在下一个时间步,根据上一个时间步保留的k个词来确定当前应该保留哪k个词。假设k=2,第一个位置概率最高的两个输出的词是”I“和”a“,这两个词都保留,然后根据第一个词计算第2个位置的词的概率分布,再取出第2个位置上2个概率最高的词。对于第3个位置和第4个位置,我们也重复这个过程。这种方法称为集束搜索(beam search)。

总结

上面的内容就是transformer的核心内容了。前前后后内容也不少,需要我们有一定的基础。 如果不懂,这么好的论文是值得回去多看几遍的。因为其是NLP划时代模型BERT的基础。

【NLP】《Attention Is All You Need》的阅读笔记相关推荐

  1. 《HyVulDect: A hybrid semantic vulnerability mining system based ongraph neural network》阅读笔记

    HyVulDect:一种基于图神经网络的混合语义漏洞挖掘系统 1.相关知识 1.1 污点分析技术 1.1.1 基本原理 定义:污点分析可以抽象为一个 三元组<sources, sinks, sa ...

  2. 论文《Learning both Weights and Connections for Efficient Neural Network》阅读笔记

    因为对深度压缩中的剪枝不太理解遂读了原文作者更早的这篇详细讲网络剪枝的文章点击打开链接 剪枝的过程为: 1.首先剪枝的前提是对已完成训练的网络 2.进行剪枝   要点:根据一个阈值去裁剪参数 a.阈值 ...

  3. 《Improved Crowd Counting Method Based onScale-Adaptive Convolutional Neural Network》论文笔记

    <Improved Crowd Counting Method Based onScale-Adaptive Convolutional Neural Network>论文笔记 论文地址 ...

  4. Developing a new mesh quality evaluation method based on convolutional neural network——方向1笔记

    方向一:网格质量的评估 基于卷积神经网络的网格质量评价方法研究(Developing a new mesh quality evaluation method based on convolution ...

  5. 《Sequence to Sequence Learning with Neural Networks》阅读笔记

    Abstract DNNs 可用于各种复杂的学习任务并取得很好的效果,但是 DNNs 不能用于序列到序列的映射.这篇论文提出了一种端到端(end-to-end)的神经网络模型,来学习这种映射关系.作者 ...

  6. 《Python神经网络编程(Make Your Own Neural Network)》读书笔记

    声明 这是作者在CSDN上的第二篇博客,关于阅读塔里克·拉希德的著作<Python神经网络编程(Make Your Own Neural Network)>之后的读书笔记.跟诸位大牛相比, ...

  7. 《TextBoxes: A Fast Text Detector with a Single Deep Neural Network》论文笔记

    参考博文: 日常阅读论文,这是在谷歌学术上搜索其引用CRNN的相关文献中被引数量比较高的一篇OCR方向的文章,这里拿来读一读. 文章目录 make decision step1:读摘要 step2:读 ...

  8. 《Poluparity Prediction on Social Platforms with Coupled Graph Neural Networks》阅读笔记

    论文地址:Popularity Prediction on Social Platforms with Coupled Graph Neural Networks 文章概览 作者提出了一种耦合图神经网 ...

  9. 6.DeepFM: A Factorization-Machine based Neural Network for CTR Prediction论文详解和代码实现

    一.总述 这篇论文来自哈工大&华为诺亚方舟实验室,主要关注如何学习user behavior背后的组合特征(feature interactions),从而最大化推荐系统的CTR.但目前的方法 ...

最新文章

  1. “智慧城市”方便百姓生活服务企业发展
  2. JQueryEasyUI学习笔记(十四)tree
  3. 2022年全球及中国滑雪铲行业品牌调研与市场消费规模分析报告
  4. 使用Forms Authentication实现用户注册、登录 (三)用户实体替换
  5. 致电以验证您的JavaFX UI的响应能力
  6. EF关闭自动创建数据库表的方式
  7. java ppt转html_word,ppt,excel转pdf,pdf转html工具类搭建
  8. php gearman 扩展,Ubuntu 12.04 安装 gearman 以及php扩展安装脚本
  9. 漫步数学分析九——级数
  10. 基于MySQL的调度系统_仓储调度系统的设计与实现(SSH,MySQL)
  11. 【HDOJ】【3415】Max Sum of Max-K-sub-sequence
  12. 关于DNF的多媒体包NPK文件的那些事儿(3) - IMGV2
  13. linux 屏幕键盘onborde,电脑虚拟键盘模拟器|On-Screen Keyboard pro(电脑屏幕键盘软件)下载 V9.0.4 官方版 - 比克尔下载...
  14. Ubuntu 16.04 显示器分辨率低
  15. 生成sis文件的诀窍
  16. 新型的Hbb项目目录结构
  17. 2020《人工智能技术服务》专业人才培养方案
  18. Java中角度和弧度的转换,三角函数,反三角函数
  19. Android未来5年的发展前景和趋势
  20. Unity 环境搭建

热门文章

  1. 拓商:如何增加店铺权重,提升店铺转化
  2. LruCache之LruCache分析
  3. win10 无法访问移动硬盘 提示“参数错误”
  4. 盈透、芝加哥交易集团及七禾网邀请演讲内容文字整理(二)
  5. 【mysql+RBAC】RBAC权限处理(转载:http://www.cnblogs.com/xiaoxi/p/5889486.html 平凡希)...
  6. 【活动预告】重磅福利来袭,这个七夕,我宠你!
  7. java 联合主键 注解_Hibernate的联合主键注解方式
  8. 跑步多久才能起到减肥的作用(每次跑30分钟)
  9. Ubuntu12.04启动时显示“超出频率范围”的解决方法
  10. 2018年春节临近,IT行业又到一年一度的调薪或是离职时