神经机器翻译(seq2seq RNN)实现详解

http://c.biancheng.net/view/1947.htmlhtml

seq2seq 是一类特殊的 RNN,在机器翻译、文本自动摘要和语音识别中有着成功的应用。本节中,咱们将讨论如何实现神经机器翻译,获得相似于谷歌神经机器翻译系统获得的结果(https://research.googleblog.com/2016/09/a-neural-network-for-machine.html)。

关键是输入一个完整的文本序列,理解整个语义,而后输出翻译结果做为另外一个序列。阅读整个序列的想法与之前的架构大相径庭,在该架构中,一组固定词汇从一种源语言翻译成目标语言。

本节受到 Minh-Thang Luong 于 2016 年所写的博士论文“Neural Machine Translation”的启发。第一个关键概念是编码器–解码器架构,其中编码器将源语句转换为表示语义的向量,而后这个向量经过解码器产生翻译结果。

编码器和解码器都是 RNN,它们能够捕捉语言中的长距离依赖关系,例如性别一致性和语法结构,而没必要事先知道它们,也不须要跨语言进行 1:1 映射。它可以流利地翻译而且具备强大的功能。python



图 1 编码器–解码器示例


来看一个 RNN 例子:将 She loves cute cats 翻译成 Elle aime les chats mignons。有两个 RNN:一个充当编码器,一个充当解码器。源语句 She loves cute cats 后面跟着一个分隔符“-”和目标语句 Elle aime les chats mignon。这两个关联语句被输入给编码器用于训练,而且解码器将产生目标语句 Elle aime les chats mignon。固然,须要大量相似例子来得到良好的训练。git



图 2 NMT的序列模型示例

NUM序列模型,一个深度循环结构示例,用于将源语句“She loves cute cats” 翻译成目标语句“Elle aimel les chats mignons”。解码器侧,前面时序中产生的单词做为输出下一个单词的输入,“_”表明语句的结束。github

如今有一些可使用的 RNN 变体,具体介绍其中的一些:算法

  • RNN 能够是单向的或双向的,后者将捕捉双向的长时间依赖关系。
  • RNN 能够有多个隐藏层,层数的选择对于优化来讲相当重要...更深的网络能够学到更多知识,另外一方面,训练须要花费很长时间并且可能会过分拟合。
  • RNN 能够有多个隐藏层,层数的选择对于优化来讲相当重要...更深的网络能够学到更多知识,另外一方面,训练须要花费很长时间并且可能会过分拟合。
  • RNN 能够具备嵌入层,其将单词映射到嵌入空间中,在嵌入空间中类似单词的映射刚好也很是接近。
  • RNN 可使用简单的重复性单元、LSTM、窥孔 LSTM 或者 GRU。


仍然考虑博士论文“Neural Machine Translation”,可使用嵌入层将输入的句子映射到一个嵌入空间。而后,存在两个链接在一块儿的 RNN——源语言的编码器和目标语言的解码器。以下图所示,有多个隐藏层和两个流动方向:前馈垂直方向链接隐藏层,水平方向是将知识从上一步转移到下一步的循环部分。api



图 3 神经机器翻译示例


本节使用 NMT(Neural Machine Translation,神经机器翻译),这是一个在 TensorFlow 上在线可得的翻译演示包。网络

NMT 可经过https://github.com/tensorflow/nmt/ 获取,具体代码可经过 GitHub 获取。架构

具体实现过程

  1. 从 GitHub 克隆 NMT:


     
  2. 下载一个训练数据集。在这个例子中,使用训练集将越南语翻译成英语,其余数据集能够在https://nlp.stanford.edu/projects/nmt/上得到,如德语和捷克语:


     
  3. 参考https://github.com/tensorflow/nmt/,这里将定义第一个嵌入层,嵌入层将输入、词汇量尺寸 V 和指望的输出尺寸嵌入到空间中。词汇量尺寸 V 中只有最频繁的单词才考虑被嵌入,全部其余单词则被打上 unknown 标签。在本例中,输入是 time-major,这意味着 max time 是第一个输入参数(https://www.tensorflow.org/api_docs/python/tf/nn/dynamic_rnn):


     
  4. 仍然参考 https://github.com/tensorflow/nmt/,这里定义一个简单的编码器,它使用 tf.nn.rnn_cell.BasicLSTMCell(num_units) 做为基本的 RNN 单元。虽然很简单,但要注意给定基本 RNN 单元,咱们利用 tf.nn.dynamic_rnn 构建了 RNN 的(见https://www.tensorflow.org/api_docs/python/tf/nn/dynamic_rnn):


     
  5. 定义解码器。首先要有一个基本的 RNN 单元:tf.nn.rnn_cell.BasicLSTMCell,以此来建立一个基本的采样解码器 tf.contrib.seq2seq.BasicDecoder,将结果输入到解码器 tf.contrib.seq2seq.dynamic_decode 中进行动态解码。


     
  6. 网络的最后一个阶段是 softmax dense 阶段,将最高隐藏状态转换为 logit 向量:


     
  7. 定义在训练阶段使用的交叉熵函数和损失:


     
  8. 定义反向传播所需的步骤,并使用适当的优化器(本例中使用 Adam)。请注意,梯度已被剪裁,Adam 使用预约义的学习率:


     
  9. 运行代码并理解不一样的执行步骤。首先,建立训练图,而后开始迭代训练。评价指标是 BLEU(bilingual evaluation understudy),这个指标是评估将一种天然语言机器翻译为另外一种天然语言的文本质量的标准,质量被认为是算法的结果和人工操做结果的一致性。正如你所看到的,指标值随着时间而增加:

解读分析

全部上述代码已经在 https://github.com/tensorflow/nmt/blob/master/nmt/model.py 上给出。关键是将两个 RNN 打包在一块儿,第一个是嵌入空间的编码器,将类似的单词映射得很接近,编码器理解训练样例的语义,并产生一个张量做为输出。而后经过将编码器的最后一个隐藏层链接到解码器的初始层能够简单地将该张量传递给解码器。函数

请注意,学习可以进行是由于损失函数基于交叉熵,且labels=decoder_outputs。学习

以下图所示,代码学习如何翻译,并经过BLEU指标的迭代跟踪进度:


下面咱们将源语言翻译成目标语言。这个想法很是简单:一个源语句做为两个组合的 RNN(编码器+解码器)的输入。一旦句子结束,解码器将发出 logit 值,采用贪婪策略输出与最大值相关的单词。

例如,单词 moi 做为来自解码器的第一个标记被输出,由于这个单词具备最大的 logit 值。此后,单词 suis 输出,等等:

图 5 带几率分布的NMT序列模型示例


解码器的输出有多种策略:

  1. 贪婪:输出对应最大logit值的单词。
  2. 采样:经过对众多logit值采样输出单词。
  3. 集束搜索:有多个预测,所以建立一个可能结果的扩展树。

翻译实现过程

  1. 制定解码器采样的贪婪策略。这很简单,由于可使用 tf.contrib.seq2seq.GreedyEmbeddingHelper 中定义的库,因为不知道目标句子的准确长度,所以这里使用启发式方法将其限制为源语句长度的两倍。


     
  2. 如今能够运行网络,输入一个从未见过的语句(inference_input_file=/tmp/my_infer_file),并让网络转换结果(inference_output_file=/tmp/nmt_model/output_infer):


两个 RNN 封装在一块儿造成编码器–解码器 RNN 网络。解码器发出的 logits 被贪婪策略转换成目标语言的单词。做为一个例子,下面显示了一个从越南语到英语的自动翻译的例子:

相关文章
相关标签/搜索