想做一个"对对联" AI?PaddlePaddle授你以"渔"——生成模型Transformer

原创
2019/01/26 21:49
阅读数 174

640?wx_fmt=jpeg


注意:本文不会教你怎么用PaddlePaddle写对联。

随着春节临近,各种“人工智能对对联”的新闻频繁出现,比如前段时间被网友们玩坏的《王斌给您对对联》,还有百度近期推出的“刷脸定制春联”。

看别人玩的这么热闹,你是不是也想搞一个?今天小编就给大家介绍一个PaddlePaddle的官方模型——Transformer,一个用以完成机器翻译等序列到序列(Seq2Seq)学习任务的一种全新网络结构,其完全使用注意力机制来实现序列到序列的建模。

文档中作为示例的不是一个对对联任务,但是聪明的你看过之后一定会想到怎么用Transformer对对联,甚至是写诗。也许明年火起来的,就是你了!


Github地址:

https://github.com/PaddlePaddle/models/blob/develop/fluid/PaddleNLP/neural_machine_translation/transformer/README_cn.md

运行本目录下的程序示例需要使用 PaddlePaddle 最新的 develop branch 版本。如果您的 PaddlePaddle 安装版本低于此要求,请按照安装文档中的说明更新 PaddlePaddle 安装版本。

PaddlePaddle 安装攻略:硬货|超长超丰富的新版本PaddlePaddle安装攻略


Transformer

以下是本例的简要目录结构及说明:

.
├── images               # README 文档中的图片
├── config.py            # 训练、预测以及模型参数配置
├── infer.py             # 预测脚本
├── model.py             # 模型定义
├── optim.py             # learning rate scheduling 计算程序
├── reader.py            # 数据读取接口
├── README.md            # 文档
├── train.py             # 训练脚本
└── gen_data.sh          # 数据生成脚本

简介

Transformer 是论文 《Attention Is All You Need 》中提出的用以完成机器翻译(machine translation, MT)等序列到序列(sequence to sequence, Seq2Seq)学习任务的一种全新网络结构,其完全使用注意力(Attention)机制来实现序列到序列的建模。

相较于此前 Seq2Seq 模型中广泛使用的循环神经网络(Recurrent Neural Network, RNN),使用(Self)Attention 进行输入序列到输出序列的变换主要具有以下优势:

  • 计算复杂度小

特征维度为 d 、长度为 n 的序列,在 RNN 中计算复杂度为 O(n * d * d) (n 个时间步,每个时间步计算 d 维的矩阵向量乘法),在 Self-Attention 中计算复杂度为 O(n * n * d) (n 个时间步两两计算 d 维的向量点积或其他相关度函数),n 通常要小于 d 。

计算并行度高

RNN 中当前时间步的计算要依赖前一个时间步的计算结果;Self-Attention 中各时间步的计算只依赖输入不依赖之前时间步输出,各时间步可以完全并行。

容易学习长程依赖(long-range dependencies)

RNN 中相距为 n 的两个位置间的关联需要 n 步才能建立;Self-Attention 中任何两个位置都直接相连;路径越短信号传播越容易。

这些也在机器翻译任务中得到了印证,Transformer 模型在训练时间大幅减少的同时取得了 WMT'14 英德翻译任务 BLEU 值的新高。此外,Transformer 在应用于成分句法分析(Constituency Parsing)任务时也有着不俗的表现,这也说明其具有较高的通用性,容易迁移到其他应用场景中。这些都表明 Transformer 有着广阔的前景。

模型概览

Transformer 同样使用了 Seq2Seq 模型中典型的编码器-解码器(Encoder-Decoder)的框架结构,整体网络结构如图1所示。

640?wx_fmt=png

 图 1. Transformer 网络结构图

Encoder 由若干相同的 layer 堆叠组成,每个 layer 主要由多头注意力(Multi-Head Attention)和全连接的前馈(Feed-Forward)网络这两个 sub-layer 构成。

  • Multi-Head Attention 在这里用于实现 Self-Attention,相比于简单的 Attention 机制,其将输入进行多路线性变换后分别计算 Attention 的结果,并将所有结果拼接后再次进行线性变换作为输出。参见图2,其中 Attention 使用的是点积(Dot-Product),并在点积后进行了 scale 的处理以避免因点积结果过大进入 softmax 的饱和区域。

  • Feed-Forward 网络会对序列中的每个位置进行相同的计算(Position-wise),其采用的是两次线性变换中间加以 ReLU 激活的结构。

此外,每个 sub-layer 后还施以 Residual Connection和 Layer Normalization [3]来促进梯度传播和模型收敛。

640?wx_fmt=png

 图 2. Multi-Head Attention

Decoder 具有和 Encoder 类似的结构,只是相比于组成 Encoder 的 layer ,在组成 Decoder 的 layer 中还多了一个 Multi-Head Attention 的 sub-layer 来实现对 Encoder 输出的 Attention,这个 Encoder-Decoder Attention 在其他 Seq2Seq 模型中也是存在的。

数据准备

WMT 数据集是机器翻译领域公认的主流数据集,WMT'16 EN-DE 数据集是其中一个中等规模的数据集,也是 Transformer 论文中用到的一个数据集,这里将其作为示例,可以直接运行 gen_data.sh 脚本进行 WMT'16 EN-DE 数据集的下载和预处理。数据处理过程主要包括 Tokenize 和 BPE 编码(byte-pair encoding);BPE 编码的数据能够较好的解决未登录词(out-of-vocabulary,OOV)的问题[4],其在 Transformer 论文中也被使用。运行成功后,将会生成文件夹 gen_data,其目录结构如下(可在 gen_data.sh 中修改):

gen_data/wmt16_ende_data_bpe 中是我们最终使用的英德翻译数据,其中 train.tok.clean.bpe.32000.en-de 为训练数据,newstest2016.tok.bpe.32000.en-de 等为验证和测试数据,。vocab_all.bpe.32000 为相应的词典文件(已加入 <s> 、<e>和 <unk> 这三个特殊符号,源语言和目标语言共享该词典文件)。

对于其他自定义数据,转换为类似 train.tok.clean.bpe.32000.en-de 的数据格式(\t 分隔的源语言和目标语言句子对,句子中的 token 之间使用空格分隔)即可;如需使用 BPE 编码,可参考,亦可以使用类似 WMT,使用 gen_data.sh 进行处理。

模型训练

train.py 是模型训练脚本。以英德翻译数据为例,可以执行以下命令进行模型训练:

上述命令中设置了源语言词典文件路径(src_vocab_fpath)、目标语言词典文件路径(trg_vocab_fpath)、训练数据文件(train_file_pattern,支持通配符)等数据相关的参数和构造 batch 方式(use_token_batch 指定了数据按照 token 数目或者 sequence 数目组成 batch)等 reader 相关的参数。有关这些参数更详细的信息可以通过执行以下命令查看:

更多模型训练相关的参数则在 config.py 中的 ModelHyperParams 和 TrainTaskConfig 内定义;ModelHyperParams 定义了 embedding 维度等模型超参数,TrainTaskConfig 定义了 warmup 步数等训练需要的参数。这些参数默认使用了 Transformer 论文中 base model 的配置,如需调整可以在该脚本中进行修改。另外这些参数同样可在执行训练脚本的命令行中设置,传入的配置会合并并覆盖 config.py 中的配置,如可以通过以下命令来训练 Transformer 论文中的 big model (如显存不够可适当减小 batch size 的值,或设置 max_length 200 过滤过长的句子,或修改某些显存使用相关环境变量的值):

有关这些参数更详细信息的请参考 config.py 中的注释说明。

训练时默认使用所有 GPU,可以通过 CUDA_VISIBLE_DEVICES 环境变量来设置使用的 GPU 数目。也可以只使用 CPU 训练(通过参数 --divice CPU 设置),训练速度相对较慢。在训练过程中,每隔一定 iteration 后(通过参数 save_freq 设置,默认为10000)保存模型到参数 model_dir 指定的目录,每个 epoch 结束后也会保存 checkpiont 到 ckpt_dir 指定的目录,每隔一定数目的 iteration (通过参数 --fetch_steps 设置,默认为100)将打印如下的日志到标准输出:

模型预测

infer.py 是模型预测脚本。以英德翻译数据为例,模型训练完成后可以执行以下命令对指定文件中的文本进行翻译:

和模型训练时类似,预测时也需要设置数据和 reader 相关的参数,并可以执行 python infer.py --help 查看这些参数的说明(部分参数意义和训练时略有不同);同样可以在预测命令中设置模型超参数,但应与模型训练时的设置一致;此外相比于模型训练,预测时还有一些额外的参数,如需要设置 model_path 来给出模型所在目录,可以设置 beam_size 和 max_out_len来指定 Beam Search 算法的搜索宽度和最大深度(翻译长度),这些参数也可以在 config.py 中的 InferTaskConfig 内查阅注释说明并进行更改设置。

执行以上预测命令会打印翻译结果到标准输出,每行输出是对应行输入的得分最高的翻译。对于使用 BPE 的英德数据,预测出的翻译结果也将是 BPE 表示的数据,要还原成原始的数据(这里指 tokenize 后的数据)才能进行正确的评估,可以使用以下命令来恢复 predict.txt 内的翻译结果到 predict.tok.txt 中(无需再次 tokenize 处理):

接下来就可以使用参考翻译对翻译结果进行 BLEU 指标的评估了。以英德翻译 newstest2016.tok.de 数据为例,执行如下命令:

可以看到类似如下的结果(为单机两卡训练 200K 个 iteration 后模型的预测结果)。

BLEU = 33.08, 64.2/39.2/26.4/18.5 (BP=0.994, ratio=0.994, hyp_len=61971, ref_len=62362)

目前在未使用 model average 的情况下,英德翻译 base model 八卡训练 100K 个 iteration 后测试 BLEU 值如下:

测试集

newstest2014

newstest2015

newstest2016

BLEU

26.25

29.15

33.64

分布式训练

Transformer 模型支持同步或者异步的分布式训练。分布式的配置主要两个方面:

1 命令行配置

  • --local,有两个取值,True表示单机训练,而False表示使用分布式训练。默认为单机训练模式。

  • --sync,有两个取值,但只有当--local参数为False才会产生影响,其中True表示同步训练模式,False表示异步训练模式。默认为同步训练模式。

2 环境变量配置

在分布式训练模式下,会手动配置训练的trainer数量和pserver数量。在网络拓扑上,每一个trainer都会和每一个pserver相连,pserver作为服务端,而trainer作为客户端。下面分pserver和trainer说明具体的参数配置:

pserver配置

  • PADDLE_IS_LOCAL=[0|1] 是否是分布式训练,0标识是分布式,1标识是单机

  • TRAINING_ROLE=PSERVER 标识当前节点是pserver

  • POD_IP=ip 设置当前pserver使用对外服务的地址

  • PADDLE_PORT=port 设置当前pserver对外服务监听端口号,和POD_IP共同构成对外的唯一标识

  • PADDLE_TRAINERS_NUM=num 设置pserver连接的trainer的数量

下面是配置的示例, 使用两个pserver, 192.168.2.2上的配置如下:

export PADDLE_PSERVERS=192.168.2.2,192.168.2.3
export POD_IP=192.168.2.2
export PADDLE_TRAINERS_NUM=2
export TRAINING_ROLE=PSERVER
export PADDLE_IS_LOCAL=0
export PADDLE_PORT=6177

192.168.2.3上的配置如下:

export PADDLE_PSERVERS=192.168.2.2,192.168.2.3
export POD_IP=192.168.2.3
export PADDLE_TRAINERS_NUM=2
export TRAINING_ROLE=PSERVER
export PADDLE_IS_LOCAL=0
export PADDLE_PORT=6177

trainer配置

  • PADDLE_IS_LOCAL=[0|1] 是否是分布式训练,0标识是分布式,1标识是单机

  • TRAINING_ROLE=TRAINER 标识当前节点是trainer

  • PADDLE_PSERVERS=[ip1,ip2,……] 设置pserver的ip地址,用于告知trainer互联的pserver的ip, 使用,分割

  • PADDLE_TRAINER_ID=num 设置当前节点的编号, 编号的取值范围为0到N-1的整数

  • PADDLE_PORT=port 设置请求的pserver服务端口号

下面是配置的示例, 使用两个trainer, trainer 1上的配置如下:

export TRAINING_ROLE=TRAINER
export PADDLE_PSERVERS=192.168.2.2,192.168.2.3
export PADDLE_TRAINERS_NUM=2
export PADDLE_TRAINER_ID=0
export PADDLE_IS_LOCAL=0
export PADDLE_PORT=6177

trainer 2上的配置如下:

export TRAINING_ROLE=TRAINER
export PADDLE_PSERVERS=192.168.2.2,192.168.2.3
export PADDLE_TRAINERS_NUM=2
export PADDLE_TRAINER_ID=1
export PADDLE_IS_LOCAL=0
export PADDLE_PORT=6177

参考文献
  1. Vaswani A, Shazeer N, Parmar N, et al. Attention is all you need[C]//Advances in Neural Information Processing Systems. 2017: 6000-6010.

  2. He K, Zhang X, Ren S, et al. Deep residual learning for image recognition[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2016: 770-778.

  3. Ba J L, Kiros J R, Hinton G E. Layer normalization[J]. arXiv preprint arXiv:1607.06450, 2016.

  4. Sennrich R, Haddow B, Birch A. Neural machine translation of rare words with subword units[J]. arXiv preprint arXiv:1508.07909, 2015.


Github地址:

https://github.com/PaddlePaddle/models/blob/develop/fluid/PaddleNLP/neural_machine_translation/transformer/README_cn.md

或点击阅读原文




期待你早日用PaddlePaddle做出自己的对联AI,也许明年火的就是你!



640?wx_fmt=png




本文分享 CSDN - 飞桨PaddlePaddle。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部