以下都跟 Hung-yi Lee 老師及 Tensorflow tutorial 幾乎一樣
# 一個 Decoder 裡頭有 N 個 DecoderLayer,
# 一個 DecoderLayer 有三個 sub-layers
# 1. 自注意的 MHA,
# 2. 關注 Encoder 輸出的 MHA
# 3. FFN
class DecoderLayer(tf.keras.layers.Layer):
def __init__(self, d_model, num_heads, dff, rate=0.1):
super(DecoderLayer, self).__init__()
self.ffn = point_wise_feed_forward_network(d_model, dff)
# 定義每個 sub-layer 用的 LayerNorm
self.layernorm1 = tf.keras.layers.LayerNormalization(epsilon=1e-6)
self.layernorm2 = tf.keras.layers.LayerNormalization(epsilon=1e-6)
self.layernorm3 = tf.keras.layers.LayerNormalization(epsilon=1e-6)
# 定義每個 sub-layer 用的 Dropout
self.dropout1 = tf.keras.layers.Dropout(rate)
self.dropout2 = tf.keras.layers.Dropout(rate)
self.dropout3 = tf.keras.layers.Dropout(rate)
def call(self, x, enc_output, training,combined_mask, inp_padding_mask):
# enc_output 為 Encoder 輸出序列,shape 為 (batch_size, input_seq_len, d_model)
# attn_weights_block_1 則為 (batch_size, num_heads, target_seq_len, target_seq_len)
# attn_weights_block_2 則為 (batch_size, num_heads, target_seq_len, input_seq_len)
# sub-layer 1: Decoder layer 自己對輸出序列做注意力。
# 我們同時需要 look ahead mask 以及輸出序列的 padding mask
attn1, attn_weights_block1 = do_MultiHeadAttention(x,x,x,combined_mask)
attn1 = self.dropout1(attn1, training=training)
out1 = self.layernorm1(attn1 + x)
# sub-layer 2: Decoder layer 關注 Encoder 的最後輸出
# 記得我們一樣需要對 Encoder 的輸出套用 padding mask
attn2, attn_weights_block2 = do_MultiHeadAttention(
enc_output,
enc_output,
out1,
inp_padding_mask
)
# (batch_size, target_seq_len, d_model)
attn2 = self.dropout2(attn2, training=training)
out2 = self.layernorm2(attn2 + out1) # (batch_size, target_seq_len, d_model)
# sub-layer 3: FFN 部分跟 Encoder layer 完全一樣
ffn_output = self.ffn(out2) # (batch_size, target_seq_len, d_model)
ffn_output = self.dropout3(ffn_output, training=training)
out3 = self.layernorm3(ffn_output + out2) # (batch_size, target_seq_len, d_model)
# 除了主要輸出 `out3` 以外,輸出 multi-head 注意權重方便之後理解模型內部狀況
return out3, attn_weights_block1, attn_weights_block2
最後跟 Encoder 一樣加上 position encoding 跟 Embedding,Decoder 就煉成了
class Decoder(tf.keras.layers.Layer):
# 初始參數跟 Encoder 只差在用 `target_vocab_size` 而非 `inp_vocab_size`
def __init__(self, num_layers, d_model, num_heads, dff, target_vocab_size,
rate=0.1):
super(Decoder, self).__init__()
self.d_model = d_model
# 為 (目標語言)建立詞嵌入層
self.embedding = tf.keras.layers.Embedding(target_vocab_size, d_model)
self.pos_encoding = positional_encoding(target_vocab_size, self.d_model)
self.dec_layers = [DecoderLayer(d_model, num_heads, dff, rate)
for _ in range(num_layers)]
self.dropout = tf.keras.layers.Dropout(rate)
# 呼叫時的參數跟 DecoderLayer 一模一樣
def call(self, x, enc_output, training,
combined_mask, inp_padding_mask):
tar_seq_len = tf.shape(x)[1]
attention_weights = {} # 用來存放每個 Decoder layer 的注意權重
# 這邊跟 Encoder 做的事情完全一樣
x = self.embedding(x) # (batch_size, tar_seq_len, d_model)
x *= tf.math.sqrt(tf.cast(self.d_model, tf.float32))
x += self.pos_encoding[:, :tar_seq_len, :]
x = self.dropout(x, training=training)
for i, dec_layer in enumerate(self.dec_layers):
x, block1, block2 = dec_layer(x, enc_output, training,
combined_mask, inp_padding_mask)
# 將從每個 Decoder layer 取得的注意權重全部存下來回傳,方便觀察
attention_weights['decoder_layer{}_block1'.format(i + 1)] = block1
attention_weights['decoder_layer{}_block2'.format(i + 1)] = block2
# x.shape == (batch_size, tar_seq_len, d_model)
return x, attention_weights
我們即將到站了,把 Encoder 跟 Decoder 組起來就完成了
class Transformer(tf.keras.Model):
def __init__(self, num_layers, d_model, num_heads, dff, input_vocab_size, target_vocab_size, rate=0.1):
super(Transformer, self).__init__()
self.encoder = Encoder(num_layers, d_model, num_heads, dff, input_vocab_size, rate)
self.decoder = Decoder(num_layers, d_model, num_heads, dff, target_vocab_size, rate)
# 這個 FFN 輸出一樣大的 logits 數,等通過 softmax 就代表每個輸入的出現機率
self.final_layer = tf.keras.layers.Dense(target_vocab_size)
def call(self, inp, tar, training, enc_padding_mask, combined_mask, dec_padding_mask):
enc_output = self.encoder(inp, training, enc_padding_mask)
dec_output, attention_weights = self.decoder(tar, enc_output, training, combined_mask, dec_padding_mask)
final_output = self.final_layer(dec_output)
return final_output, attention_weights
接下來就可以拿這個 model 去做各種的實驗了,建議是從 Tensorflow tutorial開始做起,來解決一個機器翻譯的問題,這邊就不詳細介紹了,你可以看這篇(Transformer 聖經)就會很仔細地帶你走過一次 Tensorflow tutorial。
這玩意兒這麼厲害,難道沒有什麼缺點嗎?
答案是有的,在後來的 Universal Transformers 裡面提出了 2 個 Transformers 的主要問題。
Universal Transformers 解決了上面兩個問題,讓 Transformer 不會再輕易的被 RNN 及 CNN 擊敗,不過儘管如此它還是有付出一些代價的,論文裏頭有些東西也是寫得不清不楚,它的圖靈完備性也沒有數學的證明過程,有興趣的話可以參考看看這篇論文。
但其實你可以很直覺的理解到,再一開始做 Position Encoding 的時候就加進了不是真正的輸入了,這樣子的東西設計的再怎麼厲害理應還是會有所誤差。
最後我覺得應該這麼說 "Transformer + RNN + CNN" 才是無敵的吧!XD
終於把 Transformer 複習完了, 最後幾天想要重新回歸到音樂的部份,再和大家做分享,Transformer 的部份就寫到這邊,謝謝大家。