网站数据库 权限设计, 上的网站app免费大全,怎么根据网站做二维码,南通做网站优化公司在《Transformer的PyTorch实现之若干问题探讨#xff08;一#xff09;》中探讨了Transformer的训练整体流程#xff0c;本文进一步探讨Transformer训练过程中teacher forcing的实现原理。
1.Transformer中decoder的流程
在论文《Attention is all you need》中#xff0…在《Transformer的PyTorch实现之若干问题探讨一》中探讨了Transformer的训练整体流程本文进一步探讨Transformer训练过程中teacher forcing的实现原理。
1.Transformer中decoder的流程
在论文《Attention is all you need》中关于encoder及self attention有较为详细的论述这也是网上很多教程在谈及transformer时候会重点讨论的部分。但是关于transformer的decoder部分他的结构上与encoder实际非常像但其中有一些巧妙的设计。本文会详细谈谈。首先给出一个完整transformer的结构图
上图左侧为encoder部分右侧为decoder部分。对于decoder部分将enc_input经过multi head attention后得到的张量以KV送入decoder中。而decoder阶段的masked multi head attention需要解决如何将dec_input编码成Q。最终输出的logits实际是与Q的维度一致。对于Scaled Dot-Product Attention其公式如下 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\left(\frac{QK^T}{\sqrt{d_k}}\right)V Attention(Q,K,V)softmax(dk QKT)V 在《Transformer的PyTorch实现之若干问题探讨一》中decoder阶段Q的维度为[2,8,6,64]2为batch size8为head数6为句子长度64为向量长度,K的维度为[2,8,5,64]V的维度为[2,8,5,64]。其中 Q K T QK^T QKT的维度为[2,8,6,5] 的可以理解每个查询张量Q对每个键值张K的注意力权重。之后乘以V维度为[2,8,6,64]。可以看到最终的维度是根据查询张量Q来加权值向量V。Q就是dec_input经过masked multi head attention得来。那么dec_input中实际是包含了所有的标签的。那么dec_input是如何mask掉不需要的token的呢
2.Decoder中的self attention mask
class Decoder(nn.Module):def __init__(self):super(Decoder, self).__init__()self.tgt_emb nn.Embedding(tgt_vocab_size, d_model)self.pos_emb PositionalEncoding(d_model)self.layers nn.ModuleList([DecoderLayer() for _ in range(n_layers)])def forward(self, dec_inputs, enc_inputs, enc_outputs):这三个参数对应的不是Q、K、Vdec_inputs是Qenc_outputs是K和Venc_inputs是用来计算padding mask的dec_inputs: [batch_size, tgt_len]enc_inpus: [batch_size, src_len]enc_outputs: [batch_size, src_len, d_model]dec_outputs self.tgt_emb(dec_inputs)#词序号编码成向量dec_outputs self.pos_emb(dec_outputs).cuda()#位置编码dec_self_attn_pad_mask get_attn_pad_mask(dec_inputs, dec_inputs).cuda() #[2, 6, 6]dec_self_attn_subsequence_mask get_attn_subsequence_mask(dec_inputs).cuda() #[2, 6, 6],上三角矩阵# 将两个mask叠加布尔值可以视为0和1和大于0的位置是需要被mask掉的赋为True和为0的位置是有意义的为Falsedec_self_attn_mask torch.gt((dec_self_attn_pad_mask dec_self_attn_subsequence_mask), 0).cuda()# 这是co-attention部分为啥传入的是enc_inputs而不是enc_outputsenc_outputs是向量这儿是需要通过词编码来判断是否需要mask掉dec_enc_attn_mask get_attn_pad_mask(dec_inputs, enc_inputs) #[2, 6, 5]for layer in self.layers:dec_outputs layer(dec_outputs, enc_outputs, dec_self_attn_mask, dec_enc_attn_mask)return dec_outputs # dec_outputs: [batch_size, tgt_len, d_model]上述代码为Decoder部分。可以看到有两个maskdec_self_attn_pad_mask用于将dec_inputs中的P mask掉与dec_self_attn_subsequence_mask用于实现decoder的self attention。这两个mask在后面会相加合并。这儿可以分别展示二者的值其中
dec_self_attn_pad_mask:
tensor([[[False, False, False, False, False, False],[False, False, False, False, False, False],[False, False, False, False, False, False],[False, False, False, False, False, False],[False, False, False, False, False, False],[False, False, False, False, False, False]],[[False, False, False, False, False, False],[False, False, False, False, False, False],[False, False, False, False, False, False],[False, False, False, False, False, False],[False, False, False, False, False, False],[False, False, False, False, False, False]]], devicecuda:0)#[2, 6, 6]dec_self_attn_subsequence_mask:
tensor([[[0, 1, 1, 1, 1, 1],[0, 0, 1, 1, 1, 1],[0, 0, 0, 1, 1, 1],[0, 0, 0, 0, 1, 1],[0, 0, 0, 0, 0, 1],[0, 0, 0, 0, 0, 0]],[[0, 1, 1, 1, 1, 1],[0, 0, 1, 1, 1, 1],[0, 0, 0, 1, 1, 1],[0, 0, 0, 0, 1, 1],[0, 0, 0, 0, 0, 1],[0, 0, 0, 0, 0, 0]]], devicecuda:0, dtypetorch.uint8)#[2, 6, 6]可以看到dec_self_attn_pad_mask全为false这是因为dec_input中不包含P而dec_self_attn_subsequence_mask为上三角矩阵对于每个token需要mask掉它之后的token本代码中为1或True的位置会被mask掉。接下来进一步追问为什么上三角矩阵就可以mask掉该token之后的token具体是如何实现的呢 对于前文的Scaled Dot-Product Attention公式代码中的表述实际为 def forward(self, Q, K, V, attn_mask):Q: [batch_size, n_heads, len_q, d_k]K: [batch_size, n_heads, len_k, d_k]V: [batch_size, n_heads, len_v(len_k), d_v] 全文两处用到注意力一处是self attention另一处是co attention前者不必说后者的k和v都是encoder的输出所以k和v的形状总是相同的attn_mask: [batch_size, n_heads, seq_len, seq_len]# 1) 计算注意力分数QK^T/sqrt(d_k)scores torch.matmul(Q, K.transpose(-1, -2)) / np.sqrt(d_k) # scores: [batch_size, n_heads, len_q, len_k]# 2) 进行 mask 和 softmax# mask为True的位置会被设为-1e9scores.masked_fill_(attn_mask, -1e9) # 把True设为-1e9attn nn.Softmax(dim-1)(scores) # attn: [batch_size, n_heads, len_q, len_k]# 3) 乘V得到最终的加权和context torch.matmul(attn, V) # context: [batch_size, n_heads, len_q, d_v], [2, 8, 5, 64]得出的context是每个维度(d_1-d_v)都考虑了在当前维度(这一列)当前token对所有token的注意力后更新的新的值换言之每个维度d是相互独立的每个维度考虑自己的所有token的注意力所以可以理解成1列扩展到多列返回的context: [batch_size, n_heads, len_q, d_v]本质上还是batch_size个句子只不过每个句子中词向量维度512被分成了8个部分分别由8个头各自看一部分每个头算的是整个句子(一列)的512/864个维度最后按列拼接起来return context # context: [batch_size, n_heads, len_q, d_v]其中QKV的维度都是[2, 8, 6, 64], score的维度为[2, 8, 6, 6]即每个token之间的注意力分数。这儿取出一个batch中的一个head下的注意力分数a为例a的维度为[6, 6],如图所示
如上图所示在得分score中标黄的0.71和0.24分别是S与S以及S与I的词向量相乘得到。由于I在S后面所以需要通过mask将其置为负无穷大而0.71需要保留因为是S与S在同一个位置上。因此这个mask矩阵为上三角矩阵。