您好、欢迎来到现金彩票网!
当前位置:秒速快3平台 > 双向推理 >

谷歌神经网络机器翻译NMT:人人可利用TensorFlow快速建立翻译模

发布时间:2019-06-07 06:53 来源:未知 编辑:admin

  机器翻译作为自动翻译语言之间的任务,是机器学习社区中最活跃的研究领域之一。在机器翻译的众多方法中,序列到序列(“seq2seq”)模型最近取得了巨大的成功,并已成为大多数商业翻译系统中的标准。然而,虽然seq2seq模型(如OpenNMT或tf-seq2seq)上有大量的材料,但是缺乏教学人员知识和技能的材料,可以轻松构建高质量的翻译系统。

  近日,TensorFlow在GitHub上宣布一个新的神经机器翻译(NMT)教程,让读者能够充分了解seq2seq模型,并展示如何从零开始构建翻译模型

  序列到序列(seq2seq)模型在诸如机器翻译、语音识别和文本概括等各项任务中,取得了巨大的成功。本教程为读者提供了对seq2seq模型的全面介绍,并展示了如何从头构建一个seq2seq模型。我们专注于神经机器翻译(NMT)的任务,这是第一个成功的seq2seq模型的测试平台。包含的代码是轻量级的、高质量的、生产就绪的,并与最新的研究思想结合在一起。我们通过以下方式实现此目标:

  使用最新的解码器/注意力包装器API,TensorFlow 1.2数据迭代器;

  提供提示和技巧,以构建最好的NMT模型,并复制Google的NMT(GNMT)系统;

  我们认为重要的是,提供人们可以轻松复制的基准。因此,我们提供了完整的实验结果,并对以下公开数据集的模型进行了预先训练:

  大规模:WMT评估组织提供的德语到英语的平行语料库(450万个句子对);

  我们首先介绍关于NMT的seq2seq模型的一些基本知识,说明如何构建并训练vanilla NMT模型。第二部分将详细介绍建立一个高效的NMT模式的注意力机制。然后,我们将讨论提示和技巧,以构建最佳的NMT模型(包括速度和翻译质量),例如TensorFlow最佳实践(批处理、降级),双向RNN和集束搜索。

  回到过去,传统的基于短语的翻译系统将源语句分解成多个组,然后逐句翻译。这导致翻译产品不一致性,而且翻译的水平跟人类相比差异很大。人类通读整个源句,理解它的含义,然后再翻译。而神经机器翻译(NMT)正是这么模拟的!

  图1.编码器-解码器架构。NMT一般方法的示例。编码器将源句子转换成通过解码器传递以生成翻译的“含义”向量。

  具体来说,首先,NMT系统使用编码器,读取源语句,以构建“思想”向量,表示句子意义的数字序列;然后,解码器处理句子向量,以发出翻译,如图1所示。这通常被称为编码器-解码器架构。以这种方式,NMT解决了传统的、基于短语的方法中的本地翻译问题:它可以捕获语言的长期依赖性,例如语法结构等等,并产生更流畅的翻译。

  NMT模型因具体结构而有所不同。顺序数据的自然选择是大多数NMT模型使用的循环神经网络(RNN)。通常,RNN用于编码器和解码器。然而,RNN模型在以下方面会不同:(a)方向性——单向或双向; (b)深度——单层或多层;(c)类型——通常是vanilla RNN、长短期记忆网络(LSTM)或门控循环单元(GRU)。有兴趣的读者可以在这篇博文上找到有关RNN和LSTM的更多信息。

  在本教程中,我们将一个深度多层RNN视为单向,并将LSTM作为循环单元。我们在图2中展示了一个模型的例子。在这个例子中,我们建立一个模型,将源句子“我是一个学生”翻译成一个目标句子“Je suisétudiant”。NMT模型由两个循环神经网络组成:编码器RNN简单地处理输入源句子,而不进行任何预测;另一方面,解码器在预测下一个单词的同时,处理目标句子。

  图2.神经机器翻译将源语句“我是一名学生”翻译成一个目标句子“Je suisétudiant”,展示一个深度循环架构。这里,“s”表示解码处理的开始,而“/ s”告诉解码器停止。

  要安装本教程,你需要在系统上安装TensorFlow。本教程需要安装最新版本的TensorFlow(版本1.2.1)。要安装TensorFlow,请按照安装说明进行操作。一旦安装了TensorFlow,你便可以通过运行以下方式,下载本教程的源代码:

  我们先来看看,构建一个具体代码片段的NMT模型的核心,我们将更详细的解释图2。数据准备和完整的代码将稍后介绍。这部分可参考文件model.py。

  在底层,编码器和解码器RNN作为输入接收以下内容:首先,源语句,然后指示从编码到解码模式的转换的边界标记“s”和目标句子。对于训练,我们将为系统提供以下张量,包含单词索引:

  为了提高效率,我们一次训练多个句子(batch_size)。测试略有不同,所以我们稍后再讨论一下。

  考虑到词语的分类性质,模型必须首先查找源和目标降维,以检索相应的词表示。为了使降维层可以工作,首先要为每种语言选择一个词汇表。通常,选择词汇大小V,并且只有最频繁的V被视为唯一的。所有其他单词都转换为“未知”令牌,并且都获得相同的降维。通常,降维权重在训练期间学习,每种语言一套。

  同样,我们可以构建embedding_decoder和decode_emb_inp。请注意,可以选择使用预训练的单词表示(如word2vec或Glove向量)初始化降维权重。一般来说,考虑到大量的训练数据,我们可以用scratch来学习这些降维。

  一旦检索到,则将词语降维作为输入馈送到主网络中,该主网络由两个多层RNN组成——源语言的编码器以及用于目标语言的解码器。这两个RNN原则上可以共享相同的权重; 然而,在实践中,我们经常使用两种不同的RNN参数(这些模型在拟合大型训练数据集时做得更好)。编码器RNN使用零向量作为其起始状态,建立过程如下:

  请注意,句子具有不同的长度,以避免浪费计算,我们通过source_seqence_length告诉dynamic_rnn确切的源句长度。由于我们的输入是基于时间的,我们设置time_major = True。 在这里,我们只构建一个单层LSTM,encoder_cell。我们将介绍如何构建多层LSTM,添加dropout,并在后面的部分中使用注意力。

  解码器还需要访问源信息,一个简单的方法就是用编码器的最后一个隐藏状态(encode_state)来初始化它。在图2中,我们将源代码“学生”的隐藏状态传递到解码器端。

  最后,我们还没有提到projection_layer,它是一个密集矩阵,将顶部隐藏状态转换为维度V的对数向量。我们在图2的最上面说明这一过程。

  这里,target_weights是与decode_outputs相同大小的零矩阵。它掩盖了值为0的目标序列长度之外的填充位置。

  重要的注意事项:值得指出的是,我们用batch_size来划分损失,所以我们的超参数对batch_size是“不变的”。有些人将损失除以(batch_size * num_time_steps),它可以减少短句所造成的错误。更巧妙的是,我们的超参数(以前的方式应用)不能用于后一种方式。例如,如果两种方法都使用学习1.0的SGD(随机梯度下降),则后一种方法有效地使用了更小的1 / num_time_steps的学习速率。

  我们现在已经定义了我们的NMT的前进模式。计算反向传播传递只是几行代码的问题:

  训练RNN的重要步骤之一是梯度剪切。在这里,我们按照规范剪辑。最大值max_gradient_norm通常设置为5或1的值。最后一步是选择优化器。Adam优化器是常见的选择,我们也选择学习率,学习率的值通常在0.0001到0.001之间;随着训练的进行,可以减少。

  在我们自己的实验中,我们使用标准SGD(tf.train.GradientDescentOptimizer),具有降低的学习率调度,从而产生更好的性能。 参见基准。

  我们训练我们的第一个NMT模型,从越南语翻译成英语! 我们的代码的入口是nmt.py。

  我们将使用一个小型平行语言的TED谈线个训练示例)来进行此练习。我们在这里使用的所有数据都可以在以下网址找到:。我们将使用tst2012作为我们的dev数据集,tst2013作为测试数据集。

  上述命令在12个周期内,训练了一个具有128个隐藏单元和降维的2层LSTM seq2seq模型。 我们使用的dropout值为0.2(保持概率为0.8)。如果没有错误的话,我们应该在我们训练时看到类似于下面的日志。

  当你训练你的NMT模型(一旦你拥有训练模型),你可以获得以前不可见的源语句的翻译。而这个过程就称为推理。训练和推理(测试)之间有明确的区别:在推理时,我们只能访问源语句,即编码器输入,而执行解码的方法有很多种。解码方法包括贪婪算法、采样和波束搜索解码。在这里,我们将讨论贪婪解码策略。

  我们依然使用与训练期间相同的方式对源语句进行编码从而获得encoder_state,并使用该encoder_state来初始化解码器。

  一旦解码器接收到起始符号s (参见我们代码中的tgt_sos_id),解码(翻译)过程就开始了。

  对于解码器端的每个时间步长,我们将RNN的输出视为一组逻辑。我们选择最可能的单词,id与最大逻辑值相关联,将其作为发出的单词(这就是“贪婪”行为)。例如在图3中,在第一解码步骤中,单词“moi”具有最高的翻译概率。然后,我们将这个词作为输入提供给下一个时间步长。

  该过程将一直继续,直到生成句尾标记/s 作为输出符号产生(参见我们代码中的tgt_eos_id)。

  图3.贪婪解码——训练好的NMT模型如何使用贪婪搜索生成源语句“Je suisétudiant”的翻译。

  第3步,是什么使推理与训练如此不同。推理使用的是模型预测的单词,而并非总是将正确的目标单词作为输入,以下是实现贪婪解码的代码。它与训练解码器非常相似。

  在这里,我们使用的是GreedyEmbeddingHelper而不是TrainingHelper。由于我们预先不知道目标序列长度,所以我们使用maximum_iterations来限制翻译的长度。思路是将源句长度的两倍作为解码最大长度。

  注意,即使模型仍在训练中,但只要存在训练检查点,就可以运行上述命令。有关详细信息,请参阅inference.py。

  既然已经知晓最基本的seq2seq模型,那就来进一步完善!为了建立最先进的神经机器翻译系统,我们将需要更多的“秘密武器”:注意力机制,这是Bahdanau等人于2015年首次提出的,然后由Luong等人于2015年完善。注意力机制的关键在于,在翻译过程中通过对相关源文件内容进行“注意”,从而建立起目标文件与源文件之间的直接连接。注意力机制的一个很好的附加作用就是源句和目标句之间有个可便捷查看的对齐矩阵(如图4所示)。

  图4.注意力可视化——源句和目标句之间的对齐示例。图像摘自(Bahdanau 等人在2015年发表的论文)。

  请记住,在vanilla seq2seq模型中,当开始解码过程时,我们将最后的源状态从编码器传递到解码器中。这对于较短和中等长度的句子来说效果很好;但是,对于长句,单个固定大小的隐藏状态就成了信息瓶颈。注意力机制不是放弃在源RNN中计算的所有隐藏状态,而是提供了允许解码器窥视它们的方法(将它们视为源信息的动态存储器)。通过这样做,注意力机制改进了较长句子的翻译效果。现如今,注意力机制是一种流行的标准,并已经成功应用于许多其他任务中(包括图像字幕生成,语音识别和文本自动摘要)。

  我们现在在描述Luong等人在2015提出的注意力机制的一个实例,它已被用于包括像OpenNMT这样的开源工具包在内的多个最先进的系统,以及本教程中的TF seq2seq API中。我们还将提供与注意力机制其他变体的连接。

  图5.注意力机制——(Luong等人于2015所著论文)中描述的基于注意力的NMT系统的示例。我们详细介绍了注意力计算的第一步。为了清晰起见,我们不在图(2)中显示降维和投影层。

  将当前目标隐藏状态与所有源状态进行比较,以获得注意力权重(如图4所示)。

  注意向量作为输入馈送到下一个时间步长(输入馈送)。前三个步骤可以通过以下等式来总结:

  在这里,函数score用于将目标隐藏状态$$h_t$$ 与每个源隐藏状态$$overline{h}_s$$进行比较,并将结果归一化以产生注意权重(源位置的分布)。评分函数有多种选择;通用的评分函数包括方程式中(4)给出的乘法和加法形式。一旦计算,注意力矢量 $$a_t$$ 用于导出softmax logit和loss。这类似于vanilla seq2seq模型中顶层的目标隐藏状态。函数f也可以采取其他形式。

  如上述方程式所示,有许多不同的注意事项。这些变体取决于评分函数和注意力函数的形式,以及在评分函数中是否使用的是先前的状态$$h_{t-1}$$, 而不是Bahdanau等人在2015年提出的的 $$h_t$$。根据经验来说,我们发现只有某些选择是很重要的。首先,注意力的基本形式,即目标和来源之间的直接关系。其次,将注意力向量向下馈送到下一个时间步长,以便通知网络关于过去的注意决定,正如Luong等人在2015年论文中所演示的那样。最后,评分函数的选择往往会导致不同的表现。参见基准测试结果部分。

  在执行注意力包装器时,我们借鉴了(本论文)在内存网络方面的一些术语。本教程中介绍的注意力机制是只读内存,而不是具有可读写的内存。具体来说,引用一组源隐藏状态(或其转换版本,例如,Luong评分方式中的$$Woverline{h}_s$$或者Bahong评分方式中的 $$W_2overline{h}_s$$)来作为“记忆”。在每个时间步长中,我们使用当前目标隐藏状态作为“查询”来决定要读取内存的哪个部分。通常,查询需要与对应于各个内存插槽的keys进行比较。在上述注意力机制的介绍中,我们恰好将源隐藏状态(或其转换版本,例如Bahdanau评分风格中的$$W_1h_t$$ )用作“keys”。这是一个可以从这种记忆网络术语中得到启发,以获得其他形式的注意的机制!

  得益于注意力包装器,延长我们的vanilla seq2seq代码的注意力就变得微不足道了。这部分可参考文件attention_model.py

  首先,我们需要定义注意力机制,例如(Luong等人在2015所著论文中那样):

  在以前的编码器部分中,encoder_outputs是顶层所有源隐藏状态的集合,其形状为[max_time,batch_size,num_units](因为我们将dynamic_rnn与time_major设置为True以达到高效的效果)。对于注意力机制来说,我们需要确保传递的“记忆”是批处理的,所以我们需要转置attention_states。我们将source_sequence_length传递给注意力机制,以确保注意权重正确归一化(仅在非填充位置上)。

  定义了注意力机制后,我们使用AttentionWrapper来包装解码单元格:

  为了确保能够维持注意力,我们需要使用luong, scaled_luong, bahdanau或 normed_bahdanau中的一个作为训练期间的attention标志的值。该标志指定了我们将要使用的注意力机制。此外,我们需要为注意力模型创建一个新的目录,所以我们不用重复使用以前训练过的基本NMT模型。

  训练后,我们可以使用相同的具有新的model_dir的推理命令,进行推理:

  从占位符读取输入数据(数据可以通过feed_dict或C ++ TensorFlow服务二进制文档直接提供给图表)。

  包括模型正向操作的一个子集,以及可能的用于存储session.run调用之间状态的附加特殊输入/输出。

  可变重用简单得多。例如,在评估图中,不需要重新打开具有reuse = True的可变范围,因为训练模型已经创建了这些变量。因此,相同的代码可以重用,而无需在任何地方都使用reuse = arguments。

  在分布式训练中,工作人员分别进行训练,评估和推理是很平常的。这些都需要建立自己的图形。因此,以这种方式构建系统将为你进行分布式训练做好准备。

  复杂性的主要来源在于如何在单个机器设置中的三个图表中共享变量。这通过在每个图形中使用单独的会话来解决。训练会话定期保存检查点,并且评估会话,并推断会话会从检查点中恢复参数。下面的例子显示了两种方法的主要区别。

  新方法的另一个区别在于,我们使用有状态的迭代器对象,而不是使用feed_dicts来在每个session.run调用(从而执行我们自己的批处理、降级和操作数据)中提供数据。这些迭代器使输入流水线在单机和分布式设置中都容易得多。我们将在下一节中介绍新的输入数据流水线(在TensorFlow 1.2中介绍)。

  在TensorFlow 1.2之前,用户有两种方式将数据提供给TensorFlow训练和评估流水线:

  第一种方法对于不熟悉TensorFlow的用户来说更容易,或者只需要在Python中完成异步输入修改(即自己的小型排队)即可。第二和第三种方法更加标准,但灵活性稍差一些,他们还需要启动多个python线程(队列运行器)。此外,如果使用不正确的队列可能会导致死锁或不透明的错误消息。然而,队列比使用feed_dict更有效,并且是单机和分布式训练的标准。

  从TensorFlow 1.2开始,有一个新的系统可用于将数据读入TensorFlow模型:数据集迭代器,如tf.contrib.data模块中所述。数据迭代器是灵活的,易于理解和操纵,并通过利用TensorFlow C ++运行时提高效率和多线程。

  可以从批量数据Tensor,文件名或包含多个文件名的Tensor创建数据集。例如:

  所有数据集可以通过输入过程进行相似的处理。这包括读取和清理数据、降级(在训练和评估的情况下)、过滤和批处理。

  最后,我们可以对每个句子执行词汇查找。给定一个查找表对象表,该映射将第一个元组元素从字符串向量转换为整数向量。

  连接两个数据集也很容易。如果两个文件包含彼此的逐行翻译,并将每个文件读入其自身的数据集,则可以通过以下方式创建一个包含压缩行元组的新数据集:

  可变长度句子的分批是很直接明确的。以下转换从source_target_dataset批处理batch_size元素,并将源和目标向量分别贴到每个批次中最长源和目标向量的长度。

  从此数据集发出的值将是嵌套元组,其张量具有最大尺寸为batch_size的尺寸。 结构将是:

  最后,也可以将类似大小的源句子批量化在一起。有关详细信息和完整实现,请参阅utils / iterator_utils.py文件。

  一旦迭代器被初始化,访问源或目标张量的每个session.run调用将从基础数据集请求下一个小型数据。

  编码器侧的双向性通常会提供更好的性能(随着使用更多的层,速度有一些降低)。 在这里,我们给出一个简单的例子,说明如何用单个双向层构建编码器:

  虽然贪婪解码可以给我们相当合理的翻译质量,但是集束搜索解码器可以进一步提高性能。集束搜索的想法是通过在我们翻译的同时,保留一小堆顶级候选来更好地探索所有可能的翻译的搜索空间。集束的大小称为波束宽度;大小为10的最小波束宽度通常是足够的。有关更多信息,请参阅Neubig的第7.2.3节(2017)。 以下是集束搜索如何完成的示例:

  请注意,使用相同的dynamic_decode()API调用,类似于Section解码器。一旦解码,我们可以访问如下的翻译:

  有几个超参数可以导致额外的性能。在这里,我们根据自己的经验列出一些[免责声明:其他人可能不会同意我们写的内容!]。

  优化器:虽然Adam可以导致“陌生”体系结构的合理化,但如果你可以使用SGD训练,一般会导致更好的性能。

  注意力:Bahdanau方式的注意力往往要求编码器方面的双向性运作良好; 而Luong方式的注意力往往适用于不同的设置。对于本教程代码,我们建议使用Luong&Bahdanau方式的注意力的两个改进的变体:scaling_luong和normed bahdanau。

  训练NMT模型可能需要几天时间。 在不同的GPU上放置不同的RNN层可以提高训练速度。 以下是在多个GPU上创建RNN图层的示例。

  你可能会注意到,随着GPU数量的增加,基于NMT模型的注意力的速度提高非常小。标准注意力架构的一个主要缺点是在每次步骤中使用顶层(最终)层的输出来查询注意力。这意味着每个解码步骤必须等待其前一步骤完成;因此,我们无法通过简单地将RNN层放置在多个GPU上来并行化解码过程。

  GNMT注意力架构通过使用底部(第一)层的输出来查询注意力来并行化解码器的计算。因此,一旦上一步的第一层和注意力计算完成,每个解码步骤就可以开始。我们在GNMTAttentionMultiCell中实现了该架构,这是trib.rnn.MultiRNNCell的子类。以下是使用GNMTAttentionMultiCell创建解码器单元的示例。

  我们用BLEU评分来衡量翻译质量(Papineni 等人于2002年提出)。

  这里,步进时间是指运行一个小批量(大小128)所需的时间。 对于wps,我们计算源和目标上的单词。

  训练细节:我们训练超参数的过程与英语到越南语的实验类似,但以下细节除外。使用BPE (32000个操作)将数据拆分为子字单元。我们用双向编码器(即编码器的2个双向层)训练1024单位的4层LSTM,降维dim为1024。我们训练350000步(〜10个周期);在170000步之后,我们每17000步开始减半学习率。

  前2行是2个模型(模型1、模型2)的平均结果。 第三行的结果是使用GNMT注意力([model](LINK))在4个GPU上运行结果。

  这些结果表明我们的代码为NMT建立了强大的基准系统。(请注意,WMT系统通常会使用我们目前没有的大量单语数据。)

  [注意,OpenNMT使用较小的模型,目前的最佳结果(截至本文中)为28.4,由Transformer network(Vaswani等人于2017年提出)获得,其具有明显不同的架构。

http://raggedydreams.com/shuangxiangtuili/198.html
锟斤拷锟斤拷锟斤拷QQ微锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷微锟斤拷
关于我们|联系我们|版权声明|网站地图|
Copyright © 2002-2019 现金彩票 版权所有