我们已经实现了 Transformer 编码器和 解码器并分别进行了测试,现在我们可以将它们结合起来形成一个完整的模型。我们还将学习如何创建填充掩码和前瞻掩码,通过它们来抑制在编码器或解码器计算中不被考虑的输入值。我们的最终目标仍然是将完整模型应用于自然语言处理 (NLP)。
在本教程中,您将了解如何实现完整的 Transformer 模型并创建填充掩码和前瞻掩码。
完成本教程后,您将了解:
- 如何为编码器和解码器创建填充掩码
- 如何为解码器创建前瞻掩码
- 如何将 Transformer 编码器和解码器组合成一个模型
- 如何打印出编码器和解码器层的摘要
让我们开始吧。

Transformer 编码器和解码器的结合与掩码
照片由 John O’Nolan 拍摄,部分权利保留。
教程概述
本教程分为四个部分;它们是
- Transformer 架构回顾
- 掩码
- 创建填充掩码
- 创建前瞻掩码
- 组合 Transformer 编码器和解码器
- 创建 Transformer 模型实例
- 打印出编码器和解码器层的摘要
先决条件
本教程假设您已熟悉以下内容:
Transformer 架构回顾
回想一下,Transformer 架构遵循编码器-解码器结构。左侧的编码器负责将输入序列映射到一系列连续表示;右侧的解码器接收编码器的输出以及前一个时间步的解码器输出来生成输出序列。

Transformer 架构的编码器-解码器结构
摘自“Attention Is All You Need”
在生成输出序列时,Transformer 不依赖于循环和卷积。
您已经了解了如何分别实现 Transformer 编码器和解码器。在本教程中,您将把它们结合起来形成一个完整的 Transformer 模型,并对输入值应用填充和前瞻掩码。
让我们先来了解如何应用掩码。
通过我的书 《Transformer 模型构建》 来启动您的项目。书中提供了自学教程和工作代码,指导您构建一个功能齐全的 Transformer 模型,可以
将句子从一种语言翻译成另一种语言的完整 Transformer 模型...
掩码
创建填充掩码
您应该已经熟悉在将输入值馈入编码器和解码器之前对其进行掩码处理的重要性。
正如您在 训练 Transformer 模型时将看到的,馈入编码器和解码器的输入序列首先会被填充为零,直到达到特定的序列长度。填充掩码的重要性在于确保这些零值不会与实际输入值一起被编码器和解码器处理。
让我们创建以下函数来为编码器和解码器生成填充掩码
1 2 3 4 5 6 7 8 |
from tensorflow import math, cast, float32 def padding_mask(input): # 创建一个掩码,用 1 标记输入中的零填充值 mask = math.equal(input, 0) mask = cast(mask, float32) return mask |
此函数在接收到输入后,将生成一个张量,在输入包含零值的地方标记为“1”。
因此,如果您输入以下数组
1 2 3 4 |
from numpy import array input = array([1, 2, 3, 4, 0, 0, 0]) print(padding_mask(input)) |
那么 padding_mask
函数的输出将是以下内容
1 |
tf.Tensor([0. 0. 0. 0. 1. 1. 1.], shape=(7,), dtype=float32) |
创建前瞻掩码
为了防止解码器关注后续的词语,需要前瞻掩码,这样当前词语的预测只能依赖于它之前的已知输出。
为此,让我们创建以下函数来为解码器生成前瞻掩码
1 2 3 4 5 6 7 |
from tensorflow import linalg, ones def lookahead_mask(shape): # 通过用 1.0 标记未来条目来屏蔽它们 mask = 1 - linalg.band_part(ones((shape, shape)), -1, 0) return mask |
我们将把解码器输入的长度传递给它。作为示例,我们将此长度设置为 5
1 |
print(lookahead_mask(5)) |
那么 lookahead_mask
函数返回的输出是以下内容
1 2 3 4 5 6 |
tf.Tensor( [[0. 1. 1. 1. 1.] [0. 0. 1. 1. 1.] [0. 0. 0. 1. 1.] [0. 0. 0. 0. 1.] [0. 0. 0. 0. 0.]], shape=(5, 5), dtype=float32) |
同样,1 值屏蔽了不应使用的条目。这样,每个词的预测仅依赖于它之前的词。
想开始构建带有注意力的 Transformer 模型吗?
立即参加我的免费12天电子邮件速成课程(含示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
组合 Transformer 编码器和解码器
让我们开始创建 TransformerModel
类,它继承自 Keras 中的 Model
基类
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class TransformerModel(Model): def __init__(self, enc_vocab_size, dec_vocab_size, enc_seq_length, dec_seq_length, h, d_k, d_v, d_model, d_ff_inner, n, rate, **kwargs): super(TransformerModel, self).__init__(**kwargs) # 设置编码器 self.encoder = Encoder(enc_vocab_size, enc_seq_length, h, d_k, d_v, d_model, d_ff_inner, n, rate) # 设置解码器 self.decoder = Decoder(dec_vocab_size, dec_seq_length, h, d_k, d_v, d_model, d_ff_inner, n, rate) # 定义最终的密集层 self.model_last_layer = Dense(dec_vocab_size) ... |
创建 TransformerModel
类我们的第一步是初始化先前实现的 Encoder
和 Decoder
类的实例,并将它们的输出分别赋值给变量 encoder
和 decoder
。如果您将这些类保存在单独的 Python 脚本中,请不要忘记导入它们。我将代码保存在 encoder.py 和 decoder.py 的 Python 脚本中,因此我需要相应地导入它们。
您还将包含最后一层密集层,该层生成最终输出,如同 Vaswani 等人 (2017) 的 Transformer 架构一样。
接下来,您将创建类方法 call()
,以将相关的输入馈入编码器和解码器。
首先生成填充掩码,以掩盖编码器输入以及解码器的编码器输出(当将其馈入解码器的第二个自注意力块时)
1 2 3 4 5 6 |
... def call(self, encoder_input, decoder_input, training): # 创建填充掩码以屏蔽编码器输入和解码器中的编码器输出 enc_padding_mask = self.padding_mask(encoder_input) ... |
然后生成填充掩码和前瞻掩码来屏蔽解码器输入。将它们通过元素级的 maximum
操作组合在一起
1 2 3 4 5 6 |
... # 创建并组合填充和前瞻掩码以馈入解码器 dec_in_padding_mask = self.padding_mask(decoder_input) dec_in_lookahead_mask = self.lookahead_mask(decoder_input.shape[1]) dec_in_lookahead_mask = maximum(dec_in_padding_mask, dec_in_lookahead_mask) ... |
接下来,将相关输入馈入编码器和解码器,并通过将解码器输出馈入最后一个密集层来生成 Transformer 模型输出
1 2 3 4 5 6 7 8 9 10 11 |
... # 将输入馈入编码器 encoder_output = self.encoder(encoder_input, enc_padding_mask, training) # 将编码器输出馈入解码器 decoder_output = self.decoder(decoder_input, encoder_output, dec_in_lookahead_mask, enc_padding_mask, training) # 将解码器输出通过最后的密集层 model_output = self.model_last_layer(decoder_output) return model_output |
将所有步骤结合起来,我们得到以下完整的代码列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
from encoder import Encoder from decoder import Decoder from tensorflow import math, cast, float32, linalg, ones, maximum, newaxis from tensorflow.keras import Model from tensorflow.keras.layers import Dense class TransformerModel(Model): def __init__(self, enc_vocab_size, dec_vocab_size, enc_seq_length, dec_seq_length, h, d_k, d_v, d_model, d_ff_inner, n, rate, **kwargs): super(TransformerModel, self).__init__(**kwargs) # 设置编码器 self.encoder = Encoder(enc_vocab_size, enc_seq_length, h, d_k, d_v, d_model, d_ff_inner, n, rate) # 设置解码器 self.decoder = Decoder(dec_vocab_size, dec_seq_length, h, d_k, d_v, d_model, d_ff_inner, n, rate) # 定义最终的密集层 self.model_last_layer = Dense(dec_vocab_size) def padding_mask(self, input): # 创建一个掩码,用 1.0 标记输入中的零填充值 mask = math.equal(input, 0) mask = cast(mask, float32) # 掩码的形状应该可以广播到注意力权重稍后要掩码的形状 # return mask[:, newaxis, newaxis, :] def lookahead_mask(self, shape): # 通过用 1.0 标记未来条目来屏蔽它们 mask = 1 - linalg.band_part(ones((shape, shape)), -1, 0) return mask def call(self, encoder_input, decoder_input, training): # 创建填充掩码以屏蔽编码器输入和解码器中的编码器输出 enc_padding_mask = self.padding_mask(encoder_input) # 创建并组合填充和前瞻掩码以馈入解码器 dec_in_padding_mask = self.padding_mask(decoder_input) dec_in_lookahead_mask = self.lookahead_mask(decoder_input.shape[1]) dec_in_lookahead_mask = maximum(dec_in_padding_mask, dec_in_lookahead_mask) # 将输入馈入编码器 encoder_output = self.encoder(encoder_input, enc_padding_mask, training) # 将编码器输出馈入解码器 decoder_output = self.decoder(decoder_input, encoder_output, dec_in_lookahead_mask, enc_padding_mask, training) # 将解码器输出通过最后的密集层 model_output = self.model_last_layer(decoder_output) return model_output |
请注意,我们对 padding_mask
函数返回的输出进行了一个小的更改。它的形状被调整为可以广播到训练 Transformer 模型时要掩码的注意力权重张量的形状。
创建 Transformer 模型实例
我们将使用 Vaswani 等人 (2017) 的论文 《Attention Is All You Need》中指定的参数值。
1 2 3 4 5 6 7 8 9 |
h = 8 # 自注意力头数 d_k = 64 # 线性投影查询和键的维度 d_v = 64 # 线性投影值的维度 d_ff = 2048 # 内部全连接层的维度 d_model = 512 # 模型子层输出的维度 n = 6 # 编码器堆栈中的层数 dropout_rate = 0.1 # Dropout 层中输入单元的丢弃频率 ... |
至于与输入相关的参数,在您进入 训练完整的 Transformer 模型阶段之前,您将使用虚拟值。届时,您将使用实际的句子
1 2 3 4 5 6 7 |
... enc_vocab_size = 20 # 编码器的词汇量大小 dec_vocab_size = 20 # 解码器的词汇量大小 enc_seq_length = 5 # 输入序列的最大长度 dec_seq_length = 5 # 目标序列的最大长度 ... |
现在您可以按如下方式创建 TransformerModel
类的实例
1 2 3 4 |
from model import TransformerModel # 创建模型 training_model = TransformerModel(enc_vocab_size, dec_vocab_size, enc_seq_length, dec_seq_length, h, d_k, d_v, d_model, d_ff, n, dropout_rate) |
完整的代码清单如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
enc_vocab_size = 20 # 编码器的词汇量大小 dec_vocab_size = 20 # 解码器的词汇量大小 enc_seq_length = 5 # 输入序列的最大长度 dec_seq_length = 5 # 目标序列的最大长度 h = 8 # 自注意力头数 d_k = 64 # 线性投影查询和键的维度 d_v = 64 # 线性投影值的维度 d_ff = 2048 # 内部全连接层的维度 d_model = 512 # 模型子层输出的维度 n = 6 # 编码器堆栈中的层数 dropout_rate = 0.1 # Dropout 层中输入单元的丢弃频率 # 创建模型 training_model = TransformerModel(enc_vocab_size, dec_vocab_size, enc_seq_length, dec_seq_length, h, d_k, d_v, d_model, d_ff, n, dropout_rate) |
打印出编码器和解码器层的摘要
您还可以打印出 Transformer 模型编码器和解码器块的摘要。单独打印它们的选择将允许您查看其各个子层的详细信息。要做到这一点,请在 EncoderLayer
和 DecoderLayer
类的 __init__()
方法中添加以下代码行
1 |
self.build(input_shape=[None, sequence_length, d_model]) |
然后您需要向 EncoderLayer
类添加以下方法
1 2 3 |
def build_graph(self): input_layer = Input(shape=(self.sequence_length, self.d_model)) return Model(inputs=[input_layer], outputs=self.call(input_layer, None, True)) |
以及向 DecoderLayer
类添加以下方法
1 2 3 |
def build_graph(self): input_layer = Input(shape=(self.sequence_length, self.d_model)) return Model(inputs=[input_layer], outputs=self.call(input_layer, input_layer, None, None, True)) |
这会导致 EncoderLayer
类修改如下(call()
方法下的三个点表示这与此处实现的保持不变 这里)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
from tensorflow.keras.layers import Input from tensorflow.keras import Model class EncoderLayer(Layer): def __init__(self, sequence_length, h, d_k, d_v, d_model, d_ff, rate, **kwargs): super(EncoderLayer, self).__init__(**kwargs) self.build(input_shape=[None, sequence_length, d_model]) self.d_model = d_model self.sequence_length = sequence_length self.multihead_attention = MultiHeadAttention(h, d_k, d_v, d_model) self.dropout1 = Dropout(rate) self.add_norm1 = AddNormalization() self.feed_forward = FeedForward(d_ff, d_model) self.dropout2 = Dropout(rate) self.add_norm2 = AddNormalization() def build_graph(self): input_layer = Input(shape=(self.sequence_length, self.d_model)) return Model(inputs=[input_layer], outputs=self.call(input_layer, None, True)) def call(self, x, padding_mask, training): ... |
DecoderLayer
类也可以进行类似的更改。
完成必要的更改后,您可以继续创建 EncoderLayer
和 DecoderLayer
类的实例并打印它们的摘要,如下所示:
1 2 3 4 5 6 7 8 |
from encoder import EncoderLayer from decoder import DecoderLayer encoder = EncoderLayer(enc_seq_length, h, d_k, d_v, d_model, d_ff, dropout_rate) encoder.build_graph().summary() decoder = DecoderLayer(dec_seq_length, h, d_k, d_v, d_model, d_ff, dropout_rate) decoder.build_graph().summary() |
编码器的结果摘要如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
Model: "model" __________________________________________________________________________________________________ Layer (type) Output Shape Param # Connected to ================================================================================================== input_1 (InputLayer) [(None, 5, 512)] 0 [] multi_head_attention_18 (Multi (None, 5, 512) 131776 ['input_1[0][0]', HeadAttention) 'input_1[0][0]', 'input_1[0][0]'] dropout_32 (Dropout) (None, 5, 512) 0 ['multi_head_attention_18[0][0]'] add_normalization_30 (AddNorma (None, 5, 512) 1024 ['input_1[0][0]', lization) 'dropout_32[0][0]'] feed_forward_12 (FeedForward) (None, 5, 512) 2099712 ['add_normalization_30[0][0]'] dropout_33 (Dropout) (None, 5, 512) 0 ['feed_forward_12[0][0]'] add_normalization_31 (AddNorma (None, 5, 512) 1024 ['add_normalization_30[0][0]', lization) 'dropout_33[0][0]'] ================================================================================================== Total params: 2,233,536 Trainable params: 2,233,536 不可训练参数: 0 __________________________________________________________________________________________________ |
而解码器的结果摘要如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
Model: "model_1" __________________________________________________________________________________________________ Layer (type) Output Shape Param # Connected to ================================================================================================== input_2 (InputLayer) [(None, 5, 512)] 0 [] multi_head_attention_19 (Multi (None, 5, 512) 131776 ['input_2[0][0]', HeadAttention) 'input_2[0][0]', 'input_2[0][0]'] dropout_34 (Dropout) (None, 5, 512) 0 ['multi_head_attention_19[0][0]'] add_normalization_32 (AddNorma (None, 5, 512) 1024 ['input_2[0][0]', lization) 'dropout_34[0][0]', 'add_normalization_32[0][0]', 'dropout_35[0][0]'] multi_head_attention_20 (Multi (None, 5, 512) 131776 ['add_normalization_32[0][0]', HeadAttention) 'input_2[0][0]', 'input_2[0][0]'] dropout_35 (Dropout) (None, 5, 512) 0 ['multi_head_attention_20[0][0]'] feed_forward_13 (FeedForward) (None, 5, 512) 2099712 ['add_normalization_32[1][0]'] dropout_36 (Dropout) (None, 5, 512) 0 ['feed_forward_13[0][0]'] add_normalization_34 (AddNorma (None, 5, 512) 1024 ['add_normalization_32[1][0]', lization) 'dropout_36[0][0]'] ================================================================================================== Total params: 2,365,312 Trainable params: 2,365,312 不可训练参数: 0 __________________________________________________________________________________________________ |
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
书籍
- 使用 Python 进行高级深度学习, 2019
- 用于自然语言处理的 Transformer, 2021
论文
- 注意力就是你所需要的一切, 2017
总结
在本教程中,您将了解如何实现完整的 Transformer 模型以及如何创建填充掩码和前瞻掩码。
具体来说,你学到了:
- 如何为编码器和解码器创建填充掩码
- 如何为解码器创建前瞻掩码
- 如何将 Transformer 编码器和解码器组合成一个模型
- 如何打印出编码器和解码器层的摘要
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
感谢您提供出色的教程!
我在打印编码器和解码器结构信息时发现了一些错误。
原因似乎在于 MultiHeadAttention/reshape_tensor 函数。具体来说,重塑对于具有 None 维度的张量不起作用。
我通过如下更改函数来解决了这个问题。
_________________________________________________
def reshape_tensor(self, x, heads, flag)
if flag
# 重塑和转置后的张量形状:(batch_size, heads, seq_length, -1)
x = reshape(x, shape=(shape(x)[0], shape(x)[1], heads, int(x.shape[2]/heads)))
x = transpose(x, perm=(0, 2, 1, 3))
else
# 恢复重塑和转置操作:(batch_size, seq_length, d_model)
x = transpose(x, perm=(0, 2, 1, 3))
x = reshape(x, shape=(shape(x)[0], shape(x)[1], int(x.shape[2]*x.shape[3])))
return x
_________________________________________________
这似乎有点奇怪。我想知道是否有更好的解决方案。再次感谢!
感谢 Helen 的反馈!您遇到了哪些具体的错误消息,以便我们记录需要审查的代码列表。
感谢您的回复!错误如下。
in test_transformer
encoder.build_graph().summary()
in build_graph
return Model(inputs=[input_layer], outputs=self.call(input_layer, None, True))
in call
multihead_output = self.multihead_attention(x, x, x, padding_mask)
in call *
return self.W_o(output)
ValueError: The last dimension of the inputs to a Dense layer should be defined. Found None. Full input shape received: (None, 5, None)
Call arguments received by layer “multi_head_attention_18” (type MultiHeadAttention)
\u2022 queries=tf.Tensor(shape=(None, 5, 512), dtype=float32)
\u2022 keys=tf.Tensor(shape=(None, 5, 512), dtype=float32)
\u2022 values=tf.Tensor(shape=(None, 5, 512), dtype=float32)
\u2022 mask=None
最后添加图输出的部分非常令人困惑。
它涉及到许多地方的更改。
最后,我仍然遇到一些错误。
49
50 def build_graph(self)
—> 51 input_layer = Input(shape=(self.sequence_length, self.d_model))
52 return Model(inputs=[input_layer], outputs=self.call(input_layer, None, True))
53 def call(self, x, padding_mask, training)
重现错误的 Colab
NameError: name ‘Input’ is not defined
我遇到了同样的错误。
Input 层未在导入部分导入。在第一行的末尾添加 Input
from tensorflow.keras.layers import LayerNormalization, Layer, Dense, ReLU, Dropout, Input
在构建解码器摘要时,我遇到了与编码器摘要相同的错误。
TypeError Traceback (most recent call last)
in
2 # encoder.build_graph().summary()
3
—-> 4 decoder = DecoderLayer(dec_seq_length, h, d_k, d_v, d_model, d_ff, dropout_rate)
5 decoder.build_graph().summary()
TypeError: __init__() takes 7 positional arguments but 8 were given
你好 Michel…我建议在 Google Colab 中尝试此代码。
检查 DecoderLayer 类中的 __init__ 函数。
本书在此章节之前的版本没有 dec_seq_length(只有 7 个输入参数),但如果您仔细查看本章节中的构造函数,会发现许多文本中未提及的更改。
class EncoderLayer(Layer)
# ——— sequence_length 已添加
def __init__(self, sequence_length, h, d_k, d_v, d_model, d_ff, rate, **kwargs)
super().__init__(**kwargs)
self.build(input_shape=[None, sequence_length, d_model])
# ——– 以下两行已添加
self.d_model = d_model
self.sequence_length = sequence_length
self.multihead_attention = MultiHeadAttention(h, d_k, d_v, d_model)
self.dropout1 = Dropout(rate)
self.add_norm1 = AddNormalization()
self.feed_forward = FeedForward(d_ff, d_model)
self.dropout2 = Dropout(rate)
如果您进行了所有这些更改,那么 build_graph 将可以工作,但其他所有内容很可能都会损坏。
ValueError 回溯 (最近一次调用)
in
2
3 encoder = EncoderLayer(enc_seq_length, h, d_k, d_v, d_model, d_ff, dropout_rate)
—-> 4 encoder.build_graph().summary()
5
6 decoder = DecoderLayer(dec_seq_length, h, d_k, d_v, d_model, d_ff, dropout_rate)
3 frames
/tmp/__autograph_generated_file2sd23ix_.py in tf__call(self, queries, keys, values, mask)
15 try
16 do_return = True
—> 17 retval_ = ag__.converted_call(ag__.ld(self).W_o, (ag__.ld(output),), None, fscope)
18 except
19 do_return = False
ValueError: Exception encountered when calling layer “multi_head_attention_155” (type MultiHeadAttention).
in user code
File “”, line 48, in call *
return self.W_o(output)
File “/usr/local/lib/python3.7/dist-packages/keras/utils/traceback_utils.py”, line 67, in error_handler **
raise e.with_traceback(filtered_tb) from None
File “/usr/local/lib/python3.7/dist-packages/keras/layers/core/dense.py”, line 141, in build
raise ValueError(‘The last dimension of the inputs to a Dense layer ‘
ValueError: The last dimension of the inputs to a Dense layer should be defined. Found None. Full input shape received: (None, 5, None)
Call arguments received by layer “multi_head_attention_155” (type MultiHeadAttention)
• queries=tf.Tensor(shape=(None, 5, 512), dtype=float32)
• keys=tf.Tensor(shape=(None, 5, 512), dtype=float32)
• values=tf.Tensor(shape=(None, 5, 512), dtype=float32)
• mask=None
你好 Michel…你是在输入代码还是复制粘贴代码?您可能希望在 Google Colab 中尝试一下。
此错误是由于 EncoderLayer 类中的构造函数已更改,但 Encoder 类仍然依赖于之前的实现。为了解决这个问题,Encoder 类必须考虑新的构造函数参数 seq_length。
class Transformer(tf.keras.Model)
def __init__(self, num_layers, d_model, num_heads, dff, input_vocab_size, target_vocab_size, pe_input, pe_target, rate=0.1)
super(Transformer, self).__init__()
self.encoder = Encoder(num_layers, d_model, num_heads, dff, input_vocab_size, pe_input, rate)
self.decoder = Decoder(num_layers, d_model, num_heads, dff, target_vocab_size, pe_target, rate)
self.final_layer = tf.keras.layers.Dense(target_vocab_size)
def call(self, inp, tar, training, enc_padding_mask, look_ahead_mask, dec_padding_mask)
enc_output = self.encoder(inp, training, enc_padding_mask)
dec_output, attention_weights = self.decoder(tar, enc_output, training, look_ahead_mask, dec_padding_mask)
final_output = self.final_layer(dec_output)
return final_output, attention_weights
注意:Transformer/Transformer 训练的其余代码与这些博客类似
.
.
.
.
.
.
这是我的 Transformer 类… 在训练这个子类模型时,每 3 个 epoch 后,我尝试使用以下代码行保存子类模型:`trainer.save(‘net’, save_format=’tf’)`
但是我收到了这个错误:
TypeError: tf__call() missing 3 required positional arguments: ‘enc_padding_mask’, ‘look_ahead_mask’, and ‘dec_padding_mask’
你能给我一些解决方案吗?
你好 Shivam…以下讨论可能很有帮助
https://stackoverflow.com/questions/61631360/self-defined-tensorflow-decoder-typeerror-call-missing-1-required-positio
你好,
你的 call 函数声明是
def call(self, inp, tar, training, enc_padding_mask, look_ahead_mask, dec_padding_mask)
这有三个新的掩码参数。
如果其余代码与书本相同,那么 Transformer.call() 没有这些新参数。
你好,
感谢您提供出色的教程!如何使用 Hugging Face 的 transformers 包来实现这一点?
你好 Vik…以下资源是一个很好的起点
https://machinelearning.org.cn/a-brief-introduction-to-bert/
各位专家好,
在阅读本章并进行大量实验时,我注意到一种我认为不正确的行为。想听听您的意见。
注意:在以下解释中
*) 松散遵循 int 与 float 类型
*) 松散使用“token”与“word”等术语,在本例中它们是相同的。
对此表示抱歉。
— Padding mask —
让我们假设
input_seq_len = 9 # encoded sentence 中的单词数
input_seq = [[2,3,4,5,0,0,0,0,0]]
在类 TransformerModel.padding_mask 中
mask = padding_mask(input_seq)
# 张量 [[[[0,0,0,0,1,1,1,1,1]]]], 形状 (1,1,1,9)
# 第一个“1”是 batch
# 第二个和第三个“1”是 tf.newaxis
# 最后一个维度是实际的掩码
此掩码的目的是禁止“0”token 关注实际单词(2,3,4,5)。
让我们看看 DotProductAttention.call 行中发生了什么
scores += -1e9 * mask
# scores.shape (1, heads, 9, 9)
# “1”是 batch
# 9×9 是注意力分数矩阵,它显示了每个 token 必须如何关注另一个 token。
当 TensorFlow 将 (9, 9) 分数乘以 (1, 9) 掩码矩阵时,会应用广播规则。这会产生如下形式的分数(\* 表示 -1e9)
[x11, x12, x13, x14, *,*,*,*,*]
[x21, x22, x23, x24, *,*,*,*,*]
[x31, x32, x33, x34, *,*,*,*,*]
[x41, x42, x43, x44, *,*,*,*,*]
[x51, x52, x53, x54, *,*,*,*,*]
[x61, x62, x63, x64, *,*,*,*,*]
[x71, x72, x73, x74, *,*,*,*,*]
[x81, x82, x83, x84, *,*,*,*,*]
[x91, x92, x93, x94, *,*,*,*,*]
观察 1)
上述矩阵允许第 5、6、7、8 和 9 个 token 关注第 1、2、3 和 4 个 token。我认为上述矩阵应该是
[x11, x12, x13, x14, *,*,*,*,*]
[x21, x22, x23, x24, *,*,*,*,*]
[x31, x32, x33, x34, *,*,*,*,*]
[x41, x42, x43, x44, *,*,*,*,*]
[ *, *, *, *, *,*,*,*,*]
[ *, *, *, *, *,*,*,*,*]
[ *, *, *, *, *,*,*,*,*]
[ *, *, *, *, *,*,*,*,*]
[ *, *, *, *, *,*,*,*,*]
解决方案 1)
如果观察 1 是正确的
def PaddingMask(self, input)
mask = tf.equal(input, 0)
mask = tf.cast(mask, tf.float32)
mask = mask[:, tf.newaxis, tf.newaxis, :]
new_mask = tf.maximum(mask, tf.transpose(mask, perm=(0,1,3,2)))
return new_mask
— Lookahead mask combined with padding mask —
完全相同的设置。
在 TransformerModel.call() 中
dec_lookahead_mask = tf.maximum(dec_lookahead_mask_pre, dec_padding_mask)
将看起来像
[1,0,0,0,0,0,0,0,0]
[1,1,0,0,0,0,0,0,0]
[1,1,1,0,0,0,0,0,0]
[1,1,1,1,0,0,0,0,0]
[1,1,1,1,0,0,0,0,0]
[1,1,1,1,0,0,0,0,0]
[1,1,1,1,0,0,0,0,0]
[1,1,1,1,0,0,0,0,0]
[1,1,1,1,0,0,0,0,0]
但应该是
[1,0,0,0,0,0,0,0,0]
[1,1,0,0,0,0,0,0,0]
[1,1,1,0,0,0,0,0,0]
[1,1,1,1,0,0,0,0,0]
[0,0,0,0,0,0,0,0,0]
[0,0,0,0,0,0,0,0,0]
[0,0,0,0,0,0,0,0,0]
[0,0,0,0,0,0,0,0,0]
[0,0,0,0,0,0,0,0,0]
观察 2)
上述掩码允许零 token 关注实际单词。
解决方案 2)
如果观察 2 有效,解决方案 (1) 也能修复观察 (2)。
我完全同意您的观察。零 token 不应该被允许关注实际 token,因此分数矩阵应该是块状非零的。然而,您的解决方案会产生类似以下的内容:
array([[[[0., 0., 1., 1.],
[0., 0., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]]]], dtype=float32)>
例如,如果我们有一个序列 [2,4,0,0]。这个数组在下面这行不起作用:
scores = scores + mask * -1e9
在 dot product attention 中实现掩码。原因是显而易见的,因为 softmax 是逐行操作的:零 token 的注意力分数当然会因 -1e9 而改变,但是它们都均匀地改变,并且 softmax 最终将它们标准化为相等的值。
在我的消息中,我在 1 和 0 的使用上出了差错,但代码仍然是正确的。
在 padding/lookahead 掩码中,1 表示“应被掩盖”。
当 1 乘以 -1e9 时,它会变成一个非常小的数字。所以掩码应该在需要被掩盖的位置有 1。
scores += -1e9 * mask
# 这里是形状
# (1, 8, 4, 4) + -1e9 * (1, 1, 4, 4) – 由于广播,工作正常。
你好,Jason Brownlee博士
我在运行整个Transformer模型时遇到一个问题
在按batch_size处理时,
执行“encoder_input = trainX_batch[:, 1:]”后,encoder_input的形状是[batch_size, max_len_eng – 1]
执行“decoder_input = trainY_batch[:, :-1]”后,decoder_input的形状是[batch_size, max_len_ger – 1]
所以当max_len_eng不等于max_len_ger时,
使用“tensorflow.maximum(dec_in_padding_mask, dec_in_lookahead_mask)”组合两种掩码的执行会引发如下错误:
ValueError: Dimensions must be equal, but are 64 and 23 for ‘{{node transformer_model_complete/Maximum}} = Maximum[T=DT_FLOAT](transformer_model_complete/Cast_1, transformer_model_complete/sub)’ with input shapes: [64,23], [23,23]。
你能帮我弄清楚吗?
你好David…以下讨论可能对您有帮助
https://stackoverflow.com/questions/56302243/keras-valueerror-dimensions-must-be-equal-issue
抱歉,James,我不认为这与您发布的URL有关。
在掩码编码器输入时,我遵循了这个教程:https://machinelearning.org.cn/joining-the-transformer-encoder-and-decoder-and-masking/
代码的关键部分是:
# 创建并组合填充和前瞻掩码以馈入解码器
dec_in_padding_mask = self.padding_mask(decoder_input)
dec_in_lookahead_mask = self.lookahead_mask(decoder_input.shape[1])
dec_in_lookahead_mask = maximum(dec_in_padding_mask, dec_in_lookahead_mask)
在以上三行中,decoder_input的形状是[batch_size, max_len_source_lang],例如[64, 25]
执行“dec_in_padding_mask = self.padding_mask(decoder_input)”后,
dec_in_padding_mask的形状是[64, 25]
decoder_input的形状是[batch_size, max_len_target_lang – 1],例如[64, 23]
执行“dec_in_lookahead_mask = self.lookahead_mask(decoder_input.shape[1])”后,
dec_in_lookahead_mask是一个形状为[23, 23]的方形矩阵。
那么,为什么这两个形状不同的张量可以被发送到maximum函数,这就是我在开始训练模型时遇到的错误。
抱歉,我之前打错了张量的形状,所以重写此回复。
我不认为这与您发布的URL有关,或者我在那个StackOverflow问题中没有找到完全正确的评论。
我的问题是
在掩码编码器输入时,我遵循了这个教程:https://machinelearning.org.cn/joining-the-transformer-encoder-and-decoder-and-masking/
代码的关键部分是:
# 创建并组合填充和前瞻掩码以馈入解码器
dec_in_padding_mask = self.padding_mask(decoder_input)
dec_in_lookahead_mask = self.lookahead_mask(decoder_input.shape[1])
dec_in_lookahead_mask = maximum(dec_in_padding_mask, dec_in_lookahead_mask)
在以上三行中,decoder_input的形状是[batch_size, max_len_target_lang – 1],例如[64, 23]。
执行“dec_in_padding_mask = self.padding_mask(decoder_input)”后,
dec_in_padding_mask的形状是[64, 23]。
decoder_input的形状是[batch_size, max_len_target_lang – 1],例如[64, 23]
执行“dec_in_lookahead_mask = self.lookahead_mask(decoder_input.shape[1])”后,
dec_in_lookahead_mask是一个形状为[23, 23]的方形矩阵。
那么,为什么这两个形状不同的张量([64, 23] vs [23, 23])可以被发送到maximum函数,这就是我在开始训练模型时遇到的错误。
你好David,您提到的张量形状应该是这样的:
– decoder_input: [batch_size, dec_seq_length – 1]
– dec_in_padding_mask: [batch_size, 1, 1, dec_seq_length – 1]
– dec_in_lookahead_mask: [batch_size, 1, dec_seq_length – 1, dec_seq_length – 1]
例如,我使用dec_seq_length = 12,batch_size = 64,因此我有以下张量形状:
– decoder_input: [64, 11]
– dec_in_padding_mask: [64, 1, 1, 11]
– dec_in_lookahead_mask: [64, 1, 11, 11]
您能否深入代码检查一下为什么您的张量形状与预期不符?
感谢Cristina的帮助。我发现我直接使用了本教程前面部分的演示“padding_mask”函数,该函数只返回“mask”变量,实际上,最终格式中一个同名的函数返回“[:, newaxis, newaxis, :]”,这就是我无法最大化这两个填充掩码张量的原因。
谢谢你的澄清,David。
我想为卡在最后阶段的人提供一个快速的解决方法:打印模型摘要。
您可以使用我们之前章节中使用的虚拟变量。
batch_size = 64
my_encoder_input = np.random.random((batch_size, enc_seq_length))
my_decoder_input = np.random.random((batch_size, dec_seq_length))
output = training_model(my_encoder_input, my_decoder_input, True)
–> output的形状应该是(batch_size, dec_seq_length, dec_vocab_size)
奖励:现在training_model已经被实例化,您可以运行
training_model.summary()
并且可以看到我们的模型有超过2700万个参数!
感谢Florian乐于与社区分享!
打扰一下,但是我的数据集是3个元素的列表,例如:[[ ],[ ],[ ]],[[ ],[ ],[ ]],……
dataset = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[[10, 11, 12], [13, 14, 15], [16, 17, 18]],……
这是一个有5个类别(1,2,3,4,5)的分类问题。
我应该如何为此序列分类问题使用Transformer模型?我想使用注意力机制。
感谢您的帮助
你好Mohammad…以下资源可能对您有帮助:
https://machinelearning.org.cn/transformer-models-with-attention/