1.前言

博客分为上下两篇,您现在阅读的是下篇史上最小白之Transformer详解,在阅读该篇博客之前最好你能够先明白Encoder-Decoder,Attention机制,self-Attention相关原理,可以参考上篇博客,里面我也都有做非常详细的讲解,上篇博客地址:史上最小白之Attention详解

想想自己去年学习Transformer的日子真是太艰难啦,网上的博客要么就是大佬们写得太好太专业了,小白时期的我太多地方看不懂,要么就是太简单了,很多点都没覆盖到,看完还是一知半解,这次自己做个总结,争取每个点都覆盖到,希望能对你有所帮助~

2.Transformer 原理

2.1 Transformer整体结构


上图是Transformer的完整结构图,小朋友你是否有很多问号???这都是些什么鬼,告辞!!!诶诶~先别走,接下来咱们就一步一步来攻克Transformer。

Transformer的结构图,拆解开来,主要分为图上4个部分,其中最重要的就是2和3Encoder-Decoder部分,对咯,Transformer是一个基于Encoder-Decoder框架的模型。
接下来我将按照1,2,3,4的顺序逐步介绍上图中Transformer的网络结构,这样既能够弄清楚结构原理,又能够方便理解Transformer模型的工作流程。

2.2 Transformer的inputs 输入

Transformer输入是一个序列数据,还是以上篇中提到的"Tom chase Jerry" 翻译成中文"汤姆追逐杰瑞"为例:
Encoder 的 inputs就是"Tom chase Jerry" 分词后的词向量。可以是任意形式的词向量,如word2vec,GloVe,one-hot编码。

假设上图中每一个词向量都是一个512维的词向量。

我们注意到,输入inputs embedding后需要给每个word的词向量添加位置编码positional encoding,为什么需要添加位置编码呢?
首先咱们知道,一句话中同一个词,如果词语出现位置不同,意思可能发生翻天覆地的变化,就比如:我欠他100W 和 他欠我100W。这两句话的意思一个地狱一个天堂。可见获取词语出现在句子中的位置信息是一件很重要的事情。但是咱们的Transformer 的是完全基于self-Attention地,而self-attention是不能获取词语位置信息地,就算打乱一句话中词语的位置,每个词还是能与其他词之间计算attention值,就相当于是一个功能强大的词袋模型,对结果没有任何影响。(一会儿在介绍Encoder的时候再详细说明)所以在我们输入的时候需要给每一个词向量添加位置编码。

问题又来了,这个positional encoding怎么获取呢?
1.可以通过数据训练学习得到positional encoding,类似于训练学习词向量,goole在之后的bert中的positional encoding便是由训练得到地。
2.《Attention Is All You Need》论文中Transformer使用的是正余弦位置编码。位置编码通过使用不同频率的正弦、余弦函数生成,然后和对应的位置的词向量相加,位置向量维度必须和词向量的维度一致。过程如上图,PE(positional encoding)计算公式如下:

解释一下上面的公式:
pos表示单词在句子中的绝对位置,pos=0,1,2…,例如:Jerry在"Tom chase Jerry"中的pos=2;dmodel表示词向量的维度,在这里dmodel=512;2i和2i+1表示奇偶性,i表示词向量中的第几维,例如这里dmodel=512,故i=0,1,2…255。
至于上面这个公式是怎么得来地,其实不重要,因为很有可能是作者根据经验自己造地,而且公式也不是唯一地,后续goole在bert中的positional encoding也没有再使用这种方法而是通过训练PE,说明这种求位置向量的方法还是存在一定问题地。
这里我就不做详细的介绍了,想要深究的朋友可以参考一下知乎上的这些回答:如何理解Transformer论文中的positional encoding,和三角函数有什么关系?

为什么是将positional encoding与词向量相加,而不是拼接呢?
拼接相加都可以,只是本身词向量的维度512维就已经蛮大了,再拼接一个512维的位置向量,变成1024维,这样训练起来会相对慢一些,影响效率。两者的效果是差不多地,既然效果差不多当然是选择学习习难度较小的相加了。

Transformer 的 Decoder的输入与Encoder的输出处理方法步骤是一样地,一个接受source数据,一个接受target数据,对应到上面例子里面就是:Encoder接受英文"Tom chase Jerry",Decoder接受中文"汤姆追逐杰瑞"。只是在有target数据时也就是在进行有监督训练时才会接受Outputs Embedding,进行预测时则不会接收。

至此,Transformer的第一块输入部分已经讲解完了,接下来就要进入重点部分Encoder和Decoder了。

2.2 Transformer的Encoder


看上图第2部分 Encoder block。Encoder block是由6个encoder堆叠而成,Nx=6。上图2中的灰框部分就是一个encoder的内部结构,从图中我们可以看出一个encoder由Multi-Head Attention 和 全连接神经网络Feed Forward Network构成。

Multi-Head Attention:

首先回顾一下self-attention,假如输入序列是"Thinking Machines",x1,x2就是对应地"Thinking"和"Machines"添加过位置编码之后的词向量,然后词向量通过三个权值矩阵WQ,WK,WVW^Q,W^K,W^VWQ,WK,WV,转变成为计算Attention值所需的Query,Keys,Values向量。

因为咱们再实际使用中,每一个样本,也就是每一条序列数据都是以矩阵的形式输入地,故可以看到上图中,X矩阵是由"Tinking"和"Machines"词向量组成的矩阵,然后跟过变换得到Q,K,V。假设词向量是512维,X矩阵的维度是(2,512),WQ,WK,WVW^Q,W^K,W^VWQ,WK,WV均是(512,64)维,得到的Query,Keys,Values就都是(2,64)维。

得到Q,K,V之后,接下来就是计算Attention值了。
步骤1: 输入序列中每个单词之间的相关性得分,上篇中说过计算相关性得分可以使用点积法,就是用Q中每一个向量与K中每一个向量计算点积,具体到矩阵的形式:score=Q⋅KTscore = Q\cdot K^Tscore=QKT,socre是一个(2,2)的矩阵
步骤2: 对于输入序列中每个单词之间的相关性得分进行归一化,归一化的目的主要是为了训练时梯度能够稳定。score=score/dkscore = score/\sqrt{d_k}score=score/dk

,dk就是K的维度,以上面假设为例,dk=64
步骤3: 通过softmax函数,将每个单词之间的得分向量转换成[0,1]之间的概率分布,同时更加凸显单词之间的关系。经过softmax后,score转换成一个值分布在[0,1]之间的(2,2)α概率分布矩阵
步骤4: 根据每个单词之间的概率分布,然后乘上对应的Values值,α与V进行点积,Z=softmax(score)⋅VZ = softmax(score)\cdot VZ=softmax(score)V,V的为维度是(2,64),(2,2)x(2,64)最后得到的Z是(2,64)维的矩阵
整体的计算图如下:

说了这么多好像都只是在说self-attention,那么Multi-Head Attention呢?

Multi-Head Attention 很简单,就是在self-attention的基础上,对于输入的embedding矩阵,self-attention只使用了一组WQ,WK,WVW^Q,W^K,W^VWQ,WK,WV来进行变换得到Query,Keys,Values。而Multi-Head Attention使用多组WQ,WK,WVW^Q,W^K,W^VWQ,WK,WV得到多组Query,Keys,Values,然后每组分别计算得到一个Z矩阵,最后将得到的多个Z矩阵进行拼接。Transformer里面是使用了8组不同的WQ,WK,WVW^Q,W^K,W^VWQ,WK,WV

从上图中可以看到,在经过Multi-Head Attention得到矩阵Z之后,并没有直接传入全连接神经网络FNN,而是经过了一步:Add&Normalize。

Add&Normalize:

Add

Add,就是在Z的基础上加了一个残差块X,加入残差块X的目的是为了防止在深度神经网络训练中发生退化问题,退化的意思就是深度神经网络通过增加网络的层数,Loss逐渐减小,然后趋于稳定达到饱和,然后再继续增加网络层数,Loss反而增大。可能看到这里,小朋友你一定有很多问号???为什么深度神经网络会发生退化,为什么添加残差块能够防止退化问题,残差块又是什么?这就牵扯到ResNet残差神经网络的知识了,既然是史上最小白系列,坚决不做标题党,一定把每个问题都解释清楚!

ResNet 残差神经网络:

首先解答第一个问题,为什么深度神经网络会发生退化?
举个例子:假如某个神经网络的最优网络层数是18层,但是我们在设计的时候并不知道到底多少层是最优解,本着层数越深越好的理念,我们设计了32层,那么32层神经网络中有14层其实是多余地,我们要想达到18层神经网络的最优效果,必须保证这多出来的14层网络必须进行恒等映射,恒等映射的意思就是说,输入什么,输出就是什么,可以理解成F(x)=x这样的函数,因为只有进行了这样的恒等映射咱们才能保证这多出来的14层神经网络不会影响我们最优的效果。
但现实是神经网络的参数都是训练出来地,要想保证训练出来地参数能够很精确的完成F(x)=x的恒等映射其实是很困难地。多余的层数较少还好,对效果不会有很大影响,但多余的层数一多,可能结果就不是很理想了。这个时候大神们就提出了ResNet 残差神经网络来解决神经网络退化的问题。

残差块是什么?

上图就是构造的一个残差块,可以看到X是这一层残差块的输入,也称作F(X)为残差,X为输入值,F(X)是经过第一层线性变化并激活后的输出,该图表示在残差网络中,第二层进行线性变化之后激活之前,F(X)加入了这一层输入值X,然后再进行激活后输出。在第二层输出值激活前加入X,这条路径称作shortcut连接。

为什么添加了残差块能防止神经网络退化问题呢?
咱们再来看看添加了残差块后,咱们之前说的要完成恒等映射的函数变成什么样子了。是不是就变成h(X)=F(X)+X,我们要让h(X)=X,那么是不是相当于只需要让F(X)=0就可以了,这里就巧妙了!神经网络通过训练变成0是比变成X容易很多地,因为大家都知道咱们一般初始化神经网络的参数的时候就是设置的[0,1]之间的随机数嘛。所以经过网络变换后很容易接近于0。举个例子:

假设该网络只经过线性变换,没有bias也没有激活函数。我们发现因为随机初始化权重一般偏向于0,那么经过该网络的输出值为[0.6 0.6],很明显会更接近与[0 0],而不是[2 1],相比与学习h(x)=x,模型要更快到学习F(x)=0。
并且ReLU能够将负数激活为0,过滤了负数的线性变化,也能够更快的使得F(x)=0。这样当网络自己决定哪些网络层为冗余层时,使用ResNet的网络很大程度上解决了学习恒等映射的问题,用学习残差F(x)=0更新该冗余层的参数来代替学习h(x)=x更新冗余层的参数。
这样当网络自行决定了哪些层为冗余层后,通过学习残差F(x)=0来让该层网络恒等映射上一层的输入,使得有了这些冗余层的网络效果与没有这些冗余层的网络效果相同,这样很大程度上解决了网络的退化问题。

到这里,关于Add中为什么需要加上一个X,要进行残差网络中的shortcut你清楚了吗?Transformer中加上的X也就是Multi-Head Attention的输入,X矩阵。

Normalize

为什么要进行Normalize呢?
在神经网络进行训练之前,都需要对于输入数据进行Normalize归一化,目的有二:1,能够加快训练的速度。2.提高训练的稳定性。

为什么使用Layer Normalization(LN)而不使用Batch Normalization(BN)呢?


先看图,LN是在同一个样本中不同神经元之间进行归一化,而BN是在同一个batch中不同样本之间的同一位置的神经元之间进行归一化。
BN是对于相同的维度进行归一化,但是咱们NLP中输入的都是词向量,一个300维的词向量,单独去分析它的每一维是没有意义地,在每一维上进行归一化也是适合地,因此这里选用的是LN。

Feed-Forward Networks

全连接层公式如下:
FFN(x)=max(0,xW1+b1)W2+b2FFN(x)=max(0,xW_1+b_1)W_2+b_2FFN(x)=max(0,xW1+b1)W2+b2
这里的全连接层是一个两层的神经网络,先线性变换,然后ReLU非线性,再线性变换。
这里的x就是我们Multi-Head Attention的输出Z,还是引用上面的例子,那么Z是(2,64)维的矩阵,假设W1是(64,1024),其中W2与W1维度相反(1024,64),那么按照上面的公式:
FFN(Z)=(2,64)x(64,1024)x(1024,64)=(2,64),我们发现维度没有发生变化,这两层网络就是为了将输入的Z映射到更加高维的空间中(2,64)x(64,1024)=(2,1024),然后通过非线性函数ReLU进行筛选,筛选完后再变回原来的维度。

然后经过Add&Normalize,输入下一个encoder中,经过6个encoder后输入到decoder中,至此Transformer的Encoder部分就全部介绍完了,搞懂了Encoder那么Decoder就so easy啦,基本上结构和Encoder差不多,接下来咱们就进入Decoder部分吧~

2.3 Transformer的Decoder


看上图第3部分 Decoder block。Decoder block也是由6个decoder堆叠而成,Nx=6。上图3中的灰框部分就是一个decoder的内部结构,从图中我们可以看出一个decoder由Masked Multi-Head Attention,Multi-Head Attention 和 全连接神经网络FNN构成。比Encoder多了一个Masked Multi-Head Attention,其他的结构与encoder相同,那么咱们就先来看看这个Masked Multi-Head Attention。

Transformer Decoder的输入

Decoder的输入分为两类:
一种是训练时的输入,一种是预测时的输入。
训练时的输入就是已经对准备好对应的target数据。例如翻译任务,Encoder输入"Tom chase Jerry",Decoder输入"汤姆追逐杰瑞"。
预测时的输入,一开始输入的是起始符,然后每次输入是上一时刻Transformer的输出。例如,输入"",输出"汤姆",输入"汤姆",输出"汤姆追逐",输入"汤姆追逐",输出"汤姆追逐杰瑞",输入"汤姆追逐杰瑞",输出"汤姆追逐杰瑞"结束。

Masked Multi-Head Attention

与Encoder的Multi-Head Attention计算原理一样,只是多加了一个mask码。mask 表示掩码,它对某些值进行掩盖,使其在参数更新时不产生效果。Transformer 模型里面涉及两种 mask,分别是 padding mask 和 sequence mask。为什么需要添加这两种mask码呢?

1.padding mask
什么是 padding mask 呢?因为每个批次输入序列长度是不一样的也就是说,我们要对输入序列进行对齐。具体来说,就是给在较短的序列后面填充 0。但是如果输入的序列太长,则是截取左边的内容,把多余的直接舍弃。因为这些填充的位置,其实是没什么意义的,所以我们的attention机制不应该把注意力放在这些位置上,所以我们需要进行一些处理。
具体的做法是,把这些位置的值加上一个非常大的负数(负无穷),这样的话,经过 softmax,这些位置的概率就会接近0!

2.sequence mask
sequence mask 是为了使得 decoder 不能看见未来的信息。对于一个序列,在 time_step 为 t 的时刻,我们的解码输出应该只能依赖于 t 时刻之前的输出,而不能依赖 t 之后的输出。因此我们需要想一个办法,把 t 之后的信息给隐藏起来。这在训练的时候有效,因为训练的时候每次我们是将target数据完整输入进decoder中地,预测时不需要,预测的时候我们只能得到前一时刻预测出的输出。
那么具体怎么做呢?也很简单:产生一个上三角矩阵,上三角的值全为0。把这个矩阵作用在每一个序列上,就可以达到我们的目的。

上面可能忘记说了,在Encoder中的Multi-Head Attention也是需要进行mask地,只不过Encoder中只需要padding mask即可,而Decoder中需要padding mask和sequence mask。OK除了这点mask不一样以外,其他的部分均与Encoder一样啦~

Add&Normalize也与Encoder中一样,接下来就到了Decoder中第二个Multi-Head Attention,这个Multi-Head Attention又与Encoder中有一点点不一样。

基于Encoder-Decoder 的Multi-Head Attention
Encoder中的Multi-Head Attention是基于Self-Attention地,Decoder中的第二个Multi-Head Attention就只是基于Attention,它的输入Quer来自于Masked Multi-Head Attention的输出,Keys和Values来自于Encoder中最后一层的输出。

跟我一样的牛角尖型选手可能又要发问啦,为啥Decoder中要搞两个Multi-Head Attention呢?
我个人理解是第一个Masked Multi-Head Attention是为了得到之前已经预测输出的信息,相当于记录当前时刻的输入之间的信息的意思。第二个Multi-Head Attention是为了通过当前输入的信息得到下一时刻的信息,也就是输出的信息,是为了表示当前的输入与经过encoder提取过的特征向量之间的关系来预测输出。

经过了第二个Multi-Head Attention之后的Feed Forward Network与Encoder中一样,然后就是输出进入下一个decoder,如此经过6层decoder之后到达最后的输出层。

2.4 Transformer的输出


Output如图中所示,首先经过一次线性变换,然后Softmax得到输出的概率分布,然后通过词典,输出概率最大的对应的单词作为我们的预测输出。

2.5 结构总结

好啦,以上就是Transformer的全部结构原理啦,Transformer真的不愧是近几年来最火的模型,很多细节都很巧妙,真的想要搞懂还是需要花点心思地。不知道看到这里,小伙伴们对于Transformer的结构有没有清晰一些呢?

3.Transformer优缺点

Transformer虽然好,但它也不是万能地,还是存在着一些不足之处,接下来就来介绍一下它的优缺点:
优点:
1.效果好
2.可以并行训练,速度快
3.很好地解决了长距离依赖的问题
缺点:
1.完全基于self-attention,对于词语位置之间的信息有一定的丢失,虽然加入了positional encoding来解决这个问题,但也还存在着可以优化的地方。

5.结语

Transformer是非常有潜力的模型,在Transformer基础上后来又衍生出来了BERT和GPT这两个NLP神器,而且依旧还存在着许多可以优化的地方。目前NLP在工业上的应用远不及CV广,但是自然语言是人类文明得以延续的重要的信息。没有文字,怎么回首古人的发展历史,没有语言,人类社会又怎么能够和谐运转,你看到的任何图片,听到的任何话语都在大脑里转换成里你自己能理解地文字信息,语言信息,所以NLP的前景依旧是无比巨大地。这是最好的时代,正因为NLP还一直处于探索阶段,所以现在开始学习仍旧不晚,史上最小白系列也写了几期了,我会一直写下去地,希望史上最小白系列能够对于初入NLP的同学有所帮助吧。
本人才疏学浅,有写错或者不懂的地方欢迎指正~
活到老,学到老,大家一起加油吧,奥利给!!!

6.参考

详解Transformer (Attention Is All You Need)
碎碎念:Transformer的细枝末节

史上最小白之Transformer详解相关推荐

  1. 史上最小白之BM25详解与实现

    史上最小白之BM25详解与实现 原理 BM25算法是一种计算句子与文档相关性的算法,它的原理十分简单:将输入的句子sentence进行分词,然后分别计算句子中每个词word与文档doc的相关度,然后进 ...

  2. 史上最小白之Bert详解

    1.前言 关于BERT,张俊林博士有一篇特别好的文章:从Word Embedding到Bert模型-自然语言处理中的预训练技术发展史 非常透彻地讲解了Bert是怎么样从NNLM->Word2Ve ...

  3. 史上最小白之RNN详解

    1.前言 网上目前已经有诸多优秀的RNN相关博客,但是我写博客的出发点主要是为了加深和巩固自己的理解,所以还是决定自己再进行一下总结和描述,如有不正确的地方欢迎指正~ 2.区分RNN 循环神经网络(R ...

  4. 史上最小白之Attention详解

    1.前言 在自然语言处理领域,近几年最火的是什么?是BERT!谷歌团队2018提出的用于生成词向量的BERT算法在NLP的11项任务中取得了非常出色的效果,堪称2018年深度学习领域最振奋人心的消息. ...

  5. 史上最简单MySQL教程详解(进阶篇)之存储过程(一)

    史上最简单MySQL教程详解(进阶篇)之存储过程(一) 史上最简单MySQL教程详解(进阶篇)之存储过程(一) 什么是存储过程 存储过程的作用 如何使用存储过程 创建存储过程 DELIMITER改变分 ...

  6. 史上最简单MySQL教程详解(进阶篇)之存储引擎介绍及默认引擎设置

    什么是存储引擎? MySQL存储引擎种类 MyISAM 引擎 InnoDB引擎 存储引擎操作 查看存储引擎 存储引擎的变更 修改默认引擎 什么是存储引擎? 与其他数据库例如Oracle 和SQL Se ...

  7. 史上最简单MySQL教程详解(进阶篇)之索引及失效场合总结

    史上最简单MySQL教程详解(进阶篇)之索引及其失效场合总结 什么是索引及其作用 索引的种类 各存储引擎对于索引的支持 简单介绍索引的实现 索引的设置与分析 普通索引 唯一索引(Unique Inde ...

  8. 史上最简单MySQL教程详解(进阶篇)之视图

    史上最简单MySQL教程详解(进阶篇)之视图 为什么要用视图 视图的本质 视图的作用 如何使用视图 创建视图 修改视图 删除视图 查看视图 使用视图检索 变更视图数据 WITH CHECK OPTIO ...

  9. 史上最全 JVM 大全详解、java 程序员细节到极致的一次,魔鬼

    前言 作为 Java 的从业者,在找工作的时候,一定会被问及关于 JVM 相关的知识. JVM 知识的掌握程度,在很多面试官眼里是候选人技术深度的一个重要评判标准.而大多数人可能没有对 JVM 的实际 ...

最新文章

  1. sed的基本用法和高级用法
  2. 【论文解读】GCN论文总结
  3. linux 磁盘资源管理以及IO
  4. Cisco小型局域网配置实验
  5. 解决织梦CMS友情链接的字数个数限制
  6. 快速查找对方IP经典技巧汇总
  7. iptv内容运营系统服务器架构,IPTV系统架构技术的深入解析
  8. 点击文本或按钮实现复制
  9. (一)事务与并发控制
  10. 开发QQ桌球瞄准器(1):桌球瞄准器介绍与使用方法
  11. 关于OLAP数据仓库的归纳总结
  12. python数据分析设置教程视频_炼数成金女讲师Python数据分析实战应用视频教程
  13. JS 实现数字转换为大写中文金额
  14. EmEditor中大纲正则表达式如何匹配多位序号,如从1、到999
  15. GNS3快捷安装指南
  16. Eclipse 自定义${date}变量格式的思路历程
  17. 【深度学习神经网络】--BatchNorm详解
  18. 运动手环SRRC认证办理
  19. AWS-Lambda 从传入的 S3 Event 里获取信息
  20. 简易QQ聊天室(多线程通信)

热门文章

  1. Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans,....
  2. UDP打洞、P2P组网方式研究
  3. Knockoutjs官网翻译系列(二) Observable 数组
  4. 亚马逊买家账号注册需要注意什么?
  5. 2012年度注册测绘师资格考试江西-考区合格人员名单
  6. php中如何导出表格,PHP如何实现表格Excel的导出
  7. 中职生职业生涯规划书2000字学计算机,中职生职业生涯规划书计算机专业
  8. YUV420P、YUV420SP、NV12、NV21和RGB互相转换并存储为JPEG以及PNG图片
  9. JAVA易医就医购药交互平台计算机毕业设计Mybatis+系统+数据库+调试部署
  10. 三个文本框自定义数字抽奖机