03.md 55.5 KB
Newer Older
W
wizardforcel 已提交
1 2 3 4 5 6 7 8 9
# 神经机器翻译

**机器翻译**简而言之,是指使用计算机将文本从一种语言翻译成另一种语言。 它是计算机语言学的一个分支,已经发展了几年。 目前,在美国,翻译业是一个价值 400 亿美元的产业,并且在欧洲和亚洲也正在快速发展。 翻译存在巨大的社会,政府,经济和商业需求,并且 Google,Facebook,eBay 等公司在其应用程序中广泛使用它。 尤其是 Google 的神经翻译系统是目前最先进的翻译系统之一,能够仅用一种模型执行多种语言的翻译。

早期的机器翻译系统首先将文本中的单词和短语翻译成所需目标语言的相关替代词。 但是,由于以下原因,通过这些简单的技术实现的翻译质量受到限制:

*   从源语言到目标语言的词到词映射并非始终可用。
*   即使在源语言和目标语言之间确实存在精确的词对词映射,这些语言的句法结构通常也不相互对应。 机器翻译中的此问题通常称为*对齐错误*

W
wizardforcel 已提交
10
但是,随着**循环神经网络****RNN**)架构的最新进展,机器翻译不仅提供了更高的翻译质量,而且还提供了更高的翻译质量。这种系统的复杂性远远小于传统系统。
W
wizardforcel 已提交
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

机器翻译系统大致可分为三类:基于规则的机器翻译,统计机器翻译和神经机器翻译。

在本章中,我们将介绍以下主题:

*   基于规则的机器翻译
*   统计机器学习系统
*   神经机器翻译
*   序列到序列的神经翻译
*   神经翻译的损失函数

# 技术要求

您将需要具有 Python 3,TensorFlow 和 Keras 的基础知识。

W
wizardforcel 已提交
26
[可以在 GitHub 上找到本章的代码文件](https://github.com/PacktPublishing/Intelligent-Projects-using-Python/tree/master/Chapter03)
W
wizardforcel 已提交
27

W
wizardforcel 已提交
28
[观看以下视频,查看运行中的代码](http://bit.ly/2sXYX8A)
W
wizardforcel 已提交
29 30 31 32 33 34 35 36 37

# 基于规则的机器翻译

基于经典规则的机器翻译系统严重依赖于将文本从源语言转换为目标语言的规则。 这些规则通常由语言学家创建,通常在句法,语义和词汇层面上起作用。 传统的基于规则的机器翻译系统通常分为三个阶段:

*   分析阶段
*   词汇转移阶段
*   产生阶段

W
wizardforcel 已提交
38
图 3.1 是典型的基于规则的机器翻译系统的流程图:
W
wizardforcel 已提交
39 40 41 42 43 44 45

![](img/87104398-8e42-45a6-bf08-3dbad0d1e745.png)

图 3.1:基于规则的机器翻译系统的流程图

# 分析阶段

W
wizardforcel 已提交
46
基于规则的机器翻译的第一阶段是分析阶段,其中分析源语言文本以提取与形态,词性,命名实体识别以及词义歧义有关的信息。 形态信息涉及单词的结构,词干的派生方式,词根的检测等。 词性标记器使用可能的语音标记来标记文本中的每个单词,例如名词,动词,副词,形容词等。 接下来是**命名实体识别****NER**)任务,该任务尝试将命名实体分类到预定义的存储桶中,例如人员名称,位置,组织名称,以此类推。 NER 之后是单词义消歧,它试图识别句子中如何使用特定单词。
W
wizardforcel 已提交
47

W
wizardforcel 已提交
48
# 词汇翻译阶段
W
wizardforcel 已提交
49

W
wizardforcel 已提交
50
词汇翻译阶段位于分析阶段之后,并分为两个阶段:
W
wizardforcel 已提交
51 52 53 54 55 56 57 58 59 60 61 62 63 64

*   **单词翻译**:在单词翻译中,使用双语翻译词典将在分析阶段导出的源根单词翻译为相应的目标根单词。
*   **语法翻译**:在语法翻译阶段,将进行语法修饰,包括翻译后缀等。

# 产生阶段

在生成阶段,将对翻译的文本进行验证和更正,以便在将最终翻译的文本作为附件提供之前,就动词而言,就动词,性别以及主语和宾语相对于动词的同意而言,它是正确的。 输出。 在每个步骤中,机器翻译系统都使用预定义的词典。 对于基于规则的机器翻译系统的最低限度的实现,需要以下词典:

*   用于源语言形态分析的词典
*   双语词典,包含源语言单词到目标语言对应单词的映射
*   包含用于目标单词生成的目标语言形态信息的字典

# 统计机器学习系统

W
wizardforcel 已提交
65
统计机器翻译系统通过在给定源文本的情况下最大化其条件概率来选择目标文本。 例如,假设我们有一个源文本`s`,并且我们想要导出目标语言中的最佳等效文本`t`。 可以如下得出:
W
wizardforcel 已提交
66 67 68

![](img/d5a5fe04-9f29-433c-a9bf-83303d4e5232.png)

W
wizardforcel 已提交
69
`(1)``P(t / s)`的公式可使用贝叶斯定理扩展如下:
W
wizardforcel 已提交
70 71 72

![](img/f9bcb9c3-e8e5-4d9b-bddc-cf07222a55ee.png)

W
wizardforcel 已提交
73
对于给定的源句子,`P(s)`将是固定的,因此找到最佳目标翻译结果如下:
W
wizardforcel 已提交
74 75 76

![](img/3e0fe8f4-e700-48f5-95a8-07ce870ed1c2.png)

W
wizardforcel 已提交
77
您可能想知道为什么直接将`P(s / t)P(t)`最大化而不是`P(t / s)`会带来优势。 通常,通过将问题分为两个部分来避免`P(t / s)`下很可能出现的格式错误的句子,即`P(s / t)``P(t)`,如上式所示:
W
wizardforcel 已提交
78 79 80 81 82 83 84

![](img/f4f8506a-2b5f-4c49-a434-12012a2dcf97.png)

图 3.2:统计机器翻译架构

从上图可以看出,统计机器翻译问题已分解为三个不同的子问题,如上所述:

W
wizardforcel 已提交
85
*   为目标建立**语言模型**,使我们能够估算`P(t)`
W
wizardforcel 已提交
86
*   从目标语言到源语言构建**翻译模型**,这使我们能够估算`P(s / t)`
W
wizardforcel 已提交
87
*   对可能的目标翻译进行搜索,然后选择最大化`P(s / t)P(t)`的翻译
W
wizardforcel 已提交
88 89 90 91 92

我们将讨论这三个主题中的每一个,因为这些功能是任何机器翻译问题所固有的。

# 语言模型

W
wizardforcel 已提交
93
在语言模型中,句子的概率表示为各个单词或短语的条件概率的乘积。 假设句子`t`由单词`t[1], t[2], ..., t[n]`。 根据概率的链式规则,句子`t`的概率可以表示为:
W
wizardforcel 已提交
94 95 96

![](img/5b33f286-e730-48d1-83d1-f491731ad41f.png)

W
wizardforcel 已提交
97
根据上述公式构建语言模型将需要我们估计几个订单的条件概率,这在实际中是不可能的。 为了使问题在计算上可行,一个简单的假设是仅根据前一个单词而不是之前的所有单词来对单词进行条件处理。 该假设也称为**马尔可夫假设**,该模型称为**二元模型**。 根据二元模型,单词的条件概率可以表示为:
W
wizardforcel 已提交
98 99 100 101 102 103 104

![](img/fed81518-1cd1-4beb-a689-4f153de98fea.png)

为了进一步改善结果,我们可以使用**三元模型**,该模型将句子中的特定单词置于其前面两个单词的条件下,如下所示:

![](img/b00765b5-99f5-4dc3-a9cd-32f3ae13a51f.png)

W
wizardforcel 已提交
105
对于二元模型,给定训练语料库中偶对`(t[1], t[2])`的总数,计算当前单词`t[1]`的下一个单词`t[2]`的条件概率,并根据语料库中`t[1]`出现的次数归一化:
W
wizardforcel 已提交
106 107 108

![](img/4011e558-b4fc-468a-89cc-3ae457494668.png)

W
wizardforcel 已提交
109
对于三元模型,当前单词`t[3]`在两个单词`t[1], t[2]`之前的条件概率,可以估算如下:
W
wizardforcel 已提交
110 111 112

![](img/a4e37492-f2ae-4999-b8d1-53d9afac5fba.png)

W
wizardforcel 已提交
113
超出三字母组合模型通常会导致稀疏性。 即使对于双字母组模型,我们也可能会缺少几个双字母组的条件概率,因为它们没有出现在训练语料库中。 但是,那些缺失的二元组可能非常相关,并且估计其条件概率非常重要。 不用说,`n`元模型倾向于估计出现在训练数据中的单词对的高条件概率,而忽略没有出现的单词。
W
wizardforcel 已提交
114

W
wizardforcel 已提交
115
# 语言模型的困惑度
W
wizardforcel 已提交
116

W
wizardforcel 已提交
117
困惑度指标用于评估语言模型的有用性。 假设我们已经在训练语料库上训练了一个语言模型,并且使句子或文本上的学习概率模型为`P(.)``P(.)`的困惑度是根据与训练语料库相同的总体抽取的测试集语料库进行评估的。 如果我们用`M`词表示测试集语料库,请说`(w[1], w[2], ..., w[M])`,则模型在测试集序列上的困惑性表示如下:
W
wizardforcel 已提交
118 119 120

![](img/3ae0b20f-358d-46c3-a2df-addd87872199.png)

W
wizardforcel 已提交
121
如图所示`H`的表达式可衡量每字的不确定性:
W
wizardforcel 已提交
122 123 124 125 126 127 128

![](img/1628bf07-cbf5-4c9d-a63d-de3baa23d975.png)

根据语言模型,我们可以按如下方式分解测试语料库的概率表达式:

![](img/f6cd48b3-1beb-41ae-aedb-3cc8382266ca.png)

W
wizardforcel 已提交
129
如果我们将测试集中第`i`个词的概率表示为条件,则以先前的字为条件`P(s[i])`,则测试语料库的概率如下:
W
wizardforcel 已提交
130 131 132

![](img/9028a0b4-506c-49df-a783-869bb5c75426.png)

W
wizardforcel 已提交
133
这里`P(s[i]) = P(w[i] / w[1], w[2], ..., w[i-1])`。 将`(1)``(4)`结合起来,困惑可以写成如下:
W
wizardforcel 已提交
134 135 136

![](img/785a9853-f2d3-4c28-a718-aeb9e2fcb6de.png)

W
wizardforcel 已提交
137
假设我们有一个语言模型`P(.)`和一个进行评估的测试集`I love Machine Learning`。 根据语言模型,测试集的概率如下:
W
wizardforcel 已提交
138 139 140

![](img/82e0b0cf-e3ee-43a0-962e-91bf737abd0a.png)

W
wizardforcel 已提交
141
如果语言模型的训练语料也是`I love Machine Learning`,则测试集的概率为 1,导致对数概率为`0`,且困惑为`1`。 这意味着该模型可以完全确定地生成下一个单词。
W
wizardforcel 已提交
142

W
wizardforcel 已提交
143
另一方面,如果我们有一个更现实的训练语料库,其词汇量为`N = 20,000`,并且训练数据集在测试数据集上的困惑度为 100,那么平均而言, 为了预测序列中的下一个单词,我们将搜索范围从 20,000 个单词缩小到 100 个单词。
W
wizardforcel 已提交
144 145 146 147 148

让我们看一下最坏的情况,在这种情况下,我们设法建立一个模型,其中每个单词都与序列中的先前单词无关:

![](img/a231a609-f58f-4faa-a081-64addf74b643.png)

W
wizardforcel 已提交
149
对于`M`个单词的测试集,使用`(5)`的困惑如下:
W
wizardforcel 已提交
150 151 152

![](img/ba0cff31-c3c7-49ec-9967-d48fdcdb8ae4.png)

W
wizardforcel 已提交
153
如果我们像以前一样有`N = 20,000`,那么要预测序列中的任何单词,就需要考虑词汇表中的所有`N`个单词,因为它们都是一样的。 在这种情况下,我们无法减少单词的平均搜索空间来预测序列中的单词。
W
wizardforcel 已提交
154 155 156

# 翻译模型

W
wizardforcel 已提交
157
**翻译模型**可以视为机器翻译模型的核心。 在翻译模型中,我们需要估计概率`P(s / t)`,其中`s`是源语言句子,`t`是目标语言的句子。 在这里,给出了源句,而目标是我们试图找出的句子。 因此,该概率可以称为源句子给定目标句子的可能性。 例如,假设我们正在将源文本从法语翻译为英语。 因此,在`P(s / t)`的情况下,我们的目标语言是法语,我们的源语言是英语,而在实际翻译的情况下,即`P(s / t)P(t)`,我们的源语言是法语,而我们的目标语言是英语。
W
wizardforcel 已提交
158 159 160

该翻译主要包括三个部分:

W
wizardforcel 已提交
161
*   **丰富度**:并非源语言中的所有单词在目标语言中都有对应的单词。 例如,英语句子`Santanu loves math`法语翻译为`Santanu aim les maths`。 如我们所见,英语中的`math`一词已翻译成法文的两个单词,即`les maths`。 形式上,*丰富度*定义为目标语言中源语言单词生成的单词数量上的概率分布,并且可以表示为`P(n / w[s])`,其中`w[s]`代表源词。 而不是使用硬编码的数字`n`,而是使用概率分布,因为相同的单词可能会根据上下文生成不同长度的翻译。
W
wizardforcel 已提交
162
*   **失真**:对于任何机器翻译系统,源句子和目标句子之间的单词到单词的对应关系都很重要。 但是,源语言句子中单词的位置可能并不总是与目标语言句子中对应单词的位置完全同步。 失真通过概率函数 `P(p[t], p[s], l)`覆盖了对齐的概念,其中`p[t]``p[t]`分别代表目标词和源词的位置,而`l`代表目标句子的长度。 如果源语言是英语,目标语言是法语,则`P(p[t] / p[s], l)`表示位置`p[s]`的英语单词对应于位置`p[s]`中的法语单词,其长度为`l`
W
wizardforcel 已提交
163
*   **单词到单词的翻译**:最后,我们来进行单词到单词的翻译,这通常由给定源语言单词的目标语言单词的概率分布表示。 对于给定的源语言单词`w[s]`,概率可以表示为`P(w[t] / w[s])`,其中`w[t]`代表目标语言单词。
W
wizardforcel 已提交
164 165 166

对于语言模型,需要在训练过程中估计生育率,失真率和单词到单词的翻译率。

W
wizardforcel 已提交
167
现在,让我们回到估计概率`P(s / t)`的原始问题。 如果我们用`E`表示英语句子,而用`F`表示法语句子,则需要计算`P(F / E)`的概率。 为了考虑单词的对齐方式,我们将概率修改为`P(F, a / E)`,其中`a`表示目标句子在法语中的对齐方式。 这种一致性将有助于我们注入有关畸变和生育能力的信息。
W
wizardforcel 已提交
168

W
wizardforcel 已提交
169
让我们通过一个示例来计算概率`P(F, a / E)`。 让一个特定的英语句子由五个单词的句子表示。 `e = (e[1], e[2], ..., e[5])`,实际上这是实际法语句子的正确翻译。 `f = (f[1], f[2], ..., f[6])`。 另外,让单词的相应对齐方式如下:
W
wizardforcel 已提交
170

W
wizardforcel 已提交
171 172 173 174 175 176
*   `e1 -> f6`
*   `e2 ->`不对应法语中的任何单词
*   `e3 -> f3, f4`
*   `e4 -> f1`
*   `e5 -> f2`
*   `f5 ->`不对应英语中的任何单词
W
wizardforcel 已提交
177 178 179

由于这是一个概率模型,因此该算法将尝试使用具有不同对齐方式的不同英语句子,在给定法语句子的情况下,其中具有正确对齐方式的正确英语句子应该具有最高的概率。

W
wizardforcel 已提交
180
让我们将第一个英语单词考虑为`e[1]`-它与法语单词`f[6]`对齐,并且还会产生一个法语单词,如下:
W
wizardforcel 已提交
181 182 183

![](img/688b2388-8434-4d98-8d00-66ea374148b9.png)

W
wizardforcel 已提交
184
现在,让我们将对齐方式作为两个成分的组合:失真`a[d]`和丰富度`f[d]``(1)`中的表达式可以重写如下:
W
wizardforcel 已提交
185

W
wizardforcel 已提交
186 187 188
![](img/edaf41f6-4172-4397-b791-f714571e57ca.png) 

![](img/546bd7e5-37e3-44ba-a006-3378e4664053.png)
W
wizardforcel 已提交
189

W
wizardforcel 已提交
190
如果我们仔细观察, `P(f[5] / e[1])`就是翻译概率, `P(a[f] / e[1])`是丰富度,而 `P(a[d] / e[1], f[5])`是失真概率。 我们需要针对英语句子中与给定法语句子的所有比对中的所有给定英语单词进行此活动,以计算`P(F, a / E)`。 最后,我们需要采用最佳英语句子`E_hat`和对齐方式`a_hat`,以使`P(F, a / E)P(E)`的概率最大化。 如下所示:
W
wizardforcel 已提交
191 192 193 194 195 196 197

![](img/396ee058-a561-4b7e-a60d-1a568bbb52b6.png)

这里要注意的一件事是,尝试不同的对齐方式和不同的可能单词翻译以寻求最佳翻译可能在计算上变得棘手,因此,需要部署巧妙的算法以在最短的时间内找到最佳翻译。

# 神经机器翻译

W
wizardforcel 已提交
198
**神经机器翻译****NMT**)使用深度神经网络来执行从源语言到目标语言的机器翻译。 神经翻译机将源语言中的文本作为输入序列,并将其编码为隐藏的表示形式,然后将其解码回以生成目标语言中的翻译文本序列。 该 NMT 系统的主要优势之一是,整个机器翻译系统可以从端到端一起进行训练,这与基于规则的机器翻译系统和统计机器翻译系统不同。 一般而言,在[ 神经翻译机架构。
W
wizardforcel 已提交
199 200 201 202 203 204 205 206

NMT 与其他传统方法相比的一些优点如下:

*   基于损失函数对 NMT 模型的所有参数进行端到端训练,从而降低了模型的复杂性
*   这些 NMT 模型使用的上下文比传统方法大得多,因此产生了更准确的翻译
*   NMT 模型可以更好地利用单词和短语的相似性
*   RNN 允许生成更好质量的文本,因此,相对于已翻译文本的语法而言,翻译更加准确

W
wizardforcel 已提交
207
# 编码器-解码器模型
W
wizardforcel 已提交
208

W
wizardforcel 已提交
209
下图说明了一种神经翻译机的架构,该结构使用一个 LSTM 作为编码器,将输入源语言序列编码为最终隐藏状态`h[f]`和最终存储单元状态`c[f]`。 最终的隐藏状态和单元状态`[h[f]; c[f]]`将捕获整个输入序列的上下文。 因此,`[h[f]; c[f]]`成为解码器网络可适应的良好候选者。
W
wizardforcel 已提交
210

W
wizardforcel 已提交
211
此隐藏状态和单元状态信息`[h[f]; c[f]]`作为初始隐藏状态和单元状态被馈送到解码器网络,然后解码器在目标序列上训练,其中输入目标序列相对于输出目标序列滞后一。 根据解码器,输入序列的第一个字是伪字`[START]`,而输出标签是字`c'est`。 解码器网络仅被训练为一种生成语言模型,在任何时候,输出标签`t`都是相对于输入的下一个单词,即`y[t] = x[t + 1]`。 唯一的新变化是编码器的最终隐藏状态和单元状态(即`[h[f]; c[f]]`) 解码器的隐藏状态和单元状态为翻译提供内容。
W
wizardforcel 已提交
212 213 214 215 216 217 218

这意味着可以将训练过程视为为目标语言(由解码器表示)建立语言模型,该模型以代表源语言的编码器的隐藏状态为条件:

![](img/6b5fdbd2-9d77-4305-b64c-3a62dadede35.png)

图 3.3:神经机器翻译系统的高级编码器-解码器架构

W
wizardforcel 已提交
219
如果`T`是与源语言文本`S`相对应的目标语言文本,那么为了进行训练,我们只是试图使`P[w](T[s+1] / S, T)`的对数概率相对于`W`最大化,其中`T[s+1]`表示平移一个时间步骤的目标语言文本,`W`表示编码器-解码器架构模型参数。
W
wizardforcel 已提交
220 221 222

现在我们已经讨论了编码器-解码器 NMT 的训练过程,现在我们将研究如何在推理过程中使用训练后的模型。

W
wizardforcel 已提交
223
# 将编码器-解码器模型用于推理
W
wizardforcel 已提交
224

W
wizardforcel 已提交
225
**NMT****神经翻译机**)上进行推理的架构流程与训练 NMT 略有不同。 以下是使用 NMT 执行推理的架构流程:
W
wizardforcel 已提交
226 227 228

![](img/c8e700f7-0973-4a7a-b1d8-1f00a2ef91e7.png)

W
wizardforcel 已提交
229
图 3.4:基于编码器/解码器的神经机器翻译的推理
W
wizardforcel 已提交
230

W
wizardforcel 已提交
231
在推理过程中,源语言输入序列被馈送到编码器网络并产生最终的隐藏状态和单元状态, `[h[f]; c[f]]`馈给解码器的隐藏状态和单元状态。 解码器被转换为单个时间步,馈送到解码器的第一个输入是伪`[START]`字。 因此,基于`[h[f]; c[f]]`和初始虚拟字`[START]`,解码器将输出一个字`w`,以及新的隐藏状态和单元格状态`[h[d]; c[d]]`。 这个单词`w`再次以新的隐藏状态和单元状态馈送到解码器。 `[h[d]; c[d]]`生成下一个单词。 重复此过程,直到遇到序列结束字符。
W
wizardforcel 已提交
232 233 234

# 实现序列到序列的神经翻译机

W
wizardforcel 已提交
235
我们将建立一个神经机器翻译系统,该系统将学习将英语短句子翻译成法语。 为此,我们将使用位于[这个页面](http://www.manythings.org/anki/)上的英语到法语文本语料库(`fra-eng/fra.txt`)。
W
wizardforcel 已提交
236 237 238

# 处理输入数据

W
wizardforcel 已提交
239
文本数据不能直接输入任何神经网络,因为神经网络只能理解数字。 我们将每个单词视为一个单编码的矢量,其长度等于每个语料库中出现的单词的数量。 如果英语语料库包含 1,000 个单词,则一键编码的矢量`v[e]`的尺寸为 1,000,即`v[e] ∈ R^(1000 x 1)`
W
wizardforcel 已提交
240

W
wizardforcel 已提交
241
我们将通读英语和法语语料库,并确定它们各自中唯一词的数量。 我们还将通过索引来表示单词,对于该单词的单编码向量,该单词对应的索引将设置为 1,而其余索引将设置为 0。 例如,假设在英语语料库中,我们有四个词:`Global warming is real`。 我们可以如下定义每个单词的索引:
W
wizardforcel 已提交
242

W
wizardforcel 已提交
243
| **单词** | **索引** |
W
wizardforcel 已提交
244
| --- | --- |
W
wizardforcel 已提交
245 246 247 248
| `global` | 0 |
| `warming` | 1 |
| `is` | 2 |
| `real` | 3 |
W
wizardforcel 已提交
249

W
wizardforcel 已提交
250
在这种情况下,我们可以将单词`global`的单热编码向量定义为`[1,0,0,0]^T`。 类似地,`real`的一键编码矢量可以表示为`[1,0,0,0]^T`
W
wizardforcel 已提交
251 252 253

现在,转到每个句子或记录的源语言输入,我们将有一系列单词表示为一个单编码的矢量序列。 下一个明显的问题是如何管理序列长度,因为这可能会有所不同。 最普遍接受的方法是使固定序列长度等于语料库中句子的最大序列长度,或者达到预定的合理长度。 我们将使用目标语句两次:一次作为解码器的翻译输出序列,一次作为解码器的输入序列,唯一的区别是输出序列比输入序列提前一个时间步长。 因此,输入目标序列中的第一个单词将是伪单词`[START]`,而输出目标序列中的最后一个单词将是伪单词`[END]`,标记句子序列的结尾。

W
wizardforcel 已提交
254
如果目标法语句子是`Je m'appelle Santanu`,则解码器中的输入目标和输出目标序列如下:
W
wizardforcel 已提交
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335

```py
[START],[Je],[mappelle] [Santanu]
[Je],[mappelle] [Santanu][END]
```

我们选择用制表符表示`[START]`,用下一行表示`[END]`

我们将数据创建活动分为三个部分:

*   读取源文件(英文)和目标文本(法文)的输入文件
*   从源语言和目标语言文本构建词汇表
*   将输入的英语和法语语料库处理为数字表示形式,以便可以在神经机器翻译网络中使用它们

此处说明的`read_input_file`功能可用于阅读源语言和目标语言文本:

```py
    def read_input_file(self,path,num_samples=10e13):
        input_texts = []
        target_texts = []
        input_words = set()
        target_words = set()

        with codecs.open(path, 'r', encoding='utf-8') as f:
            lines = f.read().split('\n')

        for line in lines[: min(num_samples, len(lines) - 1)]:
            input_text, target_text = line.split('\t')
              # \t as the start of sequence 
            target_text = '\t ' + target_text + ' \n'
              # \n as the end of sequence
            input_texts.append(input_text)
            target_texts.append(target_text)
            for word in input_text.split(" "):
                if word not in input_words:
                    input_words.add(word)
            for word in target_text.split(" "):
                if word not in target_words:
                    target_words.add(word)

        return input_texts,target_texts,input_words,target_words
```

`vocab_generation`功能可用于为源语言和目标语言构建单词的词汇集:

```py
    def vocab_generation(self,path,num_samples,verbose=True):

        input_texts,target_texts,input_words,target_words =   
        self.read_input_file(path,num_samples)
        input_words = sorted(list(input_words))
        target_words = sorted(list(target_words))
        self.num_encoder_words = len(input_words)
        self.num_decoder_words = len(target_words)
        self.max_encoder_seq_length = 
        max([len(txt.split(" ")) for txt in input_texts])
        self.max_decoder_seq_length =
        max([len(txt.split(" ")) for txt in target_texts])

        if verbose == True:

            print('Number of samples:', len(input_texts))
            print('Number of unique input tokens:',
                  self.num_encoder_words)
            print('Number of unique output tokens:',
                   self.num_decoder_words)
            print('Max sequence length for inputs:',
                   self.max_encoder_seq_length)
            print('Max sequence length for outputs:',
                   self.max_decoder_seq_length)

        self.input_word_index =
        dict([(word, i) for i, word in enumerate(input_words)])
        self.target_word_index = 
        dict([(word, i) for i, word in enumerate(target_words)])
        self.reverse_input_word_dict = 
        dict((i, word) for word, i in self.input_word_index.items())
        self.reverse_target_word_dict = 
        dict((i, word) for word, i in self.target_word_index.items())
```

W
wizardforcel 已提交
336
`process_input`函数利用先前功能中构建的输入和目标文本以及词汇表,将文本数据转换为数字形式,以供神经翻译机架构使用。 `process_input`功能的代码如下:
W
wizardforcel 已提交
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406

```py
        def process_input(self,input_texts,target_texts=None,verbose=True):

        encoder_input_data = 
        np.zeros((len(input_texts), self.max_encoder_seq_length,               
                 self.num_encoder_words), dtype='float32')

        decoder_input_data = 
         np.zeros((len(input_texts), self.max_decoder_seq_length, 
                  self.num_decoder_words), dtype='float32')

        decoder_target_data = 
        np.zeros((len(input_texts), self.max_decoder_seq_length, 
                 self.num_decoder_words), dtype='float32')

        if self.mode == 'train':
            for i, (input_text, target_text) in 
            enumerate(zip(input_texts,target_texts)):
                for t, word in enumerate(input_text.split(" ")):
                    try:
                        encoder_input_data[i, t, 
                                          self.input_word_index[word]] = 1.
                    except:
                        print(f'word {word} 
                             encoutered for the 1st time, skipped')
                for t, word in enumerate(target_text.split(" ")):
                # decoder_target_data is ahead of decoder_input_data
                  by one timestep
                    decoder_input_data[i, t, 
                    self.target_word_index[word]] = 1.
                    if t > 0:
                    # decoder_target_data will be ahead by one timestep
                    #and will not include the start character.
                        try:
                            decoder_target_data[i, t - 1, 
                            self.target_word_index[word]] = 1.
                        except:
                            print(f'word {word} 
                                  encoutered for the 1st time,skipped')

            return 
            encoder_input_data,decoder_input_data,decoder_target_data,
            np.array(input_texts),np.array(target_texts)

        else:
            for i, input_text in enumerate(input_texts):
                for t, word in enumerate(input_text.split(" ")):
                    try:
                        encoder_input_data[i, t, 
                                          self.input_word_index[word]] = 1.
                    except:
                        print(f'word {word} 
                        encoutered for the 1st time, skipped')

            return encoder_input_data,None,None,np.array(input_texts),None
```

`encoder_input_data` 变量将包含输入源数据,并且将是记录数,时间步数以及每个维度的维数的三维数组。 热编码向量。 类似地,`decoder_input_data` 将包含输入目标数据,而`decoder_target_data` 将包含目标标签。 在执行上述功能后,将生成训练机器翻译系统所需的所有相关输入和输出。 以下代码块包含与使用`40000`样本执行`vocab_generation`功能有关的显示统计信息:

```py

('Number of samples:', 40000)
('Number of unique input tokens:', 8658)
('Number of unique output tokens:', 16297)
('Max sequence length for inputs:', 7)
('Max sequence length for outputs:', 16)

```

W
wizardforcel 已提交
407
从前面的统计数据可以看出,`40000`语料库中输入的英语单词的数量为`8658`,文本句子的数量为`8658`,而对应的法语单词的数量为`16297`。 这表明以下事实:每个英语单词平均发出大约两个法语单词。 同样,我们看到英语句子中的最大单词数为`7`,而法语句子中的最大单词数为`14`(如果您排除了我们在法语句子中添加的`[START]``[END]`字符) 训练目的。 这也证实了以下事实:平均而言,每个要翻译的英语句子将产生双倍的单词数。
W
wizardforcel 已提交
408 409 410 411 412 413 414 415 416 417 418 419 420

让我们看一下神经翻译机的输入和目标的形状:

```py
('Shape of Source Input Tensor:',(40000, 7, 8658))
('Shape of Target Input Tensor:',(40000, 16, 16297))
(Shape of Target Output Tensor:',(40000, 16, 16297))
```

编码器数据的形状为`(40000, 7, 8658)`,其中第一维用于源语言语句的数量,第二维用于时间步长的数量,最终维是单次热编码矢量的大小, 是`8658`,与英语词汇中的`8658`源语言单词相对应。 类似地,对于目标输入和输出张量,我们看到一热编码向量的大小为`16297`,与法语词汇中的`16297`单词相对应。 法语句子的时间步长为`16`

# 定义神经机器翻译的模型

W
wizardforcel 已提交
421
如前所述,编码器将通过 LSTM 处理源输入序列,并将源文本编码为有意义的摘要。 有意义的摘要将存储在最后的序列步骤中,即隐藏和单元状态`h[f]``c[f]`。 这些向量在一起(即`[h[f]; c[f]]`)提供了有关源文本的有意义上下文,并且训练了解码器来产生具有隐藏和细胞状态向量的目标序列`[h[f]; c[f]]`
W
wizardforcel 已提交
422

W
wizardforcel 已提交
423
下图所示“图 3.5”是英语到法语翻译的训练过程的详细图。 英文句子`It's a beautiful day`通过 LSTM 转换为含义摘要,然后存储在隐藏和单元格状态向量`[h[f]; c[f]]`中。 然后使解码器根据嵌入在`[h[f]; c[f]]`中的信息,以输入源语句为条件,生成自己的目标序列。 给定源句,使在时间步`t`的解码器预测下一个目标单词,即在时间步`t + 1`的单词。 这就是为什么目标输入字和目标输出字之间有一个时间步长的滞后的原因。 对于第一步,解码器在目标文本序列中没有任何先前的单词,因此可用于预测目标单词的唯一信息是以`[h[f]; c[f]]`,作为初始隐藏和单元状态向量提供。 像编码器一样,解码器也使用 LSTM,并且如上所述,输出目标序列比输入目标序列提前一个时间步长:
W
wizardforcel 已提交
424 425 426 427 428

![](img/421b234c-10f6-4cbe-9233-97a33233b2ca.png)

图 3.5:训练时机器翻译网络流程的图示

W
wizardforcel 已提交
429
我们基于“图 3.5”中所示的架构,定义了用于训练功能`model_enc_dec`的编码器解码器端到端模型。 在这里,**编码器(LSTM 1)**依次获取源语言文本单词,并在**编码器(LSTM 1)**的最后序列步骤中捕获源语言句子或文本的整个上下文。来自编码器的上下文将作为**解码器(LSTM 2)**的初始状态进行馈送,该学习器将根据当前单词来预测下一个单词,因为在训练过程中我们会得到一个句子 / text 作为目标语言,因此解码器只需将其输入移位一个时间步即可形成目标:
W
wizardforcel 已提交
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479

```py
    def model_enc_dec(self):
        #Encoder Model
        encoder_inp = 
        Input(shape=(None,self.num_encoder_words),name='encoder_inp')
        encoder = LSTM(self.latent_dim, return_state=True,name='encoder')
        encoder_out,state_h, state_c = encoder(encoder_inp)
        encoder_states = [state_h, state_c]

        #Decoder Model
        decoder_inp = 
        Input(shape=(None,self.num_decoder_words),name='decoder_inp')
        decoder_lstm = 
        LSTM(self.latent_dim, return_sequences=True,   
        return_state=True,name='decoder_lstm')
        decoder_out, _, _ = 
        decoder_lstm(decoder_inp, initial_state=encoder_states)
        decoder_dense = 
        Dense(self.num_decoder_words, 
        activation='softmax',name='decoder_dense')
        decoder_out = decoder_dense(decoder_out)
        print(np.shape(decoder_out))
        #Combined Encoder Decoder Model
        model = Model([encoder_inp, decoder_inp], decoder_out)
        #Encoder Model 
        encoder_model = Model(encoder_inp,encoder_states)
        #Decoder Model
        decoder_inp_h = Input(shape=(self.latent_dim,))
        decoder_inp_c = Input(shape=(self.latent_dim,))
        decoder_input = Input(shape=(None,self.num_decoder_words,))
        decoder_inp_state = [decoder_inp_h,decoder_inp_c]
        decoder_out,decoder_out_h,decoder_out_c =   
        decoder_lstm(decoder_input,initial_state=decoder_inp_state)
        decoder_out = decoder_dense(decoder_out)
        decoder_out_state = [decoder_out_h,decoder_out_c]
        decoder_model = Model(inputs = 
        [decoder_input] + decoder_inp_state,output=
        [decoder_out]+ decoder_out_state)
        plot_model(model,show_shapes=True, to_file=self.outdir + 
                   'encoder_decoder_training_model.png')
        plot_model(encoder_model,show_shapes=True, to_file=self.outdir + 
                   'encoder_model.png')
        plot_model(decoder_model,show_shapes=True, to_file=self.outdir + 
                   'decoder_model.png')

        return model,encoder_model,decoder_model

```

W
wizardforcel 已提交
480
虽然训练模型是一个简单的端到端模型,但推理模型并不是那么简单,因为我们不知道每个时间步长之前解码器的输入。 我们将在“构建推理模型”部分中更详细地讨论推理模型。
W
wizardforcel 已提交
481 482 483

# 神经翻译机的损失函数

W
wizardforcel 已提交
484
神经翻译机的损失函数是用于预测模型序列中每个目标单词的平均交叉熵损失。 实际目标词和预测目标词可以是我们所采用的法语语料库中的`16,297`个词中的任何一个。 在时间步`t`处的目标标签将是单热点编码的矢量`y[t] ∈ {0,1}^16297`,法语词汇表中每个`16,297`个单词的预测输出将采用概率形式。 如果将预测的输出概率向量表示为`p[t] ∈ (0, 1)^16297`,则特定句子在每个时间步的平均分类损失由以下给出:
W
wizardforcel 已提交
485 486 487 488 489 490 491

![](img/be4d5566-e340-4986-8364-96b61806475a.png)

通过汇总所有序列时间步长上的损失,可以得出整个句子的损失,如下所示:

![](img/b7bc5575-d588-40d6-926a-4178a812cd58.png)

W
wizardforcel 已提交
492
由于我们使用小批量随机梯度下降进行工作,因此可以通过对小批量中所有句子的平均损失来获得小批量的平均成本。 如果我们使用大小为`m`的微型批量,则每个微型批量的平均损失如下:
W
wizardforcel 已提交
493 494 495 496 497 498 499

![](img/c36a2af9-5786-4cb3-baf8-29dbd0b17918.png)

最小批量成本用于计算随机梯度下降的梯度。

# 训练模型

W
wizardforcel 已提交
500
我们首先执行`model_enc_dec` 函数来定义训练模型以及`encoder_model``decoder_model`进行推理,然后将其与`categorical_crossentropy`损失和`rmsprop`一起编译 ]优化器。 我们可以尝试使用其他优化器,例如 Adam,具有动量的 SDG 等,但是目前,我们将坚持使用`rmsprop``train`功能可以定义如下:
W
wizardforcel 已提交
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523

```py
# Run training

    def train(self,encoder_input_data,decoder_input_data,
              decoder_target_data):
        print("Training...")

        model,encoder_model,decoder_model = self.model_enc_dec()

        model.compile(optimizer='rmsprop', loss='categorical_crossentropy')

        model.fit([encoder_input_data, decoder_input_data],
                  decoder_target_data,
                  batch_size=self.batch_size,
                  epochs=self.epochs,
                  validation_split=0.2)
        # Save model
        model.save(self.outdir + 'eng_2_french_dumm.h5')
        return model,encoder_model,decoder_model

```

W
wizardforcel 已提交
524
我们在 80% 的数据上训练模型,并将其余 20% 的数据用于验证。 训练/测试拆分由以下定义的功能执行:
W
wizardforcel 已提交
525 526 527 528 529 530 531 532 533 534 535 536 537

```py
def train_test_split(self,num_recs,train_frac=0.8):
        rec_indices = np.arange(num_recs)
        np.random.shuffle(rec_indices)
        train_count = int(num_recs*0.8)
        train_indices = rec_indices[:train_count]
        test_indices = rec_indices[train_count:]
        return train_indices,test_indices
```

# 建立推理模型

W
wizardforcel 已提交
538
让我们尝试回顾推理模型的工作机制,并了解如何使用已经训练的模型的组件来构建它。 该模型的编码器部分应通过以源语言中的文本句子作为输入来工作,并提供最终的隐藏和单元状态向量`[h[f]; c[f]]`作为输出。 我们不能按原样使用解码器网络,因为目标语言输入字不再可以馈送到解码器。 相反,我们将解码器网络折叠为一个步骤,并将该步骤的输出作为下一步的输入。 我们以虚拟字`[START]`作为解码器的第一个输入字,以及`[h[f]; c[f]]`,用作其初始隐藏状态和单元格状态。 目标输出字`w[1]`以及隐藏和单元状态`h[1]; c[1]`由解码器使用`[START]``[h[f]; c[f]]`,因为输入再次馈送到解码器以生成下一个字,然后重复该过程,直到解码器输出伪字`[END]`为止。 下图说明了推理过程的逐步表示形式,以便于解释:
W
wizardforcel 已提交
539 540 541 542 543

![](img/32c9e729-5872-4e25-bf42-0f87c5ba9d4d.png)

图 3.6:推理过程的逐步说明

W
wizardforcel 已提交
544
从上图可以看出,解码器第一步的输出为`C'est`,而隐藏状态和单元状态为`[h'[1]; c'[1]]`。 如虚线所示,将其再次馈送到解码器,以生成下一个字以及下一组隐藏状态和单元状态。 由于解码器输出伪结束字符`[END]`,因此重复该过程。
W
wizardforcel 已提交
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597

为了进行推断,我们可以按原样使用网络的编码器部分,并进行一些修改以使解码器折叠以使其包含一个时间步。 概括地说,无论 RNN 是一个时间步长还是几个时间步长,与 RNN 相关的权重都不会改变,因为 RNN 的所有时间步长都共享相同的权重。

为了进行推断,我们可以看到训练模型的编码器部分用作函数`model_enc_dec`中的`encoder_model`。 类似地,使用相同的解码器 LSTM 定义单独的`decoder_model`,该解码器将输入作为隐藏状态,单元状态和输入字,并输出目标字以及更新的隐藏和单元状态。 为了清楚起见,再次重复定义了推断模型的函数`model_enc_dec` `encoder_model``decoder_model`

```py
def model_enc_dec(self):
        #Encoder Model
        encoder_inp = 
        Input(shape=(None,self.num_encoder_words),name='encoder_inp')
        encoder = LSTM(self.latent_dim, return_state=True,name='encoder')
        encoder_out,state_h, state_c = encoder(encoder_inp)
        encoder_states = [state_h, state_c]

        #Decoder Model
        decoder_inp = 
        Input(shape=(None,self.num_decoder_words),name='decoder_inp')
        decoder_lstm = 
        LSTM(self.latent_dim, return_sequences=True, 
        return_state=True,name='decoder_lstm')
        decoder_out, _, _ = 
        decoder_lstm(decoder_inp, initial_state=encoder_states)
        decoder_dense =
        Dense(self.num_decoder_words, 
        activation='softmax',name='decoder_dense')
        decoder_out = decoder_dense(decoder_out)
        print(np.shape(decoder_out))
        #Combined Encoder Decoder Model
        model  = Model([encoder_inp, decoder_inp], decoder_out)
        #Encoder Model 
        encoder_model = Model(encoder_inp,encoder_states)
        #Decoder Model
        decoder_inp_h = Input(shape=(self.latent_dim,))
        decoder_inp_c = Input(shape=(self.latent_dim,))
        decoder_input = Input(shape=(None,self.num_decoder_words,))
        decoder_inp_state = [decoder_inp_h,decoder_inp_c]
        decoder_out,decoder_out_h,decoder_out_c =   
        decoder_lstm(decoder_input,initial_state=decoder_inp_state)
        decoder_out = decoder_dense(decoder_out)
        decoder_out_state = [decoder_out_h,decoder_out_c]
        decoder_model = Model(inputs = 
        [decoder_input] + decoder_inp_state,output=
        [decoder_out]+ decoder_out_state)
        plot_model(model,to_file=self.outdir + 
                   'encoder_decoder_training_model.png')
        plot_model(encoder_model,to_file=self.outdir + 'encoder_model.png')
        plot_model(decoder_model,to_file=self.outdir + 'decoder_model.png')

        return model,encoder_model,decoder_model
```

解码器一次将运行一个时间步。 在第一种情况下,它将从编码器获取隐藏状态和单元状态,并根据伪单词`[START]`猜测翻译的第一个单词。 第一步中预测的单词,连同生成的隐藏状态和单元状态一起,再次馈送到解码器以预测第二个单词,然后继续进行处理,直到预测出由虚拟单词`[END]`表示的句子结尾。

W
wizardforcel 已提交
598
现在,我们已经定义了将源句子/文本翻译成目标语言对应物所需的所有功能,我们将它们组合起来以构建一个函数,该函数会生成翻译后的句子,给定源语言输入序列或句子:
W
wizardforcel 已提交
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696

```py
    def decode_sequence(self,input_seq,encoder_model,decoder_model):
        # Encode the input as state vectors.
        states_value = encoder_model.predict(input_seq)

        # Generate empty target sequence of length 1.
        target_seq = np.zeros((1, 1, self.num_decoder_words))
        # Populate the first character of target sequence 
          with the start character.
        target_seq[0, 0, self.target_word_index['\t']] = 1.

        # Sampling loop for a batch of sequences
        stop_condition = False
        decoded_sentence = ''

        while not stop_condition:
            output_word, h, c = decoder_model.predict(
                [target_seq] + states_value)

            # Sample a token
            sampled_word_index = np.argmax(output_word[0, -1, :])
            sampled_char = 
            self.reverse_target_word_dict[sampled_word_index]
            decoded_sentence = decoded_sentence + ' ' + sampled_char

            # Exit condition: either hit max length
            # or find stop character.
            if (sampled_char == '\n' or
            len(decoded_sentence) > self.max_decoder_seq_length):
                stop_condition = True

            # Update the target sequence (of length 1).
            target_seq = np.zeros((1, 1, self.num_decoder_words))
            target_seq[0, 0, sampled_word_index] = 1.

            # Update states
            states_value = [h, c]

        return decoded_sentence
```

训练模型后,我们就对保持数据集进行推断并检查翻译质量。 `inference`功能可以编码如下:

```py
    def inference(self,model,data,encoder_model,decoder_model,in_text):
        in_list,out_list = [],[]
        for seq_index in range(data.shape[0]):

            input_seq = data[seq_index: seq_index + 1]
            decoded_sentence =  
            self.decode_sequence(input_seq,encoder_model,decoder_model)
            print('-')
            print('Input sentence:', in_text[seq_index])
            print('Decoded sentence:',decoded_sentence)
            in_list.append(in_text[seq_index])
            out_list.append(decoded_sentence)
        return in_list,out_list

```

通过调用 Python 脚本`MachineTranslation.py`,可以在保持数据集上训练和验证机器翻译模型,如下所示:

```py
python MachineTranslation.py --path '/home/santanu/ML_DS_Catalog/Machine Translation/fra-eng/fra.txt' --epochs 20 --batch_size 32 -latent_dim 128 --num_samples 40000 --outdir '/home/santanu/ML_DS_Catalog/Machine Translation/' --verbose 1 --mode train
```

保留数据集中机器翻译模型表现出色的几个英语句子的翻译结果如下,以供参考:

```py
('Input sentence:', u'Go.')
('Decoded sentence:', u' Va ! \n')
('Input sentence:', u'Wait!')
('Decoded sentence:', u' Attendez ! \n')
('Input sentence:', u'Call me.')
('Decoded sentence:', u' Appelle-moi ! \n')
('Input sentence:', u'Drop it!')
('Decoded sentence:', u' Laisse tomber ! \n')
('Input sentence:', u'Be nice.')
('Decoded sentence:', u' Soyez gentil ! \n')
('Input sentence:', u'Be fair.')
('Decoded sentence:', u' Soyez juste ! \n')
('Input sentence:', u"I'm OK.")
('Decoded sentence:', u' Je vais bien. \n')
('Input sentence:', u'I try.')
('Decoded sentence:', u' Je vais essayer.')
```

但是,在某些情况下,机器翻译的效果不佳,如下所示:

```py
('Input sentence:', u'Attack!')
('Decoded sentence:', u' ma ! \n')

('Input sentence:', u'Get up.')
('Decoded sentence:', u' un ! \n')
```

W
wizardforcel 已提交
697
总之,先前说明的神经机器翻译实现将相对较短的英语句子翻译为法语的工作相当不错。 我要强调的一件事是使用单热编码矢量来表示每种语言中的输入单词。 由于我们使用的是相对较小的 40,000 个单词的语料库,因此词汇量是合理的,因此,我们能够分别使用大小为 8,658 和 16,297 的英语和法语词汇量的一键编码矢量。 随着语料库的增加,单热点编码词向量的大小将进一步增加。 比较两个单词时,这种稀疏的高维向量没有任何相似性概念,因为即使两个单词的含义几乎相同,它们的余弦乘积也将是`0`。 在下一节中,我们将了解如何以较小的维数进行单词矢量嵌入来解决此问题。
W
wizardforcel 已提交
698 699 700

# 词向量嵌入

W
wizardforcel 已提交
701
代替单热编码矢量,可以使用词矢量嵌入来表示维的密集空间中的单词,该空间比单热编码矢量低得多。 嵌入单词`w`的单词向量可以用`v[w] ∈ R^m`表示,其中`m`是词向量嵌入的维数。 如我们所见,虽然一个热编码矢量的每个分量只能占用{0,1}的二进制值,但词矢量嵌入的分量却可以占用任何实数,因此具有更密集的表示。 相似性和类比的概念也与词向量嵌入有关。
W
wizardforcel 已提交
702

W
wizardforcel 已提交
703
通常通过诸如连续词袋法,skip-gram,GloVe 等技术来训练词向量嵌入。 我们将不对它们的实现进行过多的介绍,但中心思想是以这样的方式定义词向量嵌入:将类似的词紧密放置在`m`维欧几里得空间中:
W
wizardforcel 已提交
704 705 706 707 708

![](img/9f7522e1-949b-4004-b21d-4f95010db169.png)

图 3.7:GloVe 嵌入的相似性和类比说明

W
wizardforcel 已提交
709
在上一张图中,我们绘制了**男人****女人****国王****女王**的 GloVe 词向量嵌入的二维 TSNE 视图 。 我们可以看到,**男人****女人**具有内在的相似性,**国王****女王**的情况也是如此。 此外,我们看到**国王****男人**之间的向量差异与**女王****女人**的向量差异几乎相同,这可能代表王权的一些概念。 如我们所见,除了表达单词之间的相似性之外,还可以通过单词向量嵌入来表示类似*男人:国王:女人:女王*之类的东西。 在下一节中,我们将讨论使用 RNN 中的嵌入层将输入单词表示为单词矢量嵌入,而不是单编码的矢量。
W
wizardforcel 已提交
710 711 712

# 嵌入层

W
wizardforcel 已提交
713
嵌入层将输入单词的索引作为输入,并提供单词的单词向量嵌入作为输出。 嵌入层的尺寸为`R^(dxV)`,其中`d`是词向量嵌入的尺寸,`V`是词汇的尺寸。 嵌入层可以根据问题了解嵌入本身,也可以提供预训练的嵌入层。 在我们的案例中,我们将让神经机器翻译找出对于源语言和目标语言而言,嵌入向量应该是什么,以实现良好的翻译。 结果,我们定义的每个函数都应更改以适应嵌入层。
W
wizardforcel 已提交
714

W
wizardforcel 已提交
715
# 实现基于嵌入的 NMT
W
wizardforcel 已提交
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926

我们将需要对现有功能进行一些更改,以适应嵌入层。 首先,`process_input`将处理输入以在不同的时间步长中具有单词索引,而不是单热编码矢量,如下所示:

```py
      def process_input(self,input_texts,target_texts=None,verbose=True):

        encoder_input_data = np.zeros(
            (len(input_texts), self.max_encoder_seq_length),
            dtype='float32')

        decoder_input_data = np.zeros(
            (len(input_texts), self.max_decoder_seq_length),
            dtype='float32')

        decoder_target_data = np.zeros(
            (len(input_texts), self.max_decoder_seq_length,1),
            dtype='float32')

        if self.mode == 'train':
            for i, (input_text, target_text) in 
                    enumerate(zip(input_texts,target_texts)):
                for t, word in enumerate(input_text.split(" ")):
                    try:
                        encoder_input_data[i, t] = 
                        self.input_word_index[word]
                    except:
                        encoder_input_data[i, t] = 
                        self.num_encoder_words

                for t, word in enumerate(target_text.split(" ")):
                # decoder_target_data is ahead of decoder_input_data
                  by one timestep
                    try:
                        decoder_input_data[i, t] = 
                        self.target_word_index[word]
                    except:
                        decoder_input_data[i, t] = 
                        self.num_decoder_words 
                    if t > 0:
                    # decoder_target_data will be ahead by one timestep
                    #and will not include the start character.
                        try:
                            decoder_target_data[i, t - 1] = 
                            self.target_word_index[word]
                        except:
                            decoder_target_data[i, t - 1] = 
                            self.num_decoder_words 
            print(self.num_encoder_words)
            print(self.num_decoder_words)
            print(self.embedding_dim)
            self.english_emb = np.zeros((self.num_encoder_words + 1,
                                        self.embedding_dim))
            self.french_emb = np.zeros((self.num_decoder_words + 1,
                                        self.embedding_dim))
            return encoder_input_data,decoder_input_data,decoder_target_data,np.array(input_texts),
np.array(target_texts)
        else:
            for i, input_text in enumerate(input_texts):
                for t, word in enumerate(input_text.split(" ")):
                    try:
                        encoder_input_data[i, t] = self.input_word_index[word]

```

与以前的`process_input`函数相比,唯一的变化是,我们不再用单热编码矢量表示单词,而是用单词的索引表示。 另外,您是否注意到我们为词汇表中不存在的单词添加了额外的单词索引? 理想情况下,这不是为了训练数据而发生的,但是在测试过程中,可能会出现一个不在词汇表中的全新单词。

以下是来自输入处理的统计信息:

```py
Number of samples: 40000
Number of unique input tokens: 8658
Number of unique output tokens: 16297
Max sequence length for inputs: 7
Max sequence length for outputs: 16
('Shape of Source Input Tensor:', (40000, 7))
('Shape of Target Input Tensor:', (40000, 16))
('Shape of Target Output Tensor:', (40000, 16, 1))
```

如我们所见,源和目标输入张量现在具有`7``16`时间步长,但是没有一键编码矢量的维数。 每个时间步长都包含单词的索引。

下一个变化是关于编码器和解码器网络,以在 LSTM 层之前容纳嵌入层:

```py
    def model_enc_dec(self):
        #Encoder Model
        encoder_inp = Input(shape=(None,),name='encoder_inp')
        encoder_inp1 = 
        Embedding(self.num_encoder_words + 1,
                  self.embedding_dim,weights=[self.english_emb])
                  (encoder_inp)
        encoder = LSTM(self.latent_dim, return_state=True,name='encoder')
        encoder_out,state_h, state_c = encoder(encoder_inp1)
        encoder_states = [state_h, state_c]

        #Decoder Model
        decoder_inp = Input(shape=(None,),name='decoder_inp')
        decoder_inp1 = 
        Embedding(self.num_decoder_words+1,self.embedding_dim,weights=   
                  [self.french_emb])(decoder_inp)
        decoder_lstm = 
        LSTM(self.latent_dim, return_sequences=True, 
              return_state=True,name='decoder_lstm')
        decoder_out, _, _ = 
        decoder_lstm(decoder_inp1,initial_state=encoder_states)
        decoder_dense = Dense(self.num_decoder_words+1, 
                        activation='softmax',name='decoder_dense')
        decoder_out = decoder_dense(decoder_out)
        print(np.shape(decoder_out))
        #Combined Encoder Decoder Model
        model = Model([encoder_inp, decoder_inp], decoder_out)
        #Encoder Model 
        encoder_model = Model(encoder_inp,encoder_states)
        #Decoder Model
        decoder_inp_h = Input(shape=(self.latent_dim,))
        decoder_inp_c = Input(shape=(self.latent_dim,))
        decoder_inp_state = [decoder_inp_h,decoder_inp_c]
        decoder_out,decoder_out_h,decoder_out_c = 
        decoder_lstm(decoder_inp1,initial_state=decoder_inp_state)
        decoder_out = decoder_dense(decoder_out)
        decoder_out_state = [decoder_out_h,decoder_out_c]
        decoder_model = Model(inputs = 
                        [decoder_inp] + decoder_inp_state,output=
                        [decoder_out]+ decoder_out_state)

        return model,encoder_model,decoder_model
```

训练模型需要使用`sparse_categorical_crossentropy`进行编译,因为输出目标标签表示为索引,而不是一键编码的单词向量:

```py
    def train(self,encoder_input_data,decoder_input_data,
              decoder_target_data):
        print("Training...")

        model,encoder_model,decoder_model = self.model_enc_dec()

        model.compile(optimizer='rmsprop', 
                      loss='sparse_categorical_crossentropy')
        model.fit([encoder_input_data, decoder_input_data],
                  decoder_target_data,
                batch_size=self.batch_size,
                epochs=self.epochs,
                validation_split=0.2)
        # Save model
        model.save(self.outdir + 'eng_2_french_dumm.h5')
        return model,encoder_model,decoder_model
```

接下来,我们需要对与推理相关的功能进行修改,以适应与嵌入相关的更改。 现在,用于推断的`encoder_model``decoder_model`分别将嵌入层用于英语和法语词汇。

最后,我们可以使用`decoder_model``encoder_model`如下创建序列生成器函数:

```py
    def decode_sequence(self,input_seq,encoder_model,decoder_model):
        # Encode the input as state vectors.
        states_value = encoder_model.predict(input_seq)

        # Generate empty target sequence of length 1.
        target_seq = np.zeros((1, 1))
        # Populate the first character of target sequence
          with the start character.
        target_seq[0, 0] = self.target_word_index['\t']

        # Sampling loop for a batch of sequences
        stop_condition = False
        decoded_sentence = ''

        while not stop_condition:
            output_word, h, c = decoder_model.predict(
                [target_seq] + states_value)

            # Sample a token
            sampled_word_index = np.argmax(output_word[0, -1, :])
            try:
                sampled_char = 
                self.reverse_target_word_dict[sampled_word_index]
            except:
                sampled_char = '<unknown>'
            decoded_sentence = decoded_sentence + ' ' + sampled_char

            # Exit condition: either hit max length
            # or find stop character.
            if (sampled_char == '\n' or
            len(decoded_sentence) > self.max_decoder_seq_length):
                stop_condition = True

            # Update the target sequence (of length 1).
            target_seq = np.zeros((1, 1))
            target_seq[0, 0] = sampled_word_index

            # Update states
            states_value = [h, c]

        return decoded_sentence
```

可以通过如下运行脚本来调用模型的训练:

```py
python MachineTranslation_word2vec.py --path '/home/santanu/ML_DS_Catalog-/Machine Translation/fra-eng/fra.txt' --epochs 20 --batch_size 32 --latent_dim 128 --num_samples 40000 --outdir '/home/santanu/ML_DS_Catalog-/Machine Translation/' --verbose 1 --mode train --embedding_dim 128 
```

该模型在 GeForce GTX 1070 GPU 上进行了训练,大约需要 9.434 分钟才能训练 32,000 条记录并进行 8,000 条记录的推理。 强烈建议用户使用 GPU,因为 RNN 的计算量很大,可能需要数小时才能在 CPU 上训练相同的模型。

我们可以通过运行 python 脚本`MachineTranslation.py`来训练机器翻译模型并在保持数据集上执行验证,如下所示:

```py
python MachineTranslation.py --path '/home/santanu/ML_DS_Catalog/Machine Translation/fra-eng/fra.txt' --epochs 20 --batch_size 32 -latent_dim 128 --num_samples 40000 --outdir '/home/santanu/ML_DS_Catalog/Machine Translation/' --verbose 1 --mode train
```

W
wizardforcel 已提交
927
从嵌入向量方法获得的结果与单热编码词向量的结果相似。 这里提供了一些来自保持数据集推理的翻译:
W
wizardforcel 已提交
928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965

```py
Input sentence: Where is my book?
Decoded sentence:   est mon Tom ?
-
Input sentence: He's a southpaw.
Decoded sentence:  Il est en train de
-
Input sentence: He's a very nice boy.
Decoded sentence:  C'est un très bon
-
Input sentence: We'll be working.
Decoded sentence:  Nous pouvons faire
-
Input sentence: May I have a program?
Decoded sentence:  Puis-je une ? 

-
Input sentence: Can you make it safe?
Decoded sentence:  Peux-tu le faire
-
Input sentence: We walked to my room.
Decoded sentence:  Nous avons devons
-
Input sentence: Don't stand too close.
Decoded sentence:  Ne vous en prie.
-
Input sentence: Where's the dog?
Decoded sentence:   est le chien ?
-
Input sentence: He's a hopeless case.
Decoded sentence:  Il est un fait de
-
Input sentence: Where were we?
Decoded sentence:  Où fut ? 

```

W
wizardforcel 已提交
966
# 总结
W
wizardforcel 已提交
967 968 969

读者现在应该对几种机器翻译方法以及神经翻译机器与传统机器有何不同有很好的理解。 现在,我们还应该深入了解如何从头开始构建神经机器翻译系统,以及如何以有趣的方式扩展该系统。 借助提供的信息和实现演示,建议读者探索其他并行语料库数据集。

W
wizardforcel 已提交
970
在本章中,我们定义了嵌入层,但未使用预训练的嵌入(例如 GloVe,FastText 等)来加载它们。 建议读者使用预训练的词向量嵌入为嵌入层加载,并查看是否会产生更好的结果。 在第 4 章,“使用 GAN 进行时装行业中的样式迁移”中,我们将通过与生成性对抗网络(这是现代的革命)进行与时装业中样式迁移有关的项目。 人工智能领域。