iT邦幫忙

2021 iThome 鐵人賽

DAY 23
2
AI & Data

AI Voice Conversion系列 第 23

【Day23】 Transformer 新手包 (三)

Positional Encoding 怎麼做的

書接昨日,我們說 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).

  • 翻譯過來的意思就是 - 給任一位置 pos 的編碼 PE(pos),跟它距離 k 的位置編碼 PE(pos + k) 可表示為 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)

Mask in Decoder

  • 為了避免 Decoder 在做 Self-Attention 的時候偷看到不該看的未來資訊,在 Transformer 用了兩種 masks 來做到這件事,但不管哪種 mask,值為 1 的位置就是mask 存在的地方。

Padding mask

讓 Transformer 用來識別序列實際的內容到哪裡,因為實際上我們的訓練資料長度通常都是不一樣的,比較短的會把它補 0 補到一樣長,但這些資訊是毫無意義的,我們必須避免讓 Transformer 關注到這些位置

  # padding mask 的工作就是把序列中為 0 的位置設為 1
  padding_mask = tf.cast(tf.equal(seq, 0), tf.float32)

Look ahead mask

用來確保 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 Decoder 的部分

小結

到此差不多可以準備做看看 Transformer 了,其實還有很多小細節(尤其是資料維度處理) 的以及 Multi-Head Attention 等到實際做的時候再繼續討論吧。

參考資料

Transformer model for language understanding


上一篇
【Day22】 Transformer 新手包 (二)
下一篇
【Day24】 Transformer 實作包(一)
系列文
AI Voice Conversion30

尚未有邦友留言

立即登入留言