more than code
简介
Transformer是谷歌在论文《Attention is all you need》里面提出的模型,NLP领域的经典必读论文,在论文中,谷歌利用attention的结构,在语言表征的时候编码进上下文的信息。Transformer的模型结构如图所示。
和大多数的seq2seq模型一样,其结构也是由encoder和decoder组成的。encoder是由多个相同的层堆叠组成的,下图中左边的代表了某一层,论文里设置了6层的重复结构。而decoder是由右边的结构组成,论文里面也是设置成为6个相同的结构去解码,接下来本文将从训练过程和预测过程中tensor的变化来介绍模型的结构。
训练过程
由于模型分为encoder和decoder两个部分,而他们在训练时分别接受了不同的输入,所以这里分开来描述,我们现在以一个中英翻译任务来说明,中英翻译任务输入的是中文序列,设其中有一个序列名为input,其实际长度为 ,中文源语言序列的输入需要在序列结束加一个,并且给定输入语料中最大的序列长度是,对于那些不足的序列,我们需要进行padding,使其长度也达到。翻译任务输出的是英文,设input序列翻译结果为output,其实际长度为,同样的目标语言的语料需要在开始增加一个,在结尾增加一个,并且设输出语料中最大序列的长度为。由于训练时数据是以batch的方式输入,这里假定每个batch的大小为。
encoder部分训练
encoder训练时接受的输入是shape为的经过padding的多个序列。同时为了方便计算(也可以内部计算这个padding mask)也会输入一个padding mask的tensor,其维度和输入序列相同,在序列padding部分padding mask为0,非padding部分为1.
Block
embddding结构
首先将输入的序列数据进行embedding,设embedding的维度是E,embedding分为word embedding和position embedding,两个embedding的维度相同均为E,最终的embedding有二者相加得到,此时向量维度为,可以开始输入到encoder的Block里面
multi-head attention结构
embedding之后,tensor首先进行了multi-head attention,这里的attention是一种self-attention.
因为embedding和multi-head attention进行了residual connect,所以multi-head attention输出的hidden_size和输入的embedding_size都是一样的,都为E。在进行multi-head attention时,我们首先要构建query,key,value矩阵,正常情况下query是从target序列构建,key和value是从source序列构建,但这里是self-attention,所以都是从去构建,我们首先将reshape成的2d矩阵,然后分别接入三个全连接激活层得到,,这三个tensor,其维度均为,再重新transpose为,因为这里用的是多头注意力,所以我们还需要将E维度划分成N个头,假设,那么,,经过多头划分就变成了,之后利用这三个tensor计算attention的输出。
计算attention的时候,首先将,,transpose成,因为这里不同的head是进行独立的attention。然后根据计算公式可以得到一个attention得分。
我们对这个拆开来看看他具体做了什么。假设一个序列一个head的query如下, 其中是序列中第个位置词的query向量,
而key则用下图表示:
那么所谓的attention_score则是:
这里我们考虑从一般的attention来理解,而不是从self attention来理解,一般使用attention是为了建立source序列各位置对target序列各位置重要程度的attention向量,可以理解为我们在给target序列编码的时候,要增加一些source序列的信息进来。所以要从source序列构建key,value,从target语料构建query,而attention_scores第i行的元素是source序列中各位置对target序列的第i个位置的重要程度。另外考虑到source序列会有padding部分,padding部分不应当提供任何信息出来,所以需要在计算得分的时候,让key向量对应的padding部分计算得分为0,这里将key里面padding部分的位置在attention_score里面对应的地方变成-10000,这样在softmax之后,其输出的值就可以认为是0了。
attention_score经过softmax之后,得到attention_probs,该tensor里面的第i行,第j列的元素可以认为是在source序列中第j个位置的词附加给target序列中第i个位置词的注意力比重,最后在乘以value矩阵,可以得到target序列对source序列各位置的attention信息,因为value矩阵是从source序列构造的,所以attention可以理解为,target序列从source序列获取额外信息的过程,而self attention则是从自身序列的其他位置获取额外信息。
我们可以令attention_probs矩阵为:
value矩阵为
二者相乘,得到的attention向量如下,其中第行代表了source序列各位置提供给target序列第个位置的信息,因为是多头注意力,每个头输出的attention还要进行拼接,然后接一层全链接层合并这些注意力:
我们获取的attention是对原有信息的补充,主要是从source序列获取的,所以要讲attention矩阵加到原来的输入的embedding里面,这也就是residual connect的由来。原著论文中还在这里加入了layer normal,layer normal区别于batch normal,是对一个序列自身进行标准化,而batch normal则是对在对于一个batch内各样本的单个特征进行normal,下图左边是layer normal,右图是batch normal。
Position-wise feed-forward networks
multi-head self attention之后,我们可以还需经过Position-wise feed-forward networks,其实本质上就是一个MLP,即对于序列中的某个词,其张量为X,经过feed-forward networks之后,如下
这里的position wise是因为序列中的每个位置上的词的embedding都是乘以同样一个和,即不同位置的线性变换是完全一样的,实际操作很简单,就是对shape为的输入接一层全连接激活层即可,因为有两层变换,所以有一个中间隐藏层向量,设该向量维度为,由于广播机制,所以序列中不同的词都乘以同一个权值矩阵,然后再乘以另一个权值矩阵,变回原来的维度E
。所以shape的变化为:
在feed-forward networks之后也要做一个residual connect与layer norm。
之后输出的就可以进入下一个block
其余 block
经过其余5层block之后,我们可以获得最终encoder的输出
decoder训练
embedding结构
训练过程中的decoder,接受的输入的是标签序列,这里输入序列要移除,所以序列长度不是,而是。
首先我们也要对其进行embedding和position embedding,embedding之后的维度是,这里的embedding与encoder的embedding用的是同一组权值矩阵。
Decoder Block
Masked multi-head attention
embedding之后进入Masked self-attention layer,这里也是一个self attention,原理上和self attention是一样的,但是,增加了一个mask矩阵,所谓的mask矩阵本质上是对未来信息的遮盖,因为我们训练时输入的是一个完整的序列T,但是在预测的时候解码序列中的某个位置是无法知道未来的信息的。所以需要加一个mask矩阵,那这个mask矩阵怎么加呢,在上述self attention矩阵中,我们有一个attention_probs矩阵
它的第i行代表了编码第i个位置的时,从自身其余位置获取信息的比重,当我们用一下左下三角矩阵对其点乘时,那第i个位置的信息只能获取它之前的序列位置的信息,其attention_probs就变成了:
各位置获取的attention信息就变成了:
这里仍然要做padding mask,因为目标语料也是有padding的。
之后依然需要做add&layer normal操作。
multi-head attention(vanilla attention)
在经过masked multi-head attention之后,decoder编码的信息还需要与encoder编码的信息进行attention,即我们需要将encoder编码的序列信息提取出有用的部分给decoder序列信息,所以需要从encoder的输出去构建key和value向量,然后从decoder内部序列变量构建query向量,以此进行attention操作。
attention之后依然要做add&layer normal操作。
其余的decoder Block
将之前的decoder block的再重复五次相同的Block结构,得到最终的输出序列嵌入式表示,
projection layer
一般认为我们需要将decoder输出的向量接一个全连接层转到词表的logits空间,变成,在实际使用的时候,可以将原先的embedding矩阵拿过来transpose一下作为全连接层。Kyubyong/transformer
训练Loss计算
将logits向量接一层softmax得到概率,与目标的序列使用交叉熵损失函数计算损失函数值,就可以开启参数优化过程了。这里的目标序列是最初的语料移除一个开始符,因此这个loss是错位的,这样预测过程可以由前一个词生成下一个词。
预测过程
预测过程可以简单描述一下,首先我们要将待翻译的序列输入到encoder里面得到一个encoder编码向量,然后我们在decoder里面输入开始标记的单个元素序列,这个标记我们之前也有填充到训练过程的模型中,预测出下一个词T1(即最大可能词),然后我们将与T1拼接起来,作为decoder的输入序列,继续预测下一个词。直到预测出来的序列有为止。这里的预测过程是贪心搜索,还有beam search,暂且不提。
遗留问题:预测过程为什么用mask self attention?
参考文献
Attention Is All You Need
理解语言的 Transformer 模型