首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

神经机器翻译的训练改进和解码提速

导读:当前机器翻译模型通常采用自注意力机制进行编码,训练时直接将Ground Truth词语用作上文,并行生成所有的目标词语,极大的提升了训练速度。但测试的时候却面临以下难题:

首先,模型得不到Ground Truth,从而只能用自己生成的词作为上文,使得训练和测试的分布不一致,影响了翻译质量;

其次,由于每个目标词语的生成依赖于其之前生成的词,所以目标词语只能顺序生成,不能并行,从而影响了解码速度。

本次分享将针对以上问题,介绍他们的解决方法。

具体分享内容如下

  1. 改进训练和测试的分布不一致问题:
    • 采用计划采样的方法 (ACL 2019 best paper)
    • 采用可导的序列级损失函数进行贪心搜索解码
  2. 解码速度提升:
    • 基于Cube Pruning解码算法
    • 融入序列信息的非自回归翻译模型

背景

当前,自然语言处理方向的生成任务主要包括:机器翻译,人机对话,文章写作,文章摘要等等。目前这些问题主要是通过序列到序列模型来解决的。序列到序列模型的主要架构是一个带有注意力机制的编码器-解码器架构。这个架构基于一个重要的假设:即“源端的输入和目的端的输出之间是可以找到一个共同的语义空间。编码器的任务就是对输入进行各种变换,映射到共同语义空间上的一个点。解码器的任务是对共同语义空间的这个点进行一些反操作,将其映射到目标端空间,从而生成相应的词语。考虑到在每一步进行翻译的时候不需要关注所有的源端输入,而是仅仅关注一部分,注意力机制主要目的就是将当前步需要关注的部分找出来。

目前主流的序列到序列模型主要包括两种: 一个是RNNSearch,一个是Transformer。

RNNSearch通过RNN来将源端的输入编码成一个表示,通常源端采用的是双向RNN,这样对于每一个源端Token的编码表示都能考虑到其上下文信息。在目标端同样是使用一个RNN,它可以将翻译的历史信息给串起来,这样在当前步翻译的时候就能考虑到上文的信息。

Google在2017年提出了Transformer结构,该结构经过无数人的验证,发现非常好用,所以Transformer就成为了当前主流的序列到序列模型。Transformer主要的机制是:在生成源端表示的时候并没有使用RNN,而是使用自注意力机制来生成每一个Token的表示。这样做的好处是,在训练的时候可以并行,因为每个词都可以并行的和其它词计算attention ( RNN则只能串行 )。同样在解码端的时候,也是使用的自注意力机制。

这种模型在训练的时候都是采用的TeacherForcing形式。模型在解码当前步的时候,通常会有三个输入:解码器当前的状态,attention和上一步解码的结果。在训练的过程中,我们通常使用上一步的真实输出而非模型输出作为当前步解码的结果,这就是所谓的Teacher Forcing。

在Inference的时候通常采用Beam-Search +顺序生成的方式,在每一步都保存Top-K个最优结果。

在介绍了训练和推断之后,我们来看一下目前面临的问题,因为在训练的时候我们使用Teacher Forcing的方式,但是我们在推断的时候并不知道上一步的GroundTruth是什么,所以,我们只能将上一步预测的结果来近似为Ground Truth。这样,训练和推断在生成分布的条件上就产生了差异(Ground Truth vs Predicted),这个问题被称作为 Exposure Bias。

在训练的时候,我们还存在另一个问题。训练的时候由于我们使用的交叉熵损失函数,该损失函数只对Ground Truth友好,对于非Ground Truth的结果一视同仁。但是对于翻译任务来说,并不是只有一种翻译方式,从slides中可以看到,Output1和Ground Truth表示的是同一个意思,但是Output2和Ground Truth表示的含义就是不同了,但是在训练的时候,交叉熵损失函数会将Output1和Output2一视同仁,这样是不合理的。

在推断阶段解码的时候同样存在两个问题,在每一个解码step我们都要执行n各预测,每个预测都要得到整个词表的一个分布,所以在每一个step都要生成n*|V|个词语。而且每个时间步还必须串行,这大大影响了解码速度。

训练

1. 计划采样

对上面提到的问题进行一个小总结:

① 训练

  • 预测过程中Ground Truth的未知导致的Exposure Bias 问题
  • 交叉熵损失函数的逐词匹配所导致对于所有的非Ground Truth 一视同仁的问题。

② 解码

  • Beam Search 的Beam Size 会引入大量的计算成本
  • 自回归解码导致的无法并行问题。

首先,针对于Exposure Bias问题,我们采用的是进化采样的方法,这个就是我们2019ACL Best Paper的工作。针对与训练和测试时context不一致的情况,我们的解决方法的主要思想是,在训练的时候模仿测试时候可能会碰到的情况,这样在测试的时候就会发现,当前碰到的情况在训练的时候都碰到过,这样模型就可以应对了。

具体的做法是我们在每一步,模拟上一步的翻译结果,就是slides中的oracle,其中带*的是就是Ground Truth,在每一步,我们都会随机的选择是Oracle还是Ground Truth来作为当前步的上一步词输入。

使用上述方法,我们需要解决的 三个关键问题 是:

  • 如何生成Oracle翻译
  • Oracle和Ground Truth如何对上文进行采样
  • 如何训练模型

对于Oracle的生成,我们有两种方法,一个是生成词级别的Oracle,另一个是生成句级别的Oracle。词级Oracle即每一步都会选择最优,句子级别Oracle需要考虑到整个句子的最优。

由于RNN Search会在生成oracle的算法中会用到。在讲生成oracle的算法之前,先大体介绍一下RNN Search模型。RNN Search在当前步翻译的时候,会输入历史的隐状态信息,同时也会将上一步翻译的结果输入进去,经过一系列的变换,会得到当前步的一个隐状态 ${\rm S_j}$ ,该隐状态再经过几层全连接的计算,最终输入到softmax层得到词表中每一个词的归一化的分数。

在生成词级oracle的时候,我们会在softmax归一化分布之前加上一个Gumble Noise。Gumble Noise 的公式如slides中所示,其中 表示一个均匀分布。式子中的 表示温度,当非常大的时候,相当于argmax,当 比较小的时候,相当于均匀分布。

对于句级Oracle,我们首先采用Beam Search生成前K个候选译文,然后对选定的K个候选译文进行排序,这里的排序可以根据K个候选译文和Ground Truth计算一个BLUE值,还可以用一些其它的方法进行排序,最终选取得分最高的译文作为句级的Oracle。词级Oracle和句级Oracle 是一个局部最优和全局最优的一个区别。

对于采样,具体是怎么操作的呢?首先考虑到一点就是在刚开始训练的时候,模型的效果不怎么好,这样,无论是词级oracle的生成还是句级oracle的生成效果肯定都不是很理想,如果这时候使用oracle来引导模型训练的话, 可能会使得模型收敛的比较慢。一个比较合理的做法是,刚开始我们尽量选用Ground Truth的词,当模型快收敛的时候,我们再加大Oracle翻译的采样比例。这里的采样概率公式如slides所示,其中,随着epoch的增长,系统越来越倾向于选择oracle label。

对于训练的话,同样的采用最大化log likelihood的方式。

实验结果:除了对比Transformer 和 RNN-Search,也对比了另外两个系统,SS-NMT 和 MIXER。其中,SS-NMT也是通过计划采样的方式。MIXER的loss分为两个部分,一个部分是传统的Transformer使用的交叉熵损失函数,另外一部分是将BLEU值作为reward,然后通过policy gradient的方法对模型进行训练。

这个是在中英文新闻数据上的结果,可以看到,在RNN-Search的系统上, 我们相比于Baseline能够提升2.3个点。在Transformer系统上,相比于Baseline能够提升1.5个点。

在英德新闻数据结果上,基于RNN-Search的系统比baseline高了1.6个点,基于Transformer的系统比baseline高了1.3个点。

2. 可导的序列级目标

接下来介绍如果解决词级匹配的对于好一点的匹配和差的匹配一视同仁的问题。

这个是我们在EMNLP 2018上所做的工作。通过使用可导的序列级目标来解决词级匹配的问题。

首先介绍一下传统的序列级损失函数。传统的序列级损失函数基本上都是基于N-gram正确率的损失函数,比如,BLEU,GLEU等等。计算方法为,命中n-gram的个数/总共的n-gram的个数(candidate),其中n-gram的个数为其每个词语出现频次的乘积。

直接使用BLEU不可到的原因是因为操作中有argmax,为了使其可导,我们使用token的预测概率,而非使用argmax。这个方法和直接用BLEU作为Score,然后reinforce算法直接训练对比有啥优势?由于reinforce算法的方差比较大,所以在训练的时候是很难收敛的。而使用传统的梯度下降的方法,训练过程就会平稳的多。

这里是3-gram的例子,其中output是概率最高的词,3-gram概率的是由独立Token的输出概率相乘得到结果,然后求和会得到The total probabilistic count of 3-grams。将匹配上的3-gram的概率拿出来求和作为分子,Total probabilistic count作为分母,计算得到 Precision of 3-grams。这就是我们的loss。

这个例子用来展示整个的训练过程,这里需要注意的一点就是,和传统的teacher forcing方式不同,这里当前步输入的为上一步预测的结果(贪心搜索得到的结果),而不是ground truth的值。剩下的就是按照上页slides介绍的来计算loss。对于loss采用传统的梯度下降算法即可。下面贴的是在数据集上的结果。

从结果中可以看出,2-gram比4-gram的效果要好,这里我们给出的解释是,过多的使用自己生成的去计算的话,会存在一定程度上的错误累积。

在训练的时候,也是可以通过teacher forcing的方式来训练的,但是从图中可以看出,teacherforcing的方式收敛的比较快,但是效果不如greedy search的方式好。

解码

1. CubePruning

下面介绍在解码方面的两个工作,第一个工作要解决的是beam search每一步要计算BeamSize*|V|的问题,这个计算量大大降低了inference时候解码的速度。

这是解码过程中每个步骤的时间消耗,对于GPU来说,大部分的时间消耗在的计算上,其它三个步骤比较节省时间,对于CPU来说,最耗费时间的是最后两个步骤,因为|V|比较大。

传统的方法使用的是Beam Search,传统的 Beam Search其实是一个二维的搜索方法。其中第一维就是已经生成的部分的译文,假设Beam Size = 4,那么就是四个译文。第二维度是这四个译文都要进行下一步的Token预测计算。总共就需要计算4*|V|的概率。因为|V|的个数通常是几千上万级别的,所以这个部分的计算量就非常大。

我们的做法是将二维的搜索扩展成三维的搜索,具体的做法分为以下几步:

① Beam分组:假设我们要解码第11步,我们就将第10步解码出来相同Token的候选序列归为一组。 ② 分组预测第11步的候选Token:只用每个组得分最高的哪个候选序列来计算当前的Token分布。 ③ 近似组员的Token分布:由上一步已经知道本组最优的候选序列的下一个token的预测分布,对于组员来说,也将共享其老大计算出来的Token分布score,然后和自身的序列score相加,得到自身扩展一个Token后的score。这个score作为自身的近似分。 ④ 查找Top-K:经过上面的计算之后,这样每个组就是得分其实是一个二维矩阵,我们将矩阵横轴作为每个组员,纵轴表示当前步预测的token,然后保证右上角score最大,往右,往下都是减小。这样便于我们查找Top-K。具体请看下一张slides。

对于近似的score这里有两个选择:

  • 如果取到的candidate是预测的score,那么用真实的状态来重新计算一下这个score,这时候也顺便更新了一下自己的隐状态
  • 直接用预测的score,不使用更新的方式,这时候和老大哥共享隐状态

这个是GPU上的结果,横轴是速度,纵轴是BLEU值,可以看出在取得最优的BLEU值的情况下,我们的方法所用的时间是更短的。速度可以提升3.3倍。在CPU下,提速可以达到3.5倍。

在Beam Size=40的情况下,GPU上速度提升3.8倍,CPU上提升4.2倍。

2. 非自回归解码

最后介绍一下基于非自回归的解码方法,传统的解码方法是顺序生成的。如果能够使得解码的时候并行的方式生成,这速度将会大大的提升。

传统的非自回归模型的做法是,在Transformer Encoder端头部加一个Fertility预测,用来预测每个源端Token能翻译成一个目标端的Token,然后根据预测的结果,将源端的Token拷贝到Decoder的输入,如果一个源端Token能够翻译两个目标Token,那就拷贝两次,如果源端Token不会翻译成目标端Token,那就不拷贝。由于每一步输出的译文是没有给到下一步的,所以是可以并行的。对于Fertility的训练是采用某种对齐模型,通过计算源端和目标端的对齐关系,然后就可以得到源端和目标端的对齐结果,就可以采用监督的方式来训练Fertility分支。

该方法有一个问题,就是在翻译当前步的时候没有考虑上一步的翻译信息。这样就可能导致翻译结果的流畅度不够好。我们的方法就是在该方法的基础上添加了序列上的信息。这样模型既能并行执行,又能考虑的到前后的序列关系。

我们的工作分为两个方面,一个是在训练上添加序列信息,一个是在模型上面同样也添加序列信息。序列训练采用的是Reinforce的方法,Reinforce的方法非常难以训练,这是因为其方差非常大,方差大的原因是强化学习episode(一条轨迹从开始到结束)的搜索空间非常大,我们每次只是采样出一个episode,然后根据这个episode进行计算,通过大数定律,我们可以假设这最终得到的是一个梯度的无偏估计。但是在实际情况下,抖动是非常大的。

将Reinforce算法应用到我们这个场景,首先看第一个公式,由于目标端词的概率是独立的,所以就可以写成连乘的形式,第二个公式就是传统的Reinforce公式,就是翻译的reward。是通过前向后向算法计算出来的当前步的reward。

上面的slides介绍的是计算reward时候的不同,接下来看sampling机制的区别。根据生成前后词的独立性,每一步我们并不是采样出一个词,而是采样出K+1个词。这样的话就可以看做我们一次更新的过程中考虑到更多的episode,而不是仅用一个episode就去训练了。具体的做法是,每一步,我们先取Top-K,计算一下损失函数的值,然后从剩下的Token中再采样出来一个。我们将这两部分的loss合起来,是为了保证无偏估计。为前k个翻译的概率的和。

另外一个方法就是模型上的改进,在非自回归层的上面加上自回归层。具体的做法是,模型分为 Bottom Layer,Fusion Layer,Top Layer。Bottom Layer就是之前介绍的非自回归模型,Fusion Layer的作用是将非自回归模型的输出和其Embedding整合起来,Top-Layer和Transformer 的解码器基本一致。

实验结果:AR(Transformer),NAT(非自回归的方法),IRNAT(迭代的非自回归方法),最后是我们提出的方法,第一种是在训练的过程中引入序列信息,第二是在模型上进行改进。作为对比的数据集有三个,前两个数据集比较小。主要关注第三个数据集。可以看出,使用NAT来代替AR模型的话,效果会降6个点左右,迭代的方法会带来1到2个点的提升。我们提出的reinforce方法和传统的reinforce方法相比,有0.6个点的提升。加上回归层的模型已经接近Transformer的效果了。关于速度的提升,如果仅训练的时候采用序列信息,速度可以提升10倍。如果是NAT加上自回归层的方法,速度也可以提高4倍左右。

这里有一些翻译实例,可以看出 NAT-base的方法流畅性不够好,重复很多“more more …”,因为没有考虑序列信息,所以导致结果的流畅度不行。使用我们提出的reinforce方法,能够一定程度上的缓解流畅度的问题,但是问题还是存在。通过使用NAT+AR的方法,能够更好的缓解流畅度的问题。

研究组主页

http://nlp.ict.ac.cn/2017/

个人主页

http://nlp.ict.ac.cn/~fengyang/

今天的分享就到这里,谢谢大家。

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/3pjXAvvY8usNKC75iQtx
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券