大致流程:
6.将head1,head2,head3…head8加起来得到最终结果,维度是(句子长度,64X8 = 512)
一 .encode部分 Transformer参数变换表示
7. 首先给一个输入序列,输入也就是输入的Batch_size个句子,其shape为input = [N,T_q],也就是[batch_size = 32,max_length = 15]
8. 设置权重矩阵的大小,即Embeding = [vocab_size,num_units],也就是[vocab_size,512],通过tf.nn.embedding_lookup(Embeding ,input )函数得到一个shape为[N,T_q,num_units],即[32,15,512]也就是Transformer模型的输入Q,K,V
9. 将三个Q,K,V输入到Multi_Head attention之前,需要进行位置编码pos_embeding,得到位置编码后的shape依然是[N,T_q,num_units],此时输入的Q,K,V都是具有位置信息的向量了
10. 这一步是重点,进入多头注意力机制,这个多头注意力机制是需要运行八次的,进入多头注意力之后,首先要进行八次线性转换,Q = tf.layers.dense(queries, num_units, activation=tf.nn.relu),转换之后的Q,K,V的shape为[N,T_q,num_units],(这个就是为什么多头注意力机制会被称为多头了,实际上是经过8次线性转换(self_attention)而来)然后将得到的Q按照第2维拆分成为 h = 8 份,再按照第0维度拼接,Q_ ,K_ ,V_ = tf.concat(tf.split(Q, num_heads, axis=2), axis=0),得到之后的Q_的shape为[hN,T_q,C/h],这里的C就是num_units,[832,15,512/8=64]
11. 将Q_ ,K_ 想乘,K_经过transponse后shape为[hN,C/h=T_k,T_k],再进行scale,得到V_的权重值outputs,shape为[hN,T_q,T_k]
12. 由于再输入的时候,涉及到padding 0,为了消除这些本身没有任何信息的影响,需要对其msking,其输入为Q,K,不是Q_ ,K_ ,shape为[N,T_q,num_units],具体步骤1.将此矩阵按照第2维度相加起来,key_masks = tf.sign(tf.abs(tf.reduce_sum(keys, axis=-1))) # (N, T_k) 得到[N,T_q],(这个矩阵只可能是0和1,0是被pading为0的,1就是具有信息字词),然后再经过key_masks = tf.tile(key_masks, [num_heads, 1]) 复制,得到shape为 (hN, T_k),然后增加其一个维度,key_masks = tf.tile(tf.expand_dims(key_masks, 1), [1, tf.shape(queries)[1], 1]) 得到shape为(hN, T_q, T_k),目的就是为了和之前的outputs一样。paddings = tf.ones_like(outputs)(-2**32+1)
outputs = tf.where(tf.equal(key_masks, 0), paddings, outputs)这两行的目的就是利用tf.where()这个额函数将padding的向量置为非常非常小的数字,这样就间接的忽略了padding对总体的影响。按照相同的方法将Q也是这样
13. 运用三角矩阵方法,在解码的时候,可以选择是否屏蔽未来的信息
14. 进行droup_out,开始用V_的权值 * V_得到输出,outputs = tf.matmul(outputs, V_) # ( hN, T_q, C/h)
15. 将这个shape再重写 outputs = tf.concat(tf.split(outputs, num_heads, axis=0), axis=2 ) 得到一个和输入一样的shape (N, T_q, C),然后再进行残差连接 和normal ,outputs += queries,outputs = normalize(outputs) # (N, T_q, C),得到的最终结果和输入时一样,方便进行循环,并且输入给全连接层。
!!!小总结:以上4-9就是多头注意力机制+add,norm的部分
16. 全连接层,该层的输入也就是上面的多头注意力最后的输出outputs,输入层==》隐藏层outputs = tf.layers.conv1d(**params)隐藏层到输出层
params = {“inputs”: outputs, “filters”: num_units[1], “kernel_size”: 1,“activation”: None, “use_bias”: True};outputs = tf.layers.conv1d(**params),然后再经过残差连接+normal,就得出来shape为[N,T_k,C],encode的输出
!!!以上4-10表示的就是就是完整的encode的部分了,需要循环8次!!!
二. decode部分
其实decode和encode差不多,不过比encode多一个带有mask的self_attention机制
1.作为decode的输入,训练和预测是不一样的,A.在训练的时候,直接将目标语言拿过来,通过masked进行遮盖未来信息,即在t时刻时,其输入时目标句子t-1时刻的所有信息,在经过多头注意力机制,输出一个Q,当做下一个多头机制的三个输出之一。这也是为什么Q就相当于前一时刻(也就是t时刻之前的所有时刻)的输出,然后再跟当前时刻的输入进行注意力计算。B.而在预测的时候,由于源语言是随意的,所以就没有所谓的标准答案(目标语言),只能讲上一时刻的输出当做decode的输入
2.经过embeding,再经过pos_embeding,得到Q,K,V。跟encode一样,只是未来信息贝遮掩了,然后得到输出当做下一时刻的输入的Q。而encode的输出当做下一时刻的K,V,经过多头注意力机制,得到一个输出。然后再经过全连接层,经过一个线性转换,在通过softmax的输出一个概率结果
3.quey就是multihead的输入x,key、value是输入x经过不同线性变换(比如,1X1的卷积)得到的不同值,用于计算query中每个词的权重(self attention)