書接昨日,我們說 Positional Encoding 是人工設計的,那它在原本的論文裡面是怎麼設計的呢?
WHY?
我們來看看他們給的說法
We chose this function because we hypothesized it would allow the model to easily learn to attend by relative positions, since for any fixed offset k, PE(pos+k) can be represented as a linear function of PE(pos).
所以作者們認為在 embedding 裡加入這樣的資訊可以幫助 Transformer 學會 model 序列中的相對位置關係,實際上很難想像出這種操作有用,但反正他們是想到了而且相當有用,不過就算我們無法理解,還是可以從 TensorFlow 官方的實作上面無情的把 Code 複製過來用 XD。
def get_angles(pos, i, d_model):
angle_rates = 1 / np.power(10000, (2 * (i//2)) / np.float32(d_model))
return pos * angle_rates
def positional_encoding(position, d_model):
angle_rads = get_angles(np.arange(position)[:, np.newaxis],
np.arange(d_model)[np.newaxis, :],
d_model)
# apply sin to even indices in the array; 2i
angle_rads[:, 0::2] = np.sin(angle_rads[:, 0::2])
# apply cos to odd indices in the array; 2i+1
angle_rads[:, 1::2] = np.cos(angle_rads[:, 1::2])
pos_encoding = angle_rads[np.newaxis, ...]
return tf.cast(pos_encoding, dtype=tf.float32)
讓 Transformer 用來識別序列實際的內容到哪裡,因為實際上我們的訓練資料長度通常都是不一樣的,比較短的會把它補 0 補到一樣長,但這些資訊是毫無意義的,我們必須避免讓 Transformer 關注到這些位置
# padding mask 的工作就是把序列中為 0 的位置設為 1
padding_mask = tf.cast(tf.equal(seq, 0), tf.float32)
用來確保 Decoder 在進行自注意力機制時只會關注自己之前的資訊,在 "做 attention" 的時候我們的 q 跟 k 是從嵌入張量 [a1..a4] 得到的,這個張量的維度是一個 2 維矩陣,它看起來可能像是這樣
[[0.01,0.02,0.03]
[0.04,0.05,0.06]
[0.07,0.08,0.09]]
我們的 Look ahead mask 就會長得像這樣
[[0,1,1]
[0,0,1]
[0,0,0]]
寫出來就是長這樣
look_ahead_mask = 1 - tf.linalg.band_part(tf.ones((size, size)), -1, 0)
那實際上是怎麼操作的呢? 這兩種 mask 是可以和在一起使用的
# 產生 comined_mask 只要把兩個遮罩取大的即可
combined_mask = tf.maximum(padding_mask, look_ahead_mask)
計算一次 attention
def do_attention(q, k, v, mask):
matmul_qk = tf.matmul(q, k, transpose_b=True)
seq_k_length = tf.cast(tf.shape(k)[-1], tf.float32)
scaled_attention_logits = matmul_qk / tf.math.sqrt(seq_k_length)
# 將 mask 「加」到被丟入 softmax 前的 logits
# 這裡的 mask 可以是純 Padding mask 或是 combined_mask
if mask != None:
# (mask * -1e9) 見下方說明
scaled_attention_logits += (mask * -1e9)
# 取 softmax 是為了得到總和為 1 的比例之後對 `v` 做加權平均
attention_weights = tf.nn.softmax(scaled_attention_logits, axis=-1)
# 以注意權重對 v 做加權平均(weighted average)
return tf.matmul(attention_weights, v)
(mask * -1e9) 這件事可以讓這被加上極大負值的位置變得無關緊要,(就是有 1 的地方,我們是不想關注的),在經過 softmax 以後值變會趨近於 0。
到此差不多可以準備做看看 Transformer 了,其實還有很多小細節(尤其是資料維度處理) 的以及 Multi-Head Attention 等到實際做的時候再繼續討論吧。
Transformer model for language understanding