Transformer:没错,你只需要注意力机制
首先先说说自己对 Transformer 理解,我认为它最大的改进有如下几点:
- 提出用注意力机制来直接学习源语言内部关系和目标语言内部关系,而不是像之前用 RNN 来学;
- 对存在多种不同关系的假设,而提出多头 (Multi-head) 注意力机制,有点类似于 CNN 中多通道的概念;
- 对词语的位置,用了不同频率的 sin 和 cos 函数进行编码;
缺点在原文中没有提到,是后来在Universal Transformers中指出的,在这里加一下吧,主要是两点:
- 实践上:有些rnn轻易可以解决的问题transformer没做到,比如复制string,尤其是碰到比训练时的sequence更长的时
- 理论上:transformers非computationally universal(图灵完备),(我认为)因为无法实现“while”循环
整体结构
1.1 Encoder
Encoder由N=6个相同的layer组成,layer指的就是上图左侧的单元,最左边有个“Nx”,这里是x6个。每个Layer由两个sub-layer组成,分别是multi-head self-attention mechanism和fully connected feed-forward network。其中每个sub-layer都加了residual connection和normalisation,因此可以将sub-layer的输出表示为:
接下来按顺序解释一下这两个sub-layer:
- Multi-head self-attention
熟悉attention原理的童鞋都知道,attention可由以下形式表示:
multi-head attention则是通过h个不同的 线性变换 对Q,K,V进行投影,最后将不同的attention结果拼接起来:
self-attention则是取Q,K,V相同。
另外,文章中attention的计算采用了scaled dot-product,即:
作者同样提到了另一种复杂度相似但计算方法additive attention,在 很小的时候和dot-product结果相似,
大的时候,如果不进行缩放则表现更好,但dot-product的计算速度更快,进行缩放后可减少影响(由于softmax使梯度过小,具体可见论文中的引用)。
- Position-wise feed-forward networks
第二个sub-layer是个全连接层,之所以是position-wise是因为处理的attention输出是某一个位置i的attention输出。
1.2 Decoder
Decoder和Encoder的结构差不多,但是多了一个attention的sub-layer,这里先明确一下decoder的输入输出和解码过程:
- 输出:对应i位置的输出词的概率分布
- 输入:encoder的输出 & 对应i-1位置decoder的输出。所以中间的attention不是self-attention,它的K,V来自encoder,Q来自上一位置decoder的输出
- 解码: 这里要特别注意一下,编码可以并行计算,一次性全部encoding出来,但解码不是一次把所有序列解出来的,而是像rnn一样一个一个解出来的 ,因为要用上一个位置的输入当作attention的query
明确了解码过程之后最上面的图就很好懂了,这里主要的不同就是新加的另外要说一下新加的attention多加了一个mask,因为训练时的output都是ground truth,这样可以确保预测第i个位置时不会接触到未来的信息。
加了mask的attention原理如图(另附multi-head attention):
1.3 Positional Encoding
除了主要的Encoder和Decoder,还有数据预处理的部分。Transformer抛弃了RNN,而RNN最大的优点就是在时间序列上对数据的抽象,所以文章中作者提出两种Positional Encoding的方法,将encoding后的数据与embedding数据求和,加入了相对位置信息。
这里作者提到了两种方法:
- 用不同频率的sine和cosine函数直接计算
- 学习出一份positional embedding(参考文献)
经过实验发现两者的结果一样,所以最后选择了第一种方法,公式如下:
作者提到,方法1的好处有两点:
- 任意位置的
都可以被
的线性函数表示,三角函数特性复习下:
2. 如果是学习到的positional embedding,(个人认为,没看论文)会像词向量一样受限于词典大小。也就是只能学习到“位置2对应的向量是(1,1,1,2)”这样的表示。所以用三角公式明显不受序列长度的限制,也就是可以对 比所遇到序列的更长的序列 进行表示。
编码部分(inputs):
1. Input embedding:
1.1 将原文的所有单词汇总统计频率,删除低频词汇(比如出现次数小于20次的统一
定义为’<UNK>’);此时总共选出了假设10000个单词,则用数字编号为0~9999,一一对应,定义该对应表为word2num;然后用xaviers方法生成随机矩阵Matrix :10000行N列(10000行是确定的,对应10000个单词,N列自定义);这样就可以将10000个不同的单词通过word2num映射成10000个不同的数字(int),然后将10000个不同的数字通过Matrix映射成10000个不同的N维向量(如何映射?比如数字0,3,经过 Matrix映射分别变为向量Matrix[0],Matrix[3],维度为N维);这样,任何一个单词,都可以被映射成为唯一的一个N维向量;
Note:此处N自定义为512
1.2 翻译的时候是一个句子一个句子的翻译,所以需要定义一个句子的标准长度,比如10个单词;如果一句话不足10个单词则用0填充(对应的word即word2num表中的<Pad>),如果多了,删掉;这样一句话就是标准的10个单词;比如句子 “中国人有中国梦。”,这句话共有八个字(最后一个是结束符),经过word2num变为一列X:[1,7,3,2,1,7,6,100,0,0](注:100代表的word是结束符),X经过Matrix映射为10行N列的矩阵matX= [Matrix[1], Matrix[7], Matrix[3], Matrix[2] , Matrix[1] , Matrix[7] , Matrix[6], Matrix[100] , Matrix[0] , Matrix[0]]; embedding 到此基本结束,即完成了将一句话变为 一个矩阵,矩阵的每一行代表一个特定的单词;此处还可以scale一下,即matX*N**(1/2);
2. Positional encoding:
2.1 单词在句子中的不同位置体现了不同信息,所以需要对位置进行编码,体现不同的信息情况,此处是对绝对位置进行编码,即位置数字0,1,2,3,…N等,进行运 算编码,具体编码如下:
2.1.1 对于句子中的每一个字,其位置pos∈[0,1,2,…,9](每句话10个字),每个字是N(512)维向量,维度 i (i∈[ 0,1,2,3,4,..N])带入函数
2.1.2 经过如上函数运行一次后,获得了一个10行N列的矩阵matP;每一行代表一个绝对位置信息,此时matP的shape和matX的shape相同;
2.1.3 对于矩阵matP的每一行,第0,2,4,6,...等偶数列上的值用sin()函数激 活,第1,3,5,。。。等奇数列的值用cos()函数激活,以此更新matP;即 matP[:,0::2]=sin(matP[:,0::2]), matP[:,1::2]=cos(matP[:,1::2]);
2.2 至此positional encoding结束,最后通常也会scale一次,即对更新后的matP进行matP*N**(1/2)运算,得到再次更新的matP,此时的matP的shape还是和matX相 同;然后将matP和matX相加即matEnc=matP+matX,矩阵matEnc其shape=[10,512];
3. Multi-head attention---add&Norm---Feed Forward---add&norm 循环单元
3.1 然后matEnc进入模型编码部分的循环,即Figure1中左边红色框内部分,每个循环 单元又分为4个小部分:multi-head attention, add&norm, feedForward, add&norm;
3.2 Multi-head attention
3.2.1 Multi-head attention 由三个输入,分别为V,K,Q,此处V=K=Q=matEnc(在解码部分multi-head attention中的VKQ三者不是这种关系);
3.2.2 首先分别对V,K,Q三者分别进行线性变换,即将三者分别输入到三个单层神经网络层,激活函数选择relu,输出新的V,K,Q(三者shape都和原来shape相同,即经过线性变换时输出维度和输入维度相同);
3.2.3 然后将Q在最后一维上进行切分为num_heads(假设为8)段,然后对切分完的矩阵在axis=0维上进行concat链接起来;对V和K都进行和Q一样的操作;操作后的矩阵记为Q_,K_,V_;
3.2.4 Q_矩阵相乘 K_的转置(对最后2维),生成结果记为outputs,然后对outputs 进行scale一次更新为outputs;此次矩阵相乘是计算词与词的相关性,切成多个num_heads进行计算是为了实现对词与词之间深层次相关性进行计算;
3.2.5 对outputs进行softmax运算,更新outputs,即outputs=softmax(outputs);
3.2.6 最新的outputs(即K和Q的相关性) 矩阵相乘 V_, 其值更新为outputs;
3.2.7 最后将outputs在axis=0维上切分为num_heads段,然后在axis=2维上合并, 恢复原来Q的维度;
3.3 Add&norm
3.3.1 类似ResNet,将最初的输入与其对应的输出叠加一次,即outputs=outputs+Q, 使网络有效叠加,避免梯度消失;
3.3.2 标准化矫正一次,在outputs对最后一维计算均值和方差,用outputs减去均值除以方差+spsilon得值更新为outputs,然后变量gamma*outputs+变量beta;
3.4 feedForward
3.4.1 对outputs进行第一次卷积操作,结果更新为outputs(卷积核为1*1,每一次卷积操作的计算发生在一个词对应的向量元素上,卷积核数目即最后一维向量长度,也就是一个词对应的向量维数);
3.4.2 对最新outputs进行第二次卷积操作,卷积核仍然为1*1,卷积核数目为N;
3.5 Add&norm : 和3.3相同,经过以上操作后,此时最新的output和matEnc的shape相同;
3.6 令matEnc=outputs, 完成一次循环,然后返回到3.2开始第二次循环;共循环Nx(自定义;每一次循环其结构相同,但对应的参数是不同的,即是独立训练的);完成Nx次后,模型的编码部分完成,仍然令matEnc=outputs,准备进入解码部分;
解码部分:
1. Outputs:shifted right右移一位,是为了解码区最初初始化时第一次输入,并将其统一定义为特定值(在word2num中提前定义);
2. Outputs embedding: 同编码部分;更新outputs;
3. Positional embedding:同编码部分;更新outputs;
4. 进入解码区循环体,即figure1中右侧红框内的部分;
4.1 Masked multi-head attention: 和编码部分的multi-head attention类似,但是多了一 次masked,因为在解码部分,解码的时候时从左到右依次解码的,当解出第一个字的时候,第一个字只能与第一个字计算相关性,当解出第二个字的时候,只能计算出第二个字与第一个字和第二个字的相关性,。。。;所以需要linalg.LinearOperatorLowerTriangular进行一次mask;
4.2 Add&norm:同编码部分,更新outputs;
4.3 Multi-head attention:同编码部分,但是Q和K,V不再相同,Q=outputs,K=V=matEnc;
4.4 Add&norm:同编码部分,更新outputs;
4.5 Feed-Forward:同编码部分,更新outputs;
4.6 Add&norm: 同编码部分,更新outputs;
4.7 最新outputs和最开始进入该循环时候的outputs的shape相同;回到4.1,开始第 二次循环。。。;直到完成Nx次循环(自定义;每一次循环其结构相同,但对应的参数是不同的,即独立训练的);
5. Linear: 将最新的outputs,输入到单层神经网络中,输出层维度为“译文”有效单词总数;更新outputs;
6. Softmax: 对outputs进行softmax运算,确定模型译文和原译文比较计算loss,进行网络优化;
Weighted Transfomer: 加权就是加强,没错就是这样
在最初的 Transformer 后不久,最成功的 Transformer 的变种模型便是,Weighted(加权的) Transformer。
在整体上架构和上面提到的 Transformer 差不多,最主要的不同是对注意力机制的处理。在 Transformer 中,先通过多头注意力机制来学习多个不同方面特征,然后拼接起来传给一个线性层。
而 Weighted Transformer 则是通过两种权重值,并且将一些不同的线性层放入注意力机制中,使得各个头(head)更加多样,互动更加强。
两个权值分别是κ和α,κ 是在多头注意力机制后,对每个头进行权值加成,论文中称为 concatenate weight (拼接权重,但我并没发现有对多头进行拼接的操作,有懂的可以告诉我。) 而 α 是将 κ 加权处理后再经过一层前馈处理(就是上面的 Feed Forward,FFN)的值再次进行加权,最后将这些头直接加起来。
公式如下:
这篇论文中将这种注意力机制叫做,multi-branch attention (多支注意力机制)。值得注意的一点是,在 Weighted Transformer 中并不是三处都是用相同的多支注意力机制,在解码器捕捉目标语言内部关系的注意力机制就只用了普通的注意力机制,不知道是刻意为之还是什么。
Universal Transformer: 通用 Transformer
最近 Transformer 又得到新的突破,在谷歌大脑实习的 Mostafa Dehghan 提出更强大的 Transformer 模型:Universal Transformer,比之前的 Weighted Transformer 更进一步。
首先是比起前两篇更加规范化了,模型里面的基本模块,主要由多头注意力机制和转换函数组成。这篇论文中最主要的 idea 是,Transformer 最主要是利用注意力机制通过查看相关信息,对每个位置词语的向量进行不停的精炼。论文中几个创新点是:
-
基本模块很简洁,多头注意力机制加转换函数,不停堆叠对词语向量进行精炼;
-
每一次精炼就是一个 step ,加上句子里的位置,就会产生一个二维的位置坐标,论文中对这个位置进行了向量化,并且加入了运算中(但是我并没有懂这样做的用途,因为使用的只有最后一层的);
-
假设一句话中每个词需要精炼的程度不一样,利用 Adaptive Computation Time 方法来使得对需要多次精炼的多次精炼,而对已经精炼足够多次的不予处理。
基本模块中的公式如下:
而其中 t 等于 0的时候,也就是 H_0 的时候表示的就是词向量,之后按照上面的公式计算。
在对二位坐标进行编码时,使用的是如下公式:
Universal Transformer的产生是因为Transformer在实践上和理论上的两个缺点(参考上篇文章),universal代表的是computationally universal,即图灵完备(参考Transformer详解第三节)。主要的改动就是加上了循环,但不是时间上的循环,而是depth的循环。注意到Transformer模型其实分别用了6个layer,是fixed depth,而universal中应用了一个机制对循环的次数进行控制。
1. 模型结构
模型的结构还是和传统Transformer很相似,这里就不重复解读了,主要讲一下universal transformer的几点改动:
1.1 Recurrent机制
在Transformer中,input在经过multihead self-attention后会进入fully connected层,这里则进入了Transition层,通过一个 共享权重 的transition function继续循环计算:
这里纵向的position指的就是一个序列中各个symbol的位置(也就是在rnn中的time step),横向的time指的主要是计算上的先后顺序,比如一个序列 ,先经过embedding表示成
,在经过一层attention+transition表示成
。如果是rnn,那就要先计算
,再计算
和
,而transformer的self-attention可以同时计算
,再计算t+1的。
这样,每个self-attention+transition的输出 可以表示为:
这里Transition function可以和之前一样是fully-connected layer,也可以是separable convolution layer。
1.2 Coordinate embeddings
Transformer的positional embedding只用考虑symbol的position就可以了,这里又多了一个time维度,所以每一次循环都会重新做一次coordinate embedding,图上没有表示出来,需要看源码确认一下。Embedding公式如下:
暂时还没想清楚为什么这么运算,想清楚了说一下。。
1.3 Adaptive Computation Time (ACT)
ACT可以调整计算步数,加入ACT机制的Universal transformer被称为Adaptive universal transformer。要注意的细节是,每个position的ACT是独立的,如果一个position a在t时刻被停止了, 会被一直复制到最后一个position停止,当然也会设置一个最大时间,避免死循环。
2. 总结
Universal Transformer对transformer的缺点进行了改进,在问答、语言模型、翻译等任务上都有更好的效果,成为了新的seq2seq state-of-the-art模型。它的关键特性主要有两点:
- Weight sharing:归纳偏置是关于目标函数的假设,CNN和RNN分别假设spatial translation invariace和time translation invariance,体现为CNN卷积核在空间上的权重共享和RNN单元在时间上的权重共享,所以universal transformer也增加了这种假设,使recurrent机制中的权重共享,在增加了模型表达力的同时更加接近rnn的inductive bias。
- Conditional computation:通过加入ACT控制模型的计算次数,比固定depth的universal transformer取得了更好的结果
Gaussian Transformer: 一种自然语言推理的轻量方法
模型简介
在实现上, 我们可以通过一系列化简 (具体细节请参看我们的论文原文), 把 Gaussian self-attention 转化为 Transformer 中的一次矩阵加法操作, 如图 2 所示, 从而节省了运算量。 此外,我们发现,与使用原始的 Gaussian 分布作为先验概率相比,适当的抑制到单词自身的 attention可以对最终的实验结果有少许的提升,如图 3(b) 所示。
图 2. Attention 示例: (a) 原始的 dot-product attention;(b)&(c) Gaussian self-attention 的两种实现
图 3. 先验概率示例: (a) 原始的 Gaussian prior;(b) 抑制到自身的 Gaussian prior 变种
图 4 展示了我们模型的整体框架。 如图所示, 模型自底向上大致分为四个部分:Embedding模块、编码 (Encoding) 模块、交互 (Interaction) 模块和对比 (Comparison) 模块。
图 4. Gaussian Transformer 整体框架
Embedding 模块的作用是把自然语言文本转化为机器方便处理的向量化表示, 我们使用了单词和字符级别的 Embedding,以及 Positional Encoding。
Encoding 模块与原始的 Transformer 的 Encoder 非常类似,只是我们增加了前文引入的 Gaussian self-attention 以便更好的建模句子的局部结构。 但事实上, 句子中也存在长距离依赖, 仅仅建模句子的局部结构是不够的。为了捕获句子的全局信息,我们堆叠了 M 个 Encoding 模块。这种方式类似于多层的 CNNs 网络,层数较高的卷积层的 receptive field 要大于底层的卷积。
Interaction 模块用于捕获两个句子的交互信息。 这一部分与原始的 Transformer 的 Decoder 部分类似, 区别是我们去掉了 Positional Mask 和解码的部分。 通过堆叠 N 个 Interaction 模块,我们可以捕获高阶交互的信息。
Comparison 模块主要负责对比两个句子,分别从句子的 Encoding 和 Interaction 两个角度对比,这里我们没有使用以前模型中的复杂结构,从而节省了大量的参数。
用于信息检索的 IR-Transformer
搜狗搜索对机器翻译 Transformer 模型进行了一定的改造,建立了用于信息检索的 IR-Transformer 模型。下文将简要介绍我们模型的架构。
类似于原始的 Transformer 模型,IR-Transformer 模型也分为两大模块:处理查询信息的 query 编码器(记为 q 编码器)和处理网页标题的 title 编码器(记为 t 编码器)。注意,由于处理信息检索问题时仅需分析已有网页,而不需要生成文字或者网页,所以 IR-Transformer 架构中不包含解码器。
q 编码器、t 编码器均为多层结构,较低层的输出作为较高层的输入被进一步处理,不同层的内部结构相同。每层 q 编码器由两个亚层组成,而每层 t 编码器由三个亚层组成。q 编码器和 t 编码器的第一个亚层主要执行 self multi-head attention 操作;q 编码器和 t 编码器的最后一个亚层主要执行非线性变换操作;t 编码器的第二层主要执行 q->t multi-head attention 操作。
细心的读者会发现,该模型共用到两种 multi-head attention 操作,即 self multi-head attention(两种编码器均采用)以及 q->t multi-head attention(仅用于 t 编码器)。实际上,这两种 multi-head attention 的差别仅在于输入的来源有所不同:前者的输入只包含自身相关的信息;而后者的输入不仅包含 t 编码器自身的信息,还包含来自 q 编码器的信息。
为了更形象的说明 multi-head attention 的运算过程,我们在图 2 中展示了一个具体的例子:假设 q 编码器的输入(即用户的查询)是「今天/天气/如何」(斜杠代表分词),一个待分析网页的标题是「近期/天气/汇总」。那么,对于 q 编码器而言,如图 2(a) 所示,执行 self multi-head attention 运算意味着要计算 N 个相关性矩阵(每个矩阵由如 (今天, 今天),(今天, 天气),(今天, 如何) 等词对的相关性数值组成)。换言之,词与词之间的相关性即为 attention 机制,而计算 N 个相关性矩阵意味着 multi-head。由于词对均由 q 编码器的输入组成,所以对于 q 编码器而言,这个操作也叫 self multi-head attention。利用 N 个相关性矩阵以及查询语句的初始语义向量(embedding),「今天」、「天气」、「如何」的语义可被进一步深化为三个新向量,如图 2(a) 的大括号右侧所示。
相对于未经过 self multi-head attention 的语义向量,新的语义向量含有了上下文的信息(信息来自 N 个相关性矩阵),内涵更加丰富、全面。所以,IR-Transformer 模型拥有结合上下文语境来分析语义的能力,而该能力对于信息检索精度至关重要,我们将在下一小节重点介绍这一特点。
同理,如图 2(b) 所示,q->t multi-head attention 操作需要计算 N 个相关性矩阵(每个矩阵由如 (近期,今天),(近期,天气),(近期,如何) 等词对的相关性数值组成)。需要注意的是,词对的组成不光来自 t 编码器,还来自 q 编码器(如「今天」等词)。
经过 q 编码器、t 编码器的多层处理,用户查询和网页标题的语义已被充分抽象。利用加权平均、神经网络处理等方法,我们可以用两个高维向量来分别表示查询和网页标题。比如对于图 2 中的例子,查询和网页标题的信息可以分别表示为 q=(q_1, q_2, …, q_k) 以及 t=(t_1, t_2,…, t_k)。基于这两个高维向量的相似度(例如 cos 内积的大小),搜索引擎可以给网页打分,将高分的网页返回给发出查询请求的用户。
为了提高模型的精度以及收敛效率,我们也参考了原始文献,在 IR-Transformer 中加入了残差连接(residual connection)、层正则化(layer normalization)等技术;为了增加语序分析能力,我们在模型中加入了位置编码(positional encoding)等操作。
参考