哪些企業(yè)合適做網(wǎng)站核心關(guān)鍵詞舉例
在《Transformer的PyTorch實(shí)現(xiàn)之若干問題探討(一)》中探討了Transformer的訓(xùn)練整體流程,本文進(jìn)一步探討Transformer訓(xùn)練過程中teacher forcing的實(shí)現(xiàn)原理。
1.Transformer中decoder的流程
在論文《Attention is all you need》中,關(guān)于encoder及self attention有較為詳細(xì)的論述,這也是網(wǎng)上很多教程在談及transformer時(shí)候會重點(diǎn)討論的部分。但是關(guān)于transformer的decoder部分,他的結(jié)構(gòu)上與encoder實(shí)際非常像,但其中有一些巧妙的設(shè)計(jì)。本文會詳細(xì)談?wù)?。首先給出一個(gè)完整transformer的結(jié)構(gòu)圖:
上圖左側(cè)為encoder部分,右側(cè)為decoder部分。對于decoder部分,將enc_input經(jīng)過multi head attention后得到的張量,以K,V送入decoder中。而decoder階段的masked multi head attention需要解決如何將dec_input編碼成Q。最終輸出的logits實(shí)際是與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實(shí)現(xiàn)之若干問題探討(一)》中,decoder階段,Q的維度為[2,8,6,64](2為batch size,8為head數(shù),6為句子長度,64為向量長度),K的維度為[2,8,5,64],V的維度為[2,8,5,64]。其中, Q K T QK^T QKT的維度為[2,8,6,5] 的,可以理解每個(gè)查詢張量Q對每個(gè)鍵值張K的注意力權(quán)重。之后乘以V,維度為[2,8,6,64]??梢钥吹阶罱K的維度是根據(jù)查詢張量Q來加權(quán)值向量V。Q就是dec_input經(jīng)過masked multi head attention得來。那么,dec_input中實(shí)際是包含了所有的標(biāo)簽的。那么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):'''這三個(gè)參數(shù)對應(yīng)的不是Q、K、V,dec_inputs是Q,enc_outputs是K和V,enc_inputs是用來計(jì)算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],上三角矩陣# 將兩個(gè)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_outputs:enc_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部分??梢钥吹接袃蓚€(gè)mask:dec_self_attn_pad_mask(用于將dec_inputs中的P mask掉)與dec_self_attn_subsequence_mask(用于實(shí)現(xiàn)decoder的self attention)。這兩個(gè)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]]], device='cuda: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]]], device='cuda:0', dtype=torch.uint8)#[2, 6, 6]
可以看到,dec_self_attn_pad_mask全為false,這是因?yàn)閐ec_input中不包含P,而dec_self_attn_subsequence_mask為上三角矩陣,對于每個(gè)token,需要mask掉它之后的token(本代碼中,為1或True的位置會被mask掉)。接下來進(jìn)一步追問,為什么上三角矩陣就可以mask掉該token之后的token?具體是如何實(shí)現(xiàn)的呢?
對于前文的Scaled Dot-Product Attention公式,代碼中的表述實(shí)際為:
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) 計(jì)算注意力分?jǐn)?shù)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) 進(jìn)行 mask 和 softmax# mask為True的位置會被設(shè)為-1e9scores.masked_fill_(attn_mask, -1e9) # 把True設(shè)為-1e9attn = nn.Softmax(dim=-1)(scores) # attn: [batch_size, n_heads, len_q, len_k]# 3) 乘V得到最終的加權(quán)和context = torch.matmul(attn, V) # context: [batch_size, n_heads, len_q, d_v], [2, 8, 5, 64]'''得出的context是每個(gè)維度(d_1-d_v)都考慮了在當(dāng)前維度(這一列)當(dāng)前token對所有token的注意力后更新的新的值,換言之每個(gè)維度d是相互獨(dú)立的,每個(gè)維度考慮自己的所有token的注意力,所以可以理解成1列擴(kuò)展到多列返回的context: [batch_size, n_heads, len_q, d_v]本質(zhì)上還是batch_size個(gè)句子,只不過每個(gè)句子中詞向量維度512被分成了8個(gè)部分,分別由8個(gè)頭各自看一部分,每個(gè)頭算的是整個(gè)句子(一列)的512/8=64個(gè)維度,最后按列拼接起來'''return context # context: [batch_size, n_heads, len_q, d_v]
其中,Q,K,V的維度都是[2, 8, 6, 64], score的維度為[2, 8, 6, 6],即每個(gè)token之間的注意力分?jǐn)?shù)。這兒取出一個(gè)batch中的一個(gè)head下的注意力分?jǐn)?shù)a為例,a的維度為[6, 6],如圖所示:
如上圖所示,在得分score中,標(biāo)黃的0.71和0.24分別是S與S,以及S與I的詞向量相乘得到。由于I在S后面,所以需要通過mask將其置為負(fù)無窮大,而0.71需要保留,因?yàn)槭荢與S在同一個(gè)位置上。因此這個(gè)mask矩陣為上三角矩陣。