TensorFlow 实战(五)(5)

简介: TensorFlow 实战(五)

TensorFlow 实战(五)(4)https://developer.aliyun.com/article/1522833

让我们看看如何在 TensorFlow 中实现这一点。首先,我们将加载刚保存的模型:

model = tf.keras.models.load_model(save_path)

很容易获得编码器模型,因为我们将编码器封装为最终模型中的嵌套模型。可以通过调用 get_layer()函数来取出它。

en_model = model.get_layer("encoder")

之后,我们定义两个输入来表示解码器的两个输入:

d_inp = tf.keras.Input(shape=(1,), dtype=tf.string, name='d_infer_input')
d_state_inp = tf.keras.Input(shape=(256,), name='d_infer_state')

正如我们之前讨论的那样,我们定义了两个输入:一个表示解码器的输入(d_inp),另一个表示解码器 GRU 层的状态输入(d_state_inp)。分析形状,d_inp 接受一堆字符串的数组,就像之前一样。d_state_inp 表示 GRU 模型的状态向量,具有 256 个特征维度。

在定义输入后,我们将重现我们在训练模型中构建解码器时遵循的所有步骤。但是,我们将从训练模型中获取层,而不是创建新的随机初始化层。特别是,我们将有以下层来流输输入:

  • 解码器的向量化层(产生 d_vectorized_out)
  • 解码器的嵌入层(产生 d_emb_out)
  • 解码器的 GRU 层(产生 d_gru_out)
  • 解码器的完全连接的隐藏层(产生 d_dense1_out)
  • 最后的预测层(产生 d_final_out)

我们引入了一个重要的改变来介绍 GRU 模型。请注意,我们将 return_sequences 设置为 False,以确保解码器 GRU 仅返回最后一个输出(即不返回输出序列)。换句话说,GRU 层的输出是一个[None,256]大小的张量。这有助于我们将输出形状匹配到先前定义的 d_state_inp,使递归模型更容易构建。此外,GRU 层以 d_state_inp 作为模型中的 initial_state。这样,我们就可以将输出状态向量作为递归输入馈送到解码器:

# Generate the vectorized output of inp
d_vectorizer = model.get_layer('d_vectorizer')    
d_vectorized_out = d_vectorizer(d_inp)
# Generate the embeddings from the vectorized input
d_emb_out = model.get_layer('d_embedding')(d_vectorized_out)
# Get the GRU layer
d_gru_layer = model.get_layer("d_gru")
# Since we generate one word at a time, we will not need the return_sequences
d_gru_layer.return_sequences = False
# Get the GRU out while using d_state_inp from earlier, as the initial state
d_gru_out = d_gru_layer(d_emb_out, initial_state=d_state_inp) 
# Get the dense output
d_dense1_out = model.get_layer("d_dense_1")(d_gru_out) 
# Get the final output
d_final_out = model.get_layer("d_dense_final")(d_dense1_out) 

是时候定义最终的解码器模型了:

de_model = tf.keras.models.Model(
    inputs=[d_inp, d_state_inp], outputs=[d_final_out, d_gru_out]
)

模型接受 d_inp 和 d_state_inp 作为输入,并产生 d_final_out(即最终预测)和 d_gru_out(即 GRU 输出状态)作为输出。最后,让我们退后一步,并将我们所做的工作封装在一个名为 get_inference_model()的单个函数中,如下面的示例所示。

列出 11.11 号栏目中的机器翻译模型递归推理模型定义

import tensorflow.keras.backend as K
K.clear_session()
def get_inference_model(save_path):
    """ Load the saved model and create an inference model from that """
    model = tf.keras.models.load_model(save_path)                        ❶
    en_model = model.get_layer("encoder")                                ❷
    d_inp = tf.keras.Input(
        shape=(1,), dtype=tf.string, name='d_infer_input'
    )                                                                    ❸
    d_state_inp = tf.keras.Input(shape=(256,), name='d_infer_state')     ❹
    d_vectorizer = model.get_layer('d_vectorizer')                       ❺
    d_vectorized_out = d_vectorizer(d_inp)                               ❺
    d_emb_out = model.get_layer('d_embedding')(d_vectorized_out)         ❻
    d_gru_layer = model.get_layer("d_gru")                               ❼
    d_gru_layer.return_sequences = False                                 ❽
    d_gru_out = d_gru_layer(d_emb_out, initial_state=d_state_inp)        ❾
    d_dense1_out = model.get_layer("d_dense_1")(d_gru_out)               ❿
    d_final_out = model.get_layer("d_dense_final")(d_dense1_out)         ⓫
    de_model = tf.keras.models.Model(
        inputs=[d_inp, d_state_inp], outputs=[d_final_out, d_gru_out]    ⓬
    )
    return en_model, de_model

❶ 加载已保存的训练模型。

❷ 通过调用 get_layer()函数从加载的模型中获取编码器模型。

❸ 定义新推理解码器的第一个输入,一个输入层,以一批字符串为输入。

❹ 定义新推理解码器的第二个输入,一个输入层,以初始状态作为解码器 GRU 的输入状态传递。

❺ 生成解码器的字符串输入的向量化输出。

❻ 从矢量化输入生成嵌入。

❼ 获得解码器的 GRU 层。

❽ 由于我们一次生成一个单词,我们将不需要 return_sequences

❾ 在使用先前的 d_state_inp 作为初始状态时获得 GRU 输出。

❿ 获得密集输出。

⓫ 获得最终输出。

⓬ 定义最终的解码器。

然后,我们将定义一个函数来加载我们刚刚保存的词汇表。我们使用 JSON 格式保存了词汇表,加载词汇表所需的所有操作就是调用打开的词汇文件的 json.load()

def get_vocabularies(save_dir):
    """ Load the vocabulary files from a given path"""
    with open(os.path.join(save_dir, 'en_vocab.json'), 'r') as f:
        en_vocabulary = json.load(f)
    with open(os.path.join(save_dir, 'de_vocab.json'), 'r') as f:
        de_vocabulary = json.load(f)
    return en_vocabulary, de_vocabulary
print("Loading vocabularies")
en_vocabulary, de_vocabulary = get_vocabularies(
    os.path.join('models', 'seq2seq_vocab')
)
print("Loading weights and generating the inference model")
en_model, de_model = get_inference_model(os.path.join('models', 'seq2seq'))
print("\tDone")

接下来,我们已经拥有了生成新翻译所需的一切。如前所述,我们将创建一个输入英文句子(用 sample_en_text 表示)到编码器生成上下文向量的过程/函数。接下来,使用 SOS 标记和上下文向量进行预测,以获得第一个德语标记预测和下一个状态输出。最后,递归地将解码器的输出作为解码器的输入,直到预测的标记为 EOS。以下清单使用我们刚刚构建的推断模型描述了这一功能。

清单 11.12 使用新推断模型生成翻译

def generate_new_translation(en_model, de_model, de_vocabulary, sample_en_text):
    """ Generate a new translation """
    start_token = 'sos'    
    print("Input: {}".format(sample_en_text))                              ❶
    d_state = en_model.predict(np.array([sample_en_text]))                 ❷
    de_word = start_token                                                  ❸
    de_translation = []                                                    ❹
    while de_word != end_token:                                            ❺
        de_pred, d_state = de_model.predict([np.array([de_word]), d_state])❻
        de_word = de_vocabulary[np.argmax(de_pred[0])]                     ❼
        de_translation.append(de_word)                                     ❽
    print("Translation: {}\n".format(' '.join(de_translation)))

❶ 打印输入。

❷ 获取解码器的初始状态。

❸ 解码器的第一个输入词将始终是起始标记(即其值为 sos)。

❹ 我们在这个列表中收集翻译。

❺ 一直预测,直到我们获得结束标记(即它的值为 eos)。

❻ 使用新状态覆盖先前的状态输入。

❼ 从预测的标记 ID 中获取实际的单词。

❽ 将其添加到翻译中。

让我们在数据集中对几个测试输入运行这个,看看我们得到了什么:

for i in range(5):
    sample_en_text = test_df["EN"].iloc[i]
    generate_new_translation(en_model, de_model, de_vocabulary, sample_en_text)

这将输出

Input: The pleasure's all mine.
Translation: die [UNK] [UNK] mir eos
Input: Tom was asking for it.
Translation: tom sprach es zu tun eos
Input: He denied having been involved in the affair.
Translation: er [UNK] sich auf das [UNK] [UNK] eos
Input: Is there something in particular that you want to drink?
Translation: gibt es etwas [UNK] wenn du etwas [UNK] eos
Input: Don't run. Walk slowly.
Translation: [UNK] nicht zu fuß eos

你可以将这些英文短语/句子与谷歌翻译进行比较,看看我们的模型是如何接近它们的。对于在相对简单和小的数据集上训练的模型,我们的模型表现非常好。翻译中存在 [UNK] 标记,因为在语料库中所有不太频繁的词都被替换为 [UNK]。因此,当模型不确定应该在某个位置填充哪个单词时,它可能会输出 [UNK]。

练习 4

您决定使用 LSTM 模型而不是 GRU 模型。正如您所知,LSTM 模型有两个状态:细胞状态和输出状态。您已经构建了编码器,现在正在构建解码器。您计划调整以下代码以使用 LSTM 模型:

d_inp = tf.keras.Input(shape=(1,), dtype=tf.string)       
d_state_inp = tf.keras.Input(shape=(256,))                
d_vectorized_out = de_vectorizer(d_inp)
d_emb_out = tf.keras.layers.Embedding(de_vocab+2, 128, mask_zero=True)(d_vectorized_out)
d_gru_out = tf.keras.layers.GRU(256)(d_emb_out, initial_state=d_state_inp)
d_final_out = tf.keras.layers.Dense(
    de_vocab+2, activation='softmax'
)(d_gru_out)                    
de_model = tf.keras.models.Model(
    inputs=[d_inp, d_state_inp], outputs=[d_final_out, d_gru_out]
)

如果你在 LSTM 层中设置 return_state=True 并在某些兼容输入 x 上调用它,则输出如下

lstm_out, state_h, state_c = tf.keras.layers.LSTM(256, return_state=True)(x)

state_hstate_c 分别代表输出状态和细胞状态。

我们已经使用序列到序列架构训练了一个机器翻译模型。在下一章中,我们将探讨如何使用一种称为注意力的技术进一步改进这个模型。

摘要

  • 编码器-解码器模式在序列到序列任务中很常见,比如机器翻译。
  • 编码器接收源语言输入并生成上下文向量。
  • 上下文向量由解码器用于生成目标语言输出(即翻译)。
  • tf.keras.layers.experimental.preprocessing.TextVectorization 层允许您将标记化(即将字符串转换为标记列表,然后转换为标记 ID)集成到模型中。这使得模型可以接受字符串而不是数值。
  • 在序列到序列任务上训练模型时,可以使用教师强制训练:
  • 在教师强制训练中,编码器像往常一样接收源语言输入并生成上下文向量。然后,解码器消耗并预测翻译中的单词。换句话说,解码器在训练时以这样的方式进行,以便在给定翻译中的前一个单词(或多个单词)的情况下预测下一个单词。
  • 机器翻译模型产生的翻译质量使用 BLEU 分数进行衡量:
  • BLEU 分数使用修改后的精确度指标以及测量翻译的不同 n-gram 上的精确度来计算得分。
  • 使用教师强制训练模型后,需要定义一个单独的推理模型,使用训练后的权重:
  • 此推理模型具有相同的编码器,但解码器将以先前预测的单词作为下一步的输入,并递归预测单词,直到满足预定义的结束条件为止。

练习答案

练习 1

def vocab_size(ser):
    cnt = Counter(ser.sum())
    return len(cnt)

练习 2

# The decoder
en_repeat_out = tf.keras.layers.RepeatVector(de_seq_length)(en_gru_out)
d_gru_layer = tf.keras.layers.GRU(256, return_sequences=True, name='d_gru')
d_gru_out = d_gru_layer(en_repeat_out, initial_state=gru_out)
d_dense_layer_1 = tf.keras.layers.Dense(512, activation='relu', name='d_dense_1')
d_dense1_out = d_dense_layer_1(d_gru_out)
d_dense_layer_final = tf.keras.layers.Dense(
    de_vocab+2, activation='softmax', name='d_dense_final'
)
d_final_out = d_dense_layer_final(d_dense1_out)
# Define the full model
model = tf.keras.models.Model(
    inputs=inp, outputs=d_final_out, name='final_seq2seq'
)

练习 3

prev_bleu = None
for epoch in range(epochs):
    bleu_log = []  
    n_train_batches = en_inputs_raw.shape[0]//batch_size
    for i in range(n_train_batches):
        print("Training batch {}/{}".format(i+1, n_train_batches), end='\r')
        x = [         
            en_inputs_raw[i*batch_size:(i+1)*batch_size],  
            de_inputs_raw[i*batch_size:(i+1)*batch_size]
        ]
        y = vectorizer(de_labels_raw[i*batch_size:(i+1)*batch_size])
        model.train_on_batch(x, y)
        pred_y = model.predict(x)
        bleu_log.append(bleu_metric.calculate_bleu_from_predictions(y, pred_y)) 
    mean_bleu = np.mean(bleu_log)
    # The termination criteria
    if prev_bleu and prev_bleu > mean_bleu:
        break
    prev_bleu = mean_bleu

练习 4

d_inp = tf.keras.Input(shape=(1,), dtype=tf.string)       
d_state_h_inp = tf.keras.Input(shape=(256,))                
d_state_c_inp = tf.keras.Input(shape=(256,))                
d_vectorized_out = de_vectorizer(d_inp)                                         
d_emb_out = tf.keras.layers.Embedding(
    de_vocab+2, 128, mask_zero=True
)(d_vectorized_out)                    
d_lstm_out, d_state_h, d_state_c = tf.keras.layers.LSTM(
 256, return_state=True
)(d_emb_out, initial_state=[d_state_h_inp, d_state_c_inp])
d_final_out = tf.keras.layers.Dense(
 de_vocab+2, activation='softmax'
)(d_lstm_out) 
de_model = tf.keras.models.Model(
    inputs=[d_inp, d_state_h_inp, d_state_c_inp], 
    outputs=[d_final_out, d_state_h, d_state_c]              
)
de_model.summary()


相关文章
|
26天前
|
机器学习/深度学习 自然语言处理 TensorFlow
TensorFlow 实战(六)(2)
TensorFlow 实战(六)
24 0
|
13天前
|
机器学习/深度学习 TensorFlow API
TensorFlow与Keras实战:构建深度学习模型
本文探讨了TensorFlow和其高级API Keras在深度学习中的应用。TensorFlow是Google开发的高性能开源框架,支持分布式计算,而Keras以其用户友好和模块化设计简化了神经网络构建。通过一个手写数字识别的实战案例,展示了如何使用Keras加载MNIST数据集、构建CNN模型、训练及评估模型,并进行预测。案例详述了数据预处理、模型构建、训练过程和预测新图像的步骤,为读者提供TensorFlow和Keras的基础实践指导。
144 59
|
26天前
|
自然语言处理 算法 TensorFlow
TensorFlow 实战(六)(3)
TensorFlow 实战(六)
21 0
|
26天前
|
机器学习/深度学习 数据可视化 TensorFlow
TensorFlow 实战(六)(1)
TensorFlow 实战(六)
25 0
|
26天前
|
存储 自然语言处理 TensorFlow
TensorFlow 实战(五)(4)
TensorFlow 实战(五)
21 0
|
26天前
|
数据可视化 TensorFlow 算法框架/工具
TensorFlow 实战(八)(4)
TensorFlow 实战(八)
25 1
|
26天前
|
TensorFlow API 算法框架/工具
TensorFlow 实战(八)(3)
TensorFlow 实战(八)
25 1
|
26天前
|
机器学习/深度学习 自然语言处理 TensorFlow
TensorFlow 实战(八)(5)
TensorFlow 实战(八)
25 0
|
26天前
|
并行计算 TensorFlow 算法框架/工具
TensorFlow 实战(八)(2)
TensorFlow 实战(八)
21 0
|
26天前
|
并行计算 Ubuntu TensorFlow
TensorFlow 实战(八)(1)
TensorFlow 实战(八)
22 0