1. 前言
在上一篇文章中我们已经描述了 Transformer 的整个模型搭建过程,并逐层逐行地解释了其正向传播的原理和细节。接下来,我们将着手定义优化训练的方式,处理语料,并最终使用搭建好的 Transformer 实现一个由葡萄牙语翻译至英语的翻译器。
为了训练一个由葡萄牙语翻译至英语的翻译器,首先来观察如何处理数据从而能够正确地输入我们已经设计好的 Tranformer 模型:
class Transformer(tf.keras.Model):
...
def call(self, inp, tar, training, enc_padding_mask,
look_ahead_mask, dec_padding_mask):
...
只摘取模型的调用 call
部分,可以看出 Transformer 需要的输入:
inp
:输入序列,这里需要的是源语言(葡萄牙语)的编码表示。(嵌入表示将在编码器中完成)tar
:目标序列,这里需要的是目标语言(英语)的编码表示。(嵌入表示将在编码器中完成)training
:布尔量,规定模型是否可以训练。enc_padding_mask
:编码器,填充遮挡。look_ahead_mask
:前瞻遮挡。两个遮挡将在后面详细描述。dec_padding_mask
:解码器,填充遮挡。
由此,我们知道,为了达成目的,我们需要完成以下几个步骤:
- 创造原训练集(输入句子和目标句子)的嵌入表示
- 为我们的 Transformer 设计优化器和损失函数
- 根据情况创造填充遮挡
- 为了实现自回归创建前瞻遮挡
- 将数据输入进行训练
- 最终对训练好的模型进行评估
2. 创造原训练集的编码表示
2.1. 数据下载与读取
参考 Tensorflow 的官方教程,我们同样使用 TFDS 来进行数据的下载和载入。(应首先在本机环境或虚拟环境中安装 tensorflow_datasets
模块。
import tensorflow_datasets as tfds
examples, metadata = tfds.load('ted_hrlr_translate/pt_to_en', with_info=True,
as_supervised=True)
train_examples, val_examples = examples['train'], examples['validation']
第一行代码会访问用户目录下(Windows和Unix系系统各有不同,请参考官方文档)是否已经下载好了葡萄牙翻译至英文翻译器所需的数据集,如果不存在,则会自动下载。第二行,则将其自动转换为训练集合和测试集合两个 tf.data.Dataset
实例。
2.2. 创建子词分词器
tfds
独立于 Tensorflow,是专门用来管理和下载一些成熟的数据集的Python库。但其中有很多我认为通用性很强的函数。比如子词分词器:
tokenizer_en = tfds.features.text.SubwordTextEncoder.build_from_corpus(
(en.numpy() for pt, en in train_examples), target_vocab_size=2**13)
tokenizer_pt = tfds.features.text.SubwordTextEncoder.build_from_corpus(
(pt.numpy() for pt, en in train_examples), target_vocab_size=2**13)
上方代码分别创建了两个子词分词器,分别读取了训练集合中的全部英文和葡萄牙文,并基于这些大段的文字形成了子词分词器。
子词分词器的作用是将输入句子中的每一个单词编码为一个独一无二的数字,如果出现了子词分词器不能识别的新单词,那么就将其打散成多个可以识别的子词来编码成数字。
同样的,分词器也可以将用数字表示的句子重新转换回原有的句子。
sample_string = 'Transformer is awesome.'
tokenized_string = tokenizer_en.encode(sample_string)
print ('Tokenized string is {}'.format(tokenized_string))
# Tokenized string is [7915, 1248, 7946, 7194, 13, 2799, 7877]
original_string = tokenizer_en.decode(tokenized_string)
print ('The original string: {}'.format(original_string))
# The original string: Transformer is awesome.
assert original_string == sample_string
# 分词器转换回的句子和原始句子一定是相同的。
2.3. 数据处理
为了方便后期使用,编写一个将编码后句子加上开始标记和结束标记。利用 tf.data.Dataset
的 map
功能来批量完成这一任务。首先需