机器翻译(machine translation, MT)是用计算机来实现不同语言之间翻译的技术。被翻译的语言通常称为源语言(source language),翻译成的结果语言称为目标语言(target language)。机器翻译即实现从源语言到目标语言转换的过程,是自然语言处理的重要研究领域之一。 早期机器翻译系统多为基于规则的翻译系统,需要由语言学家编写两种语言之间的转换规则,再将这些规则录入计算机。该方法对语言学家的要求非常高,而且我们几乎无法总结一门语言会用到的所有规则,更何况两种甚至更多的语言。因此,传统机器翻译方法面临的主要挑战是无法得到一个完备的规则集合[1]。 为解决以上问题,统计机器翻译(Statistical Machine Translation, SMT)技术应运而生。在统计机器翻译技术中,转化规则是由机器自动从大规模的语料中学习得到的,而非我们人主动提供规则。因此,它克服了基于规则的翻译系统所面临的知识获取瓶颈的问题,但仍然存在许多挑战:1)人为设计许多特征(feature),但永远无法覆盖所有的语言现象;2)难以利用全局的特征;3)依赖于许多预处理环节,如词语对齐、分词或符号化(tokenization)、规则抽取、句法分析等,而每个环节的错误会逐步累积,对翻译的影响也越来越大。 近年来,深度学习技术的发展为解决上述挑战提供了新的思路。将深度学习应用于机器翻译任务的方法大致分为两类:1)仍以统计机器翻译系统为框架,只是利用神经网络来改进其中的关键模块,如语言模型、调序模型等(见图1的左半部分);2)不再以统计机器翻译系统为框架,而是直接用神经网络将源语言映射到目标语言,即端到端的神经网络机器翻译(End-to-End Neural Machine Translation, End-to-End NMT)(见图1的右半部分),简称为NMT模型。
本教程主要介绍NMT模型,以及如何用PaddlePaddle来训练一个NMT模型。 效果展示以中英翻译(中文翻译到英文)的模型为例,当模型训练完毕时,如果输入如下已分词的中文句子: 这些 是 希望 的 曙光 和 解脱 的 迹象 . 0 -5.36816 These are signs of hope and relief . <e> 1 -6.23177 These are the light of hope and relief . <e> 2 -7.7914 These are the light of hope and the relief of hope . <e> <e> 表示句子的结尾,<unk> 表示未登录词(unknown word),即未在训练字典中出现的词。
模型概览本节依次介绍GRU(Gated Recurrent Unit,门控循环单元),双向循环神经网络(Bi-directional Recurrent Neural Network),NMT模型中典型的编码器-解码器(Encoder-Decoder)框架和注意力(Attention)机制,以及柱搜索(beam search)算法。 GRU我们已经在情感分析一章中介绍了循环神经网络(RNN)及长短时间记忆网络(LSTM)。相比于简单的RNN,LSTM增加了记忆单元(memory cell)、输入门(input gate)、遗忘门(forget gate)及输出门(output gate),这些门及记忆单元组合起来大大提升了RNN处理远距离依赖问题的能力。 GRU[2]是Cho等人在LSTM上提出的简化版本,也是RNN的一种扩展,如下图所示。GRU单元只有两个门: - 重置门(reset gate):如果重置门关闭,会忽略掉历史信息,即历史不相干的信息不会影响未来的输出。 - 更新门(update gate):将LSTM的输入门和遗忘门合并,用于控制历史信息对当前时刻隐层输出的影响。如果更新门接近1,会把历史信息传递下去。
一般来说,具有短距离依赖属性的序列,其重置门比较活跃;相反,具有长距离依赖属性的序列,其更新门比较活跃。另外,Chung等人[3]通过多组实验表明,GRU虽然参数更少,但是在多个任务上都和LSTM有相近的表现。 双向循环神经网络我们已经在语义角色标注一章中介绍了一种双向循环神经网络,这里介绍Bengio团队在论文[2,4]中提出的另一种结构。该结构的目的是输入一个序列,得到其在每个时刻的特征表示,即输出的每个时刻都用定长向量表示到该时刻的上下文语义信息。 具体来说,该双向循环神经网络分别在时间维以顺序和逆序——即前向(forward)和后向(backward)——依次处理输入序列,并将每个时间步RNN的输出拼接成为最终的输出层。这样每个时间步的输出节点,都包含了输入序列中当前时刻完整的过去和未来的上下文信息。下图展示的是一个按时间步展开的双向循环神经网络。该网络包含一个前向和一个后向RNN,其中有六个权重矩阵:输入到前向隐层和后向隐层的权重矩阵(W1,W3),隐层到隐层自己的权重矩阵(W2,W5),前向隐层和后向隐层到输出层的权重矩阵(W4,W6)。注意,该网络的前向隐层和后向隐层之间没有连接。
编码器-解码器框架编码器-解码器(Encoder-Decoder)[2]框架用于解决由一个任意长度的源序列到另一个任意长度的目标序列的变换问题。即编码阶段将整个源序列编码成一个向量,解码阶段通过最大化预测序列概率,从中解码出整个目标序列。编码和解码的过程通常都使用RNN实现。
编码器编码阶段分为三步:
第3步也可以使用双向循环神经网络实现更复杂的句编码表示,具体可以用双向GRU实现。前向GRU按照词序列(x1,x2,...,xT)的顺序依次编码源语言端词,并得到一系列隐层状态(→h1,→h2,...,→hT)。类似的,后向GRU按照(xT,xT−1,...,x1)的顺序依次编码源语言端词,得到(←h1,←h2,...,←hT)。最后对于词xi,通过拼接两个GRU的结果得到它的隐层状态,即hi=[→hTi,←hTi]T。
解码器机器翻译任务的训练过程中,解码阶段的目标是最大化下一个正确的目标语言词的概率。思路是:
zi+1=ϕθ′(c,ui,zi)
其中ϕθ′是一个非线性激活函数;c=qh是源语言句子的上下文向量,在不使用注意力机制时,如果编码器的输出是源语言句子编码后的最后一个元素,则可以定义c=hT;ui是目标语言序列的第i个单词,u0是目标语言序列的开始标记
p(ui+1|u<i+1,x)=softmax(Wszi+1+bz)
其中Wszi+1+bz是对每个可能的输出单词进行打分,再用softmax归一化就可以得到第i+1个词的概率pi+1。
机器翻译任务的生成过程,通俗来讲就是根据预先训练的模型来翻译源语言句子。生成过程中的解码阶段和上述训练过程的有所差异,具体介绍请见柱搜索算法。 注意力机制如果编码阶段的输出是一个固定维度的向量,会带来以下两个问题:1)不论源语言序列的长度是5个词还是50个词,如果都用固定维度的向量去编码其中的语义和句法结构信息,对模型来说是一个非常高的要求,特别是对长句子序列而言;2)直觉上,当人类翻译一句话时,会对与当前译文更相关的源语言片段上给予更多关注,且关注点会随着翻译的进行而改变。而固定维度的向量则相当于,任何时刻都对源语言所有信息给予了同等程度的关注,这是不合理的。因此,Bahdanau等人[4]引入注意力(attention)机制,可以对编码后的上下文片段进行解码,以此来解决长句子的特征学习问题。下面介绍在注意力机制下的解码器结构。 与简单的解码器不同,这里zi的计算公式为: zi+1=ϕθ′(ci,ui,zi)
可见,源语言句子的编码向量表示为第i个词的上下文片段ci,即针对每一个目标语言中的词ui,都有一个特定的ci与之对应。ci的计算公式如下: ci=T∑j=1aijhj,ai=[ai1,ai2,...,aiT]
从公式中可以看出,注意力机制是通过对编码器中各时刻的RNN状态hj进行加权平均实现的。权重aij表示目标语言中第i个词对源语言中第j个词的注意力大小,aij的计算公式如下: begin{align} a_{ij}&=frac{exp(e_{ij})}{sum_{k=1}^{T}exp(e_{ik})}\e_{ij}&=align(z_i,h_j)\end{align} 其中,align可以看作是一个对齐模型,用来衡量目标语言中第i个词和源语言中第j个词的匹配程度。具体而言,这个程度是通过解码RNN的第i个隐层状态zi和源语言句子的第j个上下文片段hj计算得到的。传统的对齐模型中,目标语言的每个词明确对应源语言的一个或多个词(hard alignment);而在注意力模型中采用的是soft alignment,即任何两个目标语言和源语言词间均存在一定的关联,且这个关联强度是由模型计算得到的实数,因此可以融入整个NMT框架,并通过反向传播算法进行训练。
柱搜索算法柱搜索(beam search)是一种启发式图搜索算法,用于在图或树中搜索有限集合中的最优扩展节点,通常用在解空间非常大的系统(如机器翻译、语音识别)中,原因是内存无法装下图或树中所有展开的解。如在机器翻译任务中希望翻译“ 柱搜索算法使用广度优先策略建立搜索树,在树的每一层,按照启发代价(heuristic cost)(本教程中,为生成词的log概率之和)对节点进行排序,然后仅留下预先确定的个数(文献中通常称为beam width、beam size、柱宽度等)的节点。只有这些节点会在下一层继续扩展,其他节点就被剪掉了,也就是说保留了质量较高的节点,剪枝了质量较差的节点。因此,搜索所占用的空间和时间大幅减少,但缺点是无法保证一定获得最优解。 使用柱搜索算法的解码阶段,目标是最大化生成序列的概率。思路是:
注意:zi+1和pi+1的计算公式同解码器中的一样。且由于生成时的每一步都是通过贪心法实现的,因此并不能保证得到全局最优解。 数据介绍本教程使用WMT-14数据集中的bitexts(after selection)作为训练集,dev+test data作为测试集和生成集。 数据预处理我们的预处理流程包括两步:
- 将每个源语言到目标语言的平行语料库文件合并为一个文件:
- 合并每个 示例数据因为完整的数据集数据量较大,为了验证训练流程,PaddlePaddle接口paddle.dataset.wmt14中默认提供了一个经过预处理的较小规模的数据集。 该数据集有193319条训练数据,6003条测试数据,词典长度为30000。因为数据规模限制,使用该数据集训练出来的模型效果无法保证。 流程说明paddle初始化# 加载 paddle的python包 import sys import paddle.v2 as paddle # 配置只使用cpu,并且使用一个cpu进行训练 paddle.init(use_gpu=False, trainer_count=1) # 训练模式False,生成模式True is_generating = False 模型结构
src_word_id = paddle.layer.data( name='source_language_word', type=paddle.data_type.integer_value_sequence(source_dict_dim)) src_embedding = paddle.layer.embedding( input=src_word_id, size=word_vector_dim) src_forward = paddle.networks.simple_gru( input=src_embedding, size=encoder_size) src_backward = paddle.networks.simple_gru( input=src_embedding, size=encoder_size, reverse=True) encoded_vector = paddle.layer.concat(input=[src_forward, src_backward])
encoded_proj = paddle.layer.fc( act=paddle.activation.Linear(), size=decoder_size, bias_attr=False, input=encoded_vector)
backward_first = paddle.layer.first_seq(input=src_backward) decoder_boot = paddle.layer.fc( size=decoder_size, act=paddle.activation.Tanh(), bias_attr=False, input=backward_first)
def gru_decoder_with_attention(enc_vec, enc_proj, current_word): decoder_mem = paddle.layer.memory( name='gru_decoder', size=decoder_size, boot_layer=decoder_boot) context = paddle.networks.simple_attention( encoded_sequence=enc_vec, encoded_proj=enc_proj, decoder_state=decoder_mem) decoder_inputs = paddle.layer.fc( act=paddle.activation.Linear(), size=decoder_size * 3, bias_attr=False, input=[context, current_word], layer_attr=paddle.attr.ExtraLayerAttribute( error_clipping_threshold=100.0)) gru_step = paddle.layer.gru_step( name='gru_decoder', input=decoder_inputs, output_mem=decoder_mem, size=decoder_size) out = paddle.layer.mixed( size=target_dict_dim, bias_attr=True, act=paddle.activation.Softmax(), input=paddle.layer.full_matrix_projection(input=gru_step)) return out
if not is_generating: trg_embedding = paddle.layer.embedding( input=paddle.layer.data( name='target_language_word', type=paddle.data_type.integer_value_sequence(target_dict_dim)), size=word_vector_dim, param_attr=paddle.attr.ParamAttr(name='_target_language_embedding')) group_inputs.append(trg_embedding) # For decoder equipped with attention mechanism, in training, # target embeding (the groudtruth) is the data input, # while encoded source sequence is accessed to as an unbounded memory. # Here, the StaticInput defines a read-only memory # for the recurrent_group. decoder = paddle.layer.recurrent_group( name=decoder_group_name, step=gru_decoder_with_attention, input=group_inputs) lbl = paddle.layer.data( name='target_language_next_word', type=paddle.data_type.integer_value_sequence(target_dict_dim)) cost = paddle.layer.classification_cost(input=decoder, label=lbl)
if is_generating: # In generation, the decoder predicts a next target word based on # the encoded source sequence and the previous generated target word. # The encoded source sequence (encoder's output) must be specified by # StaticInput, which is a read-only memory. # Embedding of the previous generated word is automatically retrieved # by GeneratedInputs initialized by a start mark <s>. trg_embedding = paddle.layer.GeneratedInput( size=target_dict_dim, embedding_name='_target_language_embedding', embedding_size=word_vector_dim) group_inputs.append(trg_embedding) beam_gen = paddle.layer.beam_search( name=decoder_group_name, step=gru_decoder_with_attention, input=group_inputs, bos_id=0, eos_id=1, beam_size=beam_size, max_length=max_length) 注意:我们提供的配置在Bahdanau的论文[4]上做了一些简化,可参考issue #1133。 训练模型
训练开始后,可以观察到event_handler输出的日志如下: Pass 0, Batch 0, Cost 148.444983, {'classification_error_evaluator': 1.0} ......... Pass 0, Batch 10, Cost 335.896802, {'classification_error_evaluator': 0.9325153231620789} ......... 生成模型
生成开始后,可以观察到输出的日志如下: Les <unk> se <unk> au sujet de la largeur des sièges alors que de grosses commandes sont en jeu -19.0196 The <unk> will be rotated about the width of the seats , while large orders are at stake . <e> -19.1131 The <unk> will be rotated about the width of the seats , while large commands are at stake . <e> -19.5129 The <unk> will be rotated about the width of the seats , while large commands are at play . <e> 总结端到端的神经网络机器翻译是近几年兴起的一种全新的机器翻译方法。本章中,我们介绍了NMT中典型的“编码器-解码器”框架和“注意力”机制。由于NMT是一个典型的Seq2Seq(Sequence to Sequence,序列到序列)学习问题,因此,Seq2Seq中的query改写(query rewriting)、摘要、单轮对话等问题都可以用本教程的模型来解决。 |
|