论文:Attention is All You Need
参考:李沐视频【Transformer论文逐段精读】、Transformer论文逐段精读【论文精读】、李沐视频精读系列
一、摘要
主流的序列转换(sequence transduction)模型都是基于复杂的循环或卷积神经网络,这个模型包含一个编码器(encoder)和一个解码器(decoder)。具有最好性能的模型在编码和解码之间通过一个注意力机制链接编解码器。我们提出了一个新的简单网络架构——基于attention 的Transformer。其仅仅是基于注意力机制,而完全不需要之前的循环或卷积。在两个机器翻译任务上的实验BLEU 表明,该模型具有更好的性能,同时并行度更好,并且训练时间更少。(泛化到其它任务效果也不错)
二、结论
本文介绍了Transformer,这是第一个完全基于注意力的序列转换模型,用多头自注意力(multi-headed self-attention)代替了encoder-decoder架构中最常用的循环层。
三、导言
序列建模和转换问题(如机器翻译)最新方法是LSTM和GRN等。后面许多研究都围绕循环语言模型和编码器-解码器体系结构进行。
循环网络模型(RNN)通常是考虑了输入和输出序列的中字符位置的计算。当前时刻隐藏状态,是由上一时刻隐藏状态和时刻输入共同决定的。(把之前的信息都放在隐藏状态里,一个个传递下去,是RNN处理时序信息的关键)。这种固有的时序模型难以并行化处理,计算性能就很差。过早的历史信息可能被丢掉,时序信息是一步一步往后传递的,一个大的 存历史信息。每一个 计算步都需要存储,内存开销大。
attention在此之前,已经成功的应用在encoder-decoder架构中,但主要是用在如何把编码器的信息有效的传递给解码器,所以是和RNN一起使用的。
本文提出的Transformer,不再使用循环神经层,而是纯基于注意力机制,来构造输入和输出之间的全局依赖关系。Transformer可以进行更多的并行化,训练时间更短但翻译效果更好。
四、related work
使用卷积神经网络替换循环神经网络,并行计算所有输入和输出位置的隐藏表示,是扩展神经GPU,ByteNet和ConvS2S的基础,因为这样可以减少时序计算。但是CNN对长序列难以建模(因为卷积计算时,卷积核/感受野比较小,如果序列很长,需要使用多层卷积才可以将两个比较远的位置关联起来)。因为卷积计算的时候看一个比较小的窗口,比如, 3 * 3 窗口,如果 2 个像素隔得比较远,需要用很多 3 * 3 的卷积层、一层一层的叠加上去,才能把隔得很远的 2个像素联系起来。但是使用Transformer的注意力机制的话,每次(一层)就能看到序列中所有的位置,就不存在这个问题。
但是卷积的好处是,输出可以有多个通道,每个通道可以认为是识别不同的模式,作者也想得到这种多通道输出的效果,所以提出了Multi-Head Attention多头注意力机制。(模拟卷积多通道输出效果)
五、模型
大部分神经序列转换模型都使用encoder-decoder结构。编码器把一个输入序列映射成,比如一个句子有个词,是第个词,是第个词的向量表示。解码器对中的每个元素,生成输出序列,其中和可以一样长、可以不一样长,一个时间步生成一个元素。在每一步中,模型都是自回归的(auto-regressive),在生成下一个结果时,会将先前生成的结果加入输入序列来一起预测。(自回归模型的特点,过去时刻的输出可以作为当前时刻的输入)
Transformer遵循这种整体架构,对编码器和解码器使用堆叠的自注意力(stacked self-attention and point-wise)和逐点全连接层(fully-connected layers),分别如下图的左半部分和右半部分所示。
5.1 Encoder and Decoder Stacks
编码器:编码器由N=6个相同encoder层堆栈(下图标红部分)组成,每层有两个子层。
子层1:multi-head self-attention
子层2:FFNN层(前馈神经网络层,Feed Forward Neural Network),其实就是MLP,为了fancy一点,就把名字起的很长。
- 两个子层都使用残差连接(residual connection),然后进行层归一化(layer normalization)。
- 每个子层的输出做残差连接 和 LayerNorm,公式:LayerNorm(x + Sublayer(x)),其中Sublayer(x)是当前子层的输出。(Sublayer(x) 指 self-attention 或者 MLP)
- residual connections 需要输入输出维度一致,不一致需要做投影。为了简单起见,模型中的所有子层以及嵌入层的向量维度都是(如果输入输出维度不一样,残差连接就需要做投影,将其映射到统一维度)。(这和之前的CNN或MLP做法是不一样的,之前都会进行一些下采样)
tip:关于LayNorm和BatchNorm的区别以及画图对比
- BatchNorm简单的2维情况:每一行是一个样本X,每一列是一个feature。每次把一列(1个 feature)放在一个 mini-batch 里,均值变成 0, 方差变成 1 的标准化。
How:(该列向量 - mini-batch 该列向量的均值)/(mini - batch 该列向量的方差)
训练时:mini-batch 计算均值;
测试时:使用 全局 均值、方差。
BatchNorm 还会学 lambda1 beta,BatchNorm 可以通过学习将向量放缩成任意均值、任意方差 的一个向量。
- LayerNorm 跟 BatchNorm 在很多时候几乎是一样的,除了实现的方法有点不一样之外。二维输入情况:对每个样本做 Normalization(把每一行变成 均值为 0、方差为 1),不是对每个特征做normalization。
LayerNorm在操作上和BatchNorm (二维输入) 的关系:LayerNorm整个把数据转置一次,放到 BatchNorm里面出来的结果,再转置回去,基本上可以得到LayerNorm的结果。
BatchrNorm三维示意图:
列是seq序列长度n,第3维feature是每个词额外的向量,d = 512 in transformer
LayerNorm三维示意图(黄色的线):
为什么LayerNorm用的多?
首先看切割对比:举例分析:4个长度不一样的样本,0 填充到 max_len。
BatchNorm切出来的结果
BatchNorm 计算均值和方差,有效的是阴影部分,其余是0。
Mini-batch 的均值和方差:如果样本长度变化比较大的时候,每次计算小批量的均值和方差,均值和方差的抖动大。
全局的均值和方差:测试时遇到一个特别长的全新样本 (最上方蓝色阴影块),训练时未见过,训练时计算的均值和方差可能不好用。
LayerNorm切出来的结果
LayerNorm 每个样本自己算均值和方差,不需要存全局的均值和方差。它更稳定,不管样本长还是短,均值和方差是在每个样本内计算。
Batch Normalization:在特征d/通道维度做归一化,即归一化不同样本的同一特征。缺点是:
- 计算变长序列时,变长序列后面会pad 0,这些pad部分是没有意义的,这样进行特征维度做归一化缺少实际意义。
- 序列长度变化大时,计算出来的均值和方差抖动很大。
- 预测时使用训练时记录下来的全局均值和方差。如果预测时新样本特别长,超过训练时的长度,那么超过部分是没有记录的均值和方差的,预测会出现问题。
Layer Normalization:在样本b维度进行归一化,即归一化一个样本所有特征。
- NLP任务中一个序列的所有token都是同一语义空间,进行LN归一化有实际意义
- 因为实是在每个样本内做的,序列变长时相比BN,计算的数值更稳定。
- 不需要存一个全局的均值和方差,预测样本长度不影响最终结果。
解码器:解码器同样由 N=6个相同的decoder层堆栈组成,每个sub-layer的 residual connections、layer normalization。每个layer里有 2个 encoder 中的 sub-layers, decoder 有第 3 个 sub-layer,对 encoder 的输出做 multi-head attention。decoder是auto-regressive自回归。当前时刻的输入集是之前一些时刻的输出。做预测时,decoder不能看到之后时刻的输出。
- 子层1:Masked multi-head self-attention:在解码器里,Self Attention 层只允许关注到输出序列中早于当前位置之前的单词。具体做法是:在 Self Attention 分数经过 Softmax 层之前,使用attention mask,屏蔽当前位置之后的那些位置。(目的:在预测第个时刻的输出的时候,decoder不应该看到时刻以后的那些输入)所以叫Masked multi-head self Attention。(对应masked位置使用一个很大的负数-inf,使得softmax之后其对应值为0)
- 子层2:Encoder-Decoder Attention :编码器输出最终向量,将会输入到每个解码器的Encoder-Decoder Attention层,用来帮解码器把注意力集中中输入序列的合适位置。
- 子层3:FFNN
5.2 注意力机制
注意力函数是一个将一个query和一些key - value对映射成一个输出的函数,其中所有的query、key、value和output都是一些向量。具体来说,output是value的一个加权和-->输出的维度== value的维度。output 中 value 的权重=查询query和对应key的相似度orcompatibility function权重等价于 query和对应的key的相似度。
5.2.1 缩放的点积注意力(Scaled Dot-Product Attention)
缩放的点积注意力:
- 其输入为query、key(维度是)以及values(维度是)。
- 计算query和所有key的点积,得到两个向量的相似度(结果越大相似度越高);然后对每个点积结果除以。(除以目的是防止softmax函数的梯度消失)
- 点积结果输入softmax函数获得value的权重。
- 最后对value进行加权求和。
在实践中,我们同时计算一组query的attention函数,并将它们组合成一个矩阵。key和value也一起组成矩阵和。我们计算的输出矩阵为:
、矩阵的序列长度是一样的(加权求和),而矩阵的序列长度可以和前两者不一样;这种情况发生在:解码器部分的Encoder-Decoder Attention层中, 矩阵是来自解码器输出tgt,而、 矩阵则是来自编码器最后的输出memory。即tgt2 = self.multihead_attn(tgt, memory, memory, attn_mask=memory_mask,key_padding_mask=memory_key_padding_mask)[0]。但是和的维度必须一样,因为要计算点积。
Scaled Dot-Product Attention和别的注意力机制的区别:
2种常见的注意力机制:
- 加性的注意力机制(它可以处理你的 query 和 key 不等长的情况)
- 点积 dot-product 的注意力机制 (本文采用 scaled,除以 ),所以你可以看到它的名字它叫做 scale 的。
5.2.2 多头注意力机制(Multi-head attention)
与其做一个单个的注意力函数,不如说把整个query、key、value整个投影到 1个低维,投影次。然后再做次的注意力函数,把每一个函数的输出拼接在一起,然后继续投影,会得到最终的输出。
其中映射由权重矩阵完成:
.
本文采用个平行attention层或者叫head。因为有残差连接的存在使得输入和输出的维度至少是一样的。对于这些head中的每一个,我们使用,总计算成本与具有全部维度的单个head attention相似。
- 输入X和8组权重矩阵相乘,得到8组矩阵。进行attention计算,得到 8 组 矩阵(假设head=8)
- 把8组矩阵拼接起来,乘以权重矩阵,将其映射回维向量(相当于多维特征进行汇聚),得到最终的矩阵。这个矩阵包含了所有 attention heads(注意力头) 的信息。
- 矩阵Z会输入到 FFNN层。(前馈神经网络层接收的也是 1 个矩阵,而不是8个。其中每行的向量表示一个词)
应用例子:翻译Hello World --> 你好世界
计算 “好” 的时候,“好”作为 query,会跟 “hello” 向量更相近一点,给 “hello” 向量一个比较大的权重。但是 “world”跟后面的词相关, “world”跟当前的query (“好” )相关度没那么高。在算 “好” 的时候,我会给“hello” 向量一个比较大的权重。在算query“世” 的时候,会给第二个 “world”向量,一个比较大的权重。根据解码器的输入的不一样,会根据当前的query 向量,去在编码器的输出里面去挑我(当前 query)感兴趣的东西。
attention:query 注意到 当前的 query 感兴趣的东西,对当前的 query的不感兴趣的内容,可以忽略掉。attention 作用:在encoder和decoder之间传递信息。
5.2.3 基于位置的前馈神经网络(Position-wise Feed-Forward Networks)
作用在最后一个维度的 MLP。
- Position-wise: 把一个 MLP 对每一个词 (position)作用一次,对每个词作用的是同样的 MLP
- FFN: Linear + ReLU + Linear
单隐藏层的 MLP,中间 扩维到4倍 2048,最后 投影回到 512 维度大小,便于残差连接。
编码器和解码器中的每个层都包含一个全连接的前馈网络,该前馈网络分别且相同地应用于每个位置。该前馈网络包括两个线性变换,并在两个线性变换中间有一个ReLU激活函数。
Position就是序列中每个token,Position-wise 就是把MLP对每个token作用一次,且作用的是同一个MLP。说白了就是MLP只作用于最后一个维度d=512。 因为前面的attention层以及抓取了输入序列的相关信息,并做了一次汇聚(拼接后W映射回d维)。所以attention层结果已经有了序列中我感兴趣的信息,所以后面在做MLP投影映射到想要的语义空间时,只需要对每个position(token)单独做MLP就行。从attention抽取序列信息到MLP映射到需要的语义空间(非线性变换),就整个是transformer的处理信息的基础过程。
attention 作用:把整个序列里面的信息抓取出来,做一次汇聚(aggregation)。
对比transformer和RNN,发现两者都是使用MLP来进行语义空间的转换,但区别是二者传递信息的方式不一样:
- RNN是把上一时刻信息作为输入(和t时刻输入一起),传递给当前时刻,并用MLP做语义转换。
- Transformer是通过attention层直接关联到全局的序列信息,然后用MLP做语义转换。
5.2.4 Embeddings and Softmax
将输入的一个词、词语token映射成为一个长为的向量。学习到的长为 的向量来表示整个词、词语token。我们还使用普通的线性变换和softmax函数将解码器输出转换为预测的下一个token的概率。在我们的模型中,输入输出两个嵌入层,和pre-softmax线性变换共享相同的权重矩阵(这样训练起来简单一些)。最后我们将这些权重乘以(本文d为512)。编码器、解码器、最后softmax之前的3个embedding 共享权重。这样的好处是训练更简单。
这是因为一般会把一个向量的L2Norm学到接近1,这样向量维度越大,这样学到的权重值就会很小。但是位置编码是不会这样学成L2Norm(L2范数)接近1的。所以把权重乘上之后,token embedding和位置编码Positional Encoding才接近统一量级。(都在-1到1之间)
5.2.5 位置编码(Positional Encoding)
Attention计算时本身是不考虑位置信息的,这样序列顺序变化结果也是一样的。所以我们必须在序列中加入关于词符相对或者绝对位置的一些信息。
为此,我们将“位置编码”添加到token embedding中。二者维度相同,所以可以相加。有多种位置编码可以选择,例如通过学习得到的位置编码和固定的位置编码。
在这项工作中,使用不同频率的正弦和余弦函数:
其中是位置,是维度。也就是说,位置编码的每个维度对应于一个正弦曲线。 这些波长形成一个从到的集合级数。我们选择这个函数是因为我们假设它会让模型很容易学习对相对位置的关注,因为对任意确定的偏移,可以表示为 的线性函数。最终编码向量每个元素值都是在-1到1之间。
此外,我们会将编码器和解码器堆栈中的embedding和位置编码的和再加一个dropout。对于基本模型,我们使用的dropout比例是。
六、结论
为什么要使用自注意力机制?
- 每层计算的总复杂度,越少越好
- 顺序计算量,越少代表并行度越高。(顺序计算量就是下一步需要前面多少步计算完成)
- 网络中长距离依赖之间的路径长度。影响长距离依赖性能力的一个关键因素是前向和后向信号在网络中传播的路径长度。输入和输出序列中任意位置之间的这些路径越短,学习长距离依赖性就越容易。因此,我们还比较了由不同图层类型组成的网络中任意两个输入和输出位置之间的最大路径长度。
上图n是序列长度,d是token维度。
Attention 没有对数据的顺序建模,为什么比RNN好呢?
RNN 显示的建模了序列信息,理论应该比 attention 效果更好。但是Attention 用了更广泛的 inductive bias(归置偏置),使得Attention没有用空间上的假设,取得和 CNN 一样、 甚至更好的结果。
- CNN的inductive bias应该是locality和spatial invariance,即空间相近的grid elements有联系而远的没有,和空间不变性(kernel权重共享)
- RNN的inductive bias是sequentiality和time invariance,即序列顺序上的timesteps有联系,和时间变换的不变性(rnn权重共享)
七、评价
对Transformer中attention的理解:attention只是起到把整个序列的信息聚合起来的作用,后面的MLP和残差连接是缺一不可的。去掉MLP和残差连接,只有attention,也什么都训练不出来。
启示:一个新的模型可以在DL上通用。人的感知是多模态的、使得Transformer在文本、语音、视频抽取多维特征。