详解Transformer (Attention Is All You Need)

先导知识

前言

注意力(Attention)机制[2]由Bengio团队与2014年提出并在近年广泛的应用在深度学习中的各个领域,例如在计算机视觉方向用于捕捉图像上的感受野,或者NLP中用于定位关键token或者特征。谷歌团队近期提出的用于生成词向量的BERT[3]算法在NLP的11项任务中取得了效果的大幅提升,堪称2018年深度学习领域最振奋人心的消息。而BERT算法的最重要的部分便是本文中提出的Transformer的概念。

正如论文的题目所说的,Transformer中抛弃了传统的CNN和RNN,整个网络结构完全是由Attention机制组成。更准确地讲,Transformer由且仅由self-Attenion和Feed Forward Neural Network组成。一个基于Transformer的可训练的神经网络可以通过堆叠Transformer的形式进行搭建,作者的实验是通过搭建编码器和解码器各6层,总共12层的Encoder-Decoder,并在机器翻译中取得了BLEU值得新高。

作者采用Attention机制的原因是考虑到RNN(或者LSTM,GRU等)的计算限制为是顺序的,也就是说RNN相关算法只能从左向右依次计算或者从右向左依次计算,这种机制带来了两个问题:

  1. 时间片 t 的计算依赖 t−1 时刻的计算结果,这样限制了模型的并行能力;
  2. 顺序计算的过程中信息会丢失,尽管LSTM等门机制的结构一定程度上缓解了长期依赖的问题,但是对于特别长期的依赖现象,LSTM依旧无能为力。

Transformer的提出解决了上面两个问题,首先它使用了Attention机制,将序列中的任意两个位置之间的距离是缩小为一个常量;其次它不是类似RNN的顺序结构,因此具有更好的并行性,符合现有的GPU框架。论文中给出Transformer的定义是:Transformer is the first transduction model relying entirely on self-attention to compute representations of its input and output without using sequence aligned RNNs or convolution。

遗憾的是,作者的论文比较难懂,尤其是Transformer的结构细节和实现方式并没有解释清楚。尤其是论文中的 Q , V , K 究竟代表什么意思作者并没有说明。通过查阅资料,发现了一篇非常优秀的讲解Transformer的技术博客[4]。本文中的大量插图也会从该博客中截取。首先感谢Jay Alammer详细的讲解,其次推荐大家去阅读原汁原味的文章。

1. Transformer 详解

1.1 高层Transformer

论文中的验证Transformer的实验室基于机器翻译的,下面我们就以机器翻译为例子详细剖析Transformer的结构,在机器翻译中,Transformer可概括为如图1:

图1:Transformer用于机器翻译

Transformer的本质上是一个Encoder-Decoder的结构,那么图1可以表示为图2的结构:

图2:Transformer的Encoder-Decoder结构

如论文中所设置的,编码器由6个编码block组成,同样解码器是6个解码block组成。与所有的生成模型相同的是,编码器的输出会作为解码器的输入,如图3所示:

图3:Transformer的Encoder和Decoder均由6个block堆叠而成

我们继续分析每个encoder的详细结构:在Transformer的encoder中,数据首先会经过一个叫做‘self-attention’的模块得到一个加权之后的特征向量 Z ,这个 Z 便是论文公式1中的 Attention(Q,K,V) :

(1)Attention(Q,K,V)=softmax(QKTdk)V

第一次看到这个公式你可能会一头雾水,在后面的文章中我们会揭开这个公式背后的实际含义,在这一段暂时将其叫做 Z 。

得到 Z 之后,它会被送到encoder的下一个模块,即Feed Forward Neural Network。这个全连接有两层,第一层的激活函数是ReLU,第二层是一个线性激活函数,可以表示为:

(2)FFN(Z)=max(0,ZW1+b1)W2+b2

Encoder的结构如图4所示:

图4:Transformer由self-attention和Feed Forward neural network组成

Decoder的结构如图5所示,它和encoder的不同之处在于Decoder多了一个Encoder-Decoder Attention,两个Attention分别用于计算输入和输出的权值:

  1. Self-Attention:当前翻译和已经翻译的前文之间的关系;
  2. Encoder-Decnoder Attention:当前翻译和编码的特征向量之间的关系。

图5:Transformer的解码器由self-attention,encoder-decoder attention以及FFNN组成

1.2 输入编码

1.1节介绍的就是Transformer的主要框架,下面我们将介绍它的输入数据。如图6所示,首先通过Word2Vec等词嵌入方法将输入语料转化成特征向量,论文中使用的词嵌入的维度为 dmodel=512 。

图6:单词的输入编码

在最底层的block中, x 将直接作为Transformer的输入,而在其他层中,输入则是上一个block的输出。为了画图更简单,我们使用更简单的例子来表示接下来的过程,如图7所示:

图7:输入编码作为一个tensor输入到encoder中

1.3 Self-Attention

Self-Attention是Transformer最核心的内容,然而作者并没有详细讲解,下面我们来补充一下作者遗漏的地方。回想Bahdanau等人提出的用Attention\[2\],其核心内容是为输入向量的每个单词学习一个权重,例如在下面的例子中我们判断it代指的内容,

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

通过加权之后可以得到类似图8的加权情况,在讲解self-attention的时候我们也会使用图8类似的表示方式

图8:经典Attention可视化示例图

在self-attention中,每个单词有3个不同的向量,它们分别是Query向量( Q ),Key向量( K )和Value向量( V ),长度均是64。它们是通过3个不同的权值矩阵由嵌入向量 X 乘以三个不同的权值矩阵 WQ , WK , WV 得到,其中三个矩阵的尺寸也是相同的。均是 512×64 。

图9:Q,K,V的计算示例图

那么Query,Key,Value是什么意思呢?它们在Attention的计算中扮演着什么角色呢?我们先看一下Attention的计算方法,整个过程可以分成7步:

  1. 如上文,将输入单词转化成嵌入向量;
  2. 根据嵌入向量得到 q , k , v 三个向量;
  3. 为每个向量计算一个score: score=q⋅k ;
  4. 为了梯度的稳定,Transformer使用了score归一化,即除以 dk ;
  5. 对score施以softmax激活函数;
  6. softmax点乘Value值 v ,得到加权的每个输入向量的评分 v ;
  7. 相加之后得到最终的输出结果 z : z=∑v 。

上面步骤的可以表示为图10的形式。

图10:Self-Attention计算示例图

实际计算过程中是采用基于矩阵的计算方式,那么论文中的 Q , V , K 的计算方式如图11:

图11:Q,V,K的矩阵表示

图10总结为如图12所示的矩阵形式:

图12:Self-Attention的矩阵表示

这里也就是公式1的计算方式。

在self-attention需要强调的最后一点是其采用了残差网络 [5]中的short-cut结构,目的当然是解决深度学习中的退化问题,得到的最终结果如图13。

图13:Self-Attention中的short-cut连接

Query,Key,Value的概念取自于信息检索系统,举个简单的搜索的例子来说。当你在某电商平台搜索某件商品(年轻女士冬季穿的红色薄款羽绒服)时,你在搜索引擎上输入的内容便是Query,然后搜索引擎根据Query为你匹配Key(例如商品的种类,颜色,描述等),然后根据Query和Key的相似度得到匹配的内容(Value)。

self-attention中的Q,K,V也是起着类似的作用,在矩阵计算中,点积是计算两个矩阵相似度的方法之一,因此式1中使用了 QKT 进行相似度的计算。接着便是根据相似度进行输出的匹配,这里使用了加权匹配的方式,而权值就是query与key的相似度。

1.3 Multi-Head Attention

Multi-Head Attention相当于 h 个不同的self-attention的集成(ensemble),在这里我们以 h=8 举例说明。Multi-Head Attention的输出分成3步:

  1. 将数据 X 分别输入到图13所示的8个self-attention中,得到8个加权后的特征矩阵 Zi,i∈{1,2,...,8} 。
  2. 将8个 Zi 按列拼成一个大的特征矩阵;
  3. 特征矩阵经过一层全连接后得到输出 Z 。

整个过程如图14所示:

图14:Multi-Head Attention

同self-attention一样,multi-head attention也加入了short-cut机制。

1.4 Encoder-Decoder Attention

在解码器中,Transformer block比编码器中多了个encoder-cecoder attention。在encoder-decoder attention中, Q 来自于解码器的上一个输出, K 和 V 则来自于与编码器的输出。其计算方式完全和图10的过程相同。

由于在机器翻译中,解码过程是一个顺序操作的过程,也就是当解码第 k 个特征向量时,我们只能看到第 k−1 及其之前的解码结果,论文中把这种情况下的multi-head attention叫做masked multi-head attention。

1.5 损失层

解码器解码之后,解码的特征向量经过一层激活函数为softmax的全连接层之后得到反映每个单词概率的输出向量。此时我们便可以通过CTC等损失函数训练模型了。

而一个完整可训练的网络结构便是encoder和decoder的堆叠(各 N 个, N=6 ),我们可以得到图15中的完整的Transformer的结构(即论文中的图1):

图15:Transformer的完整结构图

2. 位置编码

截止目前为止,我们介绍的Transformer模型并没有捕捉顺序序列的能力,也就是说无论句子的结构怎么打乱,Transformer都会得到类似的结果。换句话说,Transformer只是一个功能更强大的词袋模型而已。

为了解决这个问题,论文中在编码词向量时引入了位置编码(Position Embedding)的特征。具体地说,位置编码会在词向量中加入了单词的位置信息,这样Transformer就能区分不同位置的单词了。

那么怎么编码这个位置信息呢?常见的模式有:a. 根据数据学习;b. 自己设计编码规则。在这里作者采用了第二种方式。那么这个位置编码该是什么样子呢?通常位置编码是一个长度为 dmodel 的特征向量,这样便于和词向量进行单位加的操作,如图16。

图16:Position Embedding

论文给出的编码公式如下:

(3)PE(pos,2i)=sin(pos100002idmodel)

(4)PE(pos,2i+1)=cos(pos100002idmodel)

在上式中, pos 表示单词的位置, i 表示单词的维度。关于位置编码的实现可在Google开源的算法中get_timing_signal_1d()函数找到对应的代码。

作者这么设计的原因是考虑到在NLP任务中,除了单词的绝对位置,单词的相对位置也非常重要。根据公式 sin(α+β)=sinαcosβ+cosαsinβ 以及cos(α+β)=cosαcosβ−sinαsinβ ,这表明位置 k+p 的位置向量可以表示为位置 k 的特征向量的线性变化,这为模型捕捉单词之间的相对位置关系提供了非常大的便利。

3. 总结

优点:(1)虽然Transformer最终也没有逃脱传统学习的套路,Transformer也只是一个全连接(或者是一维卷积)加Attention的结合体。但是其设计已经足够有创新,因为其抛弃了在NLP中最根本的RNN或者CNN并且取得了非常不错的效果,算法的设计非常精彩,值得每个深度学习的相关人员仔细研究和品位。(2)Transformer的设计最大的带来性能提升的关键是将任意两个单词的距离是1,这对解决NLP中棘手的长期依赖问题是非常有效的。(3)Transformer不仅仅可以应用在NLP的机器翻译领域,甚至可以不局限于NLP领域,是非常有科研潜力的一个方向。(4)算法的并行性非常好,符合目前的硬件(主要指GPU)环境。

缺点:(1)粗暴的抛弃RNN和CNN虽然非常炫技,但是它也使模型丧失了捕捉局部特征的能力,RNN + CNN + Transformer的结合可能会带来更好的效果。(2)Transformer失去的位置信息其实在NLP中非常重要,而论文中在特征向量中加入Position Embedding也只是一个权宜之计,并没有改变Transformer结构上的固有缺陷。

Reference

[1] Vaswani A, Shazeer N, Parmar N, et al. Attention is all you need [C]//Advances in Neural Information Processing Systems. 2017: 5998-6008.

[2] Bahdanau D, Cho K, Bengio Y. Neural machine translation by jointly learning to align and translate[J]. arXiv preprint arXiv:1409.0473, 2014.

[3] Devlin J, Chang M W, Lee K, et al. BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding[J]. arXiv preprint arXiv:1810.04805, 2018.

[4] http://jalammar.github.io/illustrated-transformer

[5] He K, Zhang X, Ren S, et al. Deep residual learning for image recognition[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2016: 770-778.

家里蹲大学研究僧

感觉blog原作者在讲Q/K/V的维度时有一点曲解了论文的意思,论文作者应该是说d[model]=512,在multi-head attention中使用d[k]=d[v]=d[model]/h=64,且论文中明确说明每层Encoder为了实现残差连接必须都输出d[model]=512的向量,而blog中在讲的时候,multi-head直接拆到8维了,应该是对论文作者的原意有误解。

论文原文:papers.nips.cc/paper/71

2019-02-26

​15

8 条回复

Jemila

可是原文说The positional encodings have the same dimension dmodel as the embeddings, so that the two can be summed.512应该是embedding vector吧

2019-02-27

​赞

家里蹲大学研究僧

Jemila

我们说的好像不是一件事儿,我是在说Scaled Dot-Product Attention里,blog中提到的三个权重矩阵维度是512*64,那么得到的Q/K/V维度是n*64了,论文原意似乎不是这样的

2019-02-27

​1

Jemila

家里蹲大学研究僧

论文是在multihead把8个n*64合并了吧

2019-02-28

​3

caixiong

我也有这个疑惑, 为了实现残差连接, 必须用multi-head的, 单个的head输出的z维度是64, 怎么可能和原始的512维embedding进行残差连接?

2019-05-17

​赞

rockguy

caixiong

multi-head的输出维度是8(head数)*64(head输出维度)=512,把多head concat起来就实现了维度一致

2019-05-23

​11

朱琪超

家里蹲大学研究僧

原文里的Q/K/V 说明了 是512*X

2019-10-31

​赞

Coldwind

rockguy

以我的理解并非如此,这个多头拼接成的矩阵N*(64*8),并不是直接和上一层输入做残差(相加),而是要经过一个W0矩阵,得到一个新的N*512作为输出。

可以参考<<the illustrated Transformers>>中的图示:jalammar.github.io/imag

2021-03-02

​4

GoPrime

Coldwind

对的 可以参考论文原图 在concat之后还要经过一层linear层才能得到 $(n, d_model)$的维度 不过在linear之前生成的 $Z$ 应该就已经是$(n, d_model)$的维度了

周末

关于decode过程的问题,我想这里加点解释。
decoder过程实际上是用上文预测下文的过程,这个过程在训练和测试阶段的代码是不同的。
在train阶段可以这么做decoder的输入是trginput = trg[:, :-1] # B * seqlen ,decoder的标签是trg[:, 1:] # B * seqlen,这样两个队列的错开了,正好相差一个,这样就在一个Batch里面可以直接训练一整个句子,而不需要for循环一个个时间步做了。
在test阶段是需要for循环的,t=0输入的<START>,for循环直到输出<END>
详细可以看
towardsdatascience.com/

评论回复

大河狸

您好,能问俩脑残问题吗。。。1.encoder是一次性输入整个要翻译的句子,然后decoder最底下那个输入像rnn一样输入Yt-1然后预测Yt是吗(我知道有那种训练阶段输入groundtruth而不是前一刻预测结果的,我意思是decoder是不是像rnn一样输入一个词生成一个词的形式)?2.encoder-decoder attention算masked吗?不算的话是因为接收了来自encoder的输出所以看到了未来的信息吗?

2018-12-05

​10

16 条回复

大师兄

作者​

  1. Decoder输出Y_t是否需要Y_{t-1}是和你的输出层RNN的设计有关,两种方式均可,RNN预测Y_t不是必须需要Y_{t-1}的;
  2. Attention的本质是为每个特征学习一个权值,当做mask看也没有问题,但是mask的值是介于[0,1]之间的值,如果为0的话,这个特征就被忽略了。

2018-12-05

​5

郝曌骏

同学你好,关于你的两个问题:

decoder底部的self attention component是输入Y_t,encoder-decoder attention不是masked.

你可以参考 nlp.seas.harvard.edu/20 中Applications of Attention in our Model章节

2020-02-13

​赞

大河狸

郝曌骏

谢谢,隔了一年多还有人回

2020-02-13

​赞

古德曼

郝曌骏

同学你好,如果encoder-decoder attention不是masked,为什么在链接最下面可视化分析那一部分Decoder src layer会呈现出标准的对角线分布?

2020-03-04

​赞

郝曌骏

古德曼

同学,你指的是 下三角分布吧。你仔细看下作者的代码,他画的其实是 self attn,而self attn是有subsequent mask的(mask后面的单词)。

2020-03-04

​赞

古德曼

郝曌骏

你好,在Decoder的可视化部分每一层有两个图,第一行是self attn,第二行是encoder-decoder attn,我看了代码发现encoder-decoder attn这一部分是没有masked,但是可视化的这一部分依然是下三角分布,不知道这里面是什么原因?

2020-03-04

​赞

古德曼

郝曌骏

按照我的理解,对于Decoder的每一个输入,都应该计算与源语言每一个单词的attn. 因此不进行masked是合理的,但是看了这个可视化的图,又让我感到困惑了,不知道为什么会产生这种形式?

2020-03-04

​赞

郝曌骏

古德曼

同学,我说的看代码,是“Attention Visualization”那里的代码。作者画图画错了. 应该是model.decoder.layers[layer].src_attn而非model.decoder.layers[layer].self_attn

2020-03-04

​赞

一只灰熊

郝曌骏

您好,我有一个问题,decoder底层输入Yt-1然后输出为Yt,这里不还是串行的吗?这样比RNN快在哪里呢?

2020-03-04

​1

郝曌骏

一只灰熊

attention可以并行啊

2020-03-04

​赞

古德曼

郝曌骏

嗯嗯,看到了!非常感谢🙏

2020-03-05

​赞

古德曼

一只灰熊

在训练的时候,由于decoder input 采用了mask机制,因此可以并行训练多个时刻的输出Yt。

2020-03-05

​5

郝曌骏

古德曼

客气 :)

2020-03-05

​赞

Alex

一只灰熊

Inference时候是串行,因为必须得到i-1的prediction才能预测i。training的时候用mask,把该词以后的词softmax后的probability mask到0,可以并行。

2020-06-24

​9

登登

当然不是了,encoder的输出也是也是一个句子,decoder是用encoder的全部输出预测一个词,同时还有额外的信息,比如要预测第6个词,会使用encoder的全部输出和翻译句子中的前5个词,我的理解是这样的

2020-12-17

​1

M2nNg

Alex

请问training这个并行,是特指的teacher forcing模式吗?因为用teacher forcing才能预知每一步的i-1的输出(直接用label)。否则还是无法并行吧?

路夫

博主提到的博客中译版在这儿 zhuanlan.zhihu.com/p/54

2020-08-12

​4

jackiexiao

这篇文章相比原文缺失了不少细节,比如sqrt(d_k),还是读原文的中翻好点

楚竹

【根据嵌入向量得到 q,k,v 三个向量】 请问这三个向量是怎么来的,分别代表什么意思,有什么区别呢

2019-07-01

​2

7 条回复

Cyoung

score=q点积k,为什么要这样做呢?

2019-10-16

​赞

gkdkiller

Q是query向量,决定key中哪些信息需要被关注。K是key向量,和Q相乘用的,作用就是一个key了。V是value向量。K和Q相乘,再过一个Relu,得到的筛选向量决定value中哪些信息需要被关注。

2019-11-13

​4

gkdkiller

Q,K,V是输入X分别乘以三个矩阵得到的,这三个矩阵都是可训练的。

2019-11-13

​2

秋风梧

q,k,v的设计灵感可能是源于检索系统 stats.stackexchange.com

2020-06-03

​1

SSKSSKSSK

gkdkiller

这三个矩阵是一个word 的embedding对应一组这三个矩阵 还是说所有的word共享一组这三个矩阵呢?

2020-12-25

​赞

gkdkiller

SSKSSKSSK

当然共享了,不然来50000个词谁的电脑撑得住

2020-12-25

​赞

小燕子

不理解QVK的维度怎么确定的,既然输入是X那这个Q指的是啥

参考:

BERT大火却不懂Transformer?读这一篇就够了 - 知乎 (zhihu.com)

详解Transformer (Attention Is All You Need) - 知乎 (zhihu.com)

 论文原文:https://papers.nips.cc/paper/7181-attention-is-all-you-need.pdf

The Illustrated Transformer – Jay Alammar – Visualizing machine learning one concept at a time.

Self-Attention和Transformer - machine-learning-notes

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值