Attention 和 Transformer 來自 Google 2017 發的論文 “ Attention Is All You Need ” 中,其中 Attention 架構結合了 CNN 及 RNN 的優點,並且能改善RNN不能平行運算的缺點。 Transformer 則是基於 Attention 架構的 Encoder 和 Decoder ,接下來將分別講述對於這兩個架構的理解。
Attention
Attention 乍看之下很新潮,但其實只要 input 2 個 vector,output 一個 value,最後 output 會根據這個 value 去加權原本的 input,得到一個新的 context vector,這樣的過程就可以稱為 Attention,至於如何去計算那個 value 就有很多選擇,可以是內積、NN等,這裡紀錄原論文使用的Scaled Dot-Product Attention。
Scaled Dot-Product Attention
對於一個input vector x (本篇 paper 的 x 是 word embedding), 透過三個矩陣W_Q、W_K、W_V 去計算 query vector (q)、key vector (k)、value vector (v)。
這時候每個 x 都能有三個 vector。接下來拿其中一個 x_i 的 query vector,去和其他所有 x_j 的 key vector 做內積,為了要減少維度造成的影響,再來會除以 vector 維度 (d_k) 的開根號。計算出來的值就是 attention weight,也就是下面公式的 alpha,它反應其他 x_j 對應的 v_j 要貢獻多少比例到 output vector。最後的 output vector b_i , 就是所有 v 透過 attention weight 加權的總和。
自己對於這個機制直觀上的理解是: x_i encode 成 query vector(此 query vector 可根據不同情況有不同解釋),key vector 表示 output b_i 需要的 query vector 模樣,那麼這兩個 vector 越相近的話,就是內積的結果越高,表示 x_j 對於 b_i 應該越重要、在計算時要貢獻越高的權重。
最後把所有 q、k、v 寫成一個矩陣的形式就是:
Multi-head Self-attention
相當於是W_Q、W_K、W_V有多個(很像CNN的多層filter概念),最後產生的output會concat起來再降維。在原paper有展示不同的 head 可能會 attend 在不同的東西上,例如長時間的資訊 vs 短時間的資訊。
Position Encoding
看完以上的機制可以發現,這個架構並未考慮 x_i 位置的資訊。所以在實作時會先在 x_i 加上 position embedding 後,再去做 Attention 需要的計算。原 paper 的 position embedding 是固定的,並非可以 train 的參數。
Transformer
Transformer 有 Encoder 和 Decoder ,Encoder 是下圖左方的部分,Decoder 是下圖右方的部分。
Encoder
input x_i 加上 position embedding 後,依序經過以下步驟:
- Multi-head attention
- Add & Norm:Add 即是把 1. 得到的Attention 的 output 各自加上原本對應的 input,後面的 Add 也都是和這裡相同:把 input 和 output 加起來。Norm 指的是 Layer Normalization:將 vector 內的 element 變成 mean = 0,variance = 1。
- Feed forward layer、Add & Norm
以上的 Block 有 N 層,一層的 output 會成為下一層的 input,最後的 ouput 再傳到 Decoder。
Decoder
Decoder 的 input 是上一個 timestamp 的 output。一樣加上 position embedding 後,依序經過:
- Masked multi-head attention:Masked 的意思是只會 attend 在已經產生的 sequence。
- Add & Norm
- Multi-head attention、Add & Norm:這裡的 Attention 會 attend 到 encoder 的輸出。也就是把 2. 的輸出拿去算 query vector。key vector 和 value vector 則用 encoder 的輸出來計算。
- Feed forward layer、Add & Norm
以上 Block 一樣也有 N 層,最後的 output 再根據 task 進行不同的操作。
Code
以下是簡單的 code 幫助理解:
def vanilla_transformer(enc_inp, dec_inp):
"""Transformer variant known as the "vanilla" transformer."""
x = embedding(enc_inp) * sqrt(d_m)
x = x + pos_encoding(x)
x = dropout(x)
for _ in range(n_enc_layers):
attn = multi_head_attention(x, x, x, None)
attn = dropout(attn)
attn = layer_normalization(x + attn)
x = pointwise_ff(attn) # GELU activation(affine map(attn))
x = layer_normalization(x + attn)
# x is at this point the output of the encoder
enc_out = x
x = embedding(dec_inp) * sqrt(d_m)
x = x + pos_encoding(x)
x = dropout(x)
mask = causal_mask(x)
for _ in range(n_dec_layers):
attn1 = multi_head_attention(x, x, x, mask)
attn1 = layer_normalization(attn1 + x)
attn2 = multi_head_attention(attn1, enc_out, enc_out, None)
attn2 = dropout(attn2)
attn2 = layer_normalization(attn1 + attn2)
x = pointwise_ff(attn2)
x = layer_normalization(attn2 + x)
return dense(x)
Application: BERT
BERT這個 pretrained model 的架構即是來自上面 Transformer 的 Encoder 的灰底區塊。
後續許多 pretrained model 也是沿用 Encoder 灰底區塊的架構,並且在 paper 內會直接稱之為 transformer,和本篇所寫的 Encoder + Decoder 不同。
BERT 做了以下兩個 task 來訓練參數:
- Masked LM:mask 掉一些 input 的字詞,在那些 mask 的位置的 output 後面接一個 Linear classifier去預測結果(因為是 Transformer 的 Encoder,所以每個字詞都有對應的一個 output)。
- Next Sentence Prediction:把兩個句子用[SEP]的符號接起來,在句子最前面接上[CLS],在[CLS]的output vector後面接上 Linear classifier。
由於以上兩件事都是不需要去 label data ,只要有大量的 document 就可以訓練,但也並不是沒有 target ,而是 target 就在句子本身,所以又有人稱為 self-supervised。