前言
這個(gè)專欄我們開始學(xué)習(xí)transformer,自推出以來transformer在深度學(xué)習(xí)中占有重要地位,不僅在NLP領(lǐng)域,在CV領(lǐng)域中也被廣泛應(yīng)用,尤其是2021年,transformer在CV領(lǐng)域可謂大殺四方。
在論文的學(xué)習(xí)之前,我們先來介紹一些專業(yè)術(shù)語。本篇就讓我們先來認(rèn)識(shí)一下encoder和decoder吧!
?? ???本人Transformer相關(guān)文章導(dǎo)航:
?【Transformer系列(1)】encoder(編碼器)和decoder(解碼器)
?【Transformer系列(2)】注意力機(jī)制、自注意力機(jī)制、多頭注意力機(jī)制、通道注意力機(jī)制、空間注意力機(jī)制超詳細(xì)講解
?【Transformer系列(3)】 《Attention Is All You Need》論文超詳細(xì)解讀(翻譯+精讀)
【Transformer系列(4)】Transformer模型結(jié)構(gòu)超詳細(xì)解讀
目錄
一、encoder
1.1 簡(jiǎn)介
1.2 代碼實(shí)現(xiàn)
1.3 transformer中的使用
1.3.1 transformer中encoder的組成
1.3.2 每個(gè)Block的組成
1.3.3 每個(gè)Block 中的具體實(shí)現(xiàn)步驟
二、decoder
2.1 簡(jiǎn)介
2.2 代碼實(shí)現(xiàn)
2.3 transformer中的使用
2.3.1transformer中decoder的組成
2.3.2 transformer中encoder和decoder的區(qū)別
2.3.3 ?Masked self attention模塊
2.3.4 ?Cross attetion模塊
2.3.5 ?具體實(shí)現(xiàn)步驟
三、encoder-decoder
3.1 簡(jiǎn)介
3.2 代碼實(shí)現(xiàn)
3.3 注意問題
?一、encoder
1.1 簡(jiǎn)介
encoder,也就是編碼器,負(fù)責(zé)將輸入序列壓縮成指定長(zhǎng)度的向量,這個(gè)向量就可以看成是這個(gè)序列的語義,然后進(jìn)行編碼,或進(jìn)行特征提取(可以看做更復(fù)雜的編碼)。
簡(jiǎn)單來說就是機(jī)器讀取數(shù)據(jù)的過程,將現(xiàn)實(shí)問題轉(zhuǎn)化成數(shù)學(xué)問題。如下圖所示:
1.2 代碼實(shí)現(xiàn)
在編碼器接口中,我們只指定長(zhǎng)度可變的序列作為編碼器的輸入X。 任何繼承這個(gè)encoder 基類的模型將完成代碼實(shí)現(xiàn)。
class encoder(nn.Module):
def __init__(self):
super(Encoder, self).__init__()
self.positional_encoding = Positional_Encoding(config.d_model)
self.muti_atten = Mutihead_Attention(config.d_model,config.dim_k,config.dim_v,config.n_heads)
self.feed_forward = Feed_Forward(config.d_model)
self.add_norm = Add_Norm()
def forward(self,x): # batch_size * seq_len 并且 x 的類型不是tensor,是普通list
x += self.positional_encoding(x.shape[1],config.d_model)
# print("After positional_encoding: {}".format(x.size()))
output = self.add_norm(x,self.muti_atten,y=x)
output = self.add_norm(output,self.feed_forward)
return output
Mutihead_Attention():多頭注意力機(jī)制:?
class Mutihead_Attention(nn.Module):
def __init__(self,d_model,dim_k,dim_v,n_heads):
super(Mutihead_Attention, self).__init__()
self.dim_v = dim_v
self.dim_k = dim_k
self.n_heads = n_heads
self.q = nn.Linear(d_model,dim_k)
self.k = nn.Linear(d_model,dim_k)
self.v = nn.Linear(d_model,dim_v)
self.o = nn.Linear(dim_v,d_model)
self.norm_fact = 1 / math.sqrt(d_model)
def generate_mask(self,dim):
# 此處是 sequence mask ,防止 decoder窺視后面時(shí)間步的信息。
# padding mask 在數(shù)據(jù)輸入模型之前完成。
matirx = np.ones((dim,dim))
mask = torch.Tensor(np.tril(matirx))
return mask==1
def forward(self,x,y,requires_mask=False):
assert self.dim_k % self.n_heads == 0 and self.dim_v % self.n_heads == 0
# size of x : [batch_size * seq_len * batch_size]
# 對(duì) x 進(jìn)行自注意力
Q = self.q(x).reshape(-1,x.shape[0],x.shape[1],self.dim_k // self.n_heads) # n_heads * batch_size * seq_len * dim_k
K = self.k(x).reshape(-1,x.shape[0],x.shape[1],self.dim_k // self.n_heads) # n_heads * batch_size * seq_len * dim_k
V = self.v(y).reshape(-1,y.shape[0],y.shape[1],self.dim_v // self.n_heads) # n_heads * batch_size * seq_len * dim_v
# print("Attention V shape : {}".format(V.shape))
attention_score = torch.matmul(Q,K.permute(0,1,3,2)) * self.norm_fact
if requires_mask:
mask = self.generate_mask(x.shape[1])
attention_score.masked_fill(mask,value=float("-inf")) # 注意這里的小Trick,不需要將Q,K,V 分別MASK,只MASKSoftmax之前的結(jié)果就好了
output = torch.matmul(attention_score,V).reshape(y.shape[0],y.shape[1],-1)
# print("Attention output shape : {}".format(output.shape))
output = self.o(output)
return output
Feed_Forward() : 兩個(gè)Linear中連接Relu即可,目的是為模型增添非線性信息,提高模型的擬合能力。
class Feed_Forward(nn.Module):
def __init__(self,input_dim,hidden_dim=2048):
super(Feed_Forward, self).__init__()
self.L1 = nn.Linear(input_dim,hidden_dim)
self.L2 = nn.Linear(hidden_dim,input_dim)
def forward(self,x):
output = nn.ReLU()(self.L1(x))
output = self.L2(output)
return output
Add_Norm():殘差連接以及LayerNorm
class Add_Norm(nn.Module):
def __init__(self):
self.dropout = nn.Dropout(config.p)
super(Add_Norm, self).__init__()
def forward(self,x,sub_layer,**kwargs):
sub_output = sub_layer(x,**kwargs)
# print("{} output : {}".format(sub_layer,sub_output.size()))
x = self.dropout(x + sub_output)
layer_norm = nn.LayerNorm(x.size()[1:])
out = layer_norm(x)
return out
1.3 transformer中的使用
1.3.1transformer中encoder的組成
transformer 中 encoder 由 6 個(gè)相同的層組成,每個(gè)層包含 2 個(gè)部分:
- Multi-Head Self-Attention
- Position-Wise Feed-Forward Network
1.3.2 每個(gè)Block的組成
自注意力機(jī)制 + 殘差鏈接 + LayerNorm + FC + 殘差鏈接 + layer Norm此時(shí)的輸出 = 一個(gè) Block 的輸出;
1.3.3 每個(gè)Block 中的具體實(shí)現(xiàn)步驟
(1)原始的輸入向量b 與輸出向量a殘差相加,得到向量a+b;
【注意】 b是原始的輸入向量,下圖中輸出向量a是考慮整個(gè)序列的輸入向量得到的結(jié)果
?(2)將向量 a+b 通過 Layer Normation 得到向量c ;
也就是下圖左邊部分:
(3)將向量c 通過 FC layer 得到向量d ;
(4)向量c 與向量d 殘差相加 ,得到向量e ;
(5)向量e 通過 Layer Norm 輸出 向量f;
(6)此時(shí)得到的輸出向量f 才是 encoder中每個(gè)Block中的一個(gè)輸出向量;
以上步驟就是下圖右邊部分:
上述步驟,便是原始論文transformer中encoder的設(shè)計(jì)啦~
二、decoder
2.1 簡(jiǎn)介
decoder,也就是解碼器,負(fù)責(zé)根據(jù)encoder部分輸出的語義向量c來做解碼工作。以翻譯為例,就是生成相應(yīng)的譯文。
簡(jiǎn)單來說,就是就數(shù)學(xué)問題,并轉(zhuǎn)換為現(xiàn)實(shí)世界的解決方案。
【注意】生成的序列是不定長(zhǎng)的。而且上一時(shí)刻的輸出通常要作為下一時(shí)刻的輸入。
2.2 代碼實(shí)現(xiàn)
在上面1.2encoder的代碼實(shí)現(xiàn)中,我們已經(jīng)實(shí)現(xiàn)了大部分decoder的模塊。
但是encoder和decoder實(shí)現(xiàn)還是有區(qū)別的:
- decoder的Muti_head_Attention引入了Mask機(jī)制
- decoder與encoder中模塊的拼接方式不同
class Decoder(nn.Module):
def __init__(self):
super(Decoder, self).__init__()
self.positional_encoding = Positional_Encoding(config.d_model)
self.muti_atten = Mutihead_Attention(config.d_model,config.dim_k,config.dim_v,config.n_heads)
self.feed_forward = Feed_Forward(config.d_model)
self.add_norm = Add_Norm()
def forward(self,x,encoder_output): # batch_size * seq_len 并且 x 的類型不是tensor,是普通list
# print(x.size())
x += self.positional_encoding(x.shape[1],config.d_model)
# print(x.size())
# 第一個(gè) sub_layer
output = self.add_norm(x,self.muti_atten,y=x,requires_mask=True)
# 第二個(gè) sub_layer
output = self.add_norm(output,self.muti_atten,y=encoder_output,requires_mask=True)
# 第三個(gè) sub_layer
output = self.add_norm(output,self.feed_forward)
return output
2.3 transformer中的使用
2.3.1transformer中decoder的組成
在transformer中decoder 也是由 6 個(gè)相同的層組成,每個(gè)層包含 3 個(gè)部分:
- Multi-Head Self-Attention
- Multi-Head Context-Attention
- Position-Wise Feed-Forward Network
2.3.2 transformer中encoder和decoder的區(qū)別
我們先來看看這個(gè)圖
(1)第一級(jí)中: 將self attention 模塊加入了Masked模塊,變成了 Masked self-attention, 這樣以來就只考慮解碼器的當(dāng)前輸入和當(dāng)前輸入的左側(cè)部分, 不考慮右側(cè)部分; ( 注意,第一級(jí)decoder的key, query, value均來自前一層decoder的輸出,但加入了Mask操作,即我們只能attend到前面已經(jīng)翻譯過的輸出的詞語,因?yàn)榉g過程我們當(dāng)前還并不知道下一個(gè)輸出詞語,這是我們之后才會(huì)推測(cè)到的。)
(2)第二級(jí)中:引入了 Cross attention 交叉注意力模塊,?在 masked self-attention 和全連接層 之間加入;
(3)Cross attention 交叉注意力模塊的輸入 Q,K,V 不是來自同一個(gè)模塊,K,V 來自編碼器的輸出, Q來自解碼器的輸出;
【注意】 解碼器的輸出一個(gè)一個(gè)產(chǎn)生的
2.3.3 ?Masked self attention模塊
舉個(gè)栗子吧~以翻譯為例:
- 輸入:我是路人賈
- 輸出: I am Jia
由上一節(jié)可知,輸入“我是路人賈”這一步是在encoder中進(jìn)行了編碼,那么這里我們具體討論decoder的操作,也就是加了?Masked self attention模塊后如何得到輸出(“I am Jia”)的過程。
第1步:
- 初始輸入: 起始符</s> + Positional Encoding(位置編碼)
- 中間輸入:(我是路人賈)Encoder Embedding
- 最終輸出:產(chǎn)生預(yù)測(cè)“I”
第2步:
- 初始輸入:起始符</s> + “I”+ Positonal Encoding
- 中間輸入:(我是路人賈)Encoder Embedding
- 最終輸出:產(chǎn)生預(yù)測(cè)“am”
第3步:
- 初始輸入:起始符</s> + “I”+ “Love”+ Positonal Encoding
- 中間輸入:(我是路人賈)Encoder Embedding
- 最終輸出:產(chǎn)生預(yù)測(cè)“Jia”
其實(shí)這個(gè)原理很簡(jiǎn)單,主要想表達(dá)的就是因?yàn)樽兂闪薓asked self attention ,所以只考慮輸入向量本身, 和輸入向量的之前的向量,即左側(cè)向量,而不去考慮后邊(右側(cè))向量。
另外,求相關(guān)性的分?jǐn)?shù)時(shí),q,k ,v 同樣的也只會(huì)考慮當(dāng)前輸入向量的左側(cè)向量部分,而不去考慮輸入向量后面的右側(cè)部分。
這里介紹一下論文在Decoder的輸入上,對(duì)Outputs的Shifted Right操作。
Shifted Right 實(shí)質(zhì)上是給輸出添加起始符/結(jié)束符,方便預(yù)測(cè)第1個(gè)Token/結(jié)束預(yù)測(cè)過程。
還是看看我們上一個(gè)栗子~
正常的輸出序列位置關(guān)系如下:
- 0-"I"
- 1-"am"
- 2-"Jia"
但在執(zhí)行的過程中,我們?cè)诔跏驾敵鲋刑砑恿似鹗挤?lt;/s>,相當(dāng)于將輸出整體右移1位(Shifted Right),所以輸出序列變成如下情況:
- 0-</s>【起始符】
- 1-“I”
- 2-“am”
- 3-“Jia”
這樣我們就可以通過起始符</s>預(yù)測(cè)“I”,也就是通過起始符預(yù)測(cè)實(shí)際的第1個(gè)輸出啦。
2.3.4 ?Cross attetion模塊
Cross attetion模塊稱為交叉注意力模塊,是因?yàn)橄蛄?q , k , v 不是來自同一個(gè)模塊。
而是將來自解碼器的輸出向量q 與來自編碼器的輸出向量 k , v運(yùn)算。
具體講來:
向量 q 與向量 k之間相乘求出注意力分?jǐn)?shù)α1 '
注意力分?jǐn)?shù)α1 '再與向量 v 相乘求和,得出向量 b (圖中表示為向量 v ) ;
?2.3.5 ?具體實(shí)現(xiàn)步驟
(1)經(jīng)過 Masked self attention:
解碼器之前的輸出作為當(dāng)前解碼器的輸入,并且訓(xùn)練過程中真實(shí)標(biāo)簽的也會(huì)輸入到解碼器中,此時(shí)這些輸入, 通過一個(gè)Masked self-attention ,得到輸出q向量,注意到這里的q是由解碼器產(chǎn)生的;
(2)經(jīng)過 Cross attention:
將向量q 與來自編碼器的輸出向量 k , v 運(yùn)算。具體講來就是向量 q 與向量 k之間相乘求出注意力分?jǐn)?shù)α1 ',注意力分?jǐn)?shù)α1 '再與向量 v 相乘求和,得出向量 b? ;
(3)經(jīng)過全連接層:
之后向量 b 便被輸入到feed?forward 層, 也即全連接層, 得到最終輸出;
上述步驟,便是原始論文transformer中decoder的設(shè)計(jì)啦~
三、encoder-decoder
剛才已經(jīng)分別了解了encoder和decoder,接下來我們?cè)賮砜纯磂ncoder-decoder這個(gè)框架吧。
3.1 簡(jiǎn)介
encoder-decoder 模型主要是 NLP 領(lǐng)域里的概念。它并不特值某種具體的算法,而是一類算法的統(tǒng)稱。encoder-decoder 算是一個(gè)通用的框架,在這個(gè)框架下可以使用不同的算法來解決不同的任務(wù)。
?其實(shí)整個(gè)過程我們可以看做是一個(gè)游戲——《你畫我猜》。玩家1從系統(tǒng)中抽取拿到題卡,然后通過畫畫的方式描述該詞。玩家2就通過畫來猜出題目中的詞是什么東東。我們拿目前應(yīng)用最深入的機(jī)器翻譯問題舉個(gè)栗子:
(畢賈索已上線~)
就醬就醬~大家懂就行~
3.2 代碼實(shí)現(xiàn)
encoder-decoder框架包含了一個(gè)編碼器和一個(gè)解碼器,并且還擁有可選的額外的參數(shù)。在前向傳播中,編碼器的輸出用于生成編碼狀態(tài),這個(gè)狀態(tài)又被解碼器作為其輸入的一部分。
class EncoderDecoder(nn.Module):
"""
A standard Encoder-Decoder architecture. Base for this and many
other models.
"""
def __init__(self, encoder, decoder, src_embed, tgt_embed, generator):
super(EncoderDecoder, self).__init__()
self.encoder = encoder
self.decoder = decoder
self.src_embed = src_embed
self.tgt_embed = tgt_embed
self.generator = generator
def forward(self, src, tgt, src_mask, tgt_mask):
"Take in and process masked src and target sequences."
return self.decode(self.encode(src, src_mask), src_mask,
tgt, tgt_mask)
def encode(self, src, src_mask):
return self.encoder(self.src_embed(src), src_mask)
def decode(self, memory, src_mask, tgt, tgt_mask):
return self.decoder(self.tgt_embed(tgt), memory, src_mask, tgt_mask)
3.3 注意問題
- 不論輸入和輸出的長(zhǎng)度是什么,中間的“向量c”長(zhǎng)度都是固定的(這是它的缺陷所在)。
- 根據(jù)不同的任務(wù)可以選擇不同的編碼器和解碼器(例如,CNN、RNN、LSTM、GRU等)
- encoder-decoder的一個(gè)顯著特征就是:它是一個(gè)end-to-end的學(xué)習(xí)算法。
- 只要符合這種框架結(jié)構(gòu)的模型都可以統(tǒng)稱為encoder-decoder模型。
本文參考:
李宏毅機(jī)器學(xué)習(xí)?文章來源:http://www.zghlxwxcb.cn/news/detail-843117.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-843117.html
到了這里,關(guān)于【Transformer系列(1)】encoder(編碼器)和decoder(解碼器)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!