作者丨蘇劍林 單位丨廣州火焰信息科技有限公司 研究方向丨NLP,神經(jīng)網(wǎng)絡(luò) 個(gè)人主頁(yè)丨kexue.fm 前言2017 年中,有兩篇類似同時(shí)也是筆者非常欣賞的論文,分別是 FaceBook 的 Convolutional Sequence to Sequence Learning 和 Google 的 Attention is All You Need,它們都算是 Seq2Seq 上的創(chuàng)新,本質(zhì)上來(lái)說(shuō),都是拋棄了 RNN 結(jié)構(gòu)來(lái)做 Seq2Seq 任務(wù)。 在本篇文章中,筆者將對(duì) Attention is All You Need 做一點(diǎn)簡(jiǎn)單的分析。當(dāng)然,這兩篇論文本身就比較火,因此網(wǎng)上已經(jīng)有很多解讀了(不過(guò)很多解讀都是直接翻譯論文的,鮮有自己的理解),因此這里盡可能多自己的文字,盡量不重復(fù)網(wǎng)上各位大佬已經(jīng)說(shuō)過(guò)的內(nèi)容。 序列編碼深度學(xué)習(xí)做 NLP 的方法,基本上都是先將句子分詞,然后每個(gè)詞轉(zhuǎn)化為對(duì)應(yīng)的詞向量序列。這樣一來(lái),每個(gè)句子都對(duì)應(yīng)的是一個(gè)矩陣 X=(x1,x2,…,xt),其中 xi 都代表著第 i 個(gè)詞的詞向量(行向量),維度為 d 維,故 請(qǐng)點(diǎn)擊此處輸入圖片描述 。這樣的話,問(wèn)題就變成了編碼這些序列了。第一個(gè)基本的思路是 RNN 層,RNN 的方案很簡(jiǎn)單,遞歸式進(jìn)行: 請(qǐng)點(diǎn)擊此處輸入圖片描述 不管是已經(jīng)被廣泛使用的 LSTM、GRU 還是最近的 SRU,都并未脫離這個(gè)遞歸框架。RNN 結(jié)構(gòu)本身比較簡(jiǎn)單,也很適合序列建模,但 RNN 的明顯缺點(diǎn)之一就是無(wú)法并行,因此速度較慢,這是遞歸的天然缺陷。 另外我個(gè)人覺(jué)得 RNN 無(wú)法很好地學(xué)習(xí)到全局的結(jié)構(gòu)信息,因?yàn)樗举|(zhì)是一個(gè)馬爾科夫決策過(guò)程。 第二個(gè)思路是 CNN 層,其實(shí) CNN 的方案也是很自然的,窗口式遍歷,比如尺寸為 3 的卷積,就是: 請(qǐng)點(diǎn)擊此處輸入圖片描述 在 FaceBook 的論文中,純粹使用卷積也完成了 Seq2Seq 的學(xué)習(xí),是卷積的一個(gè)精致且極致的使用案例,熱衷卷積的讀者必須得好好讀讀這篇文論。 CNN 方便并行,而且容易捕捉到一些全局的結(jié)構(gòu)信息,筆者本身是比較偏愛(ài) CNN 的,在目前的工作或競(jìng)賽模型中,我都已經(jīng)盡量用 CNN 來(lái)代替已有的 RNN 模型了,并形成了自己的一套使用經(jīng)驗(yàn),這部分我們以后再談。 Google的大作提供了第三個(gè)思路:純 Attention,單靠注意力就可以。 RNN 要逐步遞歸才能獲得全局信息,因此一般要雙向 RNN 才比較好;CNN 事實(shí)上只能獲取局部信息,是通過(guò)層疊來(lái)增大感受野;Attention 的思路最為粗暴,它一步到位獲取了全局信息,它的解決方案是: 請(qǐng)點(diǎn)擊此處輸入圖片描述 其中 A,B 是另外一個(gè)序列(矩陣)。如果都取 A=B=X,那么就稱為 Self Attention,它的意思是直接將 xt 與原來(lái)的每個(gè)詞進(jìn)行比較,最后算出 yt。 Attention 定義 Google 的一般化 Attention 思路也是一個(gè)編碼序列的方案,因此我們也可以認(rèn)為它跟 RNN、CNN 一樣,都是一個(gè)序列編碼的層。 請(qǐng)點(diǎn)擊此處輸入圖片描述 前面給出的是一般化的框架形式的描述,事實(shí)上 Google 給出的方案是很具體的。首先,它先把 Attention 的定義給了出來(lái): 請(qǐng)點(diǎn)擊此處輸入圖片描述 這里用的是跟 Google 的論文一致的符號(hào),其中: 請(qǐng)點(diǎn)擊此處輸入圖片描述 如果忽略激活函數(shù) softmax 的話,那么事實(shí)上它就是三個(gè) n×dk,dk×m,m×dv 的矩陣相乘,最后的結(jié)果就是一個(gè) n×dv 的矩陣。 于是我們可以認(rèn)為:這是一個(gè) Attention 層,將 n×dk 的序列 Q 編碼成了一個(gè)新的 n×dv 的序列。 那怎么理解這種結(jié)構(gòu)呢?我們不妨逐個(gè)向量來(lái)看。 請(qǐng)點(diǎn)擊此處輸入圖片描述 其中 Z 是歸一化因子。事實(shí)上 q,k,v 分別是 query,key,value 的簡(jiǎn)寫,K,V 是一一對(duì)應(yīng)的,它們就像是 key-value 的關(guān)系,那么上式的意思就是通過(guò) qt 這個(gè) query,通過(guò)與各個(gè) ks 內(nèi)積的并 softmax 的方式,來(lái)得到 qt 與各個(gè) vs 的相似度,然后加權(quán)求和,得到一個(gè) dv 維的向量。 其中因子起到調(diào)節(jié)作用,使得內(nèi)積不至于太大(太大的話 softmax 后就非 0 即 1 了,不夠“soft”了)。 請(qǐng)點(diǎn)擊此處輸入圖片描述 事實(shí)上這種 Attention 的定義并不新鮮,但由于 Google 的影響力,我們可以認(rèn)為現(xiàn)在是更加正式地提出了這個(gè)定義,并將其視為一個(gè)層地看待。 此外這個(gè)定義只是注意力的一種形式,還有一些其他選擇,比如 query 跟 key 的運(yùn)算方式不一定是點(diǎn)乘(還可以是拼接后再內(nèi)積一個(gè)參數(shù)向量),甚至權(quán)重都不一定要?dú)w一化,等等。 Multi-Head Attention 這個(gè)是 Google 提出的新概念,是 Attention 機(jī)制的完善。 請(qǐng)點(diǎn)擊此處輸入圖片描述 不過(guò)從形式上看,它其實(shí)就再簡(jiǎn)單不過(guò)了,就是把 Q,K,V 通過(guò)參數(shù)矩陣映射一下,然后再做 Attention,把這個(gè)過(guò)程重復(fù)做 h 次,結(jié)果拼接起來(lái)就行了,可謂“大道至簡(jiǎn)”了。具體來(lái)說(shuō): 請(qǐng)點(diǎn)擊此處輸入圖片描述 這里, 請(qǐng)點(diǎn)擊此處輸入圖片描述 然后: 請(qǐng)點(diǎn)擊此處輸入圖片描述 最后得到一個(gè) n×(hd?v) 的序列。所謂“多頭”(Multi-Head),就是只多做幾次同樣的事情(參數(shù)不共享),然后把結(jié)果拼接。 Self Attention 到目前為止,對(duì) Attention 層的描述都是一般化的,我們可以落實(shí)一些應(yīng)用。比如,如果做閱讀理解的話,Q 可以是篇章的詞向量序列,取 K=V 為問(wèn)題的詞向量序列,那么輸出就是所謂的 Aligned Question Embedding。 而在 Google 的論文中,大部分的 Attention 都是 Self Attention,即“自注意力”,或者叫內(nèi)部注意力。 所謂 Self Attention,其實(shí)就是 Attention(X,X,X),X 就是前面說(shuō)的輸入序列。也就是說(shuō),在序列內(nèi)部做 Attention,尋找序列內(nèi)部的聯(lián)系。 Google 論文的主要貢獻(xiàn)之一是它表明了內(nèi)部注意力在機(jī)器翻譯(甚至是一般的 Seq2Seq 任務(wù))的序列編碼上是相當(dāng)重要的,而之前關(guān)于 Seq2Seq 的研究基本都只是把注意力機(jī)制用在解碼端。 類似的事情是,目前 SQUAD 閱讀理解的榜首模型 R-Net 也加入了自注意力機(jī)制,這也使得它的模型有所提升。 當(dāng)然,更準(zhǔn)確來(lái)說(shuō),Google 所用的是 Self Multi-Head Attention: 請(qǐng)點(diǎn)擊此處輸入圖片描述 Position Embedding然而,只要稍微思考一下就會(huì)發(fā)現(xiàn),這樣的模型并不能捕捉序列的順序。換句話說(shuō),如果將 K,V 按行打亂順序(相當(dāng)于句子中的詞序打亂),那么 Attention 的結(jié)果還是一樣的。 這就表明了,到目前為止,Attention 模型頂多是一個(gè)非常精妙的“詞袋模型”而已。 這問(wèn)題就比較嚴(yán)重了,大家知道,對(duì)于時(shí)間序列來(lái)說(shuō),尤其是對(duì)于 NLP 中的任務(wù)來(lái)說(shuō),順序是很重要的信息,它代表著局部甚至是全局的結(jié)構(gòu),學(xué)習(xí)不到順序信息,那么效果將會(huì)大打折扣(比如機(jī)器翻譯中,有可能只把每個(gè)詞都翻譯出來(lái)了,但是不能組織成合理的句子)。 于是 Google 再祭出了一招——Position Embedding,也就是“位置向量”,將每個(gè)位置編號(hào),然后每個(gè)編號(hào)對(duì)應(yīng)一個(gè)向量,通過(guò)結(jié)合位置向量和詞向量,就給每個(gè)詞都引入了一定的位置信息,這樣 Attention 就可以分辨出不同位置的詞了。 Position Embedding 并不算新鮮的玩意,在 FaceBook 的 Convolutional Sequence to Sequence Learning 也用到了這個(gè)東西。但在 Google 的這個(gè)作品中,它的 Position Embedding 有幾點(diǎn)區(qū)別: 1. 以前在 RNN、CNN 模型中其實(shí)都出現(xiàn)過(guò) Position Embedding,但在那些模型中,Position Embedding 是錦上添花的輔助手段,也就是“有它會(huì)更好、沒(méi)它也就差一點(diǎn)點(diǎn)”的情況,因?yàn)?RNN、CNN 本身就能捕捉到位置信息。 但是在這個(gè)純 Attention 模型中,Position Embedding 是位置信息的唯一來(lái)源,因此它是模型的核心成分之一,并非僅僅是簡(jiǎn)單的輔助手段。 2. 在以往的 Position Embedding 中,基本都是根據(jù)任務(wù)訓(xùn)練出來(lái)的向量。而 Google 直接給出了一個(gè)構(gòu)造 Position Embedding 的公式: 請(qǐng)點(diǎn)擊此處輸入圖片描述 這里的意思是將 id 為 p 的位置映射為一個(gè) dpos 維的位置向量,這個(gè)向量的第 i 個(gè)元素的數(shù)值就是 PEi(p)。 Google 在論文中說(shuō)到他們比較過(guò)直接訓(xùn)練出來(lái)的位置向量和上述公式計(jì)算出來(lái)的位置向量,效果是接近的。因此顯然我們更樂(lè)意使用公式構(gòu)造的 Position Embedding 了。 3. Position Embedding 本身是一個(gè)絕對(duì)位置的信息,但在語(yǔ)言中,相對(duì)位置也很重要,Google 選擇前述的位置向量公式的一個(gè)重要原因如下: 由于我們有 sin(α+β)=sinα cosβ+cosα sinβ 以及 cos(α+β)=cosα cosβ?sinα sinβ,這表明位置 p+k 的向量可以表明位置 p 的向量的線性變換,這提供了表達(dá)相對(duì)位置信息的可能性。 結(jié)合位置向量和詞向量有幾個(gè)可選方案,可以把它們拼接起來(lái)作為一個(gè)新向量,也可以把位置向量定義為跟詞向量一樣大小,然后兩者加起來(lái)。 FaceBook 的論文用的是前者,而 Google 論文中用的是后者。直覺(jué)上相加會(huì)導(dǎo)致信息損失,似乎不可取,但 Google 的成果說(shuō)明相加也是很好的方案??磥?lái)我理解還不夠深刻。 一些不足之處到這里,Attention 機(jī)制已經(jīng)基本介紹完了。Attention 層的好處是能夠一步到位捕捉到全局的聯(lián)系,因?yàn)樗苯影研蛄袃蓛杀容^(代價(jià)是計(jì)算量變?yōu)???(n2),當(dāng)然由于是純矩陣運(yùn)算,這個(gè)計(jì)算量相當(dāng)也不是很嚴(yán)重)。 相比之下,RNN 需要一步步遞推才能捕捉到,而 CNN 則需要通過(guò)層疊來(lái)擴(kuò)大感受野,這是 Attention 層的明顯優(yōu)勢(shì)。 Google 論文剩下的工作,就是介紹它怎么用到機(jī)器翻譯中,這是個(gè)應(yīng)用和調(diào)參的問(wèn)題,我們這里不特別關(guān)心它。當(dāng)然,Google 的結(jié)果表明將純注意力機(jī)制用在機(jī)器翻譯中,能取得目前最好的效果,這結(jié)果的確是輝煌的。 然而,我還是想談?wù)勥@篇論文本身和 Attention 層自身的一些不足的地方。 1. 論文標(biāo)題為 Attention is All You Need,因此論文中刻意避免出現(xiàn)了 RNN、CNN 的字眼,但我覺(jué)得這種做法過(guò)于刻意了。 事實(shí)上,論文還專門命名了一種 Position-wise Feed-Forward Networks,事實(shí)上它就是窗口大小為 1 的一維卷積,因此有種為了不提卷積還專門換了個(gè)名稱的感覺(jué),有點(diǎn)不厚道。(也有可能是我過(guò)于臆測(cè)了)。 2. Attention 雖然跟 CNN 沒(méi)有直接聯(lián)系,但事實(shí)上充分借鑒了 CNN 的思想,比如 Multi-Head Attention 就是 Attention 做多次然后拼接,這跟 CNN 中的多個(gè)卷積核的思想是一致的;還有論文用到了殘差結(jié)構(gòu),這也源于 CNN 網(wǎng)絡(luò)。 3. 無(wú)法對(duì)位置信息進(jìn)行很好地建模,這是硬傷。盡管可以引入 Position Embedding,但我認(rèn)為這只是一個(gè)緩解方案,并沒(méi)有根本解決問(wèn)題。 舉個(gè)例子,用這種純 Attention 機(jī)制訓(xùn)練一個(gè)文本分類模型或者是機(jī)器翻譯模型,效果應(yīng)該都還不錯(cuò),但是用來(lái)訓(xùn)練一個(gè)序列標(biāo)注模型(分詞、實(shí)體識(shí)別等),效果就不怎么好了。 那為什么在機(jī)器翻譯任務(wù)上好?我覺(jué)得原因是機(jī)器翻譯這個(gè)任務(wù)并不特別強(qiáng)調(diào)語(yǔ)序,因此 Position Embedding 所帶來(lái)的位置信息已經(jīng)足夠了,此外翻譯任務(wù)的評(píng)測(cè)指標(biāo) BLEU 也并不特別強(qiáng)調(diào)語(yǔ)序。 4、并非所有問(wèn)題都需要長(zhǎng)程的、全局的依賴的,也有很多問(wèn)題只依賴于局部結(jié)構(gòu),這時(shí)候用純 Attention 也不大好。 事實(shí)上,Google 似乎也意識(shí)到了這個(gè)問(wèn)題,因此論文中也提到了一個(gè) restricted 版的 Self-Attention(不過(guò)論文正文應(yīng)該沒(méi)有用到它)。 它假設(shè)當(dāng)前詞只與前后 r 個(gè)詞發(fā)生聯(lián)系,因此注意力也只發(fā)生在這 2r+1 個(gè)詞之間,這樣計(jì)算量就是 ??(nr),這樣也能捕捉到序列的局部結(jié)構(gòu)了。但是很明顯,這就是卷積核中的卷積窗口的概念。 通過(guò)以上討論,我們可以體會(huì)到,把 Attention 作為一個(gè)單獨(dú)的層來(lái)看,跟 CNN、RNN 等結(jié)構(gòu)混合使用,應(yīng)該能更充分融合它們各自的優(yōu)勢(shì),而不必像 Google 論文號(hào)稱 Attention is All You Need,那樣實(shí)在有點(diǎn)“矯枉過(guò)正”了(“口氣”太大),事實(shí)上也做不到。 就論文的工作而言,也許降低一下身段,稱為 Attention is All Seq2Seq Need(事實(shí)上也這標(biāo)題的“口氣”也很大),會(huì)獲得更多的肯定。 代碼實(shí)現(xiàn)最后,為了使得本文有點(diǎn)實(shí)用價(jià)值,筆者試著給出了論文的 Multi-Head Attention 的實(shí)現(xiàn)代碼。有需要的讀者可以直接使用,或者參考著修改。 注意的是,Multi-Head 的意思雖然很簡(jiǎn)單——重復(fù)做幾次然后拼接,但事實(shí)上不能按照這個(gè)思路來(lái)寫程序,這樣會(huì)非常慢。因?yàn)?TensorFlow 是不會(huì)自動(dòng)并行的,比如: a = tf.zeros((10,10)) 其中 b,c 的計(jì)算是串聯(lián)的,盡管 b,c 沒(méi)有相互依賴。因此我們必須把 Multi-Head 的操作合并到一個(gè)張量來(lái)運(yùn)算,因?yàn)閱蝹€(gè)張量的乘法內(nèi)部則會(huì)自動(dòng)并行。 此外,我們要對(duì)序列做 Mask 以忽略填充部分的影響。一般的 Mask 是將填充部分置零,但 Attention 中的 Mask 是要在 softmax 之前,把填充部分減去一個(gè)大整數(shù)(這樣 softmax 之后就非常接近 0 了)。這些內(nèi)容都在代碼中有對(duì)應(yīng)的實(shí)現(xiàn)。 TensorFlow 版 https://github.com/bojone/attention/blob/master/attention_tf.py Keras 版 https://github.com/bojone/attention/blob/master/attention_keras.py 代碼測(cè)試 在 Keras 上對(duì) IMDB 進(jìn)行簡(jiǎn)單的測(cè)試(不做 Mask): from __future__import print_function 無(wú) Position Embedding 的結(jié)果: 請(qǐng)點(diǎn)擊此處輸入圖片描述 有 Position Embedding 的結(jié)果: 請(qǐng)點(diǎn)擊此處輸入圖片描述 貌似最高準(zhǔn)確率比單層的 LSTM 準(zhǔn)確率還高一點(diǎn),另外還可以看到 Position Embedding 能提高準(zhǔn)確率、減弱過(guò)擬合。 計(jì)算量分析 可以看到,事實(shí)上 Attention 的計(jì)算量并不低。比如 Self Attention 中,首先要對(duì) X 做三次線性映射,這計(jì)算量已經(jīng)相當(dāng)于卷積核大小為 3 的一維卷積了,不過(guò)這部分計(jì)算量還只是 ??(n) 的;然后還包含了兩次序列自身的矩陣乘法,這兩次矩陣乘法的計(jì)算量都是 ??(n2) 的,要是序列足夠長(zhǎng),這個(gè)計(jì)算量其實(shí)是很難接受的。 這也表明,restricted 版的 Attention 是接下來(lái)的研究重點(diǎn),并且將 Attention 與 CNN、RNN 混合使用,才是比較適中的道路。 結(jié)語(yǔ)感謝 Google 提供的精彩的使用案例,讓我等在大開(kāi)眼界之余,還對(duì) Attention 的認(rèn)識(shí)更深一層。Google 的這個(gè)成果在某種程度上體現(xiàn)了“大道至簡(jiǎn)”的理念,的確是 NLP 中不可多得的精品。 本文圍繞著 Google 的大作,班門弄斧一番,但愿能夠幫助有需要的讀者更好的理解 Attention。最后懇請(qǐng)大家建議和批評(píng)。 |
|