Transformer 看這一篇就夠了
之前我在這篇語言模型(五)—— Seq2Seq、Attention、Transformer學習筆記中說過要對Transformer來一個摳細節的筆記,今天它來了。由於大部分內容在上文中已有過系統地介紹,本篇筆記將側重點放在各個環節中一些重要的細節中,當然也會盡量按照主線流程來展開。歡迎食用。
全域性視角
在語言模型(五)—— Seq2Seq、Attention、Transformer學習筆記中,我們已經看到過這樣的全域性視角,這裡引述一小段過來,以便在接下來的探索中不迷失:
在Transformer中,我們仍然能夠看見他其實也是由傳統的Encoder-Decoder演化而來,還是以機器翻譯為例,大致的結構圖還是與之前一樣,其中Encoders和Decoders 部分都是由6層Encoder或Decoder堆疊而成(也可以改的更多層):
基礎版 Encoder = Self-Attention + FFN
每一個Encoder結構我們稱之為一個Encoder Block。它的內部結構如下:
如語言模型(五)—— Seq2Seq、Attention、Transformer學習筆記中所講,Encoder Block由Self-Attention和一個FFN組成。這裡需要再次強調一下Self-Attention與傳統Sequence to Sequence 中Attention(稱之為Encoder-Decoder Attention)的不同:
Encoder-Decoder Attention中是由Decoder作為Query來Attention Encoder中的隱藏態,是一個序列對另一個序列的Attention;而Self-Attention則是在一個序列樣本的上下文詞之間相互的Attention。
比如說機器翻譯中的指代問題,翻譯以下句子時
“it”
能Self-Attention到"animal"
。而原先,可能只是”動物“
Attention到"animal"
。這便是最大的區別。”
The animal didn't cross the street because it was too tired
“
那下面我們具體來看Self-Attention的細節,還是那張圖,這次我們標記出部分向量的維度和運算過程:
從圖中一點點往下看,第一個細節:
**Embedding的物件不一樣了。**對於中文來講,之前我們Embedding的物件是詞級別,比如說對於
“中國歡迎您”
這句話,在seq2seq中,我們一般會分詞”中國/歡迎/您“
,但是在這裡我們一般會將一句話切分成所謂的token級別,也就是一個字元一個字元的”中/國/歡/迎/您“
。那為什麼可以這樣呢?因為當我們分詞成”歡迎“
的時候,其實就是預先提供了一定的語義特徵,而現在模型學習能力更強了,我們認為”歡/迎“
能學習到”歡迎“
在上下文中的意思了,所以Embedding的物件也就可以不一樣了。
第二個細節:
輸入的Embedding加入了位置資訊。為了讓模型能夠學習到詞語在語句中不同位置時可能的不同含義,Self-Attention中的Embedding在傳統的Embedding基礎上加入了位置編碼Position Encoding。這是個什麼意思呢?它其實就是給了相鄰的token以不同的初始化位置資訊。為了方便計算,這樣的位置資訊往往又是有周期性的。不要懵。看到下面的公式可能更懵:
但不要被這個公式迷惑,重點是要理解這個位置編碼是要幹嘛以及簡單的實現原理:偶位置按照正弦函式編碼、奇位置按餘弦函式編碼。這樣當你在座標軸上畫出正餘弦函式影像的時候你會發現,相鄰位置的token一個在正弦函式上一個在餘弦函式上,每個token都有他自己獨特的位置,即:各token初始化位置按sin/cos週期分佈。
還有一個問題,不用這種正餘弦函式進行位置編碼可不可以?當然可以,你也可以給一串token進行0-99的位置編碼,99號用完下一個再編號0。
繼續看下圖,我們回到Self-Attention的主流程上來:
-
單詞Thinking對應的embedding維度是512維,乘上神經網路引數W_Q(512x64),得到對應的Query:q1(64維),同理得到k1。
-
q、k點乘得到Score,這一步我們已經在Seq2Seq中說過,含義其實是求了一個相似度。
-
Score除以根號d_k。再將得到的值通過softmax進行歸一化,得到Attention Weight。
那麼細節又來了,為什麼要縮放?又為什麼是根號d_k?
需要收縮的原因在於:使資料分佈更平滑,防止梯度消失。解釋:(如下圖softmax的影像)由於需要做softmax,資料分佈太不集中可能會導致某些分佈點的資料在反向求導的時候沒有梯度。我們需要通過縮放將資料分佈拉回到一個正常的分佈區間裡來。那為什麼是除以根號d_k呢,因為我們期望的是資料分佈要服從(0,1)標準正態分佈,但不縮放的時候資料分佈是服從均值為0,方差為根號d_k的正態分佈,所以要除以根號d_k,將方差拉回到1。
-
加權求和得結果z(對應Encoder-Decoder Attention中的context vector)。
回顧上面的四步,對照Encoder-Decoder Attention中的整個計算流程,不難發現一個細節:q對應Encoder-Decoder Attention中Decoder 輸出的上一時刻的隱藏層輸出h_t-1,k、v對應的Encoder-Decoder Attention中Encoder的隱藏層輸出h_s。如果有點迷糊,可以回去看一眼:Attention機制全流程詳解與細節學習筆記
得到z向量之後,按照Encoder Block的結構圖,下面將各個z傳入FFN,這樣便完成了一個Encoder Block的工作。好,暫停一下,我們來看一看這個FFN。
細節:
這裡的FFN有兩層全連線層:W2(Relu(W1x+b1))+b2,為什麼要加這個FFN?因為裡邊的啟用函式Relu為整個網路提供了非線性的學習能力,不然拿一個線性的網路去學習這個複雜的世界,那些重要的非線性特徵都沒法被髮掘出來。
升級版Encoder = Multi-Head Attention + Add&Norm +FFN
先來一張表,看看這個transformer中真正發育完全的升級版是長啥樣,再看看它和基礎版的差異。
注:”基礎版“和”升級版“是我自己起的名字(原論文作者勿怪),實際上”升級版“是真正的Transformer,為了理解上的方便,我在這裡給起了名字進行區分,也可以理解成基礎版就是沒有多頭注意力機制、沒有Add&Norm的原始版本。
基礎版 | 升級版 | |
---|---|---|
整體結構 | ||
Encoder Block | ||
Self-Attention的形態 |
當你看完這個表,你會發現,升級版的升級點主要在兩點:
- Self-Attention的形態由單頭變為多頭,原先是一個Scaled Dot-Product Attention,現在變成了多個Scaled Dot-Product Attention並行。
- Self-Attention和FFN之後都多了一步Add&Norm 的處理。
來吧,我們一個個來。先說Multi-Head Attention,為什麼要搞這麼一個東西出來?兩點原因:
- 增強了模型輸入處理的並行性;
- 同時它對輸入的 Q,K,V 進行多次不同的對映,相當於把句子投影到不同的子空間中,提高其表達能力。(原文中8個頭,其實就有八組QKV,可以理解成每一組學習的角度不一樣,學到的東西也不一樣)
那這個多頭注意力機制又是怎麼實現的呢?直接看圖:
整個Multi-Head Attention出來之後就來到了Add&Norm層,這裡邊主要包含兩部分:加入殘差(Add)和Layer Normalization(Norm)。
即:Layer Norm(X + Z)
那麼靈魂三問來了:
為什麼要加入殘差結構?
答:為了解決網路層數過深帶來的梯度消失問題。
Layer Normalization是什麼?
層級的Normalization。LN與BN作用一樣:防止梯度消失,加快收斂速度。
為什麼不用BN而用LN?
因為在這裡我們資料規範化的物件的維度一般都是(batch_size, seq_lenth, 512),如果是BN,歸一化的物件是整個一批資料(可以理解為是對batch_size這一維度)進行歸一化,因為每一條資料裡都可能會有padding補充的0元素,導致一個BN下來,整個資料的均值會降低,資料分佈也就改變了。如果是LN。它是針對於每一條資料的(可以看作是seq_lenth這一維度),一行行的歸一化不會包含padding進來的0,這樣資料的分佈也就能不被改變。
以圖說明如下:
而為什麼在影像處理中,可以用BN,因為影像的每一個畫素點都有值,沒有padding 0帶來的問題。所以可以用BN。
延申:介於LN和BN之間還有一個Group Normalization,可以另行學習。
到這裡我們就看完了Encoder部分的所有東西,那麼問題又來了:
Transformer中只要Encoder可以嗎?
當然可以,事實上,整個Encoder就像一個RNN,它就是一個特徵提取器,它生成的特徵向量可以去做很多事情,比如加一個FC做分類任務、甚至可以把它當詞向量、句向量等等。
只有在生成式任務中我們才需要用到Decoder部分,其他任務Encoder就足以勝任。
再繼續,我們來看看Decoder部分到底有啥?
Decoder = Masked Self-Attention + Encoder_Decoder Attention
還是先看基礎版的Decoder:
重點都在圖上標註了:
Decoder中第一部分的Self-Attention與Encoder中的Self-Attention基本一致,Q、K、V都來源於自己對輸入y的學習。只不過多加了一個mask操作,這個mask操作是幹嘛的呢?
因為在Decoder時(比如預測句子的時候),當前時刻是不能提前獲取到未來時刻的資訊,即得把後面的答案遮住,不能提前把答案包進去學習了。遮住後面的答案,這就是mask操作。
第二部分,有一個Encoder-Decoder Attention,這就跟Seq2Seq中講的Attention是一樣的了,需要拿Encoder的輸出和Decoder的隱態來計算Attention Weight。這裡邊的Q是自己第一部分學習到的,K和V都是Encoder輸出過來的。這點需要注意。
說完了基礎版的Decoder , 升級版自然就是多頭並行了,原理和Encoder一樣,便不再贅述。
回顧
還是這張圖,在語言模型(五)—— Seq2Seq、Attention、Transformer學習筆記的回顧環節,也是這張圖,現在當你再看到這張圖,你會發現整個Transformer的流程和一些細節應該都藏在這張圖裡了。不信你品,你仔細品。
最後有一個小小的劇透,左邊的Encoder將會是BERT的基礎,而右邊的Decoder卻又是GPT的基礎。
小結
一篇長文,花了將近五個小時來寫,但卻覺得很是值得。現在又到了凌晨兩點半,準備洗洗睡。諸位看官,看到這裡如果覺得還不錯請給一個贊|收藏|關注,一鍵三連對我來說便是最好的鼓勵了。
不積跬步,無以至千里。加油!
兩篇神文繼續貼在這:
相關文章
- Git 看這一篇就夠了Git
- 索引?看這一篇就夠了!索引
- Flutter DataTable 看這一篇就夠了Flutter
- Java 集合看這一篇就夠了Java
- 代理模式看這一篇就夠了模式
- Elasticsearch入門,看這一篇就夠了Elasticsearch
- 關於SwiftUI,看這一篇就夠了SwiftUI
- 瞭解 MongoDB 看這一篇就夠了MongoDB
- flex佈局看這一篇就夠了Flex
- ActiveMq 之JMS 看這一篇就夠了MQ
- MySQL入門看這一篇就夠了MySql
- jQuery入門看這一篇就夠了jQuery
- 入門Hbase,看這一篇就夠了
- Spring入門看這一篇就夠了Spring
- Mybatis入門看這一篇就夠了MyBatis
- Python操作MongoDB看這一篇就夠了PythonMongoDB
- Python快速入門,看這一篇就夠了!Python
- Android Architecture Components 只看這一篇就夠了Android
- MySQL,你只需看這一篇文章就夠了!MySql
- 瞭解SSL證書,看這一篇就夠了!!
- Nginx 配置常用引數,看這一篇就夠了Nginx
- SpringBoot寫後端介面,看這一篇就夠了!Spring Boot後端
- 什麼是事件管理?看這一篇就夠了!事件
- IDEA中的Git操作,看這一篇就夠了!IdeaGit
- 約束佈局ConstraintLayout看這一篇就夠了AI
- 分散式事務,只看這一篇就夠了分散式
- Java安全第一篇 | 反射看這一篇就夠了Java反射
- mongoDB看這篇就夠了MongoDB
- 圖解Transformer,讀完這篇就夠了圖解ORM
- 熱門好用的api大全,看這一篇就夠了API
- Spring Schedule定時任務看這一篇就夠了Spring
- 瞭解雲桌面,看這一篇文章就夠了!
- Flutter 拖拽控制元件Draggable看這一篇就夠了Flutter控制元件
- Spring中的BeanFactory與FactoryBean看這一篇就夠了SpringBean
- 想要告警的智慧化管理?看這一篇就夠了
- Java高階特性泛型看這一篇就夠了Java泛型
- 瞭解Java中的鎖,看這一篇就夠了!Java
- Android 單元測試只看這一篇就夠了Android