iT邦幫忙

2021 iThome 鐵人賽

DAY 25
2
AI & Data

AI Voice Conversion系列 第 25

【Day25】 Transformer 實作包(二)

開始施工

  • 今天我們要來製作 Transformer Decoder 的部分,一樣先上個圖方便施工

以下都跟 Hung-yi Lee 老師及 Tensorflow tutorial 幾乎一樣

Create DecoderLayer

#  一個 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

Create Transformer

我們即將到站了,把 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。

Transfomer 無敵的吧?

這玩意兒這麼厲害,難道沒有什麼缺點嗎?

答案是有的,在後來的 Universal Transformers 裡面提出了 2 個 Transformers 的主要問題。

  1. 有一些 RNN 可以輕鬆解決的問題 Transformer 無法很好的處理,Transformer 在處理局部訊息的時候並沒有 RNN 及 CNN 好(所以當你用原版的 Transformer 處理一些任務時是有可能被輕易地被打敗的)。
  2. Transformers 並不具有圖靈完備性(也不是很懂)
  • Universal Transformers 解決了上面兩個問題,讓 Transformer 不會再輕易的被 RNN 及 CNN 擊敗,不過儘管如此它還是有付出一些代價的,論文裏頭有些東西也是寫得不清不楚,它的圖靈完備性也沒有數學的證明過程,有興趣的話可以參考看看這篇論文。

  • 但其實你可以很直覺的理解到,再一開始做 Position Encoding 的時候就加進了不是真正的輸入了,這樣子的東西設計的再怎麼厲害理應還是會有所誤差。

  • 最後我覺得應該這麼說 "Transformer + RNN + CNN" 才是無敵的吧!XD

小結

終於把 Transformer 複習完了, 最後幾天想要重新回歸到音樂的部份,再和大家做分享,Transformer 的部份就寫到這邊,謝謝大家。

/images/emoticon/emoticon09.gif/images/emoticon/emoticon13.gif/images/emoticon/emoticon14.gif/images/emoticon/emoticon22.gif/images/emoticon/emoticon28.gif


上一篇
【Day24】 Transformer 實作包(一)
下一篇
【Day26】 音樂如何引起人的情緒
系列文
AI Voice Conversion30

尚未有邦友留言

立即登入留言