transformer的核心是self-attention,self-attention可参考:
对于机器翻译而言,分为源语言与目的语言(如英文与中文)
对源语言建立词典,大小为src_vocab_size (包括padding)
对目标语言建立词典,大小为tgt_vocab_size (包括padding)
在词典中对词进行排序(放好位置就行),按照下标给词一个数字表示(0一定表示padding,得规定好)
训练集为源语言与目标语言的一一对应关系,但是
模型中要通过矩阵加速运算,如果是一对句子一对句子的输入模型进行训练就行,但是训练是需要分批输入到模型中的。
所以一个bacth中的句子需要是等长度的所以需要padding,即填充0。
以下为一个batch的处理过程:
最终一个batch的输入格式为(B,L)
B为batch_size, L为本次batch的输入的句子的单词个数
源语言与目标语言词典中的每一个词都可以转换为一个 d m o d e l d_{model} dmodel长度的向量,词向量可以预训练得来,也可以加入到此网络中进行训练。词向量类似与一个矩阵:
源语言的词向量矩阵如上所示,每一行为一个词向量,可以用每一个词的代表数字表示当成行标直接去取对应的词向量
最终将一个batch的输入转换为(B,L,d_model)
B为batch_size, L为本次batch的输入的句子的单词个数,d_model为词向量长度
加入了padding,但是padding是不能加入到self-attention的计算中的, 这意味着什么呢?
思考一下self-attention中会产生一个单词与单词之间的关系矩阵,每个单词的query向量与所有单词的key向量点乘再除以向量长度取根号,之后经过softmax层归一化将其变为都是正数并且加起来等于 1。 (Scaled Dot-Product attention)
最终形成的矩阵格式为(L,L),表示一个句子的各个词之间的关系分数。
第一行就表示第一个单词与其他所有单词的关系分数,每一行的和为1
但是我们说单词与padding之间是没有关系的,即单词与padding之间的关系分为为0,那把softmax之后分数直接置为0
不行,因为行的和不再为1,这样就不合理了,所以我们在softmax之前做操作。
回忆一下softmax的公式:
要让某个值为0,只需要让 z i z_i zi为负无穷即可,所以只需要让softmax之前的矩阵的padding列全为无穷即可,softmax之前的矩阵如下所示:
注:
为什么不考虑padding的行呢?
我的理解是padding行最终也会输出一个向量,但是这个向量不会参数到self-attention的计算中,所以不用管它
采用论文中的描述
“编码器由N = 6个相同的层组成。每层有两个子层。
第一个是一个多头自注意机制
第二个是一个简单的,位置上完全连接的前馈网络。
我们在两个子层周围使用一个残差连接,然后是层规范化。
也就是说,每个子层的输出是LayerNorm(x +子层(x)),其中子层(x)是由子层本身实现的函数。
为了方便这些残差连接,模型中的所有子层以及嵌入层产生的输出维数dmodel = 512。”
如下所示:
注:论文中 d q = d k = d v = d m o d e l / n h e a d s d_q = d_k = d_v = d_{model} / n_{heads} dq=dk=dv=dmodel/nheads
( B , L , d m o d e l ) − > ( B , L , d q ∗ n h e a d s ) (B,L,d_{model}) -> (B,L,d_q * n_{heads}) (B,L,dmodel)−>(B,L,dq∗nheads) Q矩阵
( B , L , d m o d e l ) − > ( B , L , d k ∗ n h e a d s ) (B,L,d_{model}) -> (B,L,d_k * n_{heads}) (B,L,dmodel)−>(B,L,dk∗nheads) K矩阵
( B , L , d m o d e l ) − > ( B , L , d v ∗ n h e a d s ) (B,L,d_{model}) -> (B,L,d_v * n_{heads}) (B,L,dmodel)−>(B,L,dv∗nheads) V矩阵
一个线性层nn.Linear
就可以完成
(1)把头提出来
( B , L , d q ∗ n h e a d s ) − > ( B , n h e a d s , L , d q ) (B,L,d_q * n_{heads}) -> (B,n_{heads},L,d_q) (B,L,dq∗nheads)−>(B,nheads,L,dq) Q矩阵
( B , L , d k ∗ n h e a d s ) − > ( B , n h e a d s , L , d k ) (B,L,d_k * n_{heads}) -> (B,n_{heads},L,d_k) (B,L,dk∗nheads)−>(B,nheads,L,dk) K矩阵
( B , L , d q ∗ n h e a d s ) − > ( B , n h e a d s , L , d v ) (B,L,d_q * n_{heads}) -> (B,n_{heads},L,d_v ) (B,L,dq∗nheads)−>(B,nheads,L,dv) V矩阵
(2)Scaled Dot-Product Attention
<1> ( Q ∗ K T ) / d k (Q * K^T)/ \sqrt{d_k} (Q∗KT)/dk -> ( B , n h e a d s , L , L ) ) (B,n_{heads},L,L)) (B,nheads,L,L))
( K T K^T KT 对最后两位进行转置,变为 ( B , n h e a d s , d k , L ) ) (B,n_{heads},d_k,L)) (B,nheads,dk,L))
<2> mask结果,将padding列变为-inf
<3>对每一行进行softmax
<4>乘以V矩阵 -> ( B , n h e a d s , L , d v ) ) (B,n_{heads},L,d_v)) (B,nheads,L,dv))
<1> ( B , n h e a d s , L , d v ) ) (B,n_{heads},L,d_v)) (B,nheads,L,dv)) -> ( B , L , n h e a d s ∗ d v ) ) (B,L,n_{heads} * d_v)) (B,L,nheads∗dv))
<2> ( B , L , n h e a d s ∗ d v ) ) (B,L,n_{heads} * d_v)) (B,L,nheads∗dv)) -> ( B , L , d m o d e l ) ) (B,L,d_{model})) (B,L,dmodel)) (线性层)
( B , L , d m o d e l ) ) (B,L,d_{model})) (B,L,dmodel)) -> ( B , L , d f f ) ) (B,L,d_{ff})) (B,L,dff))
( B , L , d f f ) ) (B,L,d_{ff})) (B,L,dff)) -> ( B , L , d m o d e l ) ) (B,L,d_{model})) (B,L,dmodel))
最终encoder输出格式为 ( B , L , d m o d e l ) (B,L,d_{model}) (B,L,dmodel)
之前描述了pad_mask,是由于真正的单词与padding之间不能有关系,关系系数为0。
subsequence_mask是针对目标语言的输入embedding而言的,在self-attention时我们要算各个词之间的关系分数。考虑一下利用RNN的翻译模型,目标语言是按顺序一个一个输入进网络中,即前面的词不知道后面的词,即前面的词不能算跟后面的词之间的关系,但是后面的词可以跟前面算关系。mask矩阵如下,
而且padding的mask也要考虑,最终变为
这一部分输入与之前不同,之前是这样的
( B , L , d m o d e l ) − > ( B , L , d q ∗ n h e a d s ) (B,L,d_{model}) -> (B,L,d_q * n_{heads}) (B,L,dmodel)−>(B,L,dq∗nheads) Q矩阵
( B , L , d m o d e l ) − > ( B , L , d k ∗ n h e a d s ) (B,L,d_{model}) -> (B,L,d_k * n_{heads}) (B,L,dmodel)−>(B,L,dk∗nheads) K矩阵
( B , L , d m o d e l ) − > ( B , L , d v ∗ n h e a d s ) (B,L,d_{model}) -> (B,L,d_v * n_{heads}) (B,L,dmodel)−>(B,L,dv∗nheads) V矩阵
同一个输入,转换为Q,K,V矩阵
现在变为这样
( B , L o u t p u t s , d m o d e l ) ( 来 自 o u t p u t s ) − > ( B , L o u t p u t s , d q ∗ n h e a d s ) (B,L_{outputs},d_{model}) (来自outputs) -> (B,L_{outputs},d_q * n_{heads}) (B,Loutputs,dmodel)(来自outputs)−>(B,Loutputs,dq∗nheads) Q矩阵
( B , L i n p u t s , d m o d e l ) ( 来 自 i n t p u t s ) − > ( B , L i n p u t s , d k ∗ n h e a d s ) (B,L_{inputs},d_{model}) (来自intputs) -> (B,L_{inputs},d_k * n_{heads}) (B,Linputs,dmodel)(来自intputs)−>(B,Linputs,dk∗nheads) K矩阵
( B , L i n p u t s , d m o d e l ) ( 来 自 i n t p u t s ) − > ( B , L i n p u t s , d v ∗ n h e a d s ) (B,L_{inputs},d_{model}) (来自intputs) -> (B,L_{inputs},d_v * n_{heads}) (B,Linputs,dmodel)(来自intputs)−>(B,Linputs,dv∗nheads) V矩阵
query矩阵来自outputs,key矩阵、value矩阵来自inputs,相当于decoder从encoder的输出中选取数据来完成自己的输出,与sequence to sequence模型有异曲同工之妙。
最终Linear层之前的输出为 ( B , L o u t p u t s , d m o d e l ) (B,L_{outputs},d_{model}) (B,Loutputs,dmodel)
linear之后softmax
( B , L o u t p u t s , d m o d e l ) (B,L_{outputs},d_{model}) (B,Loutputs,dmodel) -> ( B , L o u t p u t s , t g t _ v o c a b _ s i z e ) (B,L_{outputs},tgt\_vocab\_size) (B,Loutputs,tgt_vocab_size)
选取每个位置概率最大的单词输出,即为翻译