最近打算看一下transformer系列的论文,主要的顺序就是先从最开始的Transformer开始看起,然后是DETR及其的优化。
论文地址:https://arxiv.org/pdf/1706.03762.pdf
代码地址: https://github.com/tensorflow/tensor2tensor
一、摘要以及简介
在主流的序列转录模型里面,一般使用的是encoder(编码)和decoder(解码)架构,目前性能最好的模型也是通过将注意力机制引入encoder和decoder中来实现的。作者在此提出了一个新的模型–Transformer,通过与现存的机器翻译模型对比来说明了当前模型的优点。
事实证明,transformer不仅仅在语言翻译领域应用,在视觉方面也有了新的应用,后面的DETR就是以Transformer为基础架构得到的目标检测器。
在简介中,作者阐述了当前的序列模型以及其计算过程,由于RNN等网络特性,当前结果的输出依赖于上一时刻的输出,因此整个流程难以并行计算,效率很低。另外,当输入的时序信息很长时,早前的信息可能会丢失,在这一问题在做了很多改进后并没有得到实质性的解决。
最后一段,作者提出了本文的Transformer,不再使用循环神经网络结构,而是使用纯注意力机制,可以做到在比较短的时间内,达到先前模型的效果(甚至更好)。
后续的一些说明这里就不再记录,下面主要分析Transformer架构以及算法
二、Transformer模型架构
在当前比较好的模型结构中,常会是编码器-解码器的结构。编码器会将你的输入(x1~xn),变换为(z1~zn),假设输入的是一个句子,xn就表示当前句子的第n个单词,而zn就表示当前词的向量表示。而解码器则是会将编码器的输出解码,但是一次只会生成一个词汇(假设是翻译),所以解码暗器的输出长度和输入也可以是不同的,这里就是一个自回归的问题,及当前时刻的输出也会作为下一时刻的输入。
Transformer也是使用了这种编码器-解码器的架构,将自注意力、point-wise、全连接层汇聚到一起。Transformer的结构图如下图:
在上图中,左边的部分就是编码器,右边就是解码器。
2.1 encoder&decoder
作者首先就编码器-解码器的结构分别进行了介绍,但是由于很多的名词还没有做进一步解释(Multi-Head等),这里也是现有一个大概了解后面会逐步的深挖。
encoder:编码器作者使用了6个(N=6)相同的块组成,每一个块的结构如上图左半部分所示,每一层由分为两个子层(sub-layer),第一个子层使用了自注意力机制(Multi-Head Attention),第二个子层使用了全连接层(论文中的 simple, position-wise fully connected feed-forward network),对于每个子层作者都使用了残差连接,然后再进行一步层归一化(layer-normalization)。对于每个子层的输出可以用公式:
L a y e r N o r m ( x + S u b l a y e r ( x ) ) LayerNorm(x+Sublayer(x)) LayerNorm(x+Sublayer(x))
来表示。为了简化残差链接,作者将所有子层的输出长度变为:dmodel=512.
decoder:解码器与编码器相同,同样由N=6个相同的块组合而成,但是解码器块内部的结构与编码器是不同的。在解码器中,每个块含有三个子层,多加了一个层是Masked Multi-Head Attention层。
其他两层的结构与编码器相同,至于第一个不同的masked层,因为解码器是一个自回归的过程,当前时刻的输入依赖于上一时刻的输出,而注意力机制每次都可以看到完整的输入,这就导致了模型做预测时,看到了未来的结果,为了避免这一情况发生,在训练网络的时候,解码器不应该看见当前时刻后面的输入,masked层的作用就是来屏蔽这些输入。
2.2 Attention注意力机制
注意力机制的基础是注意力函数,通过不同的query,key-value来形成映射关系,通过计算query和key的相似度,可以得到不同的value。在Transformer中,作者使用的注意力函数为:Scaled Dot-Product Attention,要求输入的query和key是等长度的,通过计算query与所有的key的内积,来判断两者之间的相似度。(内积数值越大,表明两向量之间的相似度越高,如果等于0,则两向量正交),最后通过softmax层,可以得到一个加权和为1的概率映射,其具体公式如下:
A t t e n t i o n ( Q , K , V ) = s o f t m a x ( Q K T d k ) V Attention(Q,K,V)=softmax(\frac{QK^T}{\sqrt{d_k}})V Attention(Q,K,V)=softmax(dkQKT)V
公式中,Q, K,V均为矩阵,Q表示query集合,类似的K表示keys,V表示Values,dk表示query和key的长度。常见的注意力机制分为加型和点乘两种,加型的注意力机制可以处理query和key不等长的情况,在本文作者使用了点积型的,但是多加了一个除以根号dk的步骤,这是因为当dk比较大的时候(向量长度太长),两向量之间的相对差距就会变大,softmax函数会使得大的值更大,小的更小。这样子softmax的输出会出现大的值靠近1,小的值靠近0(概率分布不均衡,向两边靠拢),这样在计算梯度时,会得到梯度比较小的情况。
y ( i ) = e z ∑ j = 1 n e j y(i) = \frac{e^z}{\sum_{j=1}^{n}e^j} y(i)=∑j=1nejez
计算流程图如下:
在这里多了一步mask操作,是因为在计算第t个query时候,Kt以及之后的都是不应当被看见的(但是的确存在),这个时候加上一个mask操作,将t时刻之后得到的点积值全部替换为一个非常大的负数,在进入softmax后,该特别大负数就会变成0,从而失去影响。
2.3 Multi-Head Attention
该结构如下图所示:
作者在这里提到,如果只做一个单独的注意力函数,不如把整个的query、keys、values投影到一个到低维h次,然后做h次的注意力函数计算,然后将输出拼接到一起,再经过一个线性层得到最终的输出。关于为什么要这样做,首先看单一的注意力函数,发现其中并没有什么可以学习的参数,有时候为了想要模型可以识别不同的模式,多投注意力机制在第一次的线性投影时,线性层就可以学习不同的投影方式(这里有点类似卷积层中,输出的多通道),进而匹配不同的模式。
该机制的公式表示为:
M u l t i H e a d ( Q , K , V ) = C o n c a t ( h e a d 1 , . . . . , h e a d h ) W o MultiHead(Q,K,V)=Concat(head_1, ....,head_h)W^o MultiHead(Q,K,V)=Concat(head1,....,headh)Wo
w h e r e h e a d 1 = A t t e n t i o n ( Q W i Q , K W i K , V W i V ) where \space head_1 = Attention(QW_{i}^{Q},KW_{i}^{K},VW_{i}^{V}) where head1=Attention(QWiQ,KWiK,VWiV)
2.4 transformer当中的自注意力机制实现
作者在这里列举了三种使用multi-Head Attentiond的情况:
在编码-解码层中,Multi-Head Attention的queries来自于前一个解码器的输出(自回归),然后Keys和values则来自于编码器的输出。
在输入层中,第一个自注意力层的输入来自于训练数据。
在解码器中,带Mask的attention层则是接受上一层的输出值作为输入。
2.5 Position-wise Feed-Forward Networks
这一层说白就是一个全连接层(MLP),但是其特殊的地方就是会对每一个Position作用一次,在序列模型中,比如输入的是一个语句,那么每一个词就会对应一个Position,该MLP会对所有的Position作用一次。
F F N ( x ) = m a x ( 0 , x W 1 + b 1 ) W 2 + b 2 FFN(x)=max(0, xW_1+b_1)W_2+b_2 FFN(x)=max(0,xW1+b1)W2+b2
从上述公式可以看出来就是线性层经过一个Relu激活层再加上一个线性层作为输出,x作为attention的输出,其维度是512的,因此经过W1的映射可以有2048维度,之后再经过W2将会输出与输入相等的512维度。该层的作用就是将我们输入的序列中感兴趣的信息映射到我们想要的语义空间。
2.6 Embedding&Positional Encoding
Embedding层再编码器和解码器当中都是存在的,在编码器中,该层的作用是学习到如何将一个单词使用一个合适的向量来进行表示。
因为attention层不会有时序信息参加进来,但是该模型处理的是时序信息,因此Positional Encoding层就可以为输入来添加时序信息(为什么时序信息是必须的–按照一句话的顺序来理解,假设打乱这句话的单词顺序,那么整句话的语义就有可能改变)
到此,整个Transformer架构就介绍完毕了,里面还有很多细节都没有谈到,很多地方也还存在疑问,关于剩下的问题处理,在看代码,做代码的阅读笔记时候会逐渐的补充完整。