神经翻译笔记5. 序列到序列模型与注意力机制

文章目录

  • 神经翻译笔记5. 序列到序列模型与注意力机制
    • 机器翻译概论
    • 编码器-解码器结构
    • 集束搜索
    • 注意力机制
    • 结语
    • 其它参考文献
    • 备注

神经翻译笔记5. 序列到序列模型与注意力机制

本系列笔记从2018年3月开始编写,虽然题名为“神经翻译笔记”,但是历经2年3个月,虽然偶尔提到一些神经翻译使用的方法(例如subword),却仍并未真正涉及机器翻译本身,颇有点“博士买驴”的感觉。不过从本章开始,终于要进入正题,聊一聊神经机器翻译用到的核心技术了(然而要跟上时代,讲述Transformer,可能还需要过很久。本章预计会写10节,将是一个漫长的过程)

本章的主要内容是讲述基于RNN的序列到序列模型与注意力机制,本文参考如下课程讲义和教材:

  • Koehn的NMT综述,13.5节

  • Neubig的NMT和s2s教程,第7、8节(除CNN和树结构)(本文写完,Neubig的这篇文章就算看完了)

  • Stanford CS224n 2020 Winter第8讲幻灯片、官方笔记

  • 其它相关的经典论文


机器翻译概论

机器翻译是自然语言处理领域出现比较早的一类任务,其在1950年开始就受到了广泛研究。该任务的目的是将源语言 S S S的句子 x x x翻译成目标语言 T T T的句子 y y y。早期的机器翻译是纯基于规则的,人们通过编写的双语词典逐词翻译。在20世纪90年代到2014年以前,机器翻译一直处于统计机器翻译时代。假设给定了一个法语句子 x x x,要找到其对应的最佳英语翻译 y y y,实际上就是要找
a r g max ⁡ y P ( y ∣ x ) \mathop{\rm arg \max}_y P(y|x) argmaxyP(yx)
根据贝叶斯定律,有
a r g max ⁡ y P ( y ∣ x ) = P ( x ∣ y ) P ( y ) \mathop{\rm arg \max}_y P(y|x) = P(x|y)P(y) argmaxyP(yx)=P(xy)P(y)
这样的分解说明要做好机器翻译,需要引入两个模型

  • 翻译模型,建模单词和词组应如何被翻译(可信性),该模型从平行语料中学习
  • 语言模型,建模如何写出好的英语(通顺性),该模型从单语语料中学习

由此带来的问题是,应该如何从平行语料中学习翻译模型 P ( x ∣ y ) P(x|y) P(xy)呢?做法是引入一个隐变量 a a a,来学习 P ( x , a ∣ y ) P(x,a|y) P(x,ay)。这里这个隐变量 a a a被称为对齐信息,即源句 x x x和目标句 y y y之间的对应关系。理想上,将源语言的每个词翻译成目标语言的对应词,就应该大功告成了,但是实际上事情远没有这么简单,其核心原因就是自然语言中不同语言之间对齐的复杂性,例如

  • 源句中有些词在目标句中无对应词。例如“Le Japon secoué par deux nouveaux séismes”翻译成英语“Japan shaken by two new quakes”,这里le在英语中就不翻译(单就单词来讲,le可以翻成the,但不符合英语文法)
  • 源句词到目标句词的一对多关系。例如“Le reste appartenait aux autochtones”翻译成“The balance was the territory of the aboriginal people”,这里“appartenait”对应“was the territory”;“aux”对应“of the”,autochtones对应“aboriginal people”。有时有的单词可以对应很多个词,例如“il a m’entarté”中“entarté”对应英语的“hit with a pie”。这种词称为繁衍词(fertile word)
  • 源句词到目标句词的多对一关系。例如“Le programme a été mis en application”翻译成“The program has been implemented”,这里“mis en application”对应“implemented”
  • 源句词到目标句词的多对多关系,或者说短语级对应关系。例如“Les pauvres sont démunis”翻译成“The poor don’t have any money”,这里“sont démunis”翻译成“don’t have any money”

为了更准确地描述这种复杂的对齐关系,成功的统计翻译系统通常都比较庞大,由许多独立的子系统构成。此外,还需要大量的特征工程工作,甚至要为不同的语言现象分别设计特征,人力成本很高

神经翻译的提出有力地改变了这样的状况,它的核心思路是只使用一个系统,也就是一个神经网络模型端到端地解决所有问题。通过大规模语料的训练,模型接收到一个简单处理过的源语言句子,就可以直接生成一个目标语言句子,不用经过其它模型人工提取特征。这种神经网络模型称为序列到序列(sequence-to-sequence, s2s)模型,因为输入是一个标识符序列,输出也是一个标识符序列。典型的序列到序列模型可以看做是由编码器解码器两个部分构成

  • 编码器负责读取整个句子,编码成一个维度固定的向量并输出
  • 解码器从编码器拿到输入的编码向量,逐步解码输出目标句的各个标识符

因此,序列到序列模型也通常被称为编码器-解码器模型(本文及后文会混用这两个名词,不对这两个概念做区分)。下面给出了一个最简单的序列到序列模型示意图(图自Neubig的turorial)

神经翻译笔记5. 序列到序列模型与注意力机制_第1张图片

编码器-解码器结构

由于还没有涉及到CNN和Transformer,因此这里先假设编码器和解码器部分都是两个单向的RNN,编码器记作 R N N f ( ⋅ ) {\rm RNN}_f(\cdot) RNNf(),解码器记作 R N N e ( ⋅ ) {\rm RNN}_e(\cdot) RNNe(),则模型可以表示为
x f ( t ) = E f [ f ( t ) ] h f ( t ) = { R N N f ( x f ( t ) , h f ( t − 1 ) ) t ≥ 1 0 o t h e r w i s e x e ( t ) = E e [ e ( t − 1 ) ] h e ( t ) = { R N N e ( x e ( t ) , h e ( t − 1 ) ) t ≥ 1 h f ∣ F ∣ o t h e r w i s e p e ( t ) = s o f t m a x ( W h s h e ( t ) + b s ) \begin{aligned} \boldsymbol{x}_f^{(t)} &= \boldsymbol{E}_f[f^{(t)}] \\ \boldsymbol{h}_f^{(t)} &= \begin{cases}{\rm RNN}_f\left(\boldsymbol{x}_f^{(t)}, \boldsymbol{h}_f^{(t-1)}\right) & t \ge 1\\ \boldsymbol{0} & {\rm otherwise}\end{cases} \\ \boldsymbol{x}_e^{(t)} &= \boldsymbol{E}_e[e^{(t-1)}] \\ \boldsymbol{h}_e^{(t)} &= \begin{cases}{\rm RNN}_e\left(\boldsymbol{x}_e^{(t)}, \boldsymbol{h}_e^{(t-1)}\right) & t \ge 1\\ \boldsymbol{h}^{|F|}_f & {\rm otherwise}\end{cases} \\ \boldsymbol{p}_e^{(t)} &= {\rm softmax}\left(\boldsymbol{W}_{hs}\boldsymbol{h}_e^{(t)} + \boldsymbol{b}_s\right) \end{aligned} xf(t)hf(t)xe(t)he(t)pe(t)=Ef[f(t)]={RNNf(xf(t),hf(t1))0t1otherwise=Ee[e(t1)]={RNNe(xe(t),he(t1))hfFt1otherwise=softmax(Whshe(t)+bs)

这里前两行对应编码器,意思是对源语言 f f f,在每一时刻(对应每个标识符)在源语言词嵌入矩阵 E f \boldsymbol{E}_f Ef中寻找该标识符对应的词向量,连同上一时刻隐藏状态一起计算该时刻句子的隐藏状态。编码器的初始隐藏状态是零向量。在经历了源句长度 ∣ F ∣ |F| F个时刻以后,模型已经看过了源句的所有标识符,并且生成了源句的编码 h f ∣ F ∣ \boldsymbol{h}_f^{|F|} hfF,我们认为这个向量包含了源句的所有信息。后三行对应编码器,大致思路类似,除去解码器需要一个额外的softmax过程来输出单词(的id),两者最大的不同是解码器在每一刻看到的输入单词实际上是上一刻的单词。其中,第0时刻是一个特殊的、固定的标识符。示意图中将这个标识符算在了目标语言中,记作 e 0 e_0 e0,但是实践中通常使用的是源语言词表中的标识符,记为,表示输入的结束。该标识符在源语言词表和目标语言词表中都有重要作用:在源语言词表中,该标识符出现意味着源句的结束,可以开始目标句的输出;而在目标语言词表中,解码阶段如果产生了该标识符,意味着解码完毕。以将法语il a m’ entarté(这里m’看做是一个独立的标识符,与后面的词分开)翻译成he hit me with a pie的训练过程为例,模型看到的整个序列实际上是il a m' entarté he hit me with a pie,在entarté这一步模型已经将源句编码完成,看到时就开始努力让这一步的输出匹配he(让he这个词的概率最大),在pie这一步则是努力让输出匹配目标语言的。对于每组样本(一个句子对),模型的目标函数是解码器部分每一时刻输出分布与实际分布交叉熵的均值。下图给出了对应的示例(图自CS224n讲义)

神经翻译笔记5. 序列到序列模型与注意力机制_第2张图片需要注意的是,序列到序列模型的训练阶段和推断阶段有不同。在训练阶段,解码器可以看到目标句的真实内容,因此无论在第 t t t时刻模型的输出多么奇怪,在第 t + 1 t+1 t+1时刻它看到的输入单词仍然是第 t t t步那个正确的词。但是,在解码阶段,由于没有真实值的存在,解码器在第 t + 1 t+1 t+1时刻看到的输入单词实际上是其第 t t t时刻的输出。这隐含地为序列到序列模型带来了两个问题

  • 如果在某一时刻解码器解码出了错误的单词,那么它将影响后面所有标识符的生成。如果错误出现得比较早,就会是一场灾难
  • 模型在解码阶段一次只能生成一个单词,拖慢了解码的速度

对于前者,在该模型提出的早期,做神经翻译的一个常见小技巧是将输入反向,例如将il a m' entarté转换成entarté m' a il。这样编码器接受的源句开头离解码器更“近”,一定程度上可以抵消RNN因两个词距离太远造成的遗忘现象,使得解码器在开始阶段产生的结果更准确,降低出错几率。在注意力机制和Transformer提出以后,这种现象得到了进一步改善。对于后者,目前还没有太好的解决办法。近几年涌现的“非自回归翻译”(non auto-regressive translation, NAT)可以并行化输出,提高了计算效率,但是模型产生的翻译句子质量暂时还无法与传统的自回归翻译相媲美

前面图中给出的编码器-解码器结构是最简单的形式。为了达到更好的模型效果,人们对编码器部分做了若干改造,例如使用双向RNN、多层RNN和引入残差连接等等,但是更革命性的是引入注意力机制。另一方面,在解码阶段,人们也在最简单的贪婪算法(每一步选择概率最高的单词输出)基础上进行了发展,其中目前应用最广泛的称为集束搜索(beam search)。本章后面两节将分别介绍这两种重要技术

集束搜索

在未改进的搜索策略中,如果在某一步解码器生成的最大概率词并非正确词,那么后面有可能一错到底,无法回头。为了解决这样的问题,很自然的做法是扩大解码器的选择范围,每次不止看最优的结果。假设目标语言的词表大小为 ∣ V ∣ |V| V,目标句的长度为 T T T,则在最理想的情况下,应该对全部序列(共 ( ∣ V ∣ ) T (|V|)^T (V)T个)进行打分,但是这样的搜索空间实在太大,实践中不可能实现

对这种全搜索的改进方法是使用集束搜索,其遵循如下步骤

  • 在解码的第1步softmax以后,选择排名最靠前的 b b b个候选词,其中 b b b是集束的“宽度”
  • 对每个候选结果进一步展开,各自在第2步产生排名最靠前的 b b b个候选,此时第2步共有 b × b = b 2 b \times b = b^2 b×b=b2个候选结果
  • 将这 b 2 b^2 b2个候选结果的得分从高到低排序,选出前 b b b个候选,剩余候选全部丢弃。记源句为 F F F,解码器产生的第一个标识符为 e 1 e_1 e1,第二个标识符为 e 2 e_2 e2,则第2步的得分为 log ⁡ P ( e 2 ∣ F , e 1 ) \log P(e_2|F, e_1) logP(e2F,e1),即评估的是到该时间步为止生成的所有序列的得分
  • 选出的前 b b b个候选有可能包含EOS,意味着此时这条分支的序列已经生成完毕。此时通常将这个已生成好的句子(称为“整句”)放在一个列表中,其余分支继续按照集束搜索过程展开
  • 重复上述提到的过程,直到
    • 生成了 n n n个整句
    • 或已经迭代了 T T T步。这里 n n n T T T都是预先定义的参数

最后,集束搜索对产生的所有结果综合打分评估。显而易见的是,如果只是计算 ∑ log ⁡ P ( e i ∣ F , e 1 , … e i − 1 ) \sum \log P(e_i|F, e_1, \ldots e_{i-1}) logP(eiF,e1,ei1),那么长句的得分肯定不如短句。通常的做法是对每个结果除以序列长度来做归一化,然后再作比较

下图给出了 b = 2 b=2 b=2的集束搜索过程的示意图(来自于CS224n讲义)

神经翻译笔记5. 序列到序列模型与注意力机制_第3张图片
集束搜索是在序列到序列模型中使用最广泛的搜索方法,但是也存在若干问题。在本章的后续小节中,将对序列到序列模型其它一些常见的魔改搜索方法做进一步介绍

注意力机制

编码器解码器结构的出现为机器翻译领域开辟了一个新的纪元,但是人们很快发现,随着句子长度变长,模型效果急剧下降,仅靠一个定长向量编码源句的所有信息有些捉襟见肘,而且模型仍然无法很好地建模长距离依赖关系。[Bahdanau2014]为此提出了注意力(attention)机制,试图将源句表达为一个向量序列,在解码时自适应地选取这个序列的一个子集,进而有效地解决了长度变大时编码器解码器失效的问题

设源句长度为 N N N,记编码器每一步的隐藏状态为 h ( 1 ) , … , h ( N ) \boldsymbol{h}^{(1)}, \ldots, \boldsymbol{h}^{(N)} h(1),,h(N),显然在这些隐藏状态中,每一个隐藏状态都对应了一个源句单词。一个比较自然的想法是,对这些隐藏状态做一个加权求和,而权重则由目标句的单词来决定。例如英文“Last week I went to the theatre”翻译成中文“上周 我 去 了 剧院”,这里“剧院”这个词肯定受“theatre”影响最大,“the”有一些,但“week”就应该没什么关系,因此“theatre”此时权重应该很高,而“week”的需要接近0——这其实就是注意力机制的基本思路。其具体做法为,在解码的每一步 t t t,对解码器在该步的隐藏状态 s ( t ) \boldsymbol{s}^{(t)} s(t),先不将其送进输出层,而是与编码器的各隐藏状态计算得分,得到一个对齐向量 a ( t ) \boldsymbol{a}^{(t)} a(t)
a ( t ) = [ s c o r e ( s ( t ) , h ( 1 ) ) , … , s c o r e ( s ( t ) , h ( N ) ) ] \boldsymbol{a}^{(t)} = \left[{\rm score}\left(\boldsymbol{s}^{(t)}, \boldsymbol{h}^{(1)}\right), \ldots, {\rm score}\left(\boldsymbol{s}^{(t)}, \boldsymbol{h}^{(N)}\right)\right] a(t)=[score(s(t),h(1)),,score(s(t),h(N))]
这里 s c o r e \rm score score是一个打分函数,稍后会重提。在得到这个得分向量以后,通过softmax操作将其归一化,就可以得到满足前面要求的权重向量 α ( t ) \boldsymbol{\alpha}^{(t)} α(t)
α ( t ) = s o f t m a x ( a ( t ) ) \boldsymbol{\alpha}^{(t)} = {\rm softmax}\left(\boldsymbol{a}^{(t)}\right) α(t)=softmax(a(t))
自然地,可以使用这个权重向量对编码器的众隐藏状态加权求均值,得到上下文向量 c ( t ) \boldsymbol{c}^{(t)} c(t)
c ( t ) = ∑ i = 1 N α i ( t ) h ( i ) \boldsymbol{c}^{(t)} = \sum_{i=1}^N \alpha_i^{(t)}\boldsymbol{h}^{(i)} c(t)=i=1Nαi(t)h(i)
将这个上下文向量 c ( t ) \boldsymbol{c}^{(t)} c(t) s ( t ) \boldsymbol{s}^{(t)} s(t)连接起来,再送进输出层求softmax
p ( y ( t ) ∣ y ( < t ) , x ) = s o f t m a x ( W o [ c ( t ) : s ( t ) ] ) \boldsymbol{p}(y^{(t)}|y^{(p(y(t)y(<t),x)=softmax(Wo[c(t):s(t)])

注意力机制的引入打破了前面提到的原始编码器解码器模型遇到的两个瓶颈。其一,解码器不只依赖于编码器产生的最终定长向量,而是会和编码器每一步的隐藏向量产生交互,获取更多信息;其二,由于解码器每一步和编码器各步都有了直接连接,因此反向传播时梯度有了很多直连通路,有效降低了多步传递带来的梯度消失/梯度爆炸等风险

更广泛地说,注意力机制可以抽象为给定查询 q q q(这里是解码器的隐藏状态),计算每个关键字 k k k对应值 v v v的加权平均值的过程(这里 k k k v v v都是编码器各步的隐藏状态)。这种加权平均可以看做是对所有关键字组成的信息产生有选择的摘要,而如何选择则是根据具体查询值决定

常见的打分函数有如下几种

  • 连接打分,也称为Bahdanau打分或加性打分,是打分函数最早的形式: v a T tanh ⁡ ( W a [ h ( i ) : s ( t ) ] ) \boldsymbol{v}_a^\mathsf{T}\tanh\left(\boldsymbol{W}_a[\boldsymbol{h}^{(i)}:\boldsymbol{s}^{(t)}]\right) vaTtanh(Wa[h(i):s(t)]),其中 v a \boldsymbol{v}_a va W a \boldsymbol{W}_a Wa是新引进的参数。这种方法被称为加性打分的原因是它可以被展开写为 v a T tanh ⁡ ( W a h h ( i ) + W a s s ( t ) ) \boldsymbol{v}_a^\mathsf{T}\tanh\left(\boldsymbol{W}_{ah}\boldsymbol{h}^{(i)} + \boldsymbol{W}_{as}\boldsymbol{s}^{(t)}\right) vaTtanh(Wahh(i)+Wass(t))
  • 通用打分,也称为Luong打分或乘性打分,于[Luong2015]被提出: s ( t ) T W a h ( i ) {\boldsymbol{s}^{(t)} }^\mathsf{T}\boldsymbol{W}_a\boldsymbol{h}^{(i)} s(t)TWah(i)
  • 内积打分,可以看做是Luong打分的简化版本: s ( t ) T h ( i ) {\boldsymbol{s}^{(t)} }^{\mathsf{T} }\boldsymbol{h}^{(i)} s(t)Th(i)。这种方法快捷且不引入额外参数,但是强制要求编码器和解码器的隐藏状态维度一致

结语

自2014年首次提出以来,编码器解码器结构的序列到序列模型已经取得了相当大的成功,成为了当前解决神经翻译问题的标配。实际上,该模型的应用范围不仅局限于机器翻译这一个领域,还可以用来解决问答、文本摘要、聊天机器人等各种问题,是NLP领域里的一种重要模型。而注意力机制的出现进一步改善了编码器解码器模型在长句上表现不佳的状况,人们在之后也提出了各种衍生版本,而纯粹使用注意力机制的Transformer也令人耳熟能详。关于注意力机制的变种和Transformer,将在本系列笔记的后续中详细介绍

其它参考文献

  • [Bahdanau2014] Bahdanau, D., Cho, K., & Bengio, Y. (2014). Neural machine translation by jointly learning to align and translate. arXiv preprint arXiv:1409.0473 (Published as a conference paper at ICLR 2015).
  • [Luong2015] Luong, M. T., Pham, H., & Manning, C. D. (2015, September). Effective Approaches to Attention-based Neural Machine Translation. In Proceedings of the 2015 Conference on Empirical Methods in Natural Language Processing (EMNLP2015) (pp. 1412-1421).

备注

[Bahdanau2014]注意力机制与[Luong2015]注意力机制的区别

前文提到注意力机制最早出现于[Bahdanau2014],[Luong2015]发表时间稍晚,但是基本处于同一时段。这两篇都是注意力机制领域的奠基之作,[Luong2015]引用了[Bahdanau2014],不过有所区别(实际上,[Bahdanau2014]与前文介绍的思路也有差别,但是前面介绍时采用了CS224n讲义和Neubig tutorial的内容,两者基本一致)。具体区别为

  • 在第 t t t步使用的解码器隐藏向量不同。[Bahdanau2014]计算得分向量时,各个分量用的实际上是解码器前一个时刻的隐藏状态,即 a B a h d a n a u ( t ) = [ s c o r e ( s ( t − 1 ) , h ( 1 ) ) , … , s c o r e ( s ( t − 1 ) , h ( N ) ) ] \boldsymbol{a}^{(t)}_{\rm Bahdanau} = \left[{\rm score}\left(\boldsymbol{s}^{(t-1)}, \boldsymbol{h}^{(1)}\right), \ldots, {\rm score}\left(\boldsymbol{s}^{(t-1)}, \boldsymbol{h}^{(N)}\right)\right] aBahdanau(t)=[score(s(t1),h(1)),,score(s(t1),h(N))],[Luong2015]用的才是解码器当前时刻的隐藏状态
  • 上下文向量与解码器隐藏向量拼接后的用法不同。[Bahdanau2014]将上下文向量和解码器当前时刻的隐藏状态拼接以后直接送去输出层求softmax,而[Luong2015]则是先过了一个全连接层,求 h ~ ( t ) = tanh ⁡ ( W c [ c ( t ) : s ( t ) ] ) \tilde{\boldsymbol{h}}^{(t)} = \tanh (\boldsymbol{W}_c[\boldsymbol{c}^{(t)}:\boldsymbol{s}^{(t)}]) h~(t)=tanh(Wc[c(t):s(t)]),再将 h ~ ( t ) \tilde{\boldsymbol{h}}^{(t)} h~(t)送进输出层求softmax: p ( y ( t ) ∣ y ( < t ) , x ) = s o f t m a x ( W o h ~ ( t ) ) \boldsymbol{p}(y^{(t)}|y^{(p(y(t)y(<t),x)=softmax(Woh~(t))
  • 打分方式不同。[Luong2015]分别实验了前述三种打分方式,[Bahdanau2014]仅实验了连接打分机制
  • 前述提到的注意力机制只是[Luong2015]的一部分,称为“全局注意力”。此文还介绍了一种“局部注意力”机制,意在计算注意力得分时仅选择编码器隐藏状态的一个子集。不过实现相对比较复杂,似乎现在也少有人用了

你可能感兴趣的:(神经翻译笔记,机器翻译,注意力机制,序列到序列模型)