基于 transformer 的編碼器-解碼器模型是 表征學習 和 模型架構(gòu) 這兩個領域多年研究成果的結(jié)晶。本文簡要介紹了神經(jīng)編碼器-解碼器模型的歷史,更多背景知識,建議讀者閱讀由 Sebastion Ruder 撰寫的這篇精彩 博文。此外,建議讀者對 自注意力 (self-attention) 架構(gòu) 有一個基本了解,可以閱讀 Jay Alammar 的 這篇博文 復習一下原始 transformer 模型。
本文分 4 個部分:
背景 - 簡要回顧了神經(jīng)編碼器-解碼器模型的歷史,重點關注基于 RNN 的模型。
編碼器-解碼器 - 闡述基于 transformer 的編碼器-解碼器模型,并闡述如何使用該模型進行推理。
編碼器 - 闡述模型的編碼器部分。
解碼器 - 闡述模型的解碼器部分。
每個部分都建立在前一部分的基礎上,但也可以單獨閱讀。這篇分享是第三部分 編碼器。
編碼器
如前一節(jié)所述, 基于 transformer 的編碼器將輸入序列映射到上下文相關的編碼序列:
仔細觀察架構(gòu),基于 transformer 的編碼器由許多 殘差注意力模塊 堆疊而成。每個編碼器模塊都包含一個 雙向 自注意力層,其后跟著兩個前饋層。這里,為簡單起見,我們忽略歸一化層 (normalization layer)。此外,我們不會深入討論兩個前饋層的作用,僅將其視為每個編碼器模塊 的輸出映射層。雙向自注意層將每個輸入向量 與全部輸入向量 相關聯(lián)并通過該機制將每個輸入向量 提煉為與其自身上下文相關的表征: 。因此,第一個編碼器塊將輸入序列 (如下圖淺綠色所示) 中的每個輸入向量從 上下文無關 的向量表征轉(zhuǎn)換為 上下文相關 的向量表征,后面每一個編碼器模塊都會進一步細化這個上下文表征,直到最后一個編碼器模塊輸出最終的上下文相關編碼 (如下圖深綠色所示)。
我們對 編碼器如何將輸入序列 "I want to buy a car EOS" 變換為上下文編碼序列這一過程進行一下可視化。與基于 RNN 的編碼器類似,基于 transformer 的編碼器也在輸入序列最后添加了一個 EOS,以提示模型輸入向量序列已結(jié)束 。
上圖中的 基于 transformer 的編碼器由三個編碼器模塊組成。我們在右側(cè)的紅框中詳細列出了第二個編碼器模塊的前三個輸入向量: , 及 。紅框下部的全連接圖描述了雙向自注意力機制,上面是兩個前饋層。如前所述,我們主要關注雙向自注意力機制。
可以看出,自注意力層的每個輸出向量 都 直接 依賴于 所有 輸入向量 。這意味著,單詞 “want” 的輸入向量表示 與單詞 “buy” (即 ) 和單詞 “I” (即 ) 直接相關。 因此,“want” 的輸出向量表征, 即 ,是一個融合了其上下文信息的更精細的表征。
我們更深入了解一下雙向自注意力的工作原理。編碼器模塊的輸入序列 中的每個輸入向量 通過三個可訓練的權(quán)重矩陣 ,, 分別投影至 key 向量 、value 向量 和 query 向量 (下圖分別以橙色、藍色和紫色表示):
請注意,對每個輸入向量 ) 而言,其所使用的權(quán)重矩陣都是 相同 的。將每個輸入向量 投影到 query 、 key 和 value 向量后,將每個 query 向量 ) 與所有 key 向量 進行比較。哪個 key 向量與 query 向量 越相似,其對應的 value 向量 對輸出向量 的影響就越重要。更具體地說,輸出向量 被定義為所有 value 向量的加權(quán)和 加上輸入向量 。而各 value 向量的權(quán)重與 和各個 key 向量 之間的余弦相似度成正比,其數(shù)學公式為 ,如下文的公式所示。關于自注意力層的完整描述,建議讀者閱讀 這篇 博文或 原始論文。
好吧,又復雜起來了。我們以上例中的一個 query 向量為例圖解一下雙向自注意層。為簡單起見,本例中假設我們的 基于 transformer 的解碼器只有一個注意力頭 config.num_heads = 1 并且沒有歸一化層。
圖左顯示了上個例子中的第二個編碼器模塊,右邊詳細可視化了第二個輸入向量 的雙向自注意機制,其對應輸入詞為 “want”。首先將所有輸入向量 投影到它們各自的 query 向量 (上圖中僅以紫色顯示前三個 query 向量), value 向量 (藍色) 和 key 向量 (橙色)。然后,將 query 向量 與所有 key 向量的轉(zhuǎn)置 ( 即 ) 相乘,隨后進行 softmax 操作以產(chǎn)生 自注意力權(quán)重 。 自注意力權(quán)重最終與各自的 value 向量相乘,并加上輸入向量 ,最終輸出單詞 “want” 的上下文相關表征, 即 (圖右深綠色表示)。整個等式顯示在圖右框的上部。 和 的相乘使得將 “want” 的向量表征與所有其他輸入 (“I”,“to”,“buy”,“a”,“car”,“EOS”) 的向量表征相比較成為可能,因此自注意力權(quán)重反映出每個輸入向量 對 “want” 一詞的最終表征 的重要程度。
為了進一步理解雙向自注意力層的含義,我們假設以下句子: “ 房子很漂亮且位于市中心,因此那兒公共交通很方便 ”。 “那兒”這個詞指的是“房子”,這兩個詞相隔 12 個字。在基于 transformer 的編碼器中,雙向自注意力層運算一次,即可將“房子”的輸入向量與“那兒”的輸入向量相關聯(lián)。相比之下,在基于 RNN 的編碼器中,相距 12 個字的詞將需要至少 12 個時間步的運算,這意味著在基于 RNN 的編碼器中所需數(shù)學運算與距離呈線性關系。這使得基于 RNN 的編碼器更難對長程上下文表征進行建模。此外,很明顯,基于 transformer 的編碼器比基于 RNN 的編碼器-解碼器模型更不容易丟失重要信息,因為編碼的序列長度相對輸入序列長度保持不變, 即 ,而 RNN 則會將 壓縮到 ,這使得 RNN 很難有效地對輸入詞之間的長程依賴關系進行編碼。
除了更容易學到長程依賴外,我們還可以看到 transformer 架構(gòu)能夠并行處理文本。從數(shù)學上講,這是通過將自注意力機制表示為 query 、 key 和 value 的矩陣乘來完成的:
輸出 是由一系列矩陣乘計算和 softmax 操作算得,因此可以有效地并行化。請注意,在基于 RNN 的編碼器模型中,隱含狀態(tài) 的計算必須按順序進行: 先計算第一個輸入向量的隱含狀態(tài) ; 然后計算第二個輸入向量的隱含狀態(tài),其取決于第一個隱含向量的狀態(tài),依此類推。RNN 的順序性阻礙了有效的并行化,并使其在現(xiàn)代 GPU 硬件上比基于 transformer 的編碼器模型的效率低得多。
太好了,現(xiàn)在我們應該對:
a) 基于 transformer 的編碼器模型如何有效地建模長程上下文表征,以及
b) 它們?nèi)绾斡行У靥幚黹L序列向量輸入這兩個方面有了比較好的理解了。
現(xiàn)在,我們寫一個 MarianMT 編碼器-解碼器模型的編碼器部分的小例子,以驗證這些理論在實踐中行不行得通。
關于前饋層在基于 transformer 的模型中所扮演的角色的詳細解釋超出了本文的范疇。Yun 等人 (2017) 的工作認為前饋層對于將每個上下文向量 映射到目標輸出空間至關重要,而單靠 自注意力 層無法達成這一目的。這里請注意,每個輸出詞元 都經(jīng)由相同的前饋層處理。更多詳細信息,建議讀者閱讀論文。
我們無須將 EOS 附加到輸入序列,雖然有工作表明,在很多情況下加入它可以提高性能。相反地,基于 transformer 的解碼器必須把 作為第 0 個目標向量,并以之為條件預測第 1 個目標向量。
fromtransformersimportMarianMTModel,MarianTokenizer importtorch tokenizer=MarianTokenizer.from_pretrained("Helsinki-NLP/opus-mt-en-de") model=MarianMTModel.from_pretrained("Helsinki-NLP/opus-mt-en-de") embeddings=model.get_input_embeddings() #createidsofencodedinputvectors input_ids=tokenizer("Iwanttobuyacar",return_tensors="pt").input_ids #passinput_idstoencoder encoder_hidden_states=model.base_model.encoder(input_ids,return_dict=True).last_hidden_state #changetheinputslightlyandpasstoencoder input_ids_perturbed=tokenizer("Iwanttobuyahouse",return_tensors="pt").input_ids encoder_hidden_states_perturbed=model.base_model.encoder(input_ids_perturbed,return_dict=True).last_hidden_state #compareshapeandencodingoffirstvector print(f"Lengthofinputembeddings{embeddings(input_ids).shape[1]}.Lengthofencoder_hidden_states{encoder_hidden_states.shape[1]}") #comparevaluesofwordembeddingof"I"forinput_idsandperturbedinput_ids print("Isencodingfor`I`equaltoitsperturbedversion?:",torch.allclose(encoder_hidden_states[0,0],encoder_hidden_states_perturbed[0,0],atol=1e-3))
輸出:
Lengthofinputembeddings7.Lengthofencoder_hidden_states7 Isencodingfor`I`equaltoitsperturbedversion?:False
我們比較一下輸入詞嵌入的序列長度 ( 即 embeddings(input_ids),對應于 ) 和 encoder_hidden_states 的長度 (對應于)。同時,我們讓編碼器對單詞序列 “I want to buy a car” 及其輕微改動版 “I want to buy a house” 分別執(zhí)行前向操作,以檢查第一個詞 “I” 的輸出編碼在更改輸入序列的最后一個單詞后是否會有所不同。
不出意外,輸入詞嵌入和編碼器輸出編碼的長度, 即 和 ,是相等的。同時,可以注意到當最后一個單詞從 “car” 改成 “house” 后, 的編碼輸出向量的值也改變了。因為我們現(xiàn)在已經(jīng)理解了雙向自注意力機制,這就不足為奇了。
順帶一提, 自編碼 模型 (如 BERT) 的架構(gòu)與 基于 transformer 的編碼器模型是完全一樣的。 自編碼 模型利用這種架構(gòu)對開放域文本數(shù)據(jù)進行大規(guī)模自監(jiān)督預訓練,以便它們可以將任何單詞序列映射到深度雙向表征。在 Devlin 等 (2018) 的工作中,作者展示了一個預訓練 BERT 模型,其頂部有一個任務相關的分類層,可以在 11 個 NLP 任務上獲得 SOTA 結(jié)果。你可以從 此處 找到 transformers 支持的所有 自編碼 模型。
責任編輯:彭菁
-
解碼器
+關注
關注
9文章
1129瀏覽量
40636 -
編碼器
+關注
關注
45文章
3573瀏覽量
133980 -
數(shù)據(jù)
+關注
關注
8文章
6808瀏覽量
88743
原文標題:編碼器 | 基于 Transformers 的編碼器-解碼器模型
文章出處:【微信號:zenRRan,微信公眾號:深度學習自然語言處理】歡迎添加關注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論