前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【序列到序列学习】使用Scheduled Sampling改善翻译质量

【序列到序列学习】使用Scheduled Sampling改善翻译质量

作者头像
用户1386409
发布2018-04-02 14:39:09
5.5K0
发布2018-04-02 14:39:09
举报
文章被收录于专栏:PaddlePaddle

生成古诗词

序列到序列学习实现两个甚至是多个不定长模型之间的映射,有着广泛的应用,包括:机器翻译、智能对话与问答、广告创意语料生成、自动编码(如金融画像编码)、判断多个文本串之间的语义相关性等。

在序列到序列学习任务中,我们首先以机器翻译任务为例,提供了多种改进模型供大家学习和使用。包括:不带注意力机制的序列到序列映射模型,这一模型是所有序列到序列学习模型的基础;使用Scheduled Sampling改善RNN模型在生成任务中的错误累积问题;带外部记忆机制的神经机器翻译,通过增强神经网络的记忆能力,来完成复杂的序列到序列学习任务。除机器翻译任务之外,我们也提供了一个基于深层LSTM网络生成古诗词,实现同语言生成的模型。

【序列到序列学习】

02

使用Scheduled Sampling

改善翻译质量

|1. 概述

序列生成任务的生成目标是在给定源输入的条件下,最大化目标序列的概率。训练时该模型将目标序列中的真实元素作为解码器每一步的输入,然后最大化下一个元素的概率。生成时上一步解码得到的元素被用作当前的输入,然后生成下一个元素。可见这种情况下训练阶段和生成阶段的解码器输入数据的概率分布并不一致。

Scheduled Sampling [1]是一种解决训练和生成时输入数据分布不一致的方法。在训练早期该方法主要使用目标序列中的真实元素作为解码器输入,可以将模型从随机初始化的状态快速引导至一个合理的状态。随着训练的进行,该方法会逐渐更多地使用生成的元素作为解码器输入,以解决数据分布不一致的问题。

标准的序列到序列模型中,如果序列前面生成了错误的元素,后面的输入状态将会收到影响,而该误差会随着生成过程不断向后累积。Scheduled Sampling以一定概率将生成的元素作为解码器输入,这样即使前面生成错误,其训练目标仍然是最大化真实目标序列的概率,模型会朝着正确的方向进行训练。因此这种方式增加了模型的容错能力

|2. 算法简介

Scheduled Sampling主要应用在序列到序列模型的训练阶段,而生成阶段则不需要使用。

训练阶段解码器在最大化第t个元素概率时,标准序列到序列模型使用上一时刻的真实元素yt−1作为输入。设上一时刻生成的元素为gt−1,Scheduled Sampling算法会以一定概率使用gt−1作为解码器输入。

设当前已经训练到了第i个mini-batch,Scheduled Sampling定义了一个概率ϵi控制解码器的输入。ϵi是一个随着i增大而衰减的变量,常见的定义方式有:

  • 线性衰减:ϵi=max(ϵ,k−c∗i),其中ϵ限制ϵi的最小值,k和c控制线性衰减的幅度。
  • 指数衰减:ϵi=ki,其中0<k<1,k控制着指数衰减的幅度。
  • 反向Sigmoid衰减:ϵi=k/(k+exp(i/k)),其中k>1,k同样控制衰减的幅度。

图1给出了这三种方式的衰减曲线,

图1. 线性衰减、指数衰减和

反向Sigmoid衰减的衰减曲线

如图2所示,在解码器的t时刻Scheduled Sampling以概率ϵi使用上一时刻的真实元素yt−1作为解码器输入,以概率1−ϵi使用上一时刻生成的元素gt−1作为解码器输入。从图1可知随着i的增大ϵi会不断减小,解码器将不断倾向于使用生成的元素作为输入,训练阶段和生成阶段的数据分布将变得越来越一致。

图2. Scheduled Sampling选择不同元素作为解码器输入示意图

|3. 模型实现

由于Scheduled Sampling是对序列到序列模型的改进,其整体实现框架与序列到序列模型较为相似。为突出本文重点,这里仅介绍与Scheduled Sampling相关的部分,完整的代码见network_conf.py。

首先导入需要的包,并定义控制衰减概率的类RandomScheduleGenerator,如下:

import numpy as np

import math

class RandomScheduleGenerator:

"""

The random sampling rate for scheduled sampling algoithm, which uses devcayed

sampling rate.

"""

...

下面将分别定义类RandomScheduleGenerator的__init__、getScheduleRate和processBatch三个方法。

__init__方法对类进行初始化,其schedule_type参数指定了使用哪种衰减方式,可选的方式有constant、linear、exponential和inverse_sigmoid。constant指对所有的mini-batch使用固定的ϵi,linear指线性衰减方式,exponential表示指数衰减方式,inverse_sigmoid表示反向Sigmoid衰减。__init__方法的参数a和b表示衰减方法的参数,需要在验证集上调优。self.schedule_computers将衰减方式映射为计算ϵi的函数。最后一行根据schedule_type将选择的衰减函数赋给self.schedule_computer变量。

def __init__(self, schedule_type, a, b):

"""

schduled_type: is the type of the decay. It supports constant, linear,

exponential, and inverse_sigmoid right now.

a: parameter of the decay (MUST BE DOUBLE)

b: parameter of the decay (MUST BE DOUBLE)

"""

self.schedule_type = schedule_type

self.a = a

self.b = b

self.data_processed_ = 0

self.schedule_computers = {

"constant": lambda a, b, d: a,

"linear": lambda a, b, d: max(a, 1 - d / b),

"exponential": lambda a, b, d: pow(a, d / b),

"inverse_sigmoid": lambda a, b, d: b / (b + math.exp(d * a / b)),

}

assert (self.schedule_type in self.schedule_computers)

self.schedule_computer = self.schedule_computers[self.schedule_type]

getScheduleRate根据衰减函数和已经处理的数据量计算ϵi。

def getScheduleRate(self): """ Get the schedule sampling rate. Usually not needed to be called by the users """ return self.schedule_computer(self.a, self.b, self.data_processed_)

processBatch方法根据概率值ϵi进行采样,得到indexes,indexes中每个元素取值为0的概率为ϵi,取值为1的概率为1−ϵi。indexes决定了解码器的输入是真实元素还是生成的元素,取值为0表示使用真实元素,取值为1表示使用生成的元素。

def processBatch(self, batch_size):

"""

Get a batch_size of sampled indexes. These indexes can be passed to a

MultiplexLayer to select from the grouth truth and generated samples

from the last time step.

"""

rate = self.getScheduleRate()

numbers = np.random.rand(batch_size)

indexes = (numbers >= rate).astype('int32').tolist()

self.data_processed_ += batch_size

return indexes

Scheduled Sampling需要在序列到序列模型的基础上增加一个输入true_token_flag,以控制解码器输入。

true_token_flags = paddle.layer.data(

name='true_token_flag',

type=paddle.data_type.integer_value_sequence(2))

这里还需要对原始reader进行封装,增加true_token_flag的数据生成器。下面以线性衰减为例说明如何调用上面定义的RandomScheduleGenerator产生true_token_flag的输入数据。

def gen_schedule_data(reader,

schedule_type="linear",

decay_a=0.75,

decay_b=1000000):

"""

Creates a data reader for scheduled sampling.

Output from the iterator that created by original reader will be

appended with "true_token_flag" to indicate whether to use true token.

:param reader: the original reader.

:type reader: callable

:param schedule_type: the type of sampling rate decay.

:type schedule_type: str

:param decay_a: the decay parameter a.

:type decay_a: float

:param decay_b: the decay parameter b.

:type decay_b: float

:return: the new reader with the field "true_token_flag".

:rtype: callable

"""

schedule_generator = RandomScheduleGenerator(schedule_type, decay_a, decay_b)

def data_reader():

for src_ids, trg_ids, trg_ids_next in reader():

yield src_ids, trg_ids, trg_ids_next, \

[0] + schedule_generator.processBatch(len(trg_ids) - 1)

return data_reader

这段代码在原始输入数据(即源序列元素src_ids、目标序列元素trg_ids和目标序列下一个元素trg_ids_next)后追加了控制解码器输入的数据。由于解码器第一个元素是序列开始符,因此将追加的数据第一个元素设置为0,表示解码器第一步始终使用真实目标序列的第一个元素(即序列开始符)。

训练时recurrent_group每一步调用的解码器函数如下:

def gru_decoder_with_attention_train(enc_vec, enc_proj, true_word,

true_token_flag):

"""

The decoder step for training.

:param enc_vec: the encoder vector for attention

:type enc_vec: LayerOutput

:param enc_proj: the encoder projection for attention

:type enc_proj: LayerOutput

:param true_word: the ground-truth target word

:type true_word: LayerOutput

:param true_token_flag: the flag of using the ground-truth target word

:type true_token_flag: LayerOutput

:return: the softmax output layer

:rtype: LayerOutput

"""

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)

gru_out_memory = paddle.layer.memory(

name='gru_out', size=target_dict_dim)

generated_word = paddle.layer.max_id(input=gru_out_memory)

generated_word_emb = paddle.layer.embedding(

input=generated_word,

size=word_vector_dim,

param_attr=paddle.attr.ParamAttr(name='_target_language_embedding'))

current_word = paddle.layer.multiplex(

input=[true_token_flag, true_word, generated_word_emb])

decoder_inputs = paddle.layer.fc(

input=[context, current_word],

size=decoder_size * 3,

act=paddle.activation.Linear(),

bias_attr=False)

gru_step = paddle.layer.gru_step(

name='gru_decoder',

input=decoder_inputs,

output_mem=decoder_mem,

size=decoder_size)

out = paddle.layer.fc(

name='gru_out',

input=gru_step,

size=target_dict_dim,

act=paddle.activation.Softmax())

return out

该函数使用memory层gru_out_memory记忆上一时刻生成的元素,根据gru_out_memory选择概率最大的词语generated_word作为生成的词语。multiplex层会在真实元素true_word和生成的元素generated_word之间做出选择,并将选择的结果作为解码器输入。multiplex层使用了三个输入,分别为true_token_flag、true_word和generated_word_emb。对于这三个输入中每个元素,若true_token_flag中的值为0,则multiplex层输出true_word中的相应元素;若true_token_flag中的值为1,则multiplex层输出generated_word_emb中的相应元素。

【参考文献】

  1. Bengio S, Vinyals O, Jaitly N, et al. Scheduled sampling for sequence prediction with recurrent neural networks//Advances in Neural Information Processing Systems. 2015: 1171-1179.
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-03-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PaddlePaddle 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
机器翻译
机器翻译(Tencent Machine Translation,TMT)结合了神经机器翻译和统计机器翻译的优点,从大规模双语语料库自动学习翻译知识,实现从源语言文本到目标语言文本的自动翻译,目前可支持十余种语言的互译。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档