以下都跟 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 的部份就写到这边,谢谢大家。
<<: 爬虫怎麽爬 从零开始的爬虫自学 DAY11 python列表基础篇
>>: [2021铁人赛 Day10] General Skills 07
Self-hosted runners 介绍 在前面几篇文章实作 GitHub Action Wor...
使用 Hook 官方设定需要遵守的两个规则,并提供了一个 linter plugin 来自动化地实行...
Deadlock tags: IT铁人 介绍 Deadlock是Multi-process常产生的问...
这次要来细讲回圈了 上次说到回圈有分成:1. for回圈,2. while回圈以及3. do whi...
Flutter - Flutter 网路 GIF 图片重复播放 参考资料 Flutter开发实战系列...