提交 3874172a 编写于 作者: T tangwei

rebase user

上级 ded22807
...@@ -57,12 +57,6 @@ class Model(object): ...@@ -57,12 +57,6 @@ class Model(object):
""" """
return self._metrics return self._metrics
def custom_preprocess(self):
"""
do something after exe.run(stratup_program) and before run()
"""
pass
def get_fetch_period(self): def get_fetch_period(self):
return self._fetch_interval return self._fetch_interval
......
...@@ -3,15 +3,15 @@ ...@@ -3,15 +3,15 @@
用户自定义数据集及配置异步Reader,需要关注以下几个步骤: 用户自定义数据集及配置异步Reader,需要关注以下几个步骤:
* [数据集整理](#数据集整理) * [数据集整理](#数据集整理)
* [在模型组网中加入输入占位符]() * [在模型组网中加入输入占位符](#在模型组网中加入输入占位符)
* [Reader实现及调试]() * [Reader实现](#Reader的实现)
* [在yaml文件中配置Reader](#在yaml文件中配置reader) * [在yaml文件中配置Reader](#在yaml文件中配置reader)
我们以CTR-DNN模型为例,给出了从数据整理,变量定义,Reader写法,调试的完整历程。 我们以CTR-DNN模型为例,给出了从数据整理,变量定义,Reader写法,调试的完整历程。
* [PaddleRec-数据及Reader快速上手]() * [数据及Reader示例-DNN](#数据及Reader示例-DNN)
#
## 数据集整理 ## 数据集整理
PaddleRec支持模型自定义数据集。 PaddleRec支持模型自定义数据集。
...@@ -65,58 +65,121 @@ self._data_var.append(var_c) ...@@ -65,58 +65,121 @@ self._data_var.append(var_c)
``` ```
至此,我们完成了在组网中定义输入数据的工作。 至此,我们完成了在组网中定义输入数据的工作。
## Reader的实现及调试 ## Reader的实现
### Reader的实现范式 ### Reader的实现范式
Reader需要一个单独的python文件进行描述 Reader的逻辑需要一个单独的python文件进行描述。我们试写一个`test_reader.py`,实现的具体流程如下:
具体流程如下:
1. 首先我们需要引入Reader基类 1. 首先我们需要引入Reader基类
```python ```python
from paddlerec.core.reader import Reader from paddlerec.core.reader import Reader
``` ```
2. 创建一个子类,继承Reader的基类,训练所需Reader命名为`TrainerReader` 2. 创建一个子类,继承Reader的基类,训练所需Reader命名为`TrainerReader`
3.`init(self)`函数中声明一些在数据读取中会用到的变量,如示例代码中的`cont_min_``categorical_range_`等,必要时可以在`config.yaml`文件中配置变量,通过`env.get_global_env()`拿到。 ```python
4. 继承并实现基类中的`generate_sample(self, line)`函数,逐行读取数据。该函数应返回一个可以迭代的reader方法(带有yield的函数不再是一个普通的函数,而是一个生成器generator,成为了可以迭代的对象,等价于一个数组、链表、文件、字符串etc.) class TrainerReader(Reader):
5. 在这个可以迭代的函数中,如示例代码中的`def reader()`,我们定义数据读取的逻辑。以行为单位的数据进行截取,转换及预处理。 def init(self):
6. 最后,我们需要将数据整理为特定的格式,才能够被dataset正确读取,并灌入的训练的网络中。简单来说,数据的输出顺序与我们在网络中创建的`inputs`必须是严格一一对应的,并转换为类似字典的形式。在示例代码中,我们使用`zip`的方法将参数名与数值构成的元组组成了一个list,并将其yield输出。 pass
def generator_sample(self, line):
pass
```
3.`init(self)`函数中声明一些在数据读取中会用到的变量,必要时可以在`config.yaml`文件中配置变量,利用`env.get_global_env()`拿到。
## 在yaml文件中配置reader 比如,我们希望从yaml文件中读取一个数据预处理变量`avg=10`,目的是将数据A的数据缩小10倍,可以这样实现:
`ctr-dnn`模型举例: - 首先更改yaml文件,在某个space下加入该变量
```yaml
...
train:
reader:
avg: 10
...
```
```yaml - 再更改Reader的init函数
reader: ```python
batch_size: 2 from paddlerec.core.utils import envs
class: "{workspace}/../criteo_reader.py" class TrainerReader(Reader):
train_data_path: "{workspace}/data/train" def init(self):
reader_debug_mode: False self.avg = envs.get_global_env("avg", None, "train.reader")
```
有以上4个需要重点关注的配置选项: def generator_sample(self, line):
pass
```
4. 继承并实现基类中的`generate_sample(self, line)`函数,逐行读取数据。
- 该函数应返回一个可以迭代的reader方法(带有yield的函数不再是一个普通的函数,而是一个生成器generator,成为了可以迭代的对象,等价于一个数组、链表、文件、字符串etc.)
- 在这个可以迭代的函数中,如示例代码中的`def reader()`,我们定义数据读取的逻辑。以行为单位的数据进行截取,转换及预处理。
- 最后,我们需要将数据整理为特定的格式,才能够被PaddleRec的Reader正确读取,并灌入的训练的网络中。简单来说,数据的输出顺序与我们在网络中创建的`inputs`必须是严格一一对应的,并转换为类似字典的形式。
示例: 假设数据ABC在文本数据中,每行以这样的形式存储:
```shell
0.1,0.2,0.3...3.0,3.1,3.2 \t 99999,99998,99997 \t 1 \n
```
则示例代码如下:
```python
from paddlerec.core.utils import envs
class TrainerReader(Reader):
def init(self):
self.avg = envs.get_global_env("avg", None, "train.reader")
def generator_sample(self, line):
- batch_size: 网络进行小批量训练的一组数据的大小 def reader(self, line):
- class: 指定数据处理及读取的`reader` python文件 # 先分割 '\n', 再以 '\t'为标志分割为list
- train_data_path: 训练数据所在地址 variables = (line.strip('\n')).split('\t')
- reader_debug_mode: 测试reader语法,及输出是否符合预期的debug模式的开关
## 自定义数据集 # A是第一个元素,并且每个数据之间使用','分割
var_a = variables[0].split(',') # list
var_a = [float(i) / self.avg for i in var_a] # 将str数据转换为float
PaddleRec支持模型自定义数据集,在model.config.yaml文件中的reader部分,通过`train_data_path`指定数据读取路径。
关于数据的tips # B是第二个元素,同样以 ',' 分割
var_b = variables[1].split(',') # list
var_b = [int(i) for i in var_b] # 将str数据转换为int
- PaddleRec 面向的是推荐与搜索领域,数据以文本格式为主 # C是第三个元素, 只有一个元素,没有分割符
- Dataset模式支持读取文本数据压缩后的`.gz`格式 var_c = variables[2]
- Dataset模式下,训练线程与数据读取线程的关系强相关,为了多线程充分利用,`强烈建议将文件拆成多个小文件`,尤其是在分布式训练场景下,可以均衡各个节点的数据量。 var_c = int(var_c) # 将str数据转换为int
var_c = [var_c] # 将单独的数据元素置入list中
## 自定义Reader # 将数据与数据名结合,组织为dict的形式
# 如下,output形式为{ A: var_a, B: var_b, C: var_c}
variable_name = ['A', 'B', 'C']
output = zip(variable_name, [var_a] + [var_b] + [var_c])
数据集准备就绪后,需要适当修改或重写一个新的reader以适配数据集或新组网。 # 将数据输出,使用yield方法,将该函数变为了一个可迭代的对象
yield output
我们以`ctr-dnn`网络举例`reader`的正确打开方式,网络文件位于`models/rank/dnn` ```
至此,我们完成了Reader的实现。
### 在yaml文件中配置Reader
在模型的yaml配置文件中,主要的修改是三个,如下
```yaml
reader:
batch_size: 2
class: "{workspace}/reader.py"
train_data_path: "{workspace}/data/train_data"
reader_debug_mode: False
```
batch_size: 顾名思义,是小批量训练时的样本大小
class: 运行改模型所需reader的路径
train_data_path: 训练数据所在文件夹
reader_debug_mode: 测试reader语法,及输出是否符合预期的debug模式的开关
#
## 数据及Reader示例-DNN
Reader代码来源于[criteo_reader.py](../models/rank/criteo_reader.py), 组网代码来源于[model.py](../models/rank/dnn/model.py)
### Criteo数据集格式 ### Criteo数据集格式
...@@ -184,13 +247,8 @@ for input in self.sparse_inputs: ...@@ -184,13 +247,8 @@ for input in self.sparse_inputs:
self._data_var.append(self.label_input) self._data_var.append(self.label_input)
if self._platform != "LINUX":
self._data_loader = fluid.io.DataLoader.from_generator(
feed_list=self._data_var, capacity=64, use_double_buffer=False, iterable=False)
``` ```
若运行于**Linux**环境下,默认使用**dataset**模式读取数据集;若运行于**windows****mac**下,默认使用**dataloader**模式读取数据集。以上两种方法是paddle.io中提供的不同模式,`dataset`运行速度更快,但依赖于linux的环境,因此会有该逻辑判断。
> Paddle的组网中不支持数据输入为`str`类型,`强烈不建议使用明文保存和读取数据`
### Criteo Reader写法 ### Criteo Reader写法
...@@ -249,24 +307,6 @@ class TrainReader(Reader): ...@@ -249,24 +307,6 @@ class TrainReader(Reader):
return reader return reader
``` ```
### 如何自定义数据读取规则
在上文我们看到了由`criteo_reader.py`实现具体的数据读取规则,那么,怎样为自己的数据集写规则呢?
具体流程如下:
1. 首先我们需要引入Reader基类
```python
from paddlerec.core.reader import Reader
```
2. 创建一个子类,继承Reader的基类,训练所需Reader命名为`TrainerReader`
3.`init(self)`函数中声明一些在数据读取中会用到的变量,如示例代码中的`cont_min_``categorical_range_`等,必要时可以在`config.yaml`文件中配置变量,通过`env.get_global_env()`拿到。
4. 继承并实现基类中的`generate_sample(self, line)`函数,逐行读取数据。该函数应返回一个可以迭代的reader方法(带有yield的函数不再是一个普通的函数,而是一个生成器generator,成为了可以迭代的对象,等价于一个数组、链表、文件、字符串etc.)
5. 在这个可以迭代的函数中,如示例代码中的`def reader()`,我们定义数据读取的逻辑。以行为单位的数据进行截取,转换及预处理。
6. 最后,我们需要将数据整理为特定的格式,才能够被dataset正确读取,并灌入的训练的网络中。简单来说,数据的输出顺序与我们在网络中创建的`inputs`必须是严格一一对应的,并转换为类似字典的形式。在示例代码中,我们使用`zip`的方法将参数名与数值构成的元组组成了一个list,并将其yield输出。如果展开来看,我们输出的数据形如
`[('dense_feature',[value]),('C1',[value]),('C2',[value]),...,('C26',[value]),('label',[value])]`
### 调试Reader ### 调试Reader
......
# 参数服务器训练简介 # 分布式-参数服务器训练简介
飞桨参数服务器原理介绍请参见: [飞桨参数服务器](https://github.com/PaddlePaddle/Fleet/blob/develop/markdown_doc/transpiler/transpiler_cpu.md) 以下文档来源于[参数服务器训练简介](https://www.paddlepaddle.org.cn/tutorials/projectdetail/454253)
如图1所示,参数服务器是分布式训练领域普遍采用的编程架构,主要包含Server和Worker两个部分,其中Server负责参数的存储和更新,而Worker负责训练。飞桨的参数服务器功能也是基于这种经典的架构进行设计和开发的,同时在这基础上进行了SGD(Stochastic Gradient Descent)算法的创新(Geometric Stochastic Gradient Descent)。当前经过大量的实验验证,最佳的方案是每台机器上启动Server和Worker两个进程,而一个Worker进程中可以包含多个用于训练的线程。 如图1所示,参数服务器是分布式训练领域普遍采用的编程架构,主要包含Server和Worker两个部分,其中Server负责参数的存储和更新,而Worker负责训练。飞桨的参数服务器功能也是基于这种经典的架构进行设计和开发的,同时在这基础上进行了SGD(Stochastic Gradient Descent)算法的创新(Geometric Stochastic Gradient Descent)。当前经过大量的实验验证,最佳的方案是每台机器上启动Server和Worker两个进程,而一个Worker进程中可以包含多个用于训练的线程。
<p align="center"> <p align="center">
......
...@@ -123,7 +123,7 @@ python -m paddlerec.run -m ./models/rank/dnn/config.yaml -e single ...@@ -123,7 +123,7 @@ python -m paddlerec.run -m ./models/rank/dnn/config.yaml -e single
### 背景介绍 ### 背景介绍
* [推荐系统](doc/rec_background.md) * [推荐系统](doc/rec_background.md)
* [分布式-参数服务器](doc/ps_background.md) * [分布式训练](doc/ps_background.md)
### 新手教程 ### 新手教程
* [环境要求](#环境要求) * [环境要求](#环境要求)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册