本文解读Transformer较为详细,是一篇两万字的长文,如果想看简短版的,请参考这篇文章
目录
1 相关背景
1.1 Transformer
1.2《Attention is all you need》
1.3 论文作者
1.4 Google brain
1.5 NIPS
1.6 BLEU score
2 摘要、结论、导言、相关工作
2.1 摘要
2.2 结论
2.3 导言
2.4 相关工作
2.4.1 为什么卷积做时序不行?
2.4.2 self-attention自注意力机制
2.4.3 End-to-end memory networks
3 模型架构
3.1 整体解读
3.2 编码器
3.2.1 结构
3.2.2 LayerNorm
3.3 解码器
3.4 Attention
3.4.1 Scaled Dot-Product Attention
3.4.2 和其他注意力区别
3.4.3 muti-head
3.4.4 Mask Multi-Head Attention
3.4.5 decoder的Multi-Head Attention
3.4.6 三个注意力使用情况
3.5 Position-wise MLP
3.6 embeddings and softmax
3.7 position encoding
4 为什么使用自注意力机制
4.1自注意力网络
4.2循环神经网络
4.3卷积神经网络
4.4被限制的自注意力网络
5 训练、结果
5.1 训练数据和批次
5.1.1 Transformer英语翻译德语
5.1.2 Transformer英语翻译法语
5.1.3 硬件和步长
5.2正则化
5.3 结果:机器翻译任务
Transformer是2017年google brain提出专注于解决自然语言处理领域问题的深度学习模型,到现在为止google shcolar中这篇论文的引用量达到7.7万,这篇文章在AI圈的影响力可能仅次于Resnet了。现在2023年火爆出圈的AIGC也基本是从Transformer为基础的网络组件,比如chatGPT中的T正是大名鼎鼎的Transformer的缩写(chatGPT全称为chat Generative Pre-trained Transformer),也就是说GPT家族的所有模型都是基于Transformer。
它是主要用来解决RNN系列在在处理长序列数据时存在一些问题,比如难以并行计算和难以捕捉长距离依赖关系,而Transformer模型则通过引入自注意力机制(Self-Attention)来解决这些问题。
斯坦福大学在2021年9月份发表了一篇长达200页的综述论文来讲Transformer及其变种,甚至提议说这是一个基础模型。
可以说21世纪最伟大、最完美、最有影响力的两个算法就是Resnet 和Transformer了。前者不仅仅在图像识别领域大放异彩,而且更是在机器学习基础研究中真正让深度学习从传统机器学习中脱颖而出,让深度学习真正变得深起来。后者也不仅仅在自然语言处理中登峰造极,在机器学习多个领域中(如CV、Data Mining、语音识别)注入了强大能量,此外Transformer由于对于文本处理的极大快捷+精确,使得AI对于内容生成产生了迅速动力,在2023年AIGC凭借chatGPT和midjourney等工具深入内嵌人类的所有领域,人工智能也正式进入了2.0时代。
《Attention is all you need》是Transformer首次被提出的论文,这篇论文在2017年一经发表后在google scholar的引用量出现了少有的长时间的攀升,此外关于Transformer的开源代码也大受欢迎。AI学术圈出现了对其大量模仿的现象,前后陆续很多论文标题都写出了X is all you need的形式。就算到了如今的2023年,关于AIGC的高水平论文依旧继续模仿这种形式,如《...,chatGPT is not all you need》。模仿这种标题形式的目的一般也是主要为了获得更大的关注,也是表明自己的文章有一个比较高的水平(水论文也不好意思来沾边)。此外,这篇的文章的结构和形式也被大量模仿。
八位作者同等贡献,排名随机
Google Brain
Ashish Vaswani:设计并实现了第一批Transformer模型,并重度参与了Transformer架构的各方面工作
Noam Shazeer:提出了缩放点积注意力、多头注意力和无参数位置表示
Lukasz Kaiser:设计和实现 tensor2tensor的各个部分,替换了早期的代码库,极大地改善了结果、加速了研究进度
Google Research
Niki Parmar:在原始代码库和tensor2tensor中设计、实现、调优和评估了无数的模型变量
Jakob Uszkoreit:提出用自注意力替代RNN
Llion Jones:尝试了新的模型变体,负责初始代码库以及高效的推理和可视化
Google Intern
Aidan N. Gomez:University of Toronto,[email protected],设计和实现tensor2tensor的各个部分,替换了早期的代码库,极大地改善了结果、加速了研究进度
Illia Polosukhin:设计并实现了第一批Transformer模型,并重度参与了Transformer架构的各方面工作
google brain与google research都是google专门研究人工智能的研究团队,Transformer提出论文就是google brain团队的代表成果。google brain是目前人工智能领域的最顶尖的研究团队之一,与他并驾齐驱的像有Deep Mind(Alphago)、openAI(chatGPT、DALL·E2)、微软研究院(Github Copilot)以及国内的Baidu Research(文心一言,百度大脑)等。
Transformer提出的论文发表在NIPS(Conference and Workshop on Neural Information Processing Systems,神经信息处理系统大会)上,NIPS是人工智能顶级会议之一,与其同一级别的还有CVPR、ICLR、ICCV、ECCV、AAAI、ICML、IJCAI、ACL等。大家最为熟知的最核心的四大顶会AAAI、IJCAI、ICML和NeurIPS,计算机视觉和自然语言为代表的CVPR和ACL这两大学术会议,有深度学习顶会“无冕之王”之称的ICLR,关于AI顶会推荐看这篇文章。
文章谈到,Transformer训练时间更少,并行度更好,效果也很好,模型架构也比较简单。Transformer模型在当时机器翻译的公开数据集(WMT 2014 English-to-German)中达到了28.4的BLEU score,比当时最好的结果要好2个BLEU score。
BLEU(BiLingual Evaluation Understudy)中文意思是“双语评估替补”。它是一种衡量机器翻译得到的文本(candidate)与参考译文(reference)之间相似程度的指标。“双语”意思是是在两个文本间进行评估,“评估”意思是衡量两个文本间的相似程度,“替补”意思是BLEU是一种替代人工评价的指标。在英文中understudy是替身演员的意思,原文中作者指出人力评估是高昂的、不可复用的,因此提出一种metric,来作为有经验的人类裁判的一种“替身”。评估的核心思想是接近专业人工翻译的机器翻译是更好的翻译。
在主流的序列转录(序列生成序列,即seq2seq,机器翻译中给一句英文生成一句中文)模型中,主要基于卷积或循环神经网络,一般使用encoder-decoder(编码器-解码器)架构。在性能最好的模型中,通常也会使用编码器-解码器之间使用注意力机制,这篇文章提出了一个新的简单的架构Transformer。作者表示用在Transformer机器翻译的两个任务上,性能表现的非常好,使用了更少的时间来训练。在公开数据集WMT 2014 English-to-German比最好的模型还要好了2个BLEU score。在公开数据集WMT 2014 English-to-French,做了一个单模型,比所有的模型的效果都要好,只在8个GPU上训练了3.5天,Transformer模型在其他的任务中也泛化的很好。
Transformer第一个仅仅使用注意力做序列转录的模型,把之前所有的循环程全部换成了Muti-Headed Self-Attention,在机器翻译任务中Transformer比其他架构训练得快得多,效果也非常的好。纯注意力机制也可以用在除了文本的其他任务上 ,包括图片、语音、视频等,让生成目标不那么时序化。最后的实现源码放在了Tensor2Tensor的库中。
在当前时序模型中,较常用的是RNN(因为当时是2017年),包括LSTM、GRU等等。在这里面有两个比较主流的模型一个叫做语言模型,一个是当你的输出结构化信息较多的时候,大家会用一个叫做编码器-解码器的架构。
RNN的特点和缺点是什么?在RNN里面,给你一个序列,它的计算是把这个序列从左往右一步一步往前做。假设一个序列是一个句子的话,即一个词一个词的看,对t个词,会计算一个输出叫做,也叫它的隐藏状态,它是由上一个隐藏状态叫做和当前第t个词本身决定的,这样他就能将历史信息通过放到当下,和当前词做一些计算,得到输出这也是RNN能够有效处理时序信息的一个关键所在,将之前的信息全部放在隐藏信息中,然后一个一个放下去。
但它的问题也来之这里,第一个因为它是一个时序,即一步一步计算的过程,比较难以并行,即算的词时,必须保证前面词输入完成,导致在时间上无法并行。在主流的GPU还有TPU(加速器)上有成千上万个线程,无法并行会导致并行度较低,使得在计算上性能较差。
第二个问题也是这个原因,因为历史信息是一步一步向后传递的,若时序比较长,在早期的时序信息,在后面的时候可能会丢掉,若不想丢掉,就会设计的比较大,在每个时间步都得存下来,导致内存空间耗费比较大。(虽然这一块大家在近些年也做了非常多的改进,比如分解啊为了并行度等等,但本质上还是没有解决太多问题)
在这篇文章之前,attention已经成功被用在了编码器和解码器之中了,主要是使编码器怎么有效传递给解码器这一部分。本文提出来transformer,是个新的模型,不再是之前大家使用的循环神经层,而是纯基于注意力机制,并行度大大提高较短时间达到较好效果。
如何使用卷积神经网络来替换掉循环神经网络使得减少时序的计算?卷积神经网络难以对长的时序进行建模,因为卷积的窗口很小,比如3x3的像素,如果两个像素间隔很远,则需要很多层卷积上一层一层叠起来才能融合这两个像素。
但若使用transformer里面的注意力机制的话,每次我都能看到所有的像素,一层就能把整个序列全部拿到。但卷积相对来说较好的地方是可以有多个输出通道,可以认为能识别不同的模式。多个输出通道这个非常有效,为达到这个效果,提出了muti-head attention,即多头注意力机制。
self-attention自注意力机制其实是transformer非常关键的一个点,但是论文明确表面self-attention并不是transformer提出来的。
memory networks是基于循环注意力机制,而不是带有符号的循环序列。End-to-end memory networks在简单语言问答和语言建模任务上表现出优秀性能。
Transformer是第一个只依赖于注意力去做encoder-decoder架构的模型
模型是深度学习论文最重要的章节,现在这些好的转录模型中表现比较好的是编码器和解码器的架构。编码器会将(,...,)(原始输入)映射为(,...,)(机器学习可以理解的向量)。比如原始输入是一个句子,这个句子有n个词,就表示第t个词,就是的向量表示。解码器拿到编码器的输出,生成一个长为m的序列(,...,)。n和m可以一样长,可以不一样长。
encoder和decoder的区别:encoder可以一次看到所有的词,但是decoder只能一个词一个词生成,auto-regressive自回归的模型。具体来说就是,编码器拿到n个词(,...,)后生成n个向量(,...,),然后解码器再依次生成y1,y2,如果要生成yt,必须要把所有的y1到yt-1全部拿到也就是说在翻译的时候,是一个词一个词往外蹦。
前面将的是传统的encoder-decoder做法,Transformer是将self-attention、point-wise、fully-connected layers进行堆叠行程encoder和decoder架构。左边就是编码器,右边就是解码器。如果是一个中文翻译英文的任务,Inputs就是中文句子。shifted right表示解码器在之前时候的一些输出作为此时的输入,一个一个右移。编码器和解码器都是由若干个Transformer Block组成。
一个Transformer Block主要由这三个基础元素构成:
Feed Forward:前馈神经网络 MLP
Multi-Head attention:多头注意力机制(可能包含自注意力、掩码机制)
Add & Norm:残差连接+Layernorm
Input Embedding表示将输入转换成向量,得到的向量值和Positional Encoding相加。Nx表示有N层,即N个Transformer的block叠在一起,比如ResNet中N个残差块的叠加。
编码器中,一个Transformer Block由两个子块组成,第一个子块由Multi-head Attention、Add & Norm组成,第二个子块由Feed Forward、Add & Norm组成,子块之间使用残差连接,最后使用layer normalization。两个子块组成了编码器的Transformer Block,N个编码器Transformer Block组成最后的编码器。
解码器中,一个Transformer Block由三个子块组成,第一个子块由Masked(掩码)Multi-head Attention、Add & Norm组成,第二个子块和第三个子块设计上与编码器一致。但是这里的解码器第二个子块的输入是编码器的输出和解码器第一个子块的输出。(解码器第一个子块的输入一个一个传递的)。这里的每个子块依旧使用的是残差连接,最后使用layer normalization,三个子块构成了解码器的Transformer Block,N个编码器Transformer Block组成最后的解码器。
编码器就是重复6个前面提到的编码器Transformer Block,所以前面提到的N=6。6个layer进行堆叠,每一个layer都由2个sub-layer组成。
第一个sub-layer就是Muti-head Attention,第二个sub-layer就是simple,position-wise fully connected feed-forward network(说白了就是一个MLP,名字长显得fancy)。
每个sub-layer都做残差连接和LayerNorm,公式:
LayerNorm(x+Sublayer(x))
Sublayer(x)指self-attention或者MLP。
残差连接的前提是,输入输出的维度保持完全一样,所谓残差连接就是下面这个公式:
y = f(x)+x
x就是原始输入,f(x)就是原始输入经过一个网络层后的输出,这个输出再加上原始输入x的输出结果,这样的网络设计方式就是残差连接。因此这里的y、f(x)、x三者的维度都是完全一致的。
(实际上就是一个数据x经过一个网络后,这个网络将其看成是一个函数将x映射成f(x),f(x)原本是这层网络的输出,我在这个输出上再加上原来的x,就这样一个简单的设计,就形成了大名鼎鼎的resnet)
在Transformer中,简单起见,固定每一层的输出维度dmodel=512
简单设计:只需调2个参数dmodel每层维度有多大和N多少层,影响后续一系列网络的设计,BERT、GPT
Remark:和CNN、MLP不一样。MLP通常空间维度往下减;CNN空间维度往下减,channel维度往上拉。
关于写作的建议:不要假设读者都知道所有的细节。可以的话,花几句话讲清楚内容
LayerNorm的Norm的意思就是Normalization即归一化,Layer即表示归一化的方式。一般情况下有两种选择,一种就是这里的LayerNorm,还有就是BatchNorm。两者相同之处就是,都需要对当前处理的向量进行相同的归一化操作:所有值减去均值,在除以方差。两者不同的地方就是,LayerNorm是对batch做Norm,而BatchNorm是对feature做Norm。
假设一个Seq(序列)有n个词,一个词有d个Feature,这里的d=512。所以d是不变的,而n是经常会变的,因为一个句子的长度即单词、汉字个数会经常发生变化。
batchnorm所做的事情是每一次把每一列,即每一个特征,在一个小mini-batch里面,将它的均值变成0方差变成1(即减去向量均值再除以方差),是在这条向量里面算出来它的均值与方差。在训练的时候可以做小批量,在预测的时候,会把一个全局的一个均值给算出来,可认为是整个数据扫描一遍之后,在所有数据上那些平均的均值方差存起来,预测的时候再使用。
假设一个mini-batch有m个样本,一个样本有n个左右的元素,也就是说每个样本的长度不同。BatchNorm会把每一个列看成一个向量,将这个向量重构成一个均值为0方差为1的新向量。这个重构的过程就是先将这个向量求出它的均值与标准差,然后将向量所有的元素都减去均值再除以方差。而layerNorm就是把一行看成一个向量,然后还是把每一个向量重构成一个均值为0方差为1的新向量。所以在实际任务中,LayerNorm可以看做是先将数据进行转置,再放入BatchNorm进行处理,将BatchNorm的处理结果再进行转置就是相当于进行了一次LayerNorm。
左边为BatchNorm的做法,右边为LayerNorm的做法:
在训练的时候可以做小批量,在预测的时候,会把一个全局的一个均值给算出来,可认为是整个数据扫描一遍之后,在所有数据上那些平均的均值方差存起来,预测的时候再使用 。
当然batchnorm还会学习一个β和lama塔出来,可以把这个向量通过学习,变成为任意方差、均值为某个值。
但在实际情况中,不管是transformer还是RNN,输入都是一个三维数据(一个batch的句子数量,一个句子词的数量,一个词的特征数量),这个时候的列变成了一个矩阵,也就是一个sequence,每个sequence都有d个feature,在本文中d=512,
BatchNorm(蓝色):
每次取一个特征,切一块(蓝色线),拉成一个向量,均值为0、方差为1的标准化。
LayerNorm(橙色):
横着切
LayerNorm为什么用的多?
时序数据中样本长度可能不一样。LayerNorm更稳定,不管样本长还是短,均值和方差是在每个样本内计算。
解码器和编码器主体上比较相似,也是由6个Transformer Block堆叠而成,但是解码器的Transformer Block是由3个sub-layer,然后每一个sub-layer之间全部用残差连接和LayerNorm全部不变。
第1个sub-layer是Masked Muti-head Attention,第2个sub-layer是Muti-head Attention,第3个sub-layer是Feed Forward,第2、3个和编码器的第1、2个是完全相同的。
解码器是auto-regressive(自回归)。当前时刻的输入集是之前一些时刻的输出。做预测时,解码器不能看到之后时刻的输出。但是注意力机制每一次能看完完整的输入,要避免这个情况的发生。
在解码器训练的时候,在预测第t个时刻的输出的时候,decoder不应该看到t时刻以后的那些输入。它的做法是通过一个带掩码 masked的注意力机制。-->保证训练和预测时行为一致。
一个注意力函数就是将一个query和一些key-value对映射成一个输出的函数。
Scaled Dot-Product Attention,可翻译为缩放点积注意力(具体怎么翻译没有约定的标准,仅用于理解)。输入由大名鼎鼎的Q(query),K(key,dk维),V(value,dv维)组成。
首先计算query和所有key的点积,点积除以缩放因子()得到一个结果,这个结果再经过softmax获得一组权重,这个权重对value进行重构就是注意力机制的结果。其中经过softmax计算得到的权重,代表的意义是query和key的相似度。
虽然key-value都没有发生变化,但是随着query的改变,因为权重的分配不一样,导致输出会有不一样,这就是所谓的注意力机制,这个过程用图片表示如下:
用公式表示如下:
Softmax补充说明:一个query给n个key-value pair,这个query会跟每个key-value pair做内积,会产生n个相似度值。传入softmax得到n个非负、求和为1的权重值。把softmax得到的权重值与value矩阵V相乘得到attention输出。
相似度补充说明:具体计算是说对每一个query和key做内积,然后把它作为相似度,可以认为两个向量做内积的事情,如果这两个向量的Norm是一样大的话,内积值越大,表示两个向量的相似度越高,如果两个向量内积等于零了,就说明两个向量是相交的,没有相似度。算出来之后再除以根号dk,就是你这个向量的长度,然后再用一个softmax得到权重。
实际计算:不会一个query一个query的计算,因为运算比较慢。把多个query写成一个矩阵,并行化运算。
Q:n*dk
K:m*dk
Q*K T:(n * dk) * (m * dk)T = (n * m)
一个query对所有key的内积值,然后再除以,再做softmax。softmax是对每一行的值做softmax,然后每一行之间是独立的,会得到权重。Attention的计算就是两次矩阵乘法、并行计算。
Scaled Dot-Product Attention补充说明:最简单的注意力机制。query和key的长度是等长的,都等于dk。value的维度是dv,输出也是dv。query和key也可以不等长,不等长用别的办法计算。
2种常见的注意力机制:加性的注意力机制(它可以处理你的query和key不等长的情况,点积dot-product的注意力机制(本文采用scaled,除),所以你可以看到它的名字它叫做scale的。
选用dot-product原因:两种注意力机制其实都差不多,点乘实现简单、高效,两次矩阵乘法计算。
scale dot-product原因除:防止softmax函数的梯度消失。
dk不是很大的时候,除不除都ok。dk比较大时(2个向量的长度比较长的时候),点积的值会比较大,or会比较小。
当你的值比较大的时候,相对的差距会变大,导致最大值softmax会更加靠近于1,剩下那些值就会更加靠近于0。值就会更加向两端靠拢,算梯度的时候,梯度比较小。softmax会让大的数据更大,小的更小。
因为softmax最后的结果是希望softmax的预测值,置信的地方尽量靠近,不置信的地方尽量靠近零,以保证收敛差不多了。这时候梯度就会变得比较小,那就会跑不动。
在trasformer里面一般用的dk比较大(本文512),所以除是一个不错的选择。
单头注意力就是self-Attention,即原始输入X经过self-Attention后生成Z向量。多头注意力,Muti-head,这里的head代表头数,所以这个head等于多少是自己定义的。
在3.4已经解释了,这篇论文的注意力机制是如何运行的,就是在原始数据的基础上生成一组Q,K,V向量,再由这组Q,K,V向量生成最后的注意力结果。而muti-head的做法,就是生成多组Q,K,V,使用attention得到多个结果,再将多个结果进行加权平均得到最后的结果。多头注意力机制的设计灵感来源于CNN的多通道特征提取。
用图与公式表示如下:
原始输入X,分别乘上一组可学习权重、、,生成一组Q,K,V向量
在过程1中是一组Q,K,V向量,多头则生成h组Q,K,V向量。生成的h组向量之间均有原始的X生成,但是各不相关,它表示的是类似于卷积X的多通道特征
h组Q,K,V向量均使用缩放点积注意力生成h个Z向量,使用concat拼接操作,将h个Z向量拼接成一个,再经过一组可学习参数生成最后的输出
Mask就是掩码的意思,由于Attention机制的缘故,每次都必须看到句子的全部,但是在生成的时候是一个一个生成的,也就是说在t时刻是不可以看到t时刻之后的东西的。因此作者设计了一个掩码机制,就是在解码器的时候内部,输入数据之前先经过一层Mask Multi-Attention,这个层网络会将t时刻之后的数据有效性给抹杀掉。
避免在t时刻,看到t时刻以后的输入。
在计算权重的时候,t时刻只用了(,...,)的结果,不要用到t时刻以后的内容。
把t时刻以后和的值换成一个很大的负数,如,进入softmax后,权重为0。V矩阵做矩阵乘法时,没看到t时刻以后的内容,只看t时刻之前的key-value pair。
可以从一个简单的角度理解,mask就是一个由0和1组成的矩阵,和Attention(scale QK)size一样,t时刻以后mask为0。
在解码器中,不再是使用的自注意力。解码器的K和V是来自编码器输出,而Q是来自解码器的Masked Multi-head Attention的输出。
编码器最后一层的输出是n个长度为d的向量,解码器的(Masked Multi-Head Attention+Add&Norm) 的输出是m个长为d的向量。
在Transformer模型中,一共有三种使用多头注意力的情况,在不考虑Transformer Block堆叠的情况,分别是编码器中的注意力层、解码器的掩码注意力层、解码器的第二个注意力层。
第一处使用,在如图中的1号标记处。编码器包含self-attention层。在self-attention层中,所有的key、value和query来自同一个地方,在这里是编码器中前一层的输出。编码器中的每个位置都可以关注编码器上一层的所有位置。
第二处使用,在如图中的2号标记处。在“编码器—解码器attention”层,query来自上面的解码器层,key和value来自编码器的输出。这允许解码器中的每个位置能关注到输入序列中的所有位置。这模仿序列到序列模型中典型的编码器—解码器的attention机制。
第三处使用,在如图中的3号标记处。类似地,解码器中的self-attention层允许解码器中的每个位置都关注解码器中直到并包括该位置的所有位置。我们需要防止解码器中的向左信息流来保持自回归属性。通过屏蔽softmax的输入中所有不合法连接的值(设置为-∞),我们在缩放版的点积attention中实现。
前面我们提到一个Transformer Block主要由三个部分组成,分别是注意力、前馈神经网络、残差连接+Layernorm。这里的前馈神经网络指的就是模型图中的Feed Forward,在3.1中我们提到过实际上是一个MLP。
文中的MLP全称是Position-wise Feed-Forward Networks,这里的MLP由两层linear组成,之间有一层ReLU:
MLP:原文解释,applied to each position separtely and identically
Point-wise:把一个MLP对每一个词(position)作用一次,对每个词作用的是同样的MLP
FFN:Linear+ReLU+Linear
单隐藏层的MLP,中间W1扩维到4倍2048,最后W2投影回到512维度大小,便于残差连接。
pytorch实现:2个线性层。pytorch在输入是3d的时候,默认在最后一个维度做计算。
也就是说原始输入有512维,将其映射成2048维,再经过ReLU后还是2048维,再经过W2映射成512维。
最简单情况:没有残差连接、没有LayerNorm、attention单头、没有投影,Attention实际上是对输入进行了一个加权和,进入point-wise MLP,point-wise MLP对每个输入的点做计算得到输出,这里的Attention作用实际上是把整个序列的信息抓取出来,做一次汇聚aggregation。
与RNN进行比较,RNN 是把上一个时刻的信息输出传入下一个时候做输入。Transformer 通过一个 attention 层,去全局的拿到整个序列里面信息,再用 MLP 做语义的转换。RNN与Transformer都是用一个线性层or一个MLP来做语义空间的转换。虽然都是NLP任务,但是如何有效的去使用序列的信息是最大的区别。
我们的输入是一个一个的词(=词源=token),需要将词映射成一个向量。embedding的意思是给任何一个词,学习一个长为d的向量表示它,在本文中d=512。
编码器、解码器、最后 softmax 之前的 3 个 embedding 共享权重。--> 训练更简单。
Note:权重*sqrt(dmodel = 512) ,学 embedding 的时候,会把每一个向量的 L2 Norm 学的比较小。
i.e. 学成 1,不论维度多大,最后的值都会 = 1。
维度大的化,学到的一些权重值就会变小,但之后还需要加上 positional encoding(不会随着维度的增加而变化)。
multiply weights by sqrt(dmodel) 使得 embedding 和 positional encosing 的 scale 差不多,可以做加法。
为什么Attention不会有时序信息呢?因为Transformer Block的注意力机制是对value进行重构,对value进行重构的权重是query和key之间的距离和序列信息无关。根本不看 key - value 对在序列哪些地方。一句话把顺序任意打乱之后,attention 出来,结果都是一样的。
把一句话的词相对顺序进行了改变,但是结果不变,这岂不是错误?
因此position encoding的作用就是加入时序信息(所谓时序信息就是先后顺序之间的关系),先看原文:
由于我们的模型不包含循环和卷积,为了让模型利用序列的顺序,我们必须注入序列中关于词符相对或者绝对位置的一些信息。 为此,我们将“位置编码”添加到编码器和解码器堆栈底部的输入嵌入中。位置编码和嵌入的维度dmodel相同,所以它们俩可以相加。 有多种位置编码可以选择,例如通过学习得到的位置编码和固定的位置编码[9]。
在这项工作中,我们使用不同频率的正弦和余弦函数:
其中pos 是位置,i 是维度。 也就是说,位置编码的每个维度对应于一个正弦曲线。 这些波长形成一个几何级数,从2π 到10000 ⋅ 2π。 我们选择这个函数是因为我们假设它允许模型很容易学习对相对位置的关注,因为对任意确定的偏移k, PEpos+k可以表示为PEpos的线性函数。
我们还使用学习到的位置嵌入9进行了试验,发现这两个版本产生几乎相同的结果(参见表 3 行(E))。 我们选择了正弦曲线,因为它可以允许模型推断比训练期间遇到的更长的序列。
思路:在RNN中将上一个时刻的输出加入到下一时刻的输入中,以此来传递时序信息。假设有一个词,它在位置i处,会将i位置信息加入到输入里面,如位置12345。
计算机表示一个 32 位的整数:32个 bit,每个 bit 上有不同的值来表示。
一个词在嵌入层表示成一个 512 维的向量,用另一个 512 维的向量来表示一个数字,位置信息 1 2 3 4 5 6 7 8......。
表示一个位置数字信息的值,怎么计算?
周期不一样的 sin 和 cos 函数计算 --> 任何一个值可以用一个长为 512 的向量来表示。
这个长为 512 、记录了时序信息的一个positional encoding,+ 嵌入层相加 --> 完成 把时序信息加进数据。
加入有一个英文句子,里面有7个单词,经过embedding后就会变成一个(7,512)的向量,由于position encoding的原因,它还会生成一个(7,512)的向量表示位置信息,而(7,512)数据的来源是用这两个公式计算出来的:、
在前面的内容中,论文主要介绍了模型整体架构,并对每个组件进行了解释。作者在这部分将进行解释,为什么这样做以及设计理念。
主要解释的是卷积、循环和自注意力使用上的区别,此外还与受限的自注意力做了对比。
如图所示,一共有三列进行比较。第一列,计算复杂度,越低越好;第二列,顺序的计算,越少越好,顺序的计算指的是你下一步的计算必须要等前面多少步计算完成,在算一个layer的时候,等待的越少,那么并行度就会越高;第三列,是指信息从一个数据点到另一个数据点需要走多远,也是越短越好。
一共有自注意力、循环、卷积、自注意力(带限制)这四种网络,分别介绍一下这四种网络的数值是如何计算的:
计算复杂度:,这里的n代表的是序列的长度,在NLP中也就是单词的个数,d则代表向量的长度。
其实说白了就是几个矩阵做运算,其中一个矩阵是你的query的矩阵乘以你的key的矩阵。
query矩阵有n行,即n个query,列数是d,即维度是d。
key也是一样的,也是n乘d,两个矩阵一乘的话,算法复杂度就是。
因为sequential operations就是那么几个矩阵乘法,矩阵里面可以认为并行度是比较高的,所以这个地方是O(1)。
最大长度是从一个点跳到另一个点要走多少步。在attention里面,就是一个query可以跟所有的key去做运算,而且输出是跟所有的value的一个加权和,所以就是说任何query跟任何一个很远的key value pair,只要一次就能过来,所以这个长度是比较短的。
循环层我们知道,如果你的序列是乘了n的话,它就一个一个做运算,每个里面它的主要计算就是一个d乘d的一个矩阵,就是一个dense layer,然后再乘以一个长为n的一个输入。
在计算复杂度上,与自注意相比取决于d和n谁比较大。在循环的时候是要一步一步做运算,当前时刻的词需要等待前面东西的完成,所以导致一个长为n的一个序列化的操作,在并行上效率很低。最初的历史信息到最后1点,需要n步才能走过去,所以rnn在第三列是O(n)
我们没有特别解释卷积在序列上怎么做。具体做法是它用一个1d的卷积,所以它的kernal就是k,不是k平,n是你的长度,d呢就是输入通道数和输出通道数。k一般就是3/5之类的,可以认为是常数
卷积的好处是经过一次卷积就完成了,里面的并行度很高,所以卷积做起来,通常比RNN要快一点。
卷积每一个是由一个长为k的窗口来看,所以一个信息在k距离内是能够一次传递,如果你超过了k,要传递信息的话,要通过多层一层一层上去,但它是一个log的操作,所以这个东西也不亏。
当我做注意力的时候,我的query只跟我最近的r个邻居去做运算,这样就不用去算n平方这个东西了,存在的问题是说,存在两个距离比较长的点,需要走几步才能过来。
一般来说,在实际中用attention主要是关心特别长的序列,能够把信息揉的较好一点,所以在实际过程中,self-attention(restricted)用的不是那么多,大家都是用最原始的版本。
(ps:如果在语音识别方向 会用到restricted,可以让语音识别变成实时的)
实际中,当你的序列长度和你模型的宽度差不多的时候,而且大家深度都一样的话,基本上三个模型的算法复杂度都是差不多的
当然你的attention和卷积相对来说计算会好一点,另外一个是说attention在信息的糅合性上会好一点,所以你可以认为这个地方还是能影响一些东西。
所以看上去是说用了self-attention之后对长数据处理得更好,而且算的不快也不会慢。
但实际上并不是这个样子,attention对模型得假设做了更少,导致需要更多得数据和更大得模型才能训练出来,达到跟rnn和cnn同样的效果。
所以导致现在基于transformer得模型呢,都是特别大、特别贵
本文主要使用了两个任务,一个是英语翻译德语,一个英语翻译法语,接下来分两个部分来介绍一下:
首先谈到了训练数据集和batch是如何处理的,前面谈到了LayerNorm和BatchNorm是分别对Feature和Batch做Norm。
英语翻译德语,用的是标准的WMT2014的任务,有450万个句子对,使用了bpe(byte-pair encoding)来做词嵌入。
大概的思想就是说,不管是英语还是德语,其实一个词里面有很多种变化,什么加ing,什么加es呀,但是直接把每一个词做成一个token的话,会导致字典里面东西比较多,而且一个东西可能会有几种变化形式,做成不一样的词的时候,之间的区别模型是不知道的,bpe相对来说就是把你的那些词根给你提出来,好处是可以将整个字典降的比较小。
Transformer英语翻译德语任务使用的是37000个token的一个字典,而且它是在英语和德语之间是共享的,就是说不再单独为英语或德语构造一个字典,好处是说我整个编码器和解码器的embedding就可以用一个东西了。模型也更加简单,也就是之前它所谓的编码器和解码器的embedding是共享权重的。
(ps:词向量,Word embedding,又叫Word嵌入式自然语言处理(NLP)中的一组语言建模和特征学习技术的统称,其中来自词汇表的单词或短语被映射到实数的向量。)
这个任务使用了一个更大的数据集,在接下来的硬件和schedule部分,训练使用了8个p100的GPU
(三年前google的大量工作还是使用GPU的,但是之后google就让内部员工尽量使用tpu,transformer里面基本都是比较大的矩阵做乘法,tpu适合处理大的矩阵乘法)
它说我们的base模型使用一个小一点的参数,每一个batch的训练时间是0.4s,一共训练了10w步,在8个GPU上训练了12个小时。就是基本上一台机器训练12个h也是不错的性能。
对于大的模型,一个batch训练需要1秒钟,他一共训练了30w步,一台机器3.5天。这其实也是可承受的范围,但之后所出现的工作很难承受。
在训练器上面使用的是Adam
β2应该不是最常用的,应该是0.99还是0.999,所以它选了一个稍微小一点的值。
学习率是用这个公式算出来的
有意思的是学习率是根据你模型的宽度的-0.5次方,就是说模型越宽,就是你学习的向量越长的时候,你的学习率要低一点,另外一点是它有个warmup,就是从一个小的值慢慢地爬到一个高的值,最后再跟你的步数按照0.5次方衰减,最后它说我的warmup_step是4000。
有意思的是几乎可以看到这个地方没有东西是可以调的,取决于:
一:adam对学习率没那么敏感
二:那里已经把模型考虑进来了,且warmup那里也算不错的schedule(时间表),所以学习率是不需要调的。
作者在一台具有8个NVIDIA P100 GPU的机器上训练Transformer模型。 使用本文描述的超参数的基础模型,每个训练步骤耗时约0.4秒。基础模型共训练了10万步约12小时。 对于我们的大模型(如表的底部所示),步长为1.0秒, 大模型训练了30万步(3.5天)。
训练期间采用三种正则化:
残差丢弃:我们将丢弃应用到每个子层的输出,在将它与子层的输入相加和规范化之前。 此外,在编码器和解码器堆栈中,我们将丢弃应用到嵌入和位置编码的和。 对于基本模型,我们使用Pdrop = 0.1丢弃率。
Label Smoothing:在训练过程中,我们使用的label smoothing的值为ϵls = 0.1[36]。 这让模型不易理解,因为模型学得更加不确定,但提高了准确性和BLEU得分。
在WMT 2014英语-德语翻译任务中,大型transformer模型(表2中的Transformer (big))比以前报道的最佳模型(包括整合模型)高出2.0 个BLEU以上,确立了一个全新的最高BLEU分数为28.4。 该模型的配置列在表3的底部。 训练在8 个P100 GPU上花费3.5 天。 即使我们的基础模型也超过了以前发布的所有模型和整合模型,且训练成本只是这些模型的一小部分。
在WMT 2014英语-法语翻译任务中,我们的大型模型的BLEU得分为41.0,超过了之前发布的所有单一模型,训练成本低于先前最先进模型的1 ∕ 4 。 英语-法语的Transformer (big) 模型使用丢弃率为Pdrop = 0.1,而不是0.3。
对于基础模型,我们使用的单个模型来自最后5个检查点的平均值,这些检查点每10分钟写一次。 对于大型模型,我们对最后20个检查点进行了平均。 我们使用beam search,beam大小为4 ,长度惩罚α = 0.6。 这些超参数是在开发集上进行实验后选定的。 在推断时,我们设置最大输出长度为输入长度+50,但在可能时尽早终止。
表2总结了我们的结果,并将我们的翻译质量和训练成本与文献中的其他模型体系结构进行了比较。 我们通过将训练时间、所使用的GPU的数量以及每个GPU的持续单精度浮点能力的估计相乘来估计用于训练模型的浮点运算的数量5。