深入分析transformer(待續)

  • 2021 年 3 月 25 日
  • AI

作為bert based model的基本組件,transformer還是很有必要徹頭徹尾地進行分析和理解,否則在後續的各類花式bert的世界裡很容易迷失自我。。。

The Illustrated Transformerjalammar.github.io圖標

transformer的抽象結構就是encoder+decoder的seq2seq結構。encoder輸入序列數據,decoder輸出序列數據。

bert這類模型大體上的架構就是在transformer上進行各種堆疊和multihead:

encoder可以繼續拆解為:

下面我們從數據流向的層面來介紹整個transformer的運行機制,從input到output層面,我覺得這也是常見的最方便快捷地理解模型地方式了。

首先是inputs,transformer的這個結構其實也可以很方便地適配多元多步時間序列預測。還是先從nlp講起再過渡到時間序列預測,為了方面描述還是給個具體的例子。

假設我們要完成一個機器翻譯的任務,源句子為」我 愛 中國「,目標句子為 」I love China「

首先,」我 愛 中國「先經過word embedding層變成4個向量,假設這四個向量為

[1,1,1,1],[2,2,2,2],[3,3,3,3],[4,4,4,4](當然這個embedding層也可以用word2vec之類的預訓練權重來進行初始化的,這裡的word embedding和textcnn,textrnn中的word embedding的意義是一樣的)

除此之外,因為transformer不像RNN一樣能夠天然地表達詞的先後順序關係,

比如「我愛你「和「你愛我「在Attention機制的視野里完全一樣。所以需要某種方法將位置資訊編碼進語義編碼中。

為了將這種語序關係的資訊囊括進來,引入了position encoding來曲線救國,這部分《attention is all you need》使用的是公式直接生成,也是讓我很費解的一個地方,而facebook的fairseq和google的bert都是直接初始化一個position embedding(注意position encoding和position embededing是兩個東西)來讓模型學習,效果也很好,說明這裡的position encoding of 公式並不是非必須這麼做的。

那麼為什麼position encoding可以用於表示詞序資訊,這要先介紹一下LSTM為什麼可以表達詞序資訊,舉個例子」我 愛 中國 我 是 中國人「進入LSTM,當這個句子經過embedding層的時候,第一個」我「和第五個」我「的embedding向量是完全一樣的,但是經過LSTM之後,第一個」我「的hidden state(或者說LSTM對word的語義編碼)和第五個」我「是不一樣的,因為第5個」我「的語義編碼包含了第五個」我「的embedding向量和第四個word的hidden state,LSTM的結構天然地能夠將語序資訊考慮到語義編碼中,其實說白了就是語義編碼的結果會隨著不同的詞的位置而改變,極端的例子就是一個句子里完全相同的n個詞,經過LSTM之後,每一個詞的embedding向量雖然一樣,但是hidden state(語義編碼)都是完全不同的,除此之外,相同的word如果前面的words完全不一樣,則即使這個word在句子中的順序相同,最終得到的語義編碼hidden state也是不一樣的。 這就是所謂的RNN能夠考慮詞序的一個最直觀的體現。

(補充

根據position encoding的程式碼可以推斷出 position embedding矩陣就是根據句子的最大長度構建的句子,size為 (max_len,d_emb),比如最長句限制為512個詞,word embedding維度為256,則 position embedding的size為(512,256);

關於position encoding和position embedding的部分打算重新寫一篇文章總結一下相對位置編碼、絕對位置編碼等相關概念,考慮到文章篇幅這裡就不繼續深入描述了。具體可見:

馬東什麼:Position encoding和Position embeddingzhuanlan.zhihu.com圖標

Anyway,當我們把word embedding和position encoding/embedding對應的資訊加起來的時候,同一個句子中相同的n個詞,他們的位置一定不相同,因此求和之後的結果一定不相同,通過這種方式模擬了RNN的序列資訊的cover能力從而將時序資訊考慮進來。

那麼到這裡其實都不難,word embedding+position embedding/encoding得到new word embedding向量,還是繼續上面的例子,假設」我 愛 中國「得到的new word embedding為:

[1.1,1.1,1.1,1.1],[2.2,2.2,2.2,2.2],[3.3,3.3,3.3,3.3],[4.4,4.4,4.4,.4.4]

首先這是一個殘差結構,[1.1,1.1,1.1,1.1],[2.2,2.2,2.2,2.2],[3.3,3.3,3.3,3.3],[4.4,4.4,4.4,.4.4] 分為兩支,一支進入 add&norm部分,一支進入multi-head attention部分。

先看這個部分:

三個箭頭代表了三個線性變換的矩陣wq,wk,wv。

關於這裡的線性矩陣wq,wk和wv,其實縱觀attention的發展歷程都會發現,attention不是直接對input進行attention score的計算的,往往是在input經過RNN或者CNN的encode之後變成一個新的語義編碼向量,然後在這個語義編碼向量上進行attention score的計算的,這裡self attention因為放棄了RNN和CNN的結構,所以使用一個線性變換層來進行語義編碼。

如果要問為什麼self attention這裡要先做線性變換語義編碼,這就涉及到為啥attention based model都是使用rnn或者cnn組件進行語義編碼之後再做attention計算。

說實話我也理解不了,attention 機制最初就是針對機器翻譯中的LSTM的hidden state(語義編碼)來進行attention score的計算的,目前來說,其實感覺我們對attention機制的效果還是沒有透徹地理解,比如說這裡引申出另一個問題,為什麼q和k不用同一個線性變換層?

實際上reformer就把q和k用同一個線性變換層來表示了,這個地方糾結不出個所以然來,就好比LSTM中的門控機製為什麼那麼設置?大家解釋了半天之後發現GRU效果也不錯,其門控機制和LSTM又不一樣,所以個人觀點就是,這個部分很難解釋為啥要語義編碼之後再attention計算,說不定不語義編碼直接對input進行attention計算也可以出效果。

接著來將multi attention head,這裡先講一個head的情況:

放大來看就是下面的圖:

這裡就是每一個head的q、v、k對應的wq,wv,wk矩陣都是不同獨立的不是shared的,這裡做映射的一個好處是可以把input的embedding的dimension經過線性變換後降低維度,然後在低維的q、v、k上進行attention計算,則計算的複雜度就可以得到較好的緩解。

上圖介紹的計算過程非常直觀了,q和k使用了score dot,也就是先點積得到112,然後除以q,v,k的dimension的維度,原文使用64維的向量所以這裡除以根號64=8.

這裡為什麼要除以根號dimension,有一個非常好的求證:

transformer中的attention為什麼scaled?www.zhihu.com圖標

這主要是attention的softmax計算的問題存在的,比如說我們僅僅對3維的向量做softmax,則[1,1,1]則softmax之後:

我們可以做個簡單的實驗:

import numpy as np
def softmax(x):
   
    x_row_max = x.max(axis=-1)
    
    x_row_max = x_row_max.reshape(list(x.shape)[:-1]+[1])
    
    x = x - x_row_max
    
    x_exp = np.exp(x)
    
    x_exp_row_sum = x_exp.sum(axis=-1).reshape(list(x.shape)[:-1]+[1])
    
    softmax = x_exp / x_exp_row_sum
    
    return softmax


a=np.ones(10)
softmax(a)

得到:

array([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1])

a=np.ones(10000)
softmax(a)

得到:

array([0.0001, 0.0001, 0.0001, …, 0.0001, 0.0001, 0.0001])

顯然,dimension越大,則每一個值最終softmax之後得到的值就越小,如果原始的輸入大小不一,有一些很大有一些很小,則在大dimension的情況下,小的數經過softmax之後會變得非常的小。

下面我們用autograd來計算softmax函數的梯度:

from autograd import numpy as np
from autograd import elementwise_grad as egrad


def softmax(a):
    """解決softmax函數的溢出問題,利用c(輸入的最大值),softmax函數減去這個最大值保證數據不溢出,softmax函數運算時加上或者
    減去某個常數並不會改變運算的結果"""
    c = np.max(a)
    exp_a = np.exp(a - c) # 溢出對策
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    return y

def cross_entropy_error(y, x):
    delta = 1e-07 # 設置一個微小值,避免對數的參數為0導致無窮大
    return - np.mean(x * np.log(y + delta)) # 注意這個log對應的是ln


y = np.array([1,0,0])
x = np.array([[10,7,5]])
x = softmax(x)
cross_entropy_error(y,x)

loss輸出為

然後求一下一階梯度:

softmax_grad=egrad(cross_entropy_error,0) 
softmax_grad(y,x)

輸出為:

可以看到,梯度的差異已經比較大了。。

我們再換個維度高一點的:

y=np.array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1])
x=np.array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20])
softmax_grad(y,x)

可以看到,梯度的差異已經很大了。。。

下面我們把x除以根號dimension,這裡是根號20

可以看到,整體的梯度差異縮小了一些,如果除以一個很大的數呢:

可以看到,大部分的梯度量綱都比較接近,對於權重來說不同的權重之間獲得的梯度更新量整體不會差異太大,權重之間的大小也會保持在比較穩定的區間里。

回到transformer,這裡的根號Dimension感覺更像是一個經驗數值,其實也可以取別的數,比如tabnet里就使用了簡單乘以0.5進行網路輸出的放縮,這裡可能是為了適配不同的q、v、k的dimension而使用的一個網路小組件。


現在假設我們的input:

[1.1,1.1,1.1,1.1],[2.2,2.2,2.2,2.2],[3.3,3.3,3.3,3.3],[4.4,4.4,4.4,.4.4] 經過單頭的self attention,得到的attention score的矩陣為

[X,X,X,X]

[X,X,X,X]

[X,X,X,X]

[X,X,X,X]

self attention的attention score矩陣,簡單來說也可以稱之為相關係數矩陣,必定是方陣,,因為是自己和自己計算,所以形式大概如上圖,假設我們的input經過了加權求和之後變成了

[1.2,1.4,1.5,1.2],[2.2,2.4,2.5,2.2],[2.2,2.4,2.5,2.2],[2.2,4.4,4.5,4.2],然後就和殘差結構的輸出

[1.1,1.1,1.1,1.1],[2.2,2.2,2.2,2.2],[3.3,3.3,3.3,3.3],[4.4,4.4,4.4,.4.4] 一起進入add&norm部分。

add部分就是這個了,residual 的結構也是比較常見的幫助深層模型進行訓練的組件,其實和transformer本身的大含義關係不大,換句話說如果以後有了另外一種不同的幫助深層模型訓練的組件,也可以替換的,就好比tabnet里用了glu,也是一種緩解梯度彌散的網路組件,都是為了幫助模型更好更快的收斂用的。

然後是layernormalization,關於這些網路組件,常見的比如結構化數據里用batchnorm比較多,nlp里layernorm比較多,就好比embedding層用spaitialdropout比較多而用dropout的比較少,簡單來說就是使用之後的效果決定的,關於為啥用layernorm:

transformer 為什麼使用 layer normalization,而不是其他的歸一化方法?www.zhihu.com圖標

這老哥太猛了。。。

主要問題是在前向傳播和反向傳播中,batchnorm的統計量和其貢獻的梯度都會呈現一定的不穩定性,而layernorm則較好的解決梯度不穩定的問題。

關於XXnormalization層的比較之前寫過:

馬東什麼:各種各樣的normalization with keras(instant bn層還沒看待續)zhuanlan.zhihu.com圖標

另外這個回答我覺得更好理解: