【NLP論文筆記】Attention Is All You Need(Transformer 模型結(jié)構(gòu)理解)

本文主要用于記錄谷歌發(fā)表于2017年的一篇論文(引用量接近上千)。該論文提出的Transformer模型也是近年來被廣泛應(yīng)用的。本筆記主要為方便初學(xué)者快速入門,以及自我回顧。。。

論文鏈接:https://arxiv.org/pdf/1706.03762.pdf
Github: https://github.com/tensorflow/tensor2tensor
官方教程鏈接:https://www.tensorflow.org/tutorials/text/transformer#encoder_and_decoder

基本目錄如下:

  1. 摘要
  2. 核心思想
  3. 總結(jié)

------------------第一菇 - 摘要------------------

1.1 論文摘要

現(xiàn)今幾乎所有主流的翻譯模型都是建立在復(fù)雜循環(huán)或卷積seq2seq框架基礎(chǔ)上的,而其中表現(xiàn)最好的模型是基于注意力機(jī)制實(shí)現(xiàn)的。本論文提出了一種新的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu),Transformer,僅僅依賴于注意力機(jī)制,擯棄了傳統(tǒng)循環(huán)或卷積網(wǎng)絡(luò)。這個(gè)新的網(wǎng)絡(luò)結(jié)構(gòu),刷爆了各大翻譯任務(wù),同時(shí)創(chuàng)造了多項(xiàng)新的記錄(英-德的翻譯任務(wù),相比之前的最好記錄提高了2個(gè)BLEU值)。而且,該模型的訓(xùn)練耗時(shí)短,并且對(duì)大數(shù)據(jù)或者有限數(shù)據(jù)集均有良好表現(xiàn)。

------------------第二菇 - 核心思想------------------

2.1 論文模型結(jié)構(gòu)

作者在開頭還是吐槽了很多傳統(tǒng)模型的弊端,之后又吹說自己的Transformer模型有多牛x,完美避開了那些復(fù)雜的神經(jīng)網(wǎng)絡(luò)模型,只用了注意力機(jī)制,不僅大大加快了模型的訓(xùn)練速度,還保證了質(zhì)量哈哈~(補(bǔ):現(xiàn)今2019年下半年來看,還真的是這樣,主流的NLP預(yù)訓(xùn)練模型底層基本都用Transformer作為特征提取層了)

在繼續(xù)往下看之前,還請(qǐng)大家思考一個(gè)問題:
現(xiàn)有的RNN體系(包括后面衍生的Attention機(jī)制)為什么無法滿足現(xiàn)階段語言模型的需求???

我個(gè)人的理解主要有兩點(diǎn):

1)現(xiàn)有體系無法解決Long-term Dependency 問題(該問題即可以簡(jiǎn)單理解為無法捕獲相距較遠(yuǎn)的詞之間的關(guān)聯(lián)關(guān)系)。其本質(zhì)原因還是RNN體系的梯度消失/爆炸問題,盡管LSTM在一定程度上能緩解該問題,然而并未能完全避免。
2)計(jì)算量的問題。該問題其實(shí)也是所有時(shí)序模型的通病,因?yàn)闀r(shí)序模型必須是串行的,可能在預(yù)測(cè)階段還能接受這個(gè)時(shí)效性,但是在訓(xùn)練階段,與能并行計(jì)算的CNN相比,時(shí)效性表現(xiàn)的就不盡如人意了。

因此,也是基于以上兩點(diǎn)的考慮,論文在背景介紹里拋出了一個(gè)貌似是新的概念self-attention(我還專門去搜了一下相關(guān)文獻(xiàn),搜到一篇IBM的,我也寫了論文筆記,大家可以參考)。話不多說,我就帶著大家一起來看看這個(gè)Transformer到底是個(gè)什么東西。(多圖預(yù)警?。?/p>

首先直接看原論文的模型架構(gòu)圖肯定是一臉懵逼,這都啥玩意?反正我第一眼看過去都是個(gè)新概念。。。想必很多初學(xué)者也是跟我一樣,所以我們還是先從更高的視角來解析Transformer模型(在讀模型結(jié)構(gòu)的時(shí)候,大家也要細(xì)心去思考,這個(gè)模型到底是如何解決掉上面我提到的RNN體系的缺陷的)。我從網(wǎng)上盜了幾張模型架構(gòu)圖【1】,方便大家理解。

2.1.1 Transformer架構(gòu)在哪里?(圖片來源
Figure 1.png

這張圖的結(jié)構(gòu)就非常清晰,就說明了一件事情!谷歌團(tuán)隊(duì)真的就只用了Transformer來做整個(gè)特征提取的orz!大家沒有看錯(cuò),這不是簡(jiǎn)化圖,這就是整個(gè)模型的全部,只有一個(gè)Transformer??!

2.1.2 Transformer里面是什么?(圖片來源
Figure 2.png

又是一張結(jié)構(gòu)非常清晰的圖,說明了2個(gè)事情。第一件事,Transformer結(jié)構(gòu)的基本組成仍舊是seq2seq那一套(參考我另一篇筆記)。第二件事,每一個(gè)ENCODERS和DECODERS部分都由6(原論文的N=6)個(gè)小的并且相同的ENCODER和DECODER組成。這幾個(gè)部分的疊加其實(shí)很有意思,大家可以想一想,之前的RNN體系雖說是深度學(xué)習(xí)模型,但其“深度”我們都是從時(shí)間的緯度來考量的,但這里,是真的多個(gè)特征提取層的疊加,是真正的縱向意義上的深度模型??~

2.1.3 ENCODER和DECODER內(nèi)部結(jié)構(gòu)是什么?(圖片來源
Figure 3.png

每一個(gè)ENCODER內(nèi)部又分為兩層,第一層為self-attention(主要是用來捕獲時(shí)序類的特征),第二層為feed-forward層(常規(guī)的特征轉(zhuǎn)換層,通過非線性的變化來轉(zhuǎn)換特征吧)。DECODER層與ENCODER層相似,但是中間多了一層Attention,其功能原理與普通的RNN體系的注意力機(jī)制相似(參考我另一篇筆記

2.1.4 self-attention是如何運(yùn)行的?(圖片來源

在原論文里,作者很霸氣的丟出了一個(gè)公式,

Attention(Q,K,V) = softmax(\frac{QK^T}{\sqrt{d_k}})V

要直接去理解這個(gè)公式,對(duì)矩陣運(yùn)算不是非常熟練的同學(xué)可能有點(diǎn)困難,因此,我們還是先拆分去考量一個(gè)單詞向量的計(jì)算過程,再回過頭來理解這個(gè)矩陣的運(yùn)算。如下圖,是進(jìn)行運(yùn)算的第一步,

Figure 4.png

對(duì)于每一個(gè)單詞向量(Embedding后的),我們都會(huì)計(jì)算出3個(gè)不同的向量,名為query, keys, values。這些向量都是我們通過模型參數(shù)計(jì)算得出來的。相比于詞向量的緯度(論文中為512),這些向量的緯度會(huì)小一點(diǎn)(為64),當(dāng)然這些向量的緯度肯定都是超參數(shù),是可以調(diào)整的。至于這三個(gè)向量的具體含義,只能讓大家從后續(xù)的計(jì)算過程中自行體會(huì)來,我也會(huì)穿插講一下自己的理解。

self-attention的第二步是對(duì)每一個(gè)詞進(jìn)行打分(dot products of the query with all keys)其實(shí)就是把當(dāng)前位置q的向量與所有位置的k向量進(jìn)行點(diǎn)積相乘(該步我的理解其實(shí)就是去考量當(dāng)前位置上的單詞與每一個(gè)位置上的單詞的一種關(guān)聯(lián)關(guān)系的程度),然后再除以\sqrt{d_k}(原論文是8,據(jù)說是可以讓訓(xùn)練收斂的更平穩(wěn)),最后再做一層softmax操作,每一個(gè)詞就會(huì)得到一個(gè)對(duì)當(dāng)前位置的打分,顯然,當(dāng)前詞應(yīng)該會(huì)對(duì)當(dāng)前位置有最高的權(quán)重。(總感覺看到了一絲rnn的意思,考慮所有的輸入序列,對(duì)當(dāng)前預(yù)測(cè)結(jié)果的影響)

self-attention的最后一步,也是很簡(jiǎn)單了,就是把權(quán)重與每一個(gè)位置的v向量加權(quán)求和,最后得到的z向量就是我們要送入到下一層前饋神經(jīng)網(wǎng)絡(luò)的。上述過程的計(jì)算示意圖如下,

Figure 5.png

至此,每一個(gè)詞向量的計(jì)算過程已經(jīng)描述清楚了,矩陣的運(yùn)算想必也是很好理解了。首先,我們計(jì)算Q,K,V三個(gè)矩陣,再根據(jù)上述的運(yùn)算過程,簡(jiǎn)化為矩陣的運(yùn)算就如下圖,

Figure 6.png

至此,對(duì)原論文中的公式就剖析完畢了(當(dāng)然也是整個(gè)self-attention)的核心。當(dāng)然大家可以思考一下,這種self-attention的結(jié)構(gòu)設(shè)計(jì)是如何解決RNN體系的Long-term Dependency問題的?

我個(gè)人的理解主要有兩點(diǎn):

1)參考計(jì)算公式,我們會(huì)發(fā)現(xiàn)考量當(dāng)前詞與其他詞關(guān)系的時(shí)候,不論距離遠(yuǎn)近,我們都會(huì)賦予相同的權(quán)重進(jìn)行計(jì)算(Q*K),以此來考量當(dāng)前詞,與其他所有詞的關(guān)聯(lián)關(guān)系~而RNN體系隨著梯度消失的問題,距離越近,相互關(guān)系捕獲的越好,但是距離越遠(yuǎn)反而難以捕獲了!因此,這就是self-attention的神奇之處~(但真的那么神奇嗎?其實(shí)是需要打一個(gè)問號(hào)的,因?yàn)槌^一段距離的單詞之間其實(shí)是真的沒有什么關(guān)系了,但這邊還是會(huì)與近距離的單詞同等對(duì)待~)
2)但是隨之而來的就是計(jì)算量和存儲(chǔ)的問題,因?yàn)槲覀円?jì)算所有詞的關(guān)聯(lián)關(guān)系,大家可以想象一下那個(gè)矩陣得有多大了~所有后面有人提出了transformer-xl的升級(jí)版(有興趣的可以去了解)

2.1.5 Multi-Head attention是如何運(yùn)行的?(圖片來源

這里Multi-Head其實(shí)沒有那么神秘,簡(jiǎn)單說就是把上述的過程,重復(fù)進(jìn)行幾次(原論文中取值為8)最后再把結(jié)果連接起來。而重復(fù)進(jìn)行的運(yùn)算中,唯一不同的就是初始Q,K,V矩陣的生成,他們分別由不同的參數(shù)矩陣計(jì)算得出,示意圖如下,

Figure 7.png
我個(gè)人對(duì)這樣處理的理解主要有兩點(diǎn):

1)擴(kuò)大了模型的視野,讓模型在計(jì)算當(dāng)前位置信息時(shí),能關(guān)注到更多其他不同位置的信息。(若是單一模型,很可能永遠(yuǎn)被當(dāng)前詞所決定)。
2)增加了模型的語意表達(dá)能力,因?yàn)樗械腝,K,V三個(gè)矩陣的生成都是互不干擾的,可能會(huì)有更多語意層面的表達(dá)(每一組都能捕獲到不一樣對(duì)信息哈,且可以并行化處理~)。

當(dāng)生成完多個(gè)輸出矩陣以后,我們會(huì)拼接所有的結(jié)果,然后與一個(gè)權(quán)重矩陣相乘(隨模型訓(xùn)練的),得到一個(gè)最終的self-attention層的輸出,因此,總結(jié)一下,self-attention的整個(gè)計(jì)算過程如下示意圖,

Figure 8.png

看到這里,想必各位對(duì)整套attention的從理論層面上已經(jīng)有所了解了,我們?cè)賮砜匆幌鹿俜浇o的源碼教程及說明是怎么寫的,看完之后可能會(huì)有不同的理解~

Multi-head attention
class MultiHeadAttention(tf.keras.layers.Layer):
  def __init__(self, d_model, num_heads):
    super(MultiHeadAttention, self).__init__()
    self.num_heads = num_heads
    self.d_model = d_model
    
    assert d_model % self.num_heads == 0
    
    self.depth = d_model // self.num_heads
    
    self.wq = tf.keras.layers.Dense(d_model)
    self.wk = tf.keras.layers.Dense(d_model)
    self.wv = tf.keras.layers.Dense(d_model)
    
    self.dense = tf.keras.layers.Dense(d_model)
        
  def split_heads(self, x, batch_size):
    """Split the last dimension into (num_heads, depth).
    Transpose the result such that the shape is (batch_size, num_heads, seq_len, depth)
    """
    x = tf.reshape(x, (batch_size, -1, self.num_heads, self.depth))
    return tf.transpose(x, perm=[0, 2, 1, 3])
    
  def call(self, v, k, q, mask):
    batch_size = tf.shape(q)[0]
    
    q = self.wq(q)  # (batch_size, seq_len, d_model)
    k = self.wk(k)  # (batch_size, seq_len, d_model)
    v = self.wv(v)  # (batch_size, seq_len, d_model)
    
    # 多頭的劃分,個(gè)人覺得有2種理解:
    # 1. 因?yàn)橄冉?jīng)過了一層線性變化,故而拆分多頭后的每一個(gè)頭都是句子的一種表達(dá)方式。
    # 2. 拆分多頭后的每一個(gè)頭都是句子的某一個(gè)局部信息的特征(本人傾向這個(gè))
    q = self.split_heads(q, batch_size)  # (batch_size, num_heads, seq_len_q, depth)
    k = self.split_heads(k, batch_size)  # (batch_size, num_heads, seq_len_k, depth)
    v = self.split_heads(v, batch_size)  # (batch_size, num_heads, seq_len_v, depth)
    
    # scaled_attention.shape == (batch_size, num_heads, seq_len_q, depth)
    # attention_weights.shape == (batch_size, num_heads, seq_len_q, seq_len_k)
    scaled_attention, attention_weights = scaled_dot_product_attention(
        q, k, v, mask)
    
    scaled_attention = tf.transpose(scaled_attention, perm=[0, 2, 1, 3])  # (batch_size, seq_len_q, num_heads, depth)

    concat_attention = tf.reshape(scaled_attention, 
                                  (batch_size, -1, self.d_model))  # (batch_size, seq_len_q, d_model)

    output = self.dense(concat_attention)  # (batch_size, seq_len_q, d_model)
        
    return output, attention_weights

Multi-head attention consists of four parts:

  • Linear layers and split into heads.
  • Scaled dot-product attention.
  • Concatenation of heads.
  • Final linear layer.

Each multi-head attention block gets three inputs; Q (query), K (key), V (value). These are put through linear (Dense) layers and split up into multiple heads.

The scaled_dot_product_attention defined above is applied to each head (broadcasted for efficiency). An appropriate mask must be used in the attention step. The attention output for each head is then concatenated (using tf.transpose, and tf.reshape) and put through a final Dense layer.

Instead of one single attention head, Q, K, and V are split into multiple heads because it allows the model to jointly attend to information at different positions from different representational spaces. After the split each head has a reduced dimensionality, so the total computation cost is the same as a single head attention with full dimensionality.

2.1.6 什么是Positional Encoding? (圖片來源

Positional Encoding的引入是為了考量輸入序列中的順序問題,他的作用就是為模型注入一些當(dāng)前詞的絕對(duì)位置或相對(duì)位置信息(告訴模型每一個(gè)詞的位置信息)。該向量是由特定的模式生成的,論文中也有公式(大概是用正弦或余弦計(jì)算得出的),然后將該向量與詞向量想加,構(gòu)造出整個(gè)模型的輸入向量。(貌似代碼的實(shí)現(xiàn)與論文中的公式有出入,有興趣的同學(xué)可以深入調(diào)研一下,我其實(shí)也沒太想明白這塊的輸入具體在什么地方起到作用)。依舊再放一張圖,方便大家的理解,

Figure 9.png
2.1.7 Residuals和Layer-Normalization的引入(圖片來源

每一個(gè)Encoder內(nèi)部,每一層都會(huì)有一個(gè)殘參連接,并且?guī)в幸粋€(gè)層-歸一化操作。這倒沒什么好展開講的,直接上圖(一個(gè)Encoder的內(nèi)部結(jié)構(gòu)圖),一目了然,

Figure 10.png
2.1.8 encoder-decoder attention(圖片來源

Decoder的結(jié)構(gòu)跟Encoder其實(shí)差不多,只不過,Decoder多了一層注意力機(jī)制(是真正的注意力機(jī)制orz,跟seq2seq那一套的注意力機(jī)制類似,不熟的參考我上一篇論文筆記)。我們來做一個(gè)對(duì)應(yīng)關(guān)系,其實(shí)大家應(yīng)該就懂了,seq2seq中的輸出隱狀態(tài),其實(shí)就是這邊上一層的Q矩陣輸出,而源輸入的隱狀態(tài),就對(duì)應(yīng)這邊Encoder出來的K,V矩陣。原理幾乎是一模一樣的,下面上一張圖,大家就能理解了,

Figure 11.png

不過還是要再多說一句,Decoder與Encoder的結(jié)構(gòu)還是略微的不同,主要體現(xiàn)在Decoder層的self-attention層會(huì)有一個(gè)mask,會(huì)把當(dāng)前位置之后的所有值都置為(-inf)意為他們對(duì)當(dāng)前預(yù)測(cè)詞不應(yīng)該起作用。

說到這里,除了最后一層,最常規(guī)的softmax層,來預(yù)測(cè)當(dāng)前輸出詞的最大概率,其他的模型結(jié)構(gòu)應(yīng)該是梳理清晰了,這個(gè)時(shí)候再回頭看,原論文中的圖,應(yīng)該就清晰多了。

Figure 12.png
2.2 Transformer架構(gòu)的好處

原論文中,獨(dú)辟蹊徑的開了一章,就叫"Why Self-Attention"。重點(diǎn)討論了一下自己的這一套self-attention在提取特征上與RNN, CNN的對(duì)照。主要分三個(gè)層面的討論,1)每一層的空間復(fù)雜度。2)并行計(jì)算的可能性。3)解決長(zhǎng)時(shí)依賴問題的最長(zhǎng)路徑。具體的對(duì)照大家可以看論文原文。這里我就聊一下自己的理解。其實(shí)本質(zhì)上來看,整一套Transformer的架構(gòu)并沒有標(biāo)題取的那么神乎其神(Attention is All You Need)哈哈,讓我一度以為,注意力框架真的能自成一套,但其實(shí)本質(zhì)還是繞不過特征提取的階段。

再貼一個(gè)官方的優(yōu)劣論證,

This general architecture has a number of advantages:

  • It make no assumptions about the temporal/spatial relationships across the data. This is ideal for processing a set of objects (for example, StarCraft units).
  • Layer outputs can be calculated in parallel, instead of a series like an RNN.
  • Distant items can affect each other's output without passing through many RNN-steps, or convolution layers (see Scene Memory Transformer for example).
  • It can learn long-range dependencies. This is a challenge in many sequence tasks.

The downsides of this architecture are:

  • For a time-series, the output for a time-step is calculated from the entire history instead of only the inputs and current hidden-state. This may be less efficient.
  • If the input does have a temporal/spatial relationship, like text, some positional encoding must be added or the model will effectively see a bag of words.
2.3 論文實(shí)驗(yàn)結(jié)果分析

論文作者把自己提出的整套框架實(shí)現(xiàn)了一遍,這里就不具體展現(xiàn)了。有興趣的讀者可以自行研讀。值得一提的是,谷歌有開源的tensor2tensor,有空還是可以讀一遍源碼,或者工業(yè)界的小伙伴,可以學(xué)一波應(yīng)用。

------------------第三菇 - 總結(jié)------------------

3.1 總結(jié)

到這里,整篇論文的核心思想及其創(chuàng)新點(diǎn)已經(jīng)說清楚了。本論文主要集中在于闡述Transformer架構(gòu),并且解釋了自己為什么要使用這一套架構(gòu)的原因(坊間謠言,為了對(duì)標(biāo)FB的convseq2seq)。

簡(jiǎn)單總結(jié)一下本文就是先羅列了一下該論文的摘要,再具體介紹了一下Transformer架構(gòu),主要是盜用了很多一個(gè)外國(guó)小哥博客的圖(他的可視化Transformer,真的能讓人快速入門,感恩),最后也談了一點(diǎn)自己對(duì)Transformer架構(gòu)的理解,總的來說,谷歌這篇還是劃時(shí)代的產(chǎn)物。希望大家讀完本文后能進(jìn)一步加深對(duì)該論文的理解。有說的不對(duì)的地方也請(qǐng)大家指出,多多交流,大家一起進(jìn)步~??

參考文獻(xiàn):
【1】https://jalammar.github.io/illustrated-transformer/

附錄:
有助于理解的自問自答

1. 假如encoder的shape為(batch_e, seq_e, word_e),decoder的shape為(batch_d, seq_d, word_d)。
   則batch_e = batch_d、seq_e != seq_d、word_e = word_d
2. 在多頭Attention中,word_e和num_heads、depth的關(guān)系?
   word_e = num_heads * depth
3. encoder、decoder的輸入為句子,句子長(zhǎng)度的限制大小是多少?
   不影響參數(shù)量,影響計(jì)算過程中的顯存和速度,要小于position_embedding的size。
4. 假如encoder的shape為(3, 4, 8),decoder的shape為(3, 4, 8),輸出的目標(biāo)語言單詞集合大小為8000,那么decoder的輸出shape是?
   (3, 4, 8000)
5. 假如encoder的shape為(3, 4, 8),decoder的shape為(3, 4, 8),padding_mask和look_ahead_mask的形狀
  (3, 1, 4)、(3, 4, 4)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容