Zixuan/couplet (#409)
* 修改文档结构 增加train on pai部分 修改文档内容 修改部分代码 * 调整结构 * 更改图片位置 * 调整文件结构 * 修改部分路径 * Create Learningpath.md * Update Learningpath.md * 调整表格 * Update Learningpath.md * Update Learningpath.md * Update Learningpath.md * Update Learningpath.md * Update Learningpath.md * 修改引言、发展历程等 * 修改引言 * 修改架构,修改openpai训练流程 * Delete Learningpath.md
@@ -2,58 +2,98 @@ Copyright © Microsoft Corporation. All rights reserved.
|
||||
适用于[License](https://github.com/Microsoft/ai-edu/blob/master/LICENSE.md)版权许可
|
||||
|
||||
|
||||
# AI对联生成
|
||||
-------
|
||||
# 智能对联
|
||||
|
||||
# 场景描述
|
||||
自然语言处理(Natural Language Processing, NLP)是人工智能领域中最重要的分支之一。本案例使用深度学习的方法,利用常见的机器翻译模型,构建了一个智能对联生成的微信小程序。通过对该案例的学习,读者能够了解NLP领域的基础理论,包括词嵌入(Word Embedding)、编码解码模型(Encoder-Decoder)、注意力机制(Attention)等重要知识点,并具备应用、实践能力。通过阅读详实的步骤介绍和参考代码,读者也能了解RNN、LSTM、GRU、Transformer等流行算法,以及Tensor2Tensor等模型库的使用,从而在应用场景中将NLP的理论知识转变成相应的实战技能。
|
||||
|
||||
## 对联的由来及特点
|
||||
该案例面向对象广泛,扩展性强,不但覆盖了NLP的几大主要知识点,还进一步结合了计算机视觉(Computer Vision)中目标检测、图像分割、图像标注等知识点。初学者可通过复现此案例来学习NLP相关的理论知识,在实践中加深对理论的理解、提高动手能力;对于进阶者,可以通过研究优化对联生成模型等方面,对该案例应用做进一步的扩展,从而提高分析、研究能力。
|
||||
|
||||
对联,也称“楹联”、“对子”,是一种由字数相同的两句话组成的对仗工整、韵律协调、语义完整的文学形式。它发源于我国古诗的对偶句,始创于五代时期,盛于明清,至今已有一千多年的历史了。对联的形式工整、平仄协调的特点,是一字一音、音形义统一的汉字特色的体现,所以,对联是汉语语言特有的文学形式,是中华民族的文化瑰宝,是我国的重要文化遗产。
|
||||
# 目录
|
||||
|
||||
在我国民间,对联有着广泛的应用。比如,过年时家门上贴春联,商店开业时门上挂对联,以及娱乐时的对对联游戏。
|
||||
* [案例介绍](#案例介绍)
|
||||
* [使用场景](#使用场景)
|
||||
* [发展历程及实现方法](#发展历程及实现方法)
|
||||
* [案例价值](#案例价值)
|
||||
* [核心知识点](#核心知识点)
|
||||
* [先修知识](#先修知识)
|
||||
* [环境与工具](#环境与工具)
|
||||
* [软件依赖](#软件依赖)
|
||||
* [案例大纲](#案例大纲)
|
||||
* [推荐学习时长](#推荐学习时长)
|
||||
* [案例详解](#案例详解)
|
||||
* [程序结构](#程序结构)
|
||||
* [工具包的选择](#工具包的选择)
|
||||
* [数据收集](#数据收集)
|
||||
* [数据预处理](#数据预处理)
|
||||
* [模型训练](#模型训练)
|
||||
* [模型推理](#模型推理)
|
||||
* [搭建后端服务](#搭建后端服务)
|
||||
* [作业和挑战](#作业和挑战)
|
||||
|
||||
对联的长度不定,短的可以只有一两个字;长的则可达几百个字。
|
||||
|
||||
## 对联的自动生成
|
||||
|
||||
### 使用统计机器学习
|
||||
# 案例介绍
|
||||
|
||||
在基于统计的机器翻译中,规则是由机器自动从大规模的语料中学习得到的,而非由人主动提供完整的规则。
|
||||
## 使用场景
|
||||
|
||||
对联是汉语语言特有的文学形式,是中华民族的文化瑰宝,是我国的重要文化遗产。在我国民间,对联有着十分广泛的应用。其中,最为常见的当属春联了。快过年的时候,每家每户必定会在自家门口贴上一副精选挑选的春联,这里面饱含着劳动人民对新的一年最美好的期盼和最朴实的祝愿:或是期许家庭和睦,或是盼望身体健康,亦或期待学有所成……
|
||||
|
||||
在万千词海中,我们如何才能带着对未来的期盼,找到最适合自己的又最为独特的对联?《联景联情》或是你最佳的选择。在这个案例中,我们开发了一款由图片生成对联的微信小程序《联景联情》。上传代表你的祝愿的图片,让《联景联情》为你创作专属于你的对联:希望新年考上理想大学,那就上传你心中的梦想学校大门的图片吧;希望尝遍人间美味,那就上传你还没吃过的美食照片吧!
|
||||
|
||||
用户在《联景联情》中任意上传一张图像,AI小程序可以根据识别出的场景和意境,结合信、达、雅的准则,智能生成3组5-7字的美文上下联,用户可以选择喜欢的对联配图发朋友圈/微博抒情,或者在其他特定场合展示文采,适合传播的属性非常适合现代人的社交展示使用场景。
|
||||
|
||||
扫描如下二维码即可打开小程序体验:
|
||||
|
||||

|
||||
|
||||
在此案例中,我们将重点介绍对联生成的实现过程。
|
||||
|
||||
## 发展历程及实现方法
|
||||
|
||||
机器翻译的发展经历了以下几个阶段(图源自[知乎](https://www.zhihu.com/question/24588198/answer/634996172?edition=yidianzixun&utm_source=yidianzixun)):
|
||||
|
||||

|
||||
|
||||
|
||||
由最初的基于规则的方法,发展到统计机器翻译,再到现在的神经网络机器翻译。
|
||||
|
||||
### 基于规则
|
||||
|
||||
基于规则的方法是由人来提供翻译规则,从词语到词语的对应,都由人来提供。由于该方法需要人类语言专家来设计规则,且规则复杂,因此开发周期长,成本高。由于基于规则的方法,通常是字字对应的转化,没有考虑上下文,因此翻译质量通常会由于使用场景的不同而产生较大的差异。
|
||||
|
||||
|
||||
### 统计机器翻译
|
||||
|
||||
在基于统计的机器翻译中,规则是由机器自动从大规模的语料中学习得到的,而非由人主动提供完整的规则。这种方法的成本较低,因为机器可以利用大量数据自动学习对应的规则,而无需人的参与。由于统计机器翻译基于大量的语料库,因此翻译质量易受语料库的多寡影响。
|
||||
|
||||
微软亚洲研究院周明老师团队早在十几年前就已经使用基于短语的统计机器学习的方法,实现了电脑自动对联系统,效果非常好,很好的展现了中国经典文化的魅力,收获了非常多的赞誉。在线体验地址是 [这里](http://duilian.msra.cn)。
|
||||
|
||||
### 使用深度学习
|
||||
### 神经网络机器翻译
|
||||
|
||||
近年来,深度神经网络学习的发展为机器翻译提供了新的思路。通常情况下,神经机器翻译使用编码器-解码器框架。编码阶段将整个源序列编码成一个(或一组)向量,解码阶段通过最大化预测序列概率,从中解码出整个目标序列,完成翻译过程。
|
||||
|
||||

|
||||

|
||||
|
||||
编码器、解码器通常使用 RNN、LSTM 来实现,也有的用 CNN 来实现,达到了比较好的性能和结果。
|
||||
|
||||
在本次案例中,我们使用深度学习的方法,实现一个对联自动生成微信小程序 —— ***联景联情***
|
||||
在本次案例中,我们将使用深度学习的方法,实现一个对联自动生成的应用。
|
||||
|
||||
可以在微信中搜索 “联景联情” 找到并使用该小程序,或者扫描如下二维码获取。
|
||||
当前,常见的深度学习算法有
|
||||
[RNN](../B6-神经网络基本原理简明教程/Step9%20-%20RNN/19.0-循环神经网络.md)、
|
||||
[LSTM](../B6-神经网络基本原理简明教程/Step9%20-%20RNN/20.1-LSTM基本原理.md)、[GRU](../B6-神经网络基本原理简明教程/Step9%20-%20RNN/20.3-GRU基本原理.md)、
|
||||
[Transformer](http://jalammar.github.io/illustrated-transformer/)。
|
||||
|
||||

|
||||
由于前馈神经网络的输入都是一批静态数据,无法处理对于随着时间变化的连续数据,或者说无法捕捉时间序列的关系,因此科学家提出了循环神经网络(RNN,Recurrent Neural Network),通过连接多个前馈神经网络的隐藏层,从而获取每个相邻时间步之间的联系。在RNN的基础上,科学家又引入了大量优化理论并从此衍生出许多改进算法,如长短期记忆网络(Long Short-Term Memory networks, LSTM)、门控循环单元网络(Gated Recurrent Unit networks, GRU)等。
|
||||
|
||||
# 案例概要
|
||||
LSTM主要解决了RNN中容易出现的梯度爆炸和梯度消失的问题,而GRU在LSTM的基础上,做了进一步的简化,但它们始终是基于RNN的算法,十分地消耗计算资源。Transformer算法则基于全新的Attention机制,放弃了循环和卷积,采用了编码器和解码器的结构,在翻译任务上的表现也更优。因此,在这里我们选择使用transformer模型来实现我们的任务。
|
||||
|
||||
## 案例描述
|
||||
|
||||
用户在小程序中轻松上传一张图像,程序提取图像信息,自动生成3组5-7字的备选上下联。用户可选择一组心仪的对联,与图像合成你的专属对联。
|
||||
## 案例价值
|
||||
|
||||
该程序是由学习微软亚洲研究院首席研发经理邹欣老师《软件工程实践》课程的几位同学(他们均在微软亚洲研究院实习)组队完成的,在完成过程中充分利用课程所学软件工程知识,结合NLP知识和软件开发技能,完成了一个端到端的应用服务。这里可以看到他们的开发感想与总结。
|
||||
此案例特色显明,生动有趣,可以激发学生们对深度学习的兴趣。在技术层面,此案例使学生对深度学习的时序模型有直观的了解。该案例面向对象广泛,扩展性强。对初学者,可重复案例的过程;对于进阶者,不论在模型选择上,还是在模型推理上,都可以有更多的扩展,可提高学生们的探索研究能力。
|
||||
|
||||
## 程序结构
|
||||
|
||||
本案例的基本程序结构如下图所示:
|
||||
|
||||

|
||||
|
||||
后续将会对每个部分进行详细说明。
|
||||
|
||||
## 涉及知识
|
||||
# 核心知识点
|
||||
|
||||
* 使用微软认知服务(Cognitive Service)中计算机视觉(computer vision)服务
|
||||
* NLP 相关知识
|
||||
@@ -62,40 +102,79 @@ Copyright © Microsoft Corporation. All rights reserved.
|
||||
* 模型库的使用
|
||||
* Tensor2Tensor
|
||||
* Fairseq
|
||||
* 在OpenPAI上训练模型
|
||||
|
||||
## 案例价值
|
||||
|
||||
此案例特色显明,生动有趣,可以激发学生们对深度学习的兴趣。在技术层面,此案例使学生对深度学习的时序模型有直观的了解。该案例面向对象广泛,扩展性强。对初学者,可重复案例的过程;对于进阶者,不论在模型选择上,还是在模型推理上,都可以有更多的扩展,可提高学生们的探索研究能力。
|
||||
# 先修知识
|
||||
* 了解RNN的基本概念和原理
|
||||
|
||||
参考链接: [循环神经网络](../B6-神经网络基本原理简明教程/Step9%20-%20RNN/19.0-循环神经网络.md)
|
||||
|
||||
* Seq2Seq模型的基本概念
|
||||
|
||||
参考链接:[序列到序列](../B6-神经网络基本原理简明教程/Step9%20-%20RNN/20.4-序列到序列.md)
|
||||
|
||||
* 了解主流深度学习框架
|
||||
|
||||
参考链接:[tensorflow](https://www.tensorflow.org/tutorials/quickstart/beginner), [Pytorch](https://pytorch-cn.readthedocs.io/zh/latest/)
|
||||
|
||||
* 熟悉github的使用
|
||||
|
||||
参考链接:[GitHub入门](https://help.github.com/cn/github/using-git)
|
||||
|
||||
|
||||
# 环境与工具
|
||||
|
||||
本案例运行在Azure虚拟机上,虚拟机的系统为Ubuntu 16.04
|
||||
|
||||
需要的软件环境如下:
|
||||
|
||||
* Python 3.5
|
||||
* tensorflow 1.14.0
|
||||
* tensor2tensor 1.14.1
|
||||
|
||||
## 软件依赖
|
||||
|
||||
在开始之前,请确保安装好以下依赖:
|
||||
|
||||
1. 训练所需 python packages 在文件 [train_requirements.txt](./code/train/train_requirerments.txt) 中。
|
||||
2. 服务所需 python packages 在文件 [conda_requirements.txt](./code/service/conda_requirements.txt) 中。
|
||||
|
||||
安装示例:
|
||||
```
|
||||
pip install -r train_reqquirements.txt
|
||||
```
|
||||
|
||||
# 案例大纲
|
||||
|
||||
|序号|内容|关键知识点|收获实战技能|
|
||||
|:---:|---|---|---|
|
||||
| 1 | 图片信息提取 | Cognitive Service | 使用Cognitive Service提取图像内容 |
|
||||
| 2 | 分词与词检索 | 结巴分词;构建用于词检索的KD-Tree | 结巴分词库的使用; KD-Tree的构建与应用 |
|
||||
| 3 | 文本表征 | 词嵌入与词向量 | 掌握常用词嵌入的方法; 用向量表征文本; 词向量库的使用 |
|
||||
| 4 | 语言模型简介| 传统语言模型; 基于神经网络的语言模型 | 了解语言模型发展历程及基本原理 |
|
||||
| 5 | Seq2Seq序列模型 | Seq2Seq, Encoder-Decoder, Attention, Transformer | 掌握Seq2Seq模型原理 |
|
||||
| 6 | 模型库的使用 | Tensor2Tensor, Fairseq | 使用模型库构建并训练语言模型;使用训练好的模型进行推理 |
|
||||
| 7 | 应用开发 | 后端开发 | HTTP服务搭建 |
|
||||
|
||||
# 推荐学习时长
|
||||
|
||||
* 初次学习的实战者:5~8 小时
|
||||
* 有一定经验学习者:3~5 小时
|
||||
|
||||
|
||||
# 案例详解
|
||||
|
||||
## 搭建环境
|
||||
## 程序结构
|
||||
|
||||
### 证书许可
|
||||
本案例的基本程序结构如下图所示:
|
||||
|
||||
由于微信小程序使用https协议访问服务器,需要申请SSL证书。本案例申请了微软的SSL证书。
|
||||

|
||||
|
||||
### 操作系统
|
||||
后续将会对每个部分进行详细说明。
|
||||
|
||||
本案例运行在Azure虚拟机上,虚拟机的系统为Ubuntu 16.04
|
||||
|
||||
### 编程语言
|
||||
|
||||
本案例的语言使用 Python3.x ,并需要安装一些 python packages。
|
||||
|
||||
1. 训练所需 python packages 在文件 [train_requirements.txt](./src/training/train_requirerments.txt) 中。
|
||||
2. 服务所需 python packages 在文件 [conda_requirements.txt](./src/service/conda_requirements.txt) 中。
|
||||
|
||||
### 框架和模型库
|
||||
|
||||
本案例使用 tensorflow 的 tensor2tensor 模型库,具体版本如下:
|
||||
- tensorflow 1.4.0
|
||||
- tensor2tensor 1.2.9
|
||||
|
||||
## 模型训练
|
||||
|
||||
### 模型选择
|
||||
## 工具包的选择
|
||||
|
||||
想要完成一个自动生成对联的小程序,想法十分美好,但想要达到这个目标,光拍拍脑袋想想是不够的,需要训练出一个能完成对联生成的自然语言理解模型。于是乎,就有两个选择:
|
||||
|
||||
@@ -109,7 +188,8 @@ Copyright © Microsoft Corporation. All rights reserved.
|
||||
本案例中,我们使用 T2T 工具包进行模型训练。
|
||||
|
||||
|
||||
### 数据收集
|
||||
|
||||
## 数据收集
|
||||
|
||||
有了模型,还需要数据。巧妇难为无米之炊,没有数据,什么都是浮云。数据从哪里来呢?GitHub 上有很多开源贡献者收集和整理了对联数据,可以进行下载使用。
|
||||
|
||||
@@ -118,18 +198,22 @@ Copyright © Microsoft Corporation. All rights reserved.
|
||||
2. Github网站上的开源古诗数据: https://github.com/chinese-poetry/chinese-poetry
|
||||
3. 微软亚洲研究院提供的10万条对联数据(非公开数据)。
|
||||
|
||||
### 数据预处理
|
||||
|
||||
#### 生成源数据文件
|
||||
## 数据预处理
|
||||
|
||||
### 生成源数据文件
|
||||
|
||||
网上提供的对联数据形式各异,需要整理成我们需要的格式。我们创建两个文本文件,命名为 train.txt.up 和 train.txt.down,存放上联和下联数据。每个上联/下联为一行,用换行符 ‘\n’ 分隔。
|
||||
|
||||
#### 生成词表文件
|
||||
### 生成词表文件
|
||||
|
||||
接下来我们要统计上下联中出现多少不同的字,用于后续的模型推理。
|
||||
|
||||
|
||||
1. 将上下联数据每个字以“空格”分隔,合并成一个文件。
|
||||
|
||||
a. 分隔数据的python代码 (split_data.py):
|
||||
|
||||
```
|
||||
import sys
|
||||
|
||||
@@ -145,26 +229,36 @@ Copyright © Microsoft Corporation. All rights reserved.
|
||||
out += '\n'
|
||||
outfile.write(out)
|
||||
```
|
||||
|
||||
b. 执行如下命令完成文件分隔
|
||||
|
||||
```
|
||||
python split_data.py train.txt.up
|
||||
python split_data.py train.txt.down
|
||||
```
|
||||
|
||||
分隔后生成两个文件:train.txt.up.clean 和 train.txt.down.clean
|
||||
|
||||
c. 合并文件为 merge.txt
|
||||
|
||||
```
|
||||
cat train.txt.up train.txt.down > merge.txt
|
||||
```
|
||||
|
||||
2. 统计文件中出现的不同字和每个字的出现次数。
|
||||
|
||||
```
|
||||
subword-nmt get-vocab –input merge.txt –output merge.txt.vocab
|
||||
subword-nmt get-vocab -–input merge.txt -–output merge.txt.vocab
|
||||
```
|
||||
|
||||
3. 去掉出现次数,只保留字
|
||||
|
||||
```
|
||||
cat merge.txt.vocab | awk ‘{print $1}’ > merge.txt.vocab.clean
|
||||
```
|
||||
|
||||
4. 将 merge.txt.vocab.clean 的前三行填充如下内容,并将字表字数加3:
|
||||
|
||||
```
|
||||
<pad>
|
||||
<EOS>
|
||||
@@ -174,49 +268,79 @@ Copyright © Microsoft Corporation. All rights reserved.
|
||||
5. 生成测试集。
|
||||
|
||||
取训练集中前 100 个数据作为测试集。(在实际训练过程中,没有用到测试集)
|
||||
|
||||
```
|
||||
head -n 100 train.txt.up > dev.txt.up
|
||||
head -n 100 train.txt.down > dev.txt.down
|
||||
```
|
||||
|
||||
#### 下载指定版本模型库
|
||||
完成以上预处理以后,我们最终得到以下文件:
|
||||
|
||||
* train.txt.up.clean
|
||||
* train.txt.down.clean
|
||||
* merge.txt.vocab.clean
|
||||
|
||||
将上述文件放到`data_dir`目录。
|
||||
|
||||
### 自定义T2T问题
|
||||
|
||||
1. 新建`usr_dir`目录。
|
||||
|
||||
2. 在[merge_vocab.py](./code/train/usr_dir/merge_vocab.py) 文件中编写下联生成模型的问题定义。
|
||||
|
||||
修改如下参数:
|
||||
1. `SRC_TRAIN_DATA` 为训练集上联数据文件
|
||||
2. `TGT_TRAIN_DATA` 为训练集下联数据文件
|
||||
3. `SRC_DEV_DATA` 为测试集上联数据文件
|
||||
4. `TGT_DEV_DATA` 为测试集下联数据文件
|
||||
5. `MERGE_VOCAB` 为最终字表文件
|
||||
6. `VOCAB_SIZE` 为字表文件中字的个数
|
||||
|
||||
该文件注册了问题类 ```TranslateUp2down``` ,用于指出如何进行上下联翻译。其中 ```generate_encoded_samples``` 函数用于处理词表、编码、创建完成时序任务的生成器的工作。
|
||||
|
||||
3. 添加一个 `__init__.py`文件,导入`merge_vocab.py`。
|
||||
```
|
||||
from . import merge_vocab
|
||||
```
|
||||
|
||||
### 检查目录
|
||||
|
||||
完成上述步骤后,请确保你的文件按如下的目录结构放置。
|
||||
|
||||
```
|
||||
git clone https://github.com/tensorflow/tensor2tensor.git
|
||||
git checkout v1.2.9
|
||||
usr_dir \
|
||||
__init__.py
|
||||
merge_vocab.py
|
||||
data_dir \
|
||||
train.txt.up.clean
|
||||
train.txt.down.clean
|
||||
merge.txt.vocab.clean
|
||||
```
|
||||
|
||||
#### 编写问题定义文件
|
||||
### 生成训练数据
|
||||
|
||||
本案例在 merge_vocab.py 文件中编写了下联生成模型的问题定义。
|
||||
在本案例中,若要使用 T2T 工具包进行训练,需要把数据转换成T2T认可的二进制文件形式。
|
||||
|
||||
文件中定义了如下参数:
|
||||
1. SRC_TRAIN_DATA 为训练集上联数据文件
|
||||
2. TGT_TRAIN_DATA 为训练集下联数据文件
|
||||
3. SRC_DEV_DATA 为测试集上联数据文件
|
||||
4. TGT_DEV_DATA 为测试集下联数据文件
|
||||
5. MERGE_VOCAB 为最终字表文件
|
||||
6. VOCAB_SIZE为字表文件中字的个数
|
||||
使用如下命令生成训练数据。
|
||||
|
||||
并注册了问题类 ```TranslateUp2down``` ,用于指出如何进行上下联翻译。其中 ```generator``` 函数用于处理词表、编码、创建完成时序任务的生成器的工作。
|
||||
|
||||
|
||||
#### 生成训练数据
|
||||
|
||||
在本案例中,若要使用 T2T 工具包进行训练,需要把数据转换成T2T认可的二进制文件形式。T2T 工具包提供了生成训练数据的命令:`t2t_datagen` 命令,本案例中使用的具体命令和参数如下:
|
||||
```
|
||||
python tensor2tensor/bin/t2t-datagen \
|
||||
--t2t_usr_dir=${DATA_DIR} \
|
||||
USR_DIR=./usr_dir
|
||||
DATA_DIR=./data_dir
|
||||
PROBLEM=translate_up2down
|
||||
|
||||
t2t-datagen \
|
||||
--t2t_usr_dir=${USR_DIR} \
|
||||
--data_dir=${DATA_DIR} \
|
||||
--problem=${PROBLEM}
|
||||
```
|
||||
|
||||
其中,
|
||||
|
||||
*t2t_usr_dir*:指定了一个目录,该目录中包涵 \_\_init\_\_.py 文件,并可以导入处理对联问题的 python 模块。本案例中创建一个 data 目录,并将其均放入此目录。在该目录中,编写 merge_vocab.py 文件,注册对联问题。并添加一个 \_\_init\_\_.py文件,将 merge_vocab.py 作为模块导入。
|
||||
`t2t_usr_dir`:指定了一个目录,该目录中包含 \_\_init\_\_.py 文件,并可以导入处理对联问题的 python 模块。在该目录中,编写 merge_vocab.py 文件,注册对联问题。
|
||||
|
||||
*data_dir*:数据目录。存放生成训练数据所需的所有源数据资源,以及生成的训练数据文件。
|
||||
`data_dir`:数据目录。存放生成训练数据所需的所有源数据资源,以及生成的训练数据文件。
|
||||
|
||||
*problem*:定义问题名称,本案例中问题名称为 translate_up2down
|
||||
`problem`:定义问题名称,本案例中问题名称为 translate_up2down
|
||||
当命令执行完毕,将会在 data 目录下生成两个文件:
|
||||
|
||||
translate_up2down-train-00000-of-00001
|
||||
@@ -224,14 +348,41 @@ python tensor2tensor/bin/t2t-datagen \
|
||||
|
||||
这便是我们需要的训练数据文件。
|
||||
|
||||
#### 训练模型
|
||||
|
||||
有了处理好的数据,我们就可以进行训练了。训练过程依然调用t2t模型训练命令:`t2t_trainer`。具体命令如下:
|
||||
|
||||
## 模型训练
|
||||
|
||||
有了处理好的数据,我们就可以进行训练了。你可以选择**本地训练**或**在OpenPAI上训练**。
|
||||
|
||||
### OpenPAI上训练
|
||||
OpenPAI 作为开源平台,提供了完整的 AI 模型训练和资源管理能力,能轻松扩展,并支持各种规模的私有部署、云和混合环境。因此,我们推荐在OpenPAI上训练。
|
||||
|
||||
完整训练过程请查阅:
|
||||
[在OpenPAI上训练](docs/train_on_pai.md)
|
||||
|
||||
|
||||
### 本地训练
|
||||
|
||||
如果你的本地机器性能较好,也可以在本地训练。
|
||||
|
||||
模型训练的代码请参考[train.sh](./code/train/train.sh)。
|
||||
|
||||
|
||||
训练过程依然调用t2t模型训练命令:`t2t_trainer`。具体命令如下:
|
||||
```
|
||||
python tensor2tensor/bin/t2t-trainer \
|
||||
TRAIN_DIR=./output
|
||||
LOG_DIR=${TRAIN_DIR}
|
||||
DATA_DIR=./data_dir
|
||||
USR_DIR=./usr_dir
|
||||
|
||||
PROBLEM=translate_up2down
|
||||
MODEL=transformer
|
||||
HPARAMS_SET=transformer_small
|
||||
|
||||
t2t-trainer \
|
||||
--t2t_usr_dir=${USR_DIR} \
|
||||
--data_dir=${DATA_DIR} \
|
||||
--problems=${PROBLEM} \
|
||||
--problem=${PROBLEM} \
|
||||
--model=${MODEL} \
|
||||
--hparams_set=${HPARAMS_SET} \
|
||||
--output_dir=${TRAIN_DIR} \
|
||||
@@ -246,34 +397,38 @@ python tensor2tensor/bin/t2t-trainer \
|
||||
|
||||
各项参数的作用和取值分别如下:
|
||||
|
||||
1) *t2t_usr_dir*:如前一小节所述,指定了处理对联问题的模块所在的目录。
|
||||
1) `t2t_usr_dir`:如前一小节所述,指定了处理对联问题的模块所在的目录。
|
||||
|
||||
2) *data_dir*:训练数据目录
|
||||
2) `data_dir`:训练数据目录
|
||||
|
||||
3) *problems*:问题名称,即translate_up2down
|
||||
3) `problem`:问题名称,即translate_up2down
|
||||
|
||||
4) *model*:训练所使用的 NLP 算法模型,本案例中使用 transformer 模型
|
||||
4) `model`:训练所使用的 NLP 算法模型,本案例中使用 transformer 模型
|
||||
|
||||
5) *hparams_set*:transformer 模型下,具体使用的模型。transformer 的各种模型定义在 tensor2tensor/models/transformer.py 文件夹内。本案例使用 transformer_small 模型。
|
||||
5) `hparams_set`:transformer 模型下,具体使用的模型。transformer 的各种模型定义在 tensor2tensor/models/transformer.py 文件夹内。本案例使用 transformer_small 模型。
|
||||
|
||||
6) *output_dir*:保存训练结果
|
||||
6) `output_dir`:保存训练结果
|
||||
|
||||
7) *keep_checkpoint_max*:保存 checkpoint 文件的最大数目
|
||||
7) `keep_checkpoint_max`:保存 checkpoint 文件的最大数目
|
||||
|
||||
8) *worker_gpu*:是否使用 GPU,以及使用多少 GPU 资源
|
||||
8) `worker_gpu`:是否使用 GPU,以及使用多少 GPU 资源
|
||||
|
||||
9) *train_steps*:总训练次数
|
||||
9) `train_steps`:总训练次数
|
||||
|
||||
10) *save_checkpoints_secs*:保存 checkpoint 的时间间隔
|
||||
10) `save_checkpoints_secs`:保存 checkpoint 的时间间隔
|
||||
|
||||
11) *schedule*:将要执行的 `tf.contrib.learn.Expeiment` 方法,比如:train, train_and_evaluate, continuous_train_and_eval,train_eval_and_decode, run_std_server
|
||||
11) `schedule`:将要执行的 `tf.contrib.learn.Expeiment` 方法,比如:train, train_and_evaluate, continuous_train_and_eval,train_eval_and_decode, run_std_server
|
||||
|
||||
12) *worker_gpu_memory_fraction*:分配的 GPU 显存空间
|
||||
12) `worker_gpu_memory_fraction`:分配的 GPU 显存空间
|
||||
|
||||
13) *hparams*:定义 batch_size 参数。
|
||||
13) `hparams`:定义 batch_size 参数。
|
||||
|
||||
好啦,我们输入完命令,点击回车,训练终于 跑起来啦!如果你在拥有一块 K80 显卡的机器上运行,只需5个小时就可以完成训练。如果你只有 CPU ,那么你只能多等几天啦。
|
||||
我们将训练过程运行在 Microsoft OpenPAI 分布式资源调度平台上,使用一块 K80 进行训练。4小时24分钟后,训练完成,得到如下模型文件:
|
||||
好啦,我们输入完命令,点击回车,训练终于跑起来啦!如果你在拥有一块 K80 显卡的机器上运行,只需5个小时就可以完成训练。如果你只有 CPU ,那么你只能多等几天啦。
|
||||
我们将训练过程运行在 Microsoft OpenPAI 分布式资源调度平台上,使用一块 K80 进行训练。
|
||||
|
||||
如果你想利用OpenPAI平台训练,可以查看[在OpenPAI上训练](docs/train_on_pai.md)。
|
||||
|
||||
4小时24分钟后,训练完成,得到如下模型文件:
|
||||
- checkpoint
|
||||
- model.ckpt-200000.data-00000-of-00003
|
||||
- model.ckpt-200000.data-00001-of-00003
|
||||
@@ -286,26 +441,89 @@ python tensor2tensor/bin/t2t-trainer \
|
||||
|
||||
## 模型推理
|
||||
|
||||
本案例中,自己编写了一个模型推理类,命名为 up2down_class.py,并将其放在 tensor2tensor/bin 目录下。
|
||||
在这一阶段,我们将使用上述训练得到的模型文件进行模型推理,利用上联生成下联。
|
||||
|
||||
该类主要实现了一个函数 `get_next`。该函数以上联语句为输入参数,通过调用 T2T 的 decoding 方法(在 tensor2tensor/bin/utils/ 目录下,decoding.py 文件),生成下联语句。
|
||||
### 新建推理脚本文件`inference.sh`
|
||||
|
||||
为了将上联语句作为输入,传输给 decoding 类,我们修改 decoding.py 文件的内容:
|
||||
点击查看[inference.sh](./code/train/inference.sh)的代码。
|
||||
|
||||
1. 在 `decode_from_file` 函数中,加入参数 `input_sentence`,传入上联语句。
|
||||
2. 在 `_get_sorted_inputs` 函数中,加入参数 `input_sentence`。并将该参数赋值给inputs数组,取代原来从 `decode_hp.delimiter` 参数中获取输入信息的方法。
|
||||
3. 注释 `decode_from_file` 函数的最后五行,并添加语句 `return decodes[sorted_keys[0]]`。将结果写入 decode 文件的做法更改为直接传回结果数据。
|
||||
在推理之前,需要注意如下几个目录:
|
||||
- `TRAIN_DIR`:上述的训练模型文件存放的目录。
|
||||
- `DATA_DIR`:训练字典文件存放目录,即之前提到的`merge.txt.vocab.clean`。
|
||||
- `USR_DIR`:自定义问题的存放目录,即之前提到的`merge_vocab.py`文件。
|
||||
|
||||
```
|
||||
TRAIN_DIR=./output
|
||||
DATA_DIR=./data_dir
|
||||
USR_DIR=./usr_dir
|
||||
|
||||
DECODE_FILE=./decode_this.txt
|
||||
|
||||
PROBLEM=translate_up2down
|
||||
MODEL=transformer
|
||||
HPARAMS=transformer_small
|
||||
|
||||
BEAM_SIZE=4
|
||||
ALPHA=0.6
|
||||
|
||||
poet=$1
|
||||
new_chars=""
|
||||
for ((i=0;i < ${#poet} ;++i))
|
||||
do
|
||||
new_chars="$new_chars ${poet:i:1}"
|
||||
done
|
||||
|
||||
echo $new_chars > decode_this.txt
|
||||
|
||||
echo "生成中..."
|
||||
|
||||
t2t-decoder \
|
||||
--t2t_usr_dir=$USR_DIR \
|
||||
--data_dir=$DATA_DIR \
|
||||
--problem=$PROBLEM \
|
||||
--model=$MODEL \
|
||||
--hparams_set=$HPARAMS \
|
||||
--output_dir=$TRAIN_DIR \
|
||||
--decode_from_file=$DECODE_FILE \
|
||||
--decode_to_file=result.txt >> /dev/null 2>&1
|
||||
|
||||
echo $new_chars
|
||||
cat result.txt
|
||||
```
|
||||
|
||||
### 开始推理
|
||||
|
||||
给`inference.sh`增加可执行权限
|
||||
```
|
||||
chmod +x ./inference.sh
|
||||
```
|
||||
|
||||
使用如下命令推理
|
||||
```
|
||||
./inference.sh [上联]
|
||||
```
|
||||
|
||||
例如,
|
||||
|
||||
```
|
||||
./inference.sh 西子湖边逢暮雨
|
||||
```
|
||||
|
||||
等待推理完成后,你可能会得到下面的输出。当然,下联的生成和你的训练集、迭代次数等都有关系,因此大概率不会有一样的结果。
|
||||
|
||||
```
|
||||
生成中...
|
||||
西 子 湖 边 逢 暮 雨
|
||||
故 里 乾 坤 日 盖 章
|
||||
```
|
||||
|
||||
推理结果也保存到了`result.txt`文件中。
|
||||
|
||||
|
||||
## 应用程序编写
|
||||
|
||||
应用程序的编写分为两部分:(1)微信小程序部分——我们称之为前端应用。(2)应用服务部分——我们称之为后端服务。
|
||||
## 搭建后端服务
|
||||
|
||||
下面我们先来看一下后端服务做了什么。
|
||||
|
||||
### 后端服务
|
||||
|
||||
#### 实体提取
|
||||
### 实体提取
|
||||
|
||||
当用户通过小程序上传图片或照片时,程序需要从图片中提取出能够描述图片的信息。本案例编写了utils.py文件。其中 do_upload_image 函数完成从上传的图片中提取实体的工作。具体过程如下:
|
||||
|
||||
@@ -315,11 +533,11 @@ python tensor2tensor/bin/t2t-trainer \
|
||||
3. 调用结束,返回的结果包含了提取出的实体信息。一张图片可以提取多个实体,组成实体数组。
|
||||
|
||||
|
||||
#### 上联匹配
|
||||
### 上联匹配
|
||||
|
||||
提取完实体信息,我们要找出与实体相匹配的上联数据。find_shanglian 函数实现了该需求,具体函数实现在 word_matching.py 中,感兴趣的同学可以查看源代码。
|
||||
|
||||
##### 使用数据
|
||||
#### 使用数据
|
||||
|
||||
在上联匹配中,我们需要用到如下几个数据文件,它们的描述如下表:
|
||||
|
||||
@@ -339,7 +557,7 @@ python tensor2tensor/bin/t2t-trainer \
|
||||
- dict_2:将上联两两相连的字组成词语(有可能两个相连字并不能称为词语,但依然组合在一起),作为key。并找到 train.txt.up 中含有该词语的上联 ID ,将该 ID 作为 value 数组的元素,生成词表。
|
||||
|
||||
|
||||
##### 传入参数
|
||||
#### 传入参数
|
||||
|
||||
函数 find_shanglian 传入至少5个参数:
|
||||
|
||||
@@ -353,19 +571,19 @@ python tensor2tensor/bin/t2t-trainer \
|
||||
|
||||
(5) 返回上联结果的数目
|
||||
|
||||
##### 实体标签翻译
|
||||
#### 实体标签翻译
|
||||
|
||||
从微软认知服务得到的实体标签(Tag)都是英文的,需要先翻译成中文。程序调用有道 API 完成中英翻译(在 Translate 函数中实现),并将{英文标签:中文翻译}保存在词典文件 en2cn_dict.txt 中。每次先在该文件中查找有否翻译完成的实体,如没有,再调用有道API。
|
||||
|
||||
##### 查找同义词
|
||||
#### 查找同义词
|
||||
|
||||
为了更多找到相关对联,我们还需要对实体词语进行同义词扩展。这用到名为 **synonyms** 的 python 包。该包的提供 nearby 方法,寻找并返回输入词语的同义词,以及他们的得分。本案例针对对每个实体标签,找到并保留至多三个得分大于 **0.7** 的同义词,并将结果在文件 synonyms_words_dict.txt 文件中缓存,方便下次查找。
|
||||
|
||||
##### 随机筛选词语
|
||||
#### 随机筛选词语
|
||||
|
||||
对每个实体,都找到至多三个同义词,并保存在同一个数组中。程序可以从当前数组包含的词语中,随机筛选75%的词汇,生成最终实体词汇列表,用于后续操作。剩下的词汇,保存在备用列表中。
|
||||
|
||||
##### 遍历词表
|
||||
#### 遍历词表
|
||||
|
||||
将实体词汇列表中的实体在词表文件 dict_1.txt 和 dict_2.txt 中进行遍历,找到含有该词汇的上联ID。具体步骤如下:
|
||||
|
||||
@@ -386,21 +604,21 @@ python tensor2tensor/bin/t2t-trainer \
|
||||
8. 根据最终上联 ID,得到具体上联数据。返回该数据。
|
||||
|
||||
|
||||
#### 生成下联
|
||||
### 生成下联
|
||||
|
||||
得到了所需要的上联数据,就要开始生成下联的工作了。该工作在 utils.py 的 `do_upload_image` 函数中继续完成。
|
||||
|
||||
调用 up2down_class.py 中的 `get_next` 函数,将上联数据作为参数一并传入。程序会用训练好的NLP模型进行推理(解码工作),对每一个候选上联,生成一个下联。
|
||||
|
||||
#### 合成对联
|
||||
### 合成对联
|
||||
|
||||
接下来,程序将每个上联和生成的下联合成一个以逗号分隔的对联形式,并以 json 的格式返回 ID 和前 N 个对联的结果(本案例中目前 code 写死为前三个)。
|
||||
|
||||
#### 重新创作
|
||||
### 重新创作
|
||||
|
||||
如果生成的下联用户不喜欢,可以更换对联。程序会根据当前上联,再次调用 `get_next` 函数,生成新的下联。该功能在函数 `do_modify_poetry` 中实现( utils.py 文件)。
|
||||
|
||||
#### 合成图片
|
||||
### 合成图片
|
||||
|
||||
程序生成下联后,会显示几个备选对联(默认是3个)。用户可以选择自己喜欢的对联,并和上传的图片合成新的对联。具体处理过程如下:
|
||||
|
||||
@@ -414,25 +632,8 @@ python tensor2tensor/bin/t2t-trainer \
|
||||
|
||||
具体程序在 synthesis_2.py 文件中实现。合成好的图片保存在相应目录下,供前端应用查找并显示给用户。
|
||||
|
||||
### 前端应用
|
||||
|
||||
编写完后端程序,我们便可以和前端应用结合在一起,搭建起一个可以给用户使用的小程序。
|
||||
|
||||
#### 申请小程序账号
|
||||
|
||||
要开发微信小程序,需要先注册小程序公众号。微信小程序要求一个邮箱账号只能注册一个小程序,并且要填写真实完整的个人或公司信息。在安全性上,要求还是挺高的。
|
||||
注册好小程序账号后,登录微信公众号的管理后台,下载微信小程序开发者工具,并进行开发设置。接下来便可以新建小程序项目,开发你的小程序了。具体教程见[微信小程序开发教程](https://developers.weixin.qq.com/miniprogram/dev/)。
|
||||
|
||||
|
||||
#### 前端应用开发
|
||||
|
||||
微信小程序开发者工具提供了开发模板,开发者可以在不同模板文件中完成前端代码,类似网页开发。
|
||||
|
||||
#### 发布小程序
|
||||
|
||||
应用开发完成,开发者需要填写用户身份管理,上传代码,提交审核。审核通过,最终将小程序发布出去。这样,就完成了微信小程序的前端开发。
|
||||
|
||||
## 运行程序
|
||||
### 启动服务
|
||||
|
||||
我们在Azure上申请了一个VM,部署好我们的环境和代码,运行后端服务。
|
||||
|
||||
@@ -474,11 +675,12 @@ python tensor2tensor/bin/t2t-trainer \
|
||||
程序启动完毕。这时,在微信小程序端就可以使用对联服务啦。
|
||||
|
||||
|
||||
|
||||
# 作业和挑战
|
||||
|
||||
1. 程序复现
|
||||
|
||||
从 GitHub 上下载70万条[对联数据](https://github.com/wb14123/couplet-dataset/releases)(couplet.tar.gz 文件),按照上述教程进行数据预处理,并使用 Tensor2Tensor 库进行模型训练。
|
||||
从 GitHub 上下载70万条[对联数据](https://github.com/wb14123/couplet-dataset/releases)(couplet.tar.gz 文件),按照上述教程进行数据预处理,并使用 Tensor2Tensor 库进行模型训练。
|
||||
|
||||
2. 增量改进
|
||||
|
||||
@@ -491,3 +693,22 @@ python tensor2tensor/bin/t2t-trainer \
|
||||
4. 扩展对联生成程序,用于古诗、绝句等的自动生成。
|
||||
|
||||
|
||||
# 总结
|
||||
|
||||
本案例利用深度学习方法构建了一个上联预测下联的对联生成模型。首先通过词嵌入对数据集编码,再利用已编码的数据训练一个Encoder-Decoder模型,从而实现对联生成的功能。另外,该案例还结合微软Cognitive Service中的目标检测,对用户上传图片进行分析,利用分析结果匹配上联,再通过训练好的模型生成下联。最后,搭建后端服务实现完整的应用功能。该案例很好地演示了从模型选择、训练、推理到搭建后端服务等完整的应用开发流程,将理论与实践结合。
|
||||
|
||||
|
||||
# 推荐阅读
|
||||
|
||||
## 最新论文
|
||||
|
||||
* [ACL 2019 | 微软8篇精选论文解读,一览最新研究进展](https://www.msra.cn/zh-cn/news/features/acl-2019)
|
||||
|
||||
## 深度文章
|
||||
|
||||
* [参数少一半,效果还更好,天津大学和微软提出Transformer压缩模型](https://www.msra.cn/zh-cn/news/features/a-tensorized-transformer-for-language-modeling)
|
||||
* [通过全新学习和推断机制提升seq2seq 模型的语法改错性能](https://www.msra.cn/zh-cn/news/features/fluency-boost-learning-and-inference-for-neural-grammatical-error-correction)
|
||||
* [微软亚洲研究院发布业界最全面的语义分析数据集MSParS](https://www.msra.cn/zh-cn/news/features/mspars)
|
||||
* [七大NLP任务最新方法与进展](https://www.msra.cn/zh-cn/news/features/machine-reasoning)
|
||||
* [机器推理在常识问答任务中的应用](https://www.msra.cn/zh-cn/news/features/machine-reasoning-for-commonsense-question-answering)
|
||||
* [机器推理在事实检测任务中的应用](https://www.msra.cn/zh-cn/news/features/machine-reasoning-for-fact-checking)
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Before you inference, please make sure your directory structure is as follow:
|
||||
#
|
||||
# usr_dir \
|
||||
# __init__.py
|
||||
# merge_vocab.py
|
||||
# data_dir \
|
||||
# train.txt.up.clean
|
||||
# train.txt.down.clean
|
||||
# merge.txt.vocab.clean
|
||||
# output \
|
||||
#
|
||||
|
||||
TRAIN_DIR=./output
|
||||
DATA_DIR=./data_dir
|
||||
USR_DIR=./usr_dir
|
||||
|
||||
DECODE_FILE=./decode_this.txt
|
||||
|
||||
PROBLEM=translate_up2down
|
||||
MODEL=transformer
|
||||
HPARAMS=transformer_small
|
||||
|
||||
BEAM_SIZE=4
|
||||
ALPHA=0.6
|
||||
|
||||
poet=$1
|
||||
new_chars=""
|
||||
for ((i=0;i < ${#poet} ;++i))
|
||||
do
|
||||
new_chars="$new_chars ${poet:i:1}"
|
||||
done
|
||||
|
||||
echo $new_chars > decode_this.txt
|
||||
|
||||
echo "生成中..."
|
||||
|
||||
t2t-decoder \
|
||||
--t2t_usr_dir=$USR_DIR \
|
||||
--data_dir=$DATA_DIR \
|
||||
--problem=$PROBLEM \
|
||||
--model=$MODEL \
|
||||
--hparams_set=$HPARAMS \
|
||||
--output_dir=$TRAIN_DIR \
|
||||
--decode_from_file=$DECODE_FILE \
|
||||
--decode_to_file=result.txt >> /dev/null 2>&1
|
||||
|
||||
echo $new_chars
|
||||
cat result.txt
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Before you train, please make sure your directory structure is as follow:
|
||||
#
|
||||
# usr_dir \
|
||||
# __init__.py
|
||||
# merge_vocab.py
|
||||
# data_dir \
|
||||
# train.txt.up.clean
|
||||
# train.txt.down.clean
|
||||
# merge.txt.vocab.clean
|
||||
#
|
||||
|
||||
|
||||
TRAIN_DIR=./output
|
||||
LOG_DIR=${TRAIN_DIR}
|
||||
DATA_DIR=./data_dir
|
||||
USR_DIR=./usr_dir
|
||||
|
||||
PROBLEM=translate_up2down
|
||||
MODEL=transformer
|
||||
HPARAMS_SET=transformer_small
|
||||
|
||||
# generate data
|
||||
t2t-datagen \
|
||||
--t2t_usr_dir=${USR_DIR} \
|
||||
--data_dir=${DATA_DIR} \
|
||||
--problem=${PROBLEM}
|
||||
|
||||
# train data
|
||||
t2t-trainer \
|
||||
--t2t_usr_dir=${USR_DIR} \
|
||||
--data_dir=${DATA_DIR} \
|
||||
--problem=${PROBLEM} \
|
||||
--model=${MODEL} \
|
||||
--hparams_set=${HPARAMS_SET} \
|
||||
--output_dir=${TRAIN_DIR} \
|
||||
--keep_checkpoint_max=1000 \
|
||||
--worker_gpu=1 \
|
||||
--train_steps=200000 \
|
||||
--save_checkpoints_secs=1800 \
|
||||
--schedule=train \
|
||||
--worker_gpu_memory_fraction=0.95 \
|
||||
--hparams="batch_size=1024" 2>&1 | tee -a ${LOG_DIR}/train_default.log
|
||||
@@ -24,6 +24,7 @@ six==1.11.0
|
||||
subword-nmt
|
||||
sympy==1.3
|
||||
synonyms==3.10.1
|
||||
tensorflow
|
||||
tensorflow==1.14.0
|
||||
tensor2tensor==1.14.1
|
||||
urllib3
|
||||
Werkzeug==0.14.1
|
||||
@@ -0,0 +1,104 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
# coding=utf-8
|
||||
""" Problem definition for translation from Up to Down."""
|
||||
## version info: tensor2tensor 1.14.1 and tensorflow 1.14.0
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
from tensor2tensor.data_generators import generator_utils
|
||||
from tensor2tensor.data_generators import problem
|
||||
from tensor2tensor.data_generators import text_encoder
|
||||
from tensor2tensor.data_generators import text_problems
|
||||
from tensor2tensor.data_generators import translate
|
||||
from tensor2tensor.utils import registry
|
||||
|
||||
import tensorflow as tf
|
||||
|
||||
SRC_TRAIN_DATA = 'train.in.txt'
|
||||
TGT_TRAIN_DATA = 'train.out.txt'
|
||||
SRC_DEV_DATA = 'test.in.txt'
|
||||
TGT_DEV_DATA = 'text.out.txt'
|
||||
MERGE_VOCAB = 'vocab.txt'
|
||||
VOCAB_SIZE = 9122
|
||||
LOCATION_OF_DATA = os.path.abspath(os.path.dirname(__file__)) + '/'
|
||||
|
||||
_TRAIN_DATASETS = [
|
||||
LOCATION_OF_DATA + SRC_TRAIN_DATA,
|
||||
LOCATION_OF_DATA + TGT_TRAIN_DATA
|
||||
]
|
||||
|
||||
_DEV_DATASETS = [
|
||||
LOCATION_OF_DATA + SRC_DEV_DATA,
|
||||
LOCATION_OF_DATA + TGT_DEV_DATA
|
||||
]
|
||||
|
||||
_VOCAB_FILES = [
|
||||
LOCATION_OF_DATA + MERGE_VOCAB
|
||||
]
|
||||
|
||||
EOS = text_encoder.EOS_ID
|
||||
|
||||
@registry.register_problem
|
||||
# class TranslateUp2down(text_problems.Text2TextProblem):
|
||||
class TranslateUp2down(text_problems.Text2TextProblem):
|
||||
|
||||
@property
|
||||
def approx_vocab_size(self):
|
||||
return VOCAB_SIZE
|
||||
|
||||
@property
|
||||
def vocab_name(self):
|
||||
return _VOCAB_FILES[0]
|
||||
|
||||
@property
|
||||
def input_space_id(self):
|
||||
return problem.SpaceID.EN_TOK
|
||||
|
||||
@property
|
||||
def target_space_id(self):
|
||||
return problem.SpaceID.EN_TOK
|
||||
|
||||
@property
|
||||
def is_generate_per_split(self):
|
||||
# generate_data will shard the data into TRAIN and EVAL for us
|
||||
return False
|
||||
|
||||
@property
|
||||
def dataset_splits(self):
|
||||
"""Splits of data to produce and number of output shards for each."""
|
||||
return [{
|
||||
"split": problem.DatasetSplit.TRAIN,
|
||||
"shards": 9,
|
||||
},{
|
||||
"split": problem.DatasetSplit.EVAL,
|
||||
"shards":1,
|
||||
}]
|
||||
|
||||
def generate_encoded_samples(self, data_dir, tmp_dir, dataset_split):
|
||||
train = dataset_split == problem.DatasetSplit.TRAIN
|
||||
datasets = _TRAIN_DATASETS if train else _DEV_DATASETS
|
||||
|
||||
vocab_list = []
|
||||
print("=======Get Vocab from ", self.vocab_name, '...', end='')
|
||||
with open(self.vocab_name, 'r', encoding='utf-8') as f:
|
||||
vocab_list = f.read().splitlines()
|
||||
print("=======Done")
|
||||
|
||||
vocab = text_encoder.TokenTextEncoder(vocab_filename=None, vocab_list=vocab_list, replace_oov="<UNK>", num_reserved_ids=text_encoder.NUM_RESERVED_TOKENS)
|
||||
|
||||
return text_problems.text2text_generate_encoded(text_problems.text2text_txt_iterator(datasets[0], datasets[1]), vocab, vocab)
|
||||
|
||||
|
||||
def feature_encoders(self,data_dir):
|
||||
tf.logging.info("Loading and processing vocabulary from: %s" % _VOCAB_FILES[0])
|
||||
vocab_list = []
|
||||
with open(self.vocab_name, 'r', encoding='utf-8') as f:
|
||||
vocab_list = f.read().splitlines()
|
||||
tf.logging.info("Done")
|
||||
vocab_token = text_encoder.TokenTextEncoder(vocab_filename=None, vocab_list=vocab_list, replace_oov="<UNK>", num_reserved_ids=text_encoder.NUM_RESERVED_TOKENS)
|
||||
return {"inputs": vocab_token, "targets": vocab_token}
|
||||
@@ -1,9 +1,7 @@
|
||||
{
|
||||
"jobName": "test_couplet_001",
|
||||
"image": "tobeyqin/pai.build.cu80:my",
|
||||
"jobName": "train_couplet_demo",
|
||||
"image": "vzich/poet:tensorflow",
|
||||
"codeDir": "$PAI_DEFAULT_FS_URI/$PAI_USER_NAME/$PAI_JOB_NAME",
|
||||
"dataDir": "$PAI_DEFAULT_FS_URI/Data/$PAI_JOB_NAME",
|
||||
"outputDir": "$PAI_DEFAULT_FS_URI/Output/$PAI_JOB_NAME",
|
||||
"taskRoles": [
|
||||
{
|
||||
"name": "test_001",
|
||||
@@ -11,7 +9,7 @@
|
||||
"cpuNumber": 2,
|
||||
"gpuNumber": 1,
|
||||
"memoryMB": 8192,
|
||||
"command": "pip3 --quiet install future && cd $PAI_JOB_NAME && chmod +x train.sh && python3 run_samples.py"
|
||||
"command": "pip3 --quiet install future && cd $PAI_JOB_NAME && ls -al && chmod +x train.sh && python3 run_samples.py"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import hdfs
|
||||
import subprocess
|
||||
|
||||
hdfs_url = "YOUR_HDFS_URL"
|
||||
hdfs_user_name = "YOUR_HDFS_USERNAME"
|
||||
root_dir = "YOUR_PROJECT_ROOT_DIR"
|
||||
|
||||
class HDFSHelper(object):
|
||||
|
||||
def __init__(self, hdfs_url, hdfs_user_name, hdfs_root):
|
||||
self.__client = hdfs.InsecureClient(hdfs_url, root=hdfs_root, user=hdfs_user_name)
|
||||
self.__client.set_permission(hdfs_root,777)
|
||||
|
||||
def Download(self, hdfs_path, local_path):
|
||||
print("Downloading from {} to {}".format(hdfs_path, local_path))
|
||||
os.makedirs(local_path, exist_ok=True)
|
||||
self.__client.download(hdfs_path, local_path)
|
||||
|
||||
def Upload(self, local_path, hdfs_path):
|
||||
print("Uploading from {} to {}".format(local_path, hdfs_path))
|
||||
self.__client.makedirs(hdfs_path)
|
||||
self.__client.upload(hdfs_path, local_path, overwrite=True)
|
||||
|
||||
hdfsHelper = HDFSHelper(hdfs_url, hdfs_user_name, root_dir)
|
||||
|
||||
# Downloading data
|
||||
hdfsHelper.Download(os.path.join(root_dir, "data"), ".")
|
||||
|
||||
# Call train.sh
|
||||
subprocess.call("./train.sh", shell=True)
|
||||
|
||||
# Uploading data
|
||||
jobName = os.environ['PAI_JOB_NAME']
|
||||
output_dir = os.path.join(root_dir, "output", jobName)
|
||||
hdfsHelper.Upload("./output/", output_dir)
|
||||
@@ -1,50 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
HOME_DIR=$(cd `dirname $0`; pwd)
|
||||
echo $HOME_DIR
|
||||
|
||||
# clone and checkout tensor2tensor library to v1.2.9
|
||||
cd $HOME_DIR
|
||||
git clone https://github.com/tensorflow/tensor2tensor.git
|
||||
cd tensor2tensor
|
||||
git checkout v1.2.9
|
||||
cd ..
|
||||
mv tensor2tensor tensor2tensor-1.2.9
|
||||
|
||||
# install tensor2tensor
|
||||
export LC_ALL=C
|
||||
pip3 install tensor2tensor==1.14.1
|
||||
pip3 install requests==2.21.0
|
||||
|
||||
# set environment
|
||||
CODE_DIR=${HOME_DIR}/tensor2tensor-1.2.9
|
||||
export PYTHONPATH=${CODE_DIR}:${PYTHONPATH}
|
||||
binFile=${CODE_DIR}/tensor2tensor/bin
|
||||
|
||||
TRAIN_DIR=${HOME_DIR}/output
|
||||
LOG_DIR=${TRAIN_DIR}
|
||||
|
||||
DATA_DIR=${HOME_DIR}/data
|
||||
USR_DIR=${DATA_DIR}
|
||||
|
||||
|
||||
PROBLEM=translate_up2down
|
||||
MODEL=transformer
|
||||
HPARAMS_SET=transformer_small
|
||||
#HPARAMS_SET=transformer_base
|
||||
|
||||
mkdir -p ${TRAIN_DIR}
|
||||
|
||||
#install python packages
|
||||
python -m pip install -r pip_requirements.txt
|
||||
|
||||
# generate data
|
||||
python ${binFile}/t2t-datagen \
|
||||
--t2t_usr_dir=${USR_DIR} \
|
||||
--data_dir=${DATA_DIR} \
|
||||
--problem=${PROBLEM}
|
||||
echo start generate data...
|
||||
|
||||
t2t-datagen \
|
||||
--t2t_usr_dir=${USR_DIR} \
|
||||
--data_dir=${DATA_DIR} \
|
||||
--problem=${PROBLEM}
|
||||
|
||||
# train model
|
||||
setting=default
|
||||
|
||||
python ${binFile}/t2t-trainer \
|
||||
echo start training...
|
||||
|
||||
t2t-trainer \
|
||||
--t2t_usr_dir=${USR_DIR} \
|
||||
--data_dir=${DATA_DIR} \
|
||||
--problems=${PROBLEM} \
|
||||
--problem=${PROBLEM} \
|
||||
--model=${MODEL} \
|
||||
--hparams_set=${HPARAMS_SET} \
|
||||
--output_dir=${TRAIN_DIR} \
|
||||
@@ -54,4 +46,4 @@ python ${binFile}/t2t-trainer \
|
||||
--save_checkpoints_secs=1800 \
|
||||
--schedule=train \
|
||||
--worker_gpu_memory_fraction=0.95 \
|
||||
--hparams="batch_size=1024" 2>&1 | tee -a ${LOG_DIR}/train_${setting}.log
|
||||
--hparams="batch_size=1024" 2>&1 | tee -a ${LOG_DIR}/train_${setting}.log
|
||||
@@ -0,0 +1,216 @@
|
||||
# 使用HDFS提交任务
|
||||
|
||||
> **注意:** OpenPAI 部署了一个 HDFS 服务来保存日志和其它文件。 虽然此 HDFS 也可用来存储文件,但**不推荐**这样做。 因为 OpenPAI 集群的服务器可能会频繁增减,磁盘空间也有可能不够,因此无法保证存储的质量。
|
||||
|
||||
|
||||
## 提交训练任务
|
||||
|
||||
OpenPAI的每一个任务是通过创建一个独立的Docker环境进行的,Docker的文件系统与服务器的文件系统是隔离的,所以在Docker中无法通过相对路径或绝对路径直接访问到我们需要训练的数据。除了共享文件夹以外,我们还可以通过HDFS方式上传和下载数据。具体流程可以参考下图。
|
||||
|
||||

|
||||
|
||||
|
||||
### 上传数据
|
||||
|
||||
为了上传数据方便,我们将`usr_dir`和`data_dir`合并为一个文件夹,目录结构如下。
|
||||
|
||||
```
|
||||
data \
|
||||
__init__.py
|
||||
merge_vocab.py
|
||||
train.txt.up.clean
|
||||
train.txt.down.clean
|
||||
merge.txt.vocab.clean
|
||||
```
|
||||
因此,在上传时仅需上传data文件夹即可。
|
||||
|
||||
但是,需要注意的是,在后续训练的目录定义中,需要将`t2t_usr_dir`和`data_dir`都指定为该data目录。
|
||||
|
||||
|
||||
我们可以直接在`HDFS EXPLORER`中选择文件夹后右键,选择上传文件或文件夹。
|
||||
|
||||

|
||||
|
||||
我们只需要将预处理完后的data文件夹上传至相应的目录即可。在此,我们选择上传到`/demo/data`,当然,你也可以上传到其他目录,该目录在后续下载数据到Docker时会使用到。
|
||||
|
||||
|
||||
|
||||
### 新建文件
|
||||
要提交训练任务,我们需要新建如下几个文件:
|
||||
* [train.sh](../code/train_on_pai/train.sh)
|
||||
* [run_samples.py](../code/train_on_pai/run_samples.py)
|
||||
* [new_job.pai.json](../code/train_on_pai/new_job.pai.json)
|
||||
|
||||
|
||||
以上文件(除`new_job.pai.json`外)会随任务的提交自动上传至服务器(需要配置`.vscode/settings.json`,后续会提到),同时也会自动下载到Docker环境中,因此我们可以在Docker中访问到它们,这里面包含了训练任务的脚本。
|
||||
|
||||
#### 1. `train.sh`
|
||||
|
||||
此为用于训练的脚本,包含了tensor2tensor的安装,以及执行训练的命令。
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
HOME_DIR=$(cd `dirname $0`; pwd)
|
||||
echo $HOME_DIR
|
||||
cd $HOME_DIR
|
||||
|
||||
# install tensor2tensor
|
||||
export LC_ALL=C
|
||||
pip3 install tensor2tensor==1.14.1
|
||||
pip3 install requests==2.21.0
|
||||
|
||||
# set environment
|
||||
TRAIN_DIR=${HOME_DIR}/output
|
||||
LOG_DIR=${TRAIN_DIR}
|
||||
DATA_DIR=${HOME_DIR}/data
|
||||
USR_DIR=${DATA_DIR}
|
||||
|
||||
PROBLEM=translate_up2down
|
||||
MODEL=transformer
|
||||
HPARAMS_SET=transformer_small
|
||||
|
||||
mkdir -p ${TRAIN_DIR}
|
||||
|
||||
# generate data
|
||||
echo start generate data...
|
||||
|
||||
t2t-datagen \
|
||||
--t2t_usr_dir=${USR_DIR} \
|
||||
--data_dir=${DATA_DIR} \
|
||||
--problem=${PROBLEM}
|
||||
|
||||
# train model
|
||||
setting=default
|
||||
|
||||
echo start training...
|
||||
|
||||
t2t-trainer \
|
||||
--t2t_usr_dir=${USR_DIR} \
|
||||
--data_dir=${DATA_DIR} \
|
||||
--problem=${PROBLEM} \
|
||||
--model=${MODEL} \
|
||||
--hparams_set=${HPARAMS_SET} \
|
||||
--output_dir=${TRAIN_DIR} \
|
||||
--keep_checkpoint_max=1000 \
|
||||
--worker_gpu=1 \
|
||||
--train_steps=200000 \
|
||||
--save_checkpoints_secs=1800 \
|
||||
--schedule=train \
|
||||
--worker_gpu_memory_fraction=0.95 \
|
||||
--hparams="batch_size=1024" 2>&1 | tee -a ${LOG_DIR}/train_${setting}.log
|
||||
|
||||
```
|
||||
|
||||
#### 2. `run_samples.py`
|
||||
|
||||
此为用于执行任务的运行脚本,包含了下载数据至Docker,调用`train.sh`脚本开始训练,以及完成训练以后的数据上传。这是我们训练任务程序运行的入口,在OpenPAI开始任务后,我们会首先调用此脚本,具体调用方法在`new_job.pai.json`中介绍。
|
||||
|
||||
注意这里需要替换以下几个变量:
|
||||
1. `hdfs_url` : 替换为openPAI中的pai-master结点
|
||||
2. `hdfs_user_name` :替换为你的用户名
|
||||
3. `root_dir` :替换为存放数据的根目录,用于下载训练数据至Docker。在此,由于我们将数据上传至了`/demo/data`,因此将`root_dir`替换为`/demo`。项目训练完成后会将结果上传至`/demo/output`。
|
||||
|
||||
```
|
||||
import os
|
||||
import sys
|
||||
import hdfs
|
||||
import subprocess
|
||||
|
||||
hdfs_url = "YOUR_HDFS_URL"
|
||||
hdfs_user_name = "YOUR_HDFS_USERNAME"
|
||||
root_dir = "YOUR_PROJECT_ROOT_DIR"
|
||||
|
||||
class HDFSHelper(object):
|
||||
|
||||
def __init__(self, hdfs_url, hdfs_user_name, hdfs_root):
|
||||
self.__client = hdfs.InsecureClient(hdfs_url, root=hdfs_root, user=hdfs_user_name)
|
||||
self.__client.set_permission(hdfs_root,777)
|
||||
|
||||
def Download(self, hdfs_path, local_path):
|
||||
print("Downloading from {} to {}".format(hdfs_path, local_path))
|
||||
os.makedirs(local_path, exist_ok=True)
|
||||
self.__client.download(hdfs_path, local_path)
|
||||
|
||||
def Upload(self, local_path, hdfs_path):
|
||||
print("Uploading from {} to {}".format(local_path, hdfs_path))
|
||||
self.__client.makedirs(hdfs_path)
|
||||
self.__client.upload(hdfs_path, local_path, overwrite=True)
|
||||
|
||||
hdfsHelper = HDFSHelper(hdfs_url, hdfs_user_name, root_dir)
|
||||
|
||||
# Downloading data
|
||||
hdfsHelper.Download(os.path.join(root_dir, "data"), ".")
|
||||
|
||||
# Call train.sh
|
||||
subprocess.call("./train.sh", shell=True)
|
||||
|
||||
# Uploading data
|
||||
jobName = os.environ['PAI_JOB_NAME']
|
||||
output_dir = os.path.join(root_dir, "output", jobName)
|
||||
hdfsHelper.Upload("./output/", output_dir)
|
||||
```
|
||||
|
||||
|
||||
#### 3. `new_job.pai.json`
|
||||
此为用于提交训练任务的配置文件,其中`taskRoles`字段下的`command`为openPAI创建完Docker环境后开始执行的命令,`image`字段用于指定docker hub中的Docker镜像。
|
||||
|
||||
```
|
||||
{
|
||||
"jobName": "train_couplet_demo",
|
||||
"image": "vzich/poet:tensorflow",
|
||||
"codeDir": "$PAI_DEFAULT_FS_URI/$PAI_USER_NAME/$PAI_JOB_NAME",
|
||||
"taskRoles": [
|
||||
{
|
||||
"name": "demo_001",
|
||||
"taskNumber": 1,
|
||||
"cpuNumber": 2,
|
||||
"gpuNumber": 1,
|
||||
"memoryMB": 8192,
|
||||
"command": "pip3 --quiet install future && cd $PAI_JOB_NAME && ls -al && chmod +x train.sh && python3 run_samples.py"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
我们在此文件中配置`command`为
|
||||
```
|
||||
pip3 --quiet install future && cd $PAI_JOB_NAME && ls -al && chmod +x train.sh && python3 run_samples.py
|
||||
```
|
||||
这就实现了在任务一开始便运行`run_samples.py`。
|
||||
|
||||
|
||||
|
||||
### 提交任务
|
||||
1. 在提交任务之前,我们需要修改`.vscode/settings.json`为:
|
||||
|
||||
```
|
||||
{
|
||||
"pai.job.upload.enabled": true,
|
||||
"pai.job.upload.exclude": [],
|
||||
"pai.job.upload.include": [
|
||||
"**/*.py",
|
||||
"**/*.sh"
|
||||
],
|
||||
"pai.job.generateJobName.enabled": true
|
||||
}
|
||||
```
|
||||
完成以后,提交任务会自动将当前目录中的.py文件和.sh文件一起上传。
|
||||
|
||||
2. 右键`new_job.pai.json`,选择`Submit Job to PAI Cluster`。
|
||||
|
||||

|
||||
|
||||
此时,就完成了任务的提交。
|
||||
|
||||
成功提交任务后,VS Code会将上一步新建的若干.py和.sh文件同时上传至此次任务的目录,在Docker创建完后会自动执行`new_job.pai.json`文件中`taskRoles`下的`command`字段内容。
|
||||
|
||||
|
||||
### 下载数据
|
||||
|
||||
在任务运行完成后,Docker会自动销毁,对此我们在`run_sample.py`中实现了让Docker训练完成后自动将结果上传至服务器相应的目录。因此,我们只需要在`HDFS EXPLORER`的相应目录中找到我们训练的结果即可。在示例中,我们可以在`/demo/output`中找到训练结果。
|
||||
|
||||
找到对应的结果,右键可以选择下载到本地。
|
||||
|
||||

|
||||
|
||||
至此,我们便完成了OpenPAI的任务提交、训练以及获取训练结果啦。
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
@@ -0,0 +1,215 @@
|
||||
# 在OpenPAI上训练
|
||||
|
||||
> OpenPAI 作为开源平台,提供了完整的 AI 模型训练和资源管理能力,能轻松扩展,并支持各种规模的私有部署、云和混合环境。
|
||||
> ## 适用场景
|
||||
> 1. 在团队间共享强大的 AI 计算资源(例如,GPU、FPGA 集群)。
|
||||
> 2. 在组织内共享或重用 AI 资产(如模型、数据、运行环境等) 。
|
||||
> 3. 构建易于 IT 运维管理的 AI 计算平台。
|
||||
> 4. 在同一个环境中完成模型训练过程。
|
||||
>
|
||||
> [了解更多](https://github.com/microsoft/pai/blob/master/README_zh_CN.md#ai-%E5%BC%80%E6%94%BE%E5%B9%B3%E5%8F%B0openpai-)
|
||||
|
||||
|
||||
## 前提准备
|
||||
在开始之前,我们需要在自己的高性能服务器上[部署OpenPAI](https://github.com/microsoft/pai/blob/master/README_zh_CN.md#%E9%83%A8%E7%BD%B2)。
|
||||
如果你的学校或公司已经部署了OpenPAI,那么你需要先注册一个OpenPAI账号。
|
||||
|
||||
## 安装OpenPAI的VS Code插件
|
||||
为了方便我们提交任务到OpenPAI上,我们可以使用VS Code中的插件Open Platform for AI。
|
||||
|
||||
1. 按F1,输入“install extensions”,选择 `Extensions: Install Extensions` 。
|
||||
|
||||

|
||||
|
||||
|
||||
2. 搜索“openpai”,选择 `OpenPAI VS Code Client` ,点击“Install”即可完成安装。
|
||||
|
||||

|
||||
|
||||
|
||||
3. 安装完成后,左侧边栏可以看到OpenPAI的图标。
|
||||
|
||||

|
||||
|
||||
## 配置OpenPAI账号信息
|
||||
|
||||
1. 点击侧边栏 `PAI CLUSTER EXPLORER` 的“+”号。或者,按`F1`后,输入“Add Pai Cluster”,选择`PAI:Add Pai Cluster`。
|
||||
|
||||

|
||||
|
||||
|
||||
2. 输入OpenPAI的服务器IP后回车,会自动生成一份配置文件,我们需要修改文件中的`username`和`password`字段为自己的账号密码,保存文件即可。
|
||||
|
||||

|
||||
|
||||
3. 此时,按“Ctrl+Shift+E”后,我们可以看到侧边栏 `PAI CLUSTER EXPLORER` 中会显示已经登录的服务器。
|
||||
|
||||

|
||||
|
||||
点击侧边栏的OpenPAI的图标,我们可以在 `HDFS EXPLORER` 中看到已登录的服务器的资源,可以新建文件夹或上传文件。
|
||||
|
||||

|
||||
|
||||
|
||||
至此,我们就成功完成了登录。
|
||||
|
||||
|
||||
## 提交训练任务
|
||||
|
||||
OpenPAI的每一个任务是通过创建一个独立的Docker环境进行的,Docker的文件系统与服务器的文件系统是隔离的,所以在Docker中无法通过相对路径或绝对路径直接访问到我们需要训练的数据。我们可以通过把共享文件夹挂载到Docker中,从而实现对数据的访问。具体流程可以参考下图。
|
||||
|
||||

|
||||
|
||||
|
||||
### 共享文件夹
|
||||
|
||||
为了方便起见,我们将`usr_dir`和`data_dir`合并为一个文件夹,目录结构如下。
|
||||
|
||||
```
|
||||
data \
|
||||
__init__.py
|
||||
merge_vocab.py
|
||||
train.txt.up.clean
|
||||
train.txt.down.clean
|
||||
merge.txt.vocab.clean
|
||||
```
|
||||
|
||||
但是,需要注意的是,在后续训练的目录定义中,需要将`t2t_usr_dir`和`data_dir`都指定为该data目录。
|
||||
|
||||
在这里我们仅需将data文件夹共享,并挂载到Docker上即可开始训练。
|
||||
|
||||
具体操作请参考:[通用流程](https://github.com/microsoft/pai/blob/master/docs/zh_CN/user/storage.md#通用流程)。
|
||||
|
||||
|
||||
### 新建文件
|
||||
要提交训练任务,我们需要新建如下几个文件:
|
||||
* [train.sh](../code/train_on_pai/train.sh)
|
||||
* [new_job.pai.json](../code/train_on_pai/new_job.pai.json)
|
||||
|
||||
|
||||
`train.sh`会随任务的提交自动上传至服务器(需要配置`.vscode/settings.json`,后续会提到),同时也会自动下载到Docker环境中,因此我们可以在Docker中访问到它们,这里面包含了训练任务的脚本。
|
||||
|
||||
#### 1. `train.sh`
|
||||
|
||||
此为用于训练的脚本,包含了tensor2tensor的安装,以及执行训练的命令。
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
HOME_DIR=$(cd `dirname $0`; pwd)
|
||||
echo $HOME_DIR
|
||||
cd $HOME_DIR
|
||||
|
||||
# install tensor2tensor
|
||||
export LC_ALL=C
|
||||
pip3 install tensor2tensor==1.14.1
|
||||
pip3 install requests==2.21.0
|
||||
|
||||
# set environment
|
||||
TRAIN_DIR=${HOME_DIR}/output
|
||||
LOG_DIR=${TRAIN_DIR}
|
||||
DATA_DIR=${HOME_DIR}/data
|
||||
USR_DIR=${DATA_DIR}
|
||||
|
||||
PROBLEM=translate_up2down
|
||||
MODEL=transformer
|
||||
HPARAMS_SET=transformer_small
|
||||
|
||||
mkdir -p ${TRAIN_DIR}
|
||||
|
||||
# generate data
|
||||
echo start generate data...
|
||||
|
||||
t2t-datagen \
|
||||
--t2t_usr_dir=${USR_DIR} \
|
||||
--data_dir=${DATA_DIR} \
|
||||
--problem=${PROBLEM}
|
||||
|
||||
# train model
|
||||
setting=default
|
||||
|
||||
echo start training...
|
||||
|
||||
t2t-trainer \
|
||||
--t2t_usr_dir=${USR_DIR} \
|
||||
--data_dir=${DATA_DIR} \
|
||||
--problem=${PROBLEM} \
|
||||
--model=${MODEL} \
|
||||
--hparams_set=${HPARAMS_SET} \
|
||||
--output_dir=${TRAIN_DIR} \
|
||||
--keep_checkpoint_max=1000 \
|
||||
--worker_gpu=1 \
|
||||
--train_steps=200000 \
|
||||
--save_checkpoints_secs=1800 \
|
||||
--schedule=train \
|
||||
--worker_gpu_memory_fraction=0.95 \
|
||||
--hparams="batch_size=1024" 2>&1 | tee -a ${LOG_DIR}/train_${setting}.log
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### 2. `new_job.pai.json`
|
||||
此为用于提交训练任务的配置文件,其中`taskRoles`字段下的`command`为openPAI创建完Docker环境后开始执行的命令,`image`字段用于指定docker hub中的Docker镜像。
|
||||
|
||||
开始前应先替换所有变量为相应的值,包括:\<AddressOfSharedServer\>,\<SharedFolder\>,\<Username\>,以及 \<Password\>。
|
||||
```
|
||||
{
|
||||
"jobName": "train_couplet_demo",
|
||||
"image": "vzich/poet:tensorflow",
|
||||
"codeDir": "$PAI_DEFAULT_FS_URI/$PAI_USER_NAME/$PAI_JOB_NAME",
|
||||
"taskRoles": [
|
||||
{
|
||||
"name": "demo_001",
|
||||
"taskNumber": 1,
|
||||
"cpuNumber": 2,
|
||||
"gpuNumber": 1,
|
||||
"memoryMB": 8192,
|
||||
"command": "apt update && apt install -y cifs-utils && mkdir /models && mount -t cifs //<AddressOfSharedServer>/<SharedFolder> /models -o username=<Username>,password=<Password> && cd /models && pip3 --quiet install future && chmod +x train.sh && train.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
command 字段分为以下几步:
|
||||
|
||||
1. **准备环境**。`apt update && apt install -y cifs-utils` 安装了 `cifs-utils` 来挂载代码文件夹。
|
||||
如果将所有依赖都包含在 Docker 映像中,可以在每次运行前省下下载和安装时间。 但如果这些依赖更新得非常频繁,或不同的 Job 需要大量的依赖,则可以在 Job 运行时安装。
|
||||
|
||||
2. **准备文件**。 `mkdir /models && mount -t cifs //<AddressOfSharedServer>/<SharedFolder> /models -o username=<UserName>,password=<Password> && cd /models`,挂在了包含代码的共享文件夹。 如果还有其它文件夹包含了数据或模型,也可以在此挂载上。
|
||||
|
||||
3. **执行核心逻辑**。 `chmod +x train.sh && train.sh`运行训练脚本`train.sh`。
|
||||
|
||||
4. **保存输出**。 Docker 容器会在每次 Job 完成后被删除。 因此,如果需要任何结果文件,要将其保存到 Docker 容器之外。`train.sh`脚本中将训练后的模型和检查点都保存在了共享文件夹`models/output`。
|
||||
|
||||
注意,此例将所有步骤都放到了 command 字段中。 有些步骤可以放到 Bash 或 Python 脚本中,然后用一条命令来运行。这样可以用脚本来处理更复杂的逻辑。
|
||||
|
||||
|
||||
|
||||
### 提交任务
|
||||
1. 在提交任务之前,我们需要修改`.vscode/settings.json`为:
|
||||
|
||||
```
|
||||
{
|
||||
"pai.job.upload.enabled": true,
|
||||
"pai.job.upload.exclude": [],
|
||||
"pai.job.upload.include": [
|
||||
"**/*.sh"
|
||||
],
|
||||
"pai.job.generateJobName.enabled": true
|
||||
}
|
||||
```
|
||||
完成以后,提交任务会自动将当前目录中的.sh文件一起上传。
|
||||
|
||||
2. 右键`new_job.pai.json`,选择`Submit Job to PAI Cluster`。
|
||||
|
||||

|
||||
|
||||
此时,就完成了任务的提交。
|
||||
|
||||
成功提交任务后,VS Code会将上一步新建的.sh文件同时上传至此次任务的目录,在Docker创建完后会自动执行`new_job.pai.json`文件中`taskRoles`下的`command`字段内容。
|
||||
|
||||
|
||||
### 训练结果
|
||||
|
||||
在任务运行完成后,Docker会自动销毁,由于我们将训练结果保存至了共享文件夹的`output`目录,我们可以在`output`中找到我们的训练结果。
|
||||
|
||||
至此,我们便完成了OpenPAI的任务提交、训练以及获取训练结果啦。
|
||||
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 20 KiB |
@@ -1,37 +0,0 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import hdfs
|
||||
|
||||
#hdfs.client._Request.webhdfs_prefix = "/webhdfs/api/v1"
|
||||
_hdfs_client = None
|
||||
_hdfs_url = "add your ip and port for pai hdfs here"
|
||||
_hdfs_user_name = "add your user name here"
|
||||
_hdfs_root = "/samples//"
|
||||
|
||||
|
||||
def _hdfs_initialize():
|
||||
global _hdfs_client
|
||||
if _hdfs_client is None:
|
||||
_hdfs_client = hdfs.InsecureClient(_hdfs_url, root=_hdfs_root, user=_hdfs_user_name)
|
||||
_hdfs_client.set_permission(_hdfs_root, 777)
|
||||
|
||||
def _hdfs_download(hdfs_path, local_path):
|
||||
_hdfs_initialize()
|
||||
_hdfs_client.download(hdfs_path, local_path)
|
||||
|
||||
def _hdfs_upload(local_path, hdfs_path):
|
||||
_hdfs_initialize()
|
||||
_hdfs_client.makedirs(hdfs_path)
|
||||
_hdfs_client.upload(hdfs_path, local_path, overwrite=True)
|
||||
|
||||
def Download(hdfs_path, local_path):
|
||||
print("Downloading from {} to {}".format(hdfs_path, local_path))
|
||||
os.makedirs(local_path, exist_ok=True)
|
||||
_hdfs_download(hdfs_path, local_path)
|
||||
|
||||
def Upload(local_path, hdfs_path):
|
||||
print("Uploading from {} to {}".format(local_path, hdfs_path))
|
||||
_hdfs_upload(local_path, hdfs_path)
|
||||
@@ -1,25 +0,0 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
import os
|
||||
import hdfshelper
|
||||
import subprocess
|
||||
|
||||
jobName = os.environ['PAI_JOB_NAME'].split('~')[1]
|
||||
|
||||
print("Downloading data...")
|
||||
hdfshelper.Download("/samples/data/", ".")
|
||||
hdfshelper.Download("/samples/code/pip_requirements_gpu.txt", ".")
|
||||
|
||||
print("Running...")
|
||||
subprocess.call("./train.sh", shell=True)
|
||||
|
||||
print("Uploading data...")
|
||||
output_dir = os.path.join("/samples", "output", jobName)
|
||||
hdfshelper.Upload("./output/", output_dir)
|
||||
# hdfshelper.Upload("./data/checkpoint", output_dir)
|
||||
# hdfshelper.Upload("./data/model.ckpt-200000.data-00000-of-00003", output_dir)
|
||||
# hdfshelper.Upload("./data/model.ckpt-200000.data-00001-of-00003", output_dir)
|
||||
# hdfshelper.Upload("./data/model.ckpt-200000.data-00002-of-00003", output_dir)
|
||||
# hdfshelper.Upload("./data/model.ckpt-200000.index", output_dir)
|
||||
# hdfshelper.Upload("./data/model.ckpt-200000.meta", output_dir)
|
||||
@@ -1,18 +0,0 @@
|
||||
HOME_DIR=$(cd `dirname $0`; pwd)
|
||||
CODE_DIR=${HOME_DIR}/tensor2tensor
|
||||
export PYTHONPATH=${CODE_DIR}:${PYTHONPATH}
|
||||
binFile=${CODE_DIR}/tensor2tensor/bin
|
||||
|
||||
PROBLEM=translate_up2down
|
||||
MODEL=transformer
|
||||
HPARAMS_SET=transformer_small
|
||||
|
||||
DATA_DIR=${HOME_DIR}/data
|
||||
|
||||
mkdir -p $DATA_DIR
|
||||
|
||||
# Generate data
|
||||
python ${binFile}/t2t-datagen \
|
||||
--t2t_usr_dir=$DATA_DIR \
|
||||
--data_dir=$DATA_DIR \
|
||||
--problem=$PROBLEM
|
||||
@@ -1,96 +0,0 @@
|
||||
# 环境配置
|
||||
|
||||
Ubuntu 16.04
|
||||
|
||||
Anaconda3 安装 python3.x 环境,或本机 Python3.x 环境
|
||||
|
||||
安装 training/train_requirements.txt 中所需要的 python packages
|
||||
|
||||
|
||||
# 数据收集
|
||||
|
||||
1. 从 GitHub 网站下载 couplet v1.0 release 版本的数据,并解压。
|
||||
|
||||
**GitHub网站**:https://github.com/wb14123/couplet-dataset/releases
|
||||
|
||||
**文件名**:couplet.tar.gz
|
||||
|
||||
2. 下载 Tensor2Tensor 库 v1.2.9 版本
|
||||
```
|
||||
git clone https://github.com/tensorflow/tensor2tensor.git
|
||||
git checkout v1.2.9
|
||||
```
|
||||
|
||||
|
||||
# 数据处理
|
||||
|
||||
Couplet 数据解压后,有两个文件夹,分别存放 train 和 test 的上下联数据。 In 为上联, out 为下联。在训练过程中,我们只需对 train 的数据进行预处理。
|
||||
|
||||
1. 创建 data 子目录。
|
||||
2. 将 couplet 中 train 和 test 目录下的数据移动至 data 目录下,并重命名为:
|
||||
```
|
||||
train.in.txt,train.out.txt,test.in.txt,test.out.txt。
|
||||
```
|
||||
3. 在 data 目录中,合并上下联文件并保存结果:
|
||||
```
|
||||
cat train.in.txt train.out.txt > train.merge.txt
|
||||
```
|
||||
4. 在data 目录中,统计字表数目并保存字表:
|
||||
|
||||
需要安装 subword-nmt 包:
|
||||
```
|
||||
python -m pip install subword-nmt
|
||||
```
|
||||
|
||||
统计字数命令:
|
||||
```
|
||||
subword-nmt get-vocab –input train.merge.txt –output train.merge.txt.vocab
|
||||
```
|
||||
5. 在 data 目录中,去掉字表文件中的汉字出现次数,只留字表:
|
||||
```
|
||||
cat train.merge.txt.vocab | awk ‘{print $1}’ > merge.txt.vocab.clean
|
||||
```
|
||||
6. 将 merge.txt.vocab.clean 的前三行填充如下内容,并将字表字数加3:
|
||||
```
|
||||
<pad>
|
||||
<EOS>
|
||||
<UNK>
|
||||
```
|
||||
7. 将 training/usr_dir 目录下的 merge_vocab.py 和 \_\_init\_\_.py 文件拷贝到 data 目录。
|
||||
8. 修改 merge_vocab.py 中的内容:
|
||||
|
||||
a. SRC_TRAIN_DATA 为训练集上联数据文件
|
||||
|
||||
b. TGT_TRAIN_DATA 为训练集下联数据文件
|
||||
|
||||
c. SRC_DEV_DATA 为测试集上联数据文件
|
||||
|
||||
d. TGT_DEV_DATA 为测试集下联数据文件
|
||||
|
||||
e. MERGE_VOCAB 为最终字表文件
|
||||
|
||||
f. VOCAB_SIZE 为字表文件中字的个数
|
||||
|
||||
|
||||
# 数据生成
|
||||
|
||||
1. 修改 data_gen.sh 脚本内容。
|
||||
2. 运行 `bash data_gen.sh` 脚本,完成数据生成工作。
|
||||
|
||||
|
||||
# 数据训练
|
||||
|
||||
1. 创建 output 目录存放训练结果。
|
||||
2. 修改 train.sh 脚本内容,如 *train_steps*,*batch_size* 等为你想要的数值。
|
||||
3. 运行 `bash train.sh` 脚本,完成数据处理工作。
|
||||
4. 数据训练完成,则在 output 目录下生成训练数据。
|
||||
|
||||
我设置的 batch_size 为 100000 ,所以最终需要的数据文件为:
|
||||
```
|
||||
model.ckpt-100000.data-00000-of-00003
|
||||
model.ckpt-100000.data-00001-of-00003
|
||||
model.ckpt-100000.data-00002-of-00003
|
||||
model.ckpt-100000.index
|
||||
model.ckpt-100000.meta
|
||||
checkpoint
|
||||
```
|
||||
@@ -1,91 +0,0 @@
|
||||
PROBLEM=translate_up2down
|
||||
MODEL=transformer
|
||||
HPARAMS_SET=transformer_small
|
||||
|
||||
HOME_DIR=$(cd `dirname $0`; pwd)
|
||||
CODE_DIR=$HOME_DIR/tensor2tensor-1.2.9
|
||||
DATA_DIR=$HOME_DIR/data
|
||||
|
||||
USR_LIB_DIR=$HOME_DIR/usr_dir
|
||||
|
||||
mosesdecoder=${HOME_DIR}/mosesdecoder
|
||||
|
||||
TRAIN_DIR=${HOME_DIR}/train
|
||||
TEST_DIR=$HOME_DIR/test
|
||||
LOG_DIR=$HOME_DIR/log
|
||||
export PYTHONPATH=$CODE_DIR:$PYTHONPATH
|
||||
|
||||
mkdir -p $TEST_DIR
|
||||
|
||||
BEAM_SIZE=4
|
||||
ALPHA=0.6
|
||||
BATCH_SIZE=32
|
||||
|
||||
START_POINT=1
|
||||
First_lan_file=
|
||||
Second_lan_file=
|
||||
#EXTRA=supervise1
|
||||
|
||||
|
||||
while true; do
|
||||
ids=$(ls ${TRAIN_DIR} | grep "model\.ckpt-[0-9]*.index" | grep -o "[0-9]*")
|
||||
echo "All Ids:" ${ids}
|
||||
|
||||
for i in ${ids}; do
|
||||
if [ $i -gt $START_POINT ]; then
|
||||
TEST_RESULT_DIR=${TEST_DIR}/$i-BEAM${BEAM_SIZE}-ALPHA${ALPHA} # in TMP_DIR
|
||||
if test -s $TEST_RESULT_DIR/BLEU.txt; then
|
||||
echo $i, "already tested"
|
||||
else
|
||||
echo "testing" $i
|
||||
rm -rf $TEST_RESULT_DIR
|
||||
mkdir -p $TEST_RESULT_DIR
|
||||
cp $TRAIN_DIR/model.ckpt-${i}.* $TEST_RESULT_DIR
|
||||
touch $TEST_RESULT_DIR/checkpoint
|
||||
echo model_checkpoint_path: \"model.ckpt-${i}\" >> $TEST_RESULT_DIR/checkpoint
|
||||
echo all_model_checkpoint_paths: \"model.ckpt-${i}\" >> $TEST_RESULT_DIR/checkpoint
|
||||
|
||||
#the test result has finished bpe/token
|
||||
cp $DATA_DIR/$First_lan_file $TEST_RESULT_DIR
|
||||
cp $DATA_DIR/$Second_lan_file $TEST_RESULT_DIR
|
||||
|
||||
|
||||
#Fir -> Sec
|
||||
|
||||
python $CODE_DIR/tensor2tensor/bin/t2t-decoder \
|
||||
--t2t_usr_dir=$USR_LIB_DIR \
|
||||
--data_dir=$DATA_DIR \
|
||||
--problems=$PROBLEM \
|
||||
--model=$MODEL \
|
||||
--hparams_set=$HPARAMS_SET \
|
||||
--hparams="batch_size=1024" \
|
||||
--output_dir=$TEST_RESULT_DIR \
|
||||
--decode_hparams="beam_size=$BEAM_SIZE,alpha=$ALPHA,batch_size=$BATCH_SIZE" \
|
||||
--worker_gpu=1 \
|
||||
--decode_from_file=$TEST_RESULT_DIR/$First_lan_file \
|
||||
--decode_to_file=$TEST_RESULT_DIR/$First_lan_file.${i} \
|
||||
2>&1 \
|
||||
| tee $LOG_DIR/test-$i-BEAM${BEAM_SIZE}-ALPHA${ALPHA}-ENWAR.log.txt
|
||||
|
||||
mv $TEST_RESULT_DIR/$First_lan_file.${i}*.decodes $TEST_RESULT_DIR/$First_lan_file.${i}.decodes
|
||||
echo "EN -> WAR:" >> $TEST_RESULT_DIR/BLEU.txt
|
||||
$mosesdecoder/scripts/generic/multi-bleu.perl \
|
||||
$TEST_RESULT_DIR/$Second_lan_file < $TEST_RESULT_DIR/$First_lan_file.${i}.decodes \
|
||||
| tee -a $TEST_RESULT_DIR/BLEU.txt
|
||||
|
||||
rm -r $TEST_RESULT_DIR/model.ckpt-*
|
||||
echo "$id:"
|
||||
cat $TEST_RESULT_DIR/BLEU.txt
|
||||
fi
|
||||
else
|
||||
echo "pass" $i
|
||||
fi
|
||||
done
|
||||
cat $TEST_DIR/ALL_BLEU.txt
|
||||
break
|
||||
done
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
# coding=utf-8
|
||||
""" Problem definition for translation from Up to Down."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import tensorflow as tf
|
||||
|
||||
#from tensor2tensor.data_generators.wmt import WMTProblem
|
||||
from tensor2tensor.data_generators.translate import TranslateProblem
|
||||
from tensor2tensor.data_generators import problem
|
||||
from tensor2tensor.data_generators import text_encoder
|
||||
from tensor2tensor.utils import registry
|
||||
from tensor2tensor.models import transformer
|
||||
|
||||
|
||||
SRC_LAN = 'up'
|
||||
TGT_LAN = 'down'
|
||||
SRC_TRAIN_DATA = 'Your up train file'
|
||||
TGT_TRAIN_DATA = 'Your down train file'
|
||||
SRC_DEV_DATA = 'Your up dev file'
|
||||
TGT_DEV_DATA = 'Your down dev file'
|
||||
MERGE_VOCAB = 'merge.tok.vocab.clean'
|
||||
VOCAB_SIZE = 7033
|
||||
LOCATION_OF_DATA = os.path.abspath(os.path.dirname(__file__)) + '/'
|
||||
|
||||
_TRAIN_DATASETS = [
|
||||
LOCATION_OF_DATA + SRC_TRAIN_DATA,
|
||||
LOCATION_OF_DATA + TGT_TRAIN_DATA
|
||||
]
|
||||
|
||||
_DEV_DATASETS = [
|
||||
LOCATION_OF_DATA + SRC_DEV_DATA,
|
||||
LOCATION_OF_DATA + TGT_DEV_DATA
|
||||
]
|
||||
|
||||
_VOCAB_FILES = [
|
||||
LOCATION_OF_DATA + MERGE_VOCAB
|
||||
]
|
||||
|
||||
|
||||
def bi_vocabs_token2id_generator(source_path, target_path, token_vocab, eos=None):
|
||||
"""Generator for sequence-to-sequence tasks that uses tokens.
|
||||
|
||||
This generator assumes the files at source_path and target_path have
|
||||
the same number of lines and yields dictionaries of "inputs" and "targets"
|
||||
where inputs are token ids from the " "-split source (and target, resp.) lines
|
||||
converted to integers using the token_map.
|
||||
|
||||
Args:
|
||||
source_path: path to the file with source sentences.
|
||||
target_path: path to the file with target sentences.
|
||||
source_token_vocab: text_encoder.TextEncoder object.
|
||||
target_token_vocab: text_encoder.TextEncoder object.
|
||||
eos: integer to append at the end of each sequence (default: None).
|
||||
|
||||
Yields:
|
||||
A dictionary {"inputs": source-line, "targets": target-line} where
|
||||
the lines are integer lists converted from tokens in the file lines.
|
||||
"""
|
||||
eos_list = [] if eos is None else [eos]
|
||||
with tf.gfile.GFile(source_path, mode="r") as source_file:
|
||||
with tf.gfile.GFile(target_path, mode="r") as target_file:
|
||||
source, target = source_file.readline(), target_file.readline()
|
||||
while source and target:
|
||||
source_ints = token_vocab.encode(source.strip()) + eos_list
|
||||
target_ints = token_vocab.encode(target.strip()) + eos_list
|
||||
yield {"inputs": source_ints, "targets": target_ints}
|
||||
source, target = source_file.readline(), target_file.readline()
|
||||
|
||||
|
||||
@registry.register_problem
|
||||
class TranslateUp2down(TranslateProblem):
|
||||
"""Problem spec for Up to Down translation."""
|
||||
|
||||
|
||||
@property
|
||||
def vocab_size(self):
|
||||
return VOCAB_SIZE # subtract for compensation
|
||||
|
||||
@property
|
||||
def num_shards(self):
|
||||
return 1
|
||||
|
||||
@property
|
||||
def vocab_name(self):
|
||||
return MERGE_VOCAB
|
||||
|
||||
@property
|
||||
def input_space_id(self):
|
||||
return problem.SpaceID.EN_TOK
|
||||
|
||||
@property
|
||||
def target_space_id(self):
|
||||
return problem.SpaceID.EN_TOK
|
||||
|
||||
|
||||
# Pre-process two vocabularies and build a generator.
|
||||
def generator(self, data_dir, tmp_dir, train):
|
||||
# Load vocabulary.
|
||||
tf.logging.info("Loading and processing vocabulary for %s from:" % ("training" if train else "validation"))
|
||||
print(' ' + _VOCAB_FILES[0] + ' ... ', end='')
|
||||
sys.stdout.flush()
|
||||
with open(_VOCAB_FILES[0], 'r', encoding='utf-8') as f:
|
||||
vocab_list = f.read().splitlines()
|
||||
print('Done')
|
||||
|
||||
# Truncate the vocabulary depending on the given size (strip the reserved tokens).
|
||||
vocab_list = vocab_list[3:]
|
||||
|
||||
# Insert the <UNK>.
|
||||
vocab_list.insert(0, "<UNK>")
|
||||
|
||||
# Auto-insert the reserved tokens as: <pad>=0 <EOS>=1 and <UNK>=2.
|
||||
vocab = text_encoder.TokenTextEncoder(vocab_filename=None, vocab_list=vocab_list,
|
||||
replace_oov="<UNK>", num_reserved_ids=text_encoder.NUM_RESERVED_TOKENS)
|
||||
|
||||
# Select the path: train or dev (small train).
|
||||
datapath = _TRAIN_DATASETS if train else _DEV_DATASETS
|
||||
|
||||
# Build a generator.
|
||||
return bi_vocabs_token2id_generator(datapath[0], datapath[1], vocab, text_encoder.EOS_ID)
|
||||
|
||||
|
||||
# Build bi-vocabs feature encoders for decoding.
|
||||
def feature_encoders(self, data_dir):
|
||||
# Load vocabulary.
|
||||
tf.logging.info("Loading and processing vocabulary from: %s" % _VOCAB_FILES[0])
|
||||
with open(_VOCAB_FILES[0], 'r', encoding='utf-8') as f:
|
||||
vocab_list = f.read().splitlines()
|
||||
tf.logging.info("Done")
|
||||
|
||||
# Truncate the vocabulary depending on the given size (strip the reserved tokens).
|
||||
vocab_list = vocab_list[3:]
|
||||
|
||||
# Insert the <UNK>.
|
||||
vocab_list.insert(0, "<UNK>")
|
||||
|
||||
# Auto-insert the reserved tokens as: <pad>=0 <EOS>=1 and <UNK>=2.
|
||||
encoder = text_encoder.TokenTextEncoder(vocab_filename=None, vocab_list=vocab_list,
|
||||
replace_oov="<UNK>",
|
||||
num_reserved_ids=text_encoder.NUM_RESERVED_TOKENS)
|
||||
|
||||
return {"inputs": encoder, "targets": encoder}
|
||||
|
||||
|
||||
|
||||