机器翻译是一项具有挑战性的任务,包含一些使用高度复杂的语言知识开发的大型统计模型。 神经机器翻译的工作原理是——利用深层神经网络来解决机器翻译问题。
在本教程中,你将了解如何开发一个神经机器翻译系统,可以用于将德语翻译成英语。
学习完本教程后,你将知道:
- 如何清理和准备数据来训练神经机器翻译系统
- 如何开发机器翻译的编码器 - 解码器模型
- 如何使用训练有素的模型对新输入短语进行推理,并对模型技巧进行评价
让我们开始吧。
如何在 Keras 开发神经机器翻译系统 照片由 Björn Groß 提供
教程概述
教程分为 4 个部分:
- 德语翻译成英语的数据集
- 准备文本数据
- 训练神经翻译模型
- 评估神经翻译模型
Python 环境
本教程预设你安装了 Python 3 SciPy 环境。
你必须安装带有 TensorFlow 或 Theano 后端的 Keras(2.0 或更高版本)。
本教程还假定你已经安装了 NumPy 和 Matplotlib。
关于安装环境,如需要帮助,请看这篇文章:
- How to Setup a Python Environment for Machine Learning and Deep Learning with Anaconda(http://t.cn/R87zlIb )
德语翻译成英语的数据集
在本教程中,我们将使用德语译成英语的数据集作为语言学习的抽认卡的基础。
数据集可以从 ManyThings.org(http://www.manythings.org/ ) 网站获得,案例是从 Tatoeba Project(http://tatoeba.org/home ) 项目中提取出来的。该数据集由德语短语和英语单词组成,我们使用的是 Anki flashcard software(https://apps.ankiweb.net/ ) 软件。
该页面提供了许多语言对的列表,我鼓励你探索其他语言:
Tab-delimited Bilingual Sentence Pairs(http://www.manythings.org/anki/ )
本教程中使用的数据集可以在这里下载:
German – English deu-eng.zip(http://www.manythings.org/anki/deu-eng.zip )
将数据集下载到当前工作目录并解压;例如:
你会有一个叫 deu.txt 的文件。txt 中包含 152,820 对德语阶段的英语,每一行有一行,并有一个标签分隔语言。
例如,文件的前 5 行看起来如下:
我们使用德语词句序列作为输入,将其翻译成英语的的词句序列,这就是我们的预测问题。
这个开发的模型将适用于一些初学者级别的德语短语。
准备文本数据
下一步是准备用于建模的文本数据。
先查看原始数据,请注意,你所有看到的内容,很可能就是我们在数据清洗过程中所需要用到的。
例如,在我回顾原始数据时,请注意到以下几点:
- 有标点符号。
- 文本包含大写和小写。
- 在德语中有一些特殊的字符。
- 英语中有重复的短语,有不同的德语翻译。
- 这个文件是按句子长度排序的,在文件的末尾有很长的句子。
一个好的文本清理程序可以处理一些或全部的这些问题。
数据准备分为两部分:
- 清理文档
- 分离文档
1. 清理文档
首先,我们必须以保存 Unicode 德语字符的方式加载数据。下面的函数名为 load_doc(),它将把文件加载为一个文本 blob。
每行包含一对短语,先是英语,然后是德语,然后用制表符分隔。
我们必须逐行拆分已加载的文本。函数 to_pairs() 将分割加载的文本。
我们现在准备好清理每个句子。我们将执行的具体清理操作如下:
- 删除所有非输出字符。
- 删除所有标点字符。
- 将所有 Unicode 字符规范化为 ASCII(如拉丁字符)。
- 将案例规范化为小写。
- 删除所有不按字母顺序排列的令牌。
我们将在加载的数据集中对每一对语句执行这些操作。
clean_pairs() 函数执行这些操作。
最后,既然数据已经被清理,我们可以将短语对列表保存到准备使用的文件中。
函数 save_clean_data() 使用 pickle API 将清理文本列表保存到文件中。
将所有这些组合在一起,下面列出了完整的示例。
运行该示例将在当前工作目录中创建一个名为 “english-german.pkl” 的清理文本。
一些清理文本的例子被打印出来,以便我们在运行结束时进行评估,以确认清理的操作是按预期执行的。
2. 分离文本
清理的数据包含了超过 15 万个短语对,而其中的一些对到文件的结尾是很长的。
这是一个很好的例子来开发一个小的翻译模型。 模型的复杂性随着实例数量、短语长度和词汇量的增加而增加。
虽然我们有一个良好的数据集来进行建模,但是我们会稍微简化这个问题,以极大地减少模型所需的模型的大小,并将所需的训练时间转换成适合模型的时间。
你可以在更完整的数据集上开发一个模型作为扩展。我们将通过将数据集减少到文件中的前 10,000 个示例来简化问题;这些将是数据集中最短的短语。
此外,我们将把前 9000 个例子作为培训的示例和剩下的 1,000 个例子来测试 fit 模型。
下面是一个完整的示例,它加载干净的数据,拆分它,并将数据的分割部分保存到新文件中。
运行这个例子创建了三个新文件: english-german-both.pkl 它包含了我们可以用来定义问题参数的所有用于训练和测试例子,例如 max 短语长度和词汇表,以及 english-german-train.pkl 和 english-german-test.pkl 文件。用于训练和测试数据集的 pkl 文件。
现在我们已经准备好开发翻译模型。
训练神经翻译模型
在这部分中,我们会来建立翻译模型。
这部分包含了加载和准备好清洗好的文本数据给模型,然后在这些数据上定义和训练该模型。
让我们开始加载数据集,以便于准备数据。下面的函数:load_clean_sentences() 用于加载训练的数据集,反过来也可以加载测试的数据集。
我们会使用或者结合训练,测试数据集定义了对最大长度和问题的词汇量。
这挺简单。我们能够从单独的数据集中定义这些属性,然后在测试集中截断太长或者是超过词汇量的例子。
我们使用 Keras Tokenize 类去讲词汇映射成数值,如建模所需要的。我们会使用分离标记生成器给英语序列和德文序列,下面这个函数是 create_tokenizer() 会训练在一列短语中的标记生成器。
同样地,max_length() 函数会找在一列单词中最长的序列。
我们可以调用这些函数结合数据集来准备标记生成器,词汇大小和最大的长度,英文和德文短语。
现在我们准备开始训练数据集。
每个输入输出序列都必须编码成数值,并填充为最大的词汇长度。
这是因为,我们要使用一个嵌入的单词给输出序列,并对输出序列进行热编码。下面这个函数为:encode_sequences() 能执行这些操作,并返回结果。
输出序列需要一次热编码。这是应为模型会预测每个词汇的可能性作为输出。
函数 encode_output() 会热编码英文到输出序列中。
我们可以使用这两个函数准备训练和测试数据集给训练模型。
现在可以开始定义模型了。
在这个问题上,我们使用了编码 - 解码器 LSTM 模型。在这个架构中,输出序列是一个前端模型编码器编码好的序列,后端模型称为解码器,会一个词汇一个词汇地进行解码。
函数 define_model() 定义了模型,用了一些参数来设置模型,比如说输入输出的词汇量大小,和输入输出的最大词汇长度,和最大数量的内存单元。
这个模型设置在这问题不是最优的,这意味你有很多的潜力来调整它,提高翻译的技巧。
最后,我们开始训练模型。
我们在批大小(batch size)大小为 64 的情况下在所有样本数据集上完成 30 次训练迭代
我们使用检查点来确保每次在测试集中,模型技能提高时,模型都被保存到文件中。
我们可以将所有这些结合在一起,并适用于神经翻译模型。
下面列出了完整的工作示例。
首先运行示例打印数据集的参数摘要,例如词汇大小和最大短语长度。
接下来,打印定义的模型的摘要,允许我们确认模型配置。
该模型的图也被创建,提供了关于模型配置的另一个视角。
接下来,我们开始训练模型。
现代 CPU 硬件每个时代大约需要 30 秒;不需要 GPU。
在运行过程中,模型将被保存到文件 model.h5 中,准备在下一步中进行推理。
评估神经网络翻译模型
我们会评估训练和测试数据集。
该模型应该在训练数据集上表现得非常好,理想的情况是已经推广到在测试数据集上表现良好。
理想情况下,我们将使用单独的验证数据集来帮助选择训练期间的模型而不是测试集。你可以试试这个作为扩展。
清洗好的的数据集必须像之前一样加载和准备。
接下来,训练期间保存的最佳模型必须加载。
评估包含了两个步骤:首先生成翻译的输出序列,然后重复这个过程中的许多输入的例子,总结模型的技巧在多个案例。
从推论开始,模型可以以一次性的方式预测整个输出序列。
这是一系列整数,我们可以枚举并在标记器中查找以映射回单词。
下面这个函数 word_for_id(), 将执行这个反向映射:
我们可以对翻译中的每个整数执行此映射,并将结果作为一串单词来返回。 下面的函数 predict_sequence() 对单个编码的源短语执行此操作。
接下来,我们可以对数据集中的每个源短语重复此操作,并将预测结果与英文中的预期目标短语进行比较。
我们可以在屏幕中打印一些对比结果,来筛选模型在实践中的表现。
我们还将计算 BLEU 得分,以获得模型表现如何的定量概念。
evaluate_model() 函数实现了这个内容,为提供的数据集中的每个短语调用上述 predict_sequence() 函数。
我们可以将所有这些结合在一起,并在训练和测试数据集上评估加载的模型。
下面提供了完整的代码展示。
首先运行示例打印源文本,期望和预测翻译的示例,以及训练数据集的分数,然后打印测试数据集。
考虑到数据集的随机洗牌和神经网络的随机性,你的具体结果将有所不同。
首先查看测试数据集的结果,我们可以看到这些翻译是可读的并且大部分是正确的。
比如: “ich liebe dich” 正确地翻译为 “i love you“
我们还可以看到 BLEU-4 得分为 0.51,这提供了我们对这个模型可能期望的上限。
看看测试集的结果,看看可读的翻译,这不是一件容易的事情。
比如说,我们看到 “ich mag dich nicht” 翻译成 “我不喜欢你”。
我们也看到一些糟糕的翻译和一个很好的例子,模型可能会受到进一步的调整,比如说 “ich bin etwas beschwipst” 翻译成 “我有一点点” 而不是预想的 “我有点醉了”
BLEU-4 得分为 0.076238,提供了一个基本的技能来进一步改进模型。
拓展
本节列出了一些您可能希望拓展讨论的想法。
- 数据清洗。可以对数据执行不同的数据清理操作,例如不去除标点符号或规范化大小写,或者删除重复的英语短语。
- 词汇表。可以对词汇表进行细化,或者删除在数据集中使用少于 5 次或 10 次的单词,替换为 “unk”。
- 更多的数据。用于拟合模型的数据集可以扩展到 50,000,100,000 个短语或更多。
- 输入顺序。输入短语的顺序可以颠倒,这已经有相关报告表明能为提升能力,或者可以使用双向输入层。
- 层数。编码器和解码器模型可以用附加层进行扩展,并进行更多的训练迭代,从而为模型提供更多的表征能力。
- 存储单元。编码器和解码器中的存储器单元数量可以增加,为模型提供更多的表征能力。
- 正则。该模型可以使用正则化,如权重或激活正则化,或在 LSTM 层使用丢弃。
- 预训练的词向量。预先训练的单词向量可以在模型中使用
- 递归模型。可以使用该模型的递归公式,其中输出序列中的下一个单词可以以输入序列和到目前为止产生的输出序列为条件。
延伸阅读
这里提供了关于这个话题的一些材料,如果你想深入了解,可以查阅这些内容。
Tab-delimited Bilingual Sentence Pairs(http://www.manythings.org/anki/ )
German – English deu-eng.zip(http://www.manythings.org/anki/deu-eng.zip )
Encoder-Decoder Long Short-Term Memory Networks(http://t.cn/RQcOsqr )
总结
在这个教程中,你可以学到——如何去建立一个神经翻译系统去翻译德国词语为英文。
尤其是学习到了以下这些要点:
- 如何清洗数据,准备好训练神经翻译系统的数据
- 如何开发机器翻译的编码器 - 解码器模型
- 如何使用训练有素的模型对新输入词组进行推理并评估模型的技巧