Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
BaiXuePrincess
PaddleRec
提交
5a0982fa
P
PaddleRec
项目概览
BaiXuePrincess
/
PaddleRec
与 Fork 源项目一致
Fork自
PaddlePaddle / PaddleRec
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
P
PaddleRec
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
5a0982fa
编写于
5月 13, 2020
作者:
C
chengmo
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
add reader doc
上级
33fc1750
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
201 addition
and
77 deletion
+201
-77
doc/custom_dataset_reader.md
doc/custom_dataset_reader.md
+187
-60
doc/rec_background.md
doc/rec_background.md
+2
-0
readme.md
readme.md
+12
-17
未找到文件。
doc/custom_dataset_reader.md
浏览文件 @
5a0982fa
# PaddleRec 自定义数据集及Reader
## dataset数据读取
为了能高速运行CTR模型的训练,我们使用
`dataset`
API进行高性能的IO,dataset是为多线程及全异步方式量身打造的数据读取方式,每个数据读取线程会与一个训练线程耦合,形成了多生产者-多消费者的模式,会极大的加速我们的模型训练。
## 数据集及reader配置简介
如何在我们的训练中引入dataset读取方式呢?无需变更数据格式,只需在我们的训练代码中加入以下内容,便可达到媲美二进制读取的高效率,以下是一个比较完整的流程
:
以
`ctr-dnn`
模型举例
:
### 引入dataset
```
yaml
reader
:
batch_size
:
2
class
:
"
{workspace}/../criteo_reader.py"
train_data_path
:
"
{workspace}/data/train"
reader_debug_mode
:
False
```
有以上4个需要重点关注的配置选项:
-
batch_size: 网络进行小批量训练的一组数据的大小
-
class: 指定数据处理及读取的
`reader`
python文件
-
train_data_path: 训练数据所在地址
-
reader_debug_mode: 测试reader语法,及输出是否符合预期的debug模式的开关
## 自定义数据集
PaddleRec支持模型自定义数据集,在model.config.yaml文件中的reader部分,通过
`train_data_path`
指定数据读取路径。
> 关于数据的tips
>
> - PaddleRec 面向的是推荐与搜索领域,数据以文本格式为主
> - Dataset模式支持读取文本数据压缩后的`.gz`格式
> - Dataset模式下,训练线程与数据读取线程的关系强相关,为了多线程充分利用,`强烈建议将文件拆成多个小文件`,尤其是在分布式训练场景下,可以均衡各个节点的数据量。
## 自定义Reader
数据集准备就绪后,需要适当修改或重写一个新的reader以适配数据集或新组网。
我们以
`ctr-dnn`
网络举例
`reader`
的正确打开方式,网络文件位于
`models/rank/dnn`
。
### Criteo数据集格式
CTR-DNN训练及测试数据集选用
[
Display Advertising Challenge
](
https://www.kaggle.com/c/criteo-display-ad-challenge/
)
所用的Criteo数据集。该数据集包括两部分:训练集和测试集。训练集包含一段时间内Criteo的部分流量,测试集则对应训练数据后一天的广告点击流量。
每一行数据格式如下所示:
```
bash
<label> <integer feature 1> ... <integer feature 13> <categorical feature 1> ... <categorical feature 26>
```
其中
```<label>```
表示广告是否被点击,点击用1表示,未点击用0表示。
```<integer feature>```
代表数值特征(连续特征),共有13个连续特征。
```<categorical feature>```
代表分类特征(离散特征),共有26个离散特征。相邻两个特征用
```\t```
分隔,缺失特征用空格表示。测试集中
```<label>```
特征已被移除。
### Criteo数据集的预处理
1.
通过工厂类
`fluid.DatasetFactory()`
创建一个dataset对象。
2.
将我们定义好的数据输入格式传给dataset,通过
`dataset.set_use_var(inputs)`
实现。
3.
指定我们的数据读取方式,由
`dataset_generator.py`
实现数据读取的规则,后面将会介绍读取规则的实现。
4.
指定数据读取的batch_size。
5.
指定数据读取的线程数,该线程数和训练线程应保持一致,两者为耦合的关系。
6.
指定dataset读取的训练文件的列表。
数据预处理共包括两步:
-
将原始训练集按9:1划分为训练集和验证集
-
数值特征(连续特征)需进行归一化处理,但需要注意的是,对每一个特征
```<integer feature i>```
,归一化时用到的最大值并不是用全局最大值,而是取排序后95%位置处的特征值作为最大值,同时保留极值。
### CTR网络输入的定义
正如前所述,Criteo数据集中,分为连续数据与离散(稀疏)数据,所以整体而言,CTR-DNN模型的数据输入层包括三个,分别是:
`dense_input`
用于输入连续数据,维度由超参数
`dense_feature_dim`
指定,数据类型是归一化后的浮点型数据。
`sparse_input_ids`
用于记录离散数据,在Criteo数据集中,共有26个slot,所以我们创建了名为
`C1~C26`
的26个稀疏参数输入,并设置
`lod_level=1`
,代表其为变长数据,数据类型为整数;最后是每条样本的
`label`
,代表了是否被点击,数据类型是整数,0代表负样例,1代表正样例。
在Paddle中数据输入的声明使用
`paddle.fluid.layers.data()`
,会创建指定类型的占位符,数据IO会依据此定义进行数据的输入。
稀疏参数输入的定义:
```
python
def
get_dataset
(
inputs
,
args
)
dataset
=
fluid
.
DatasetFactory
().
create_dataset
()
dataset
.
set_use_var
(
inputs
)
dataset
.
set_pipe_command
(
"python dataset_generator.py"
)
dataset
.
set_batch_size
(
args
.
batch_size
)
dataset
.
set_thread
(
int
(
args
.
cpu_num
))
file_list
=
[
str
(
args
.
train_files_path
)
+
"/%s"
%
x
for
x
in
os
.
listdir
(
args
.
train_files_path
)
def
sparse_inputs
():
ids
=
envs
.
get_global_env
(
"hyper_parameters.sparse_inputs_slots"
,
None
,
self
.
_namespace
)
sparse_input_ids
=
[
fluid
.
layers
.
data
(
name
=
"S"
+
str
(
i
),
shape
=
[
1
],
lod_level
=
1
,
dtype
=
"int64"
)
for
i
in
range
(
1
,
ids
)
]
logger
.
info
(
"file list: {}"
.
format
(
file_list
))
return
dataset
,
file_list
return
sparse_input_ids
```
### 如何指定数据读取规则
稠密参数输入的定义:
```
python
def
dense_input
():
dim
=
envs
.
get_global_env
(
"hyper_parameters.dense_input_dim"
,
None
,
self
.
_namespace
)
在上文我们提到了由
`dataset_generator.py`
实现具体的数据读取规则,那么,怎样为dataset创建数据读取的规则呢?
以下是
`dataset_generator.py`
的全部代码,具体流程如下:
1.
首先我们需要引入dataset的库,位于
`paddle.fluid.incubate.data_generator`
。
2.
声明一些在数据读取中会用到的变量,如示例代码中的
`cont_min_`
、
`categorical_range_`
等。
3.
创建一个子类,继承dataset的基类,基类有多种选择,如果是多种数据类型混合,并且需要转化为数值进行预处理的,建议使用
`MultiSlotDataGenerator`
;若已经完成了预处理并保存为数据文件,可以直接以
`string`
的方式进行读取,使用
`MultiSlotStringDataGenerator`
,能够进一步加速。在示例代码,我们继承并实现了名为
`CriteoDataset`
的dataset子类,使用
`MultiSlotDataGenerator`
方法。
4.
继承并实现基类中的
`generate_sample`
函数,逐行读取数据。该函数应返回一个可以迭代的reader方法(带有yield的函数不再是一个普通的函数,而是一个生成器generator,成为了可以迭代的对象,等价于一个数组、链表、文件、字符串etc.)
5.
在这个可以迭代的函数中,如示例代码中的
`def reader()`
,我们定义数据读取的逻辑。例如对以行为单位的数据进行截取,转换及预处理。
6.
最后,我们需要将数据整理为特定的格式,才能够被dataset正确读取,并灌入的训练的网络中。简单来说,数据的输出顺序与我们在网络中创建的
`inputs`
必须是严格一一对应的,并转换为类似字典的形式。在示例代码中,我们使用
`zip`
的方法将参数名与数值构成的元组组成了一个list,并将其yield输出。如果展开来看,我们输出的数据形如
`[('dense_feature',[value]),('C1',[value]),('C2',[value]),...,('C26',[value]),('label',[value])]`
dense_input_var
=
fluid
.
layers
.
data
(
name
=
"D"
,
shape
=
[
dim
],
dtype
=
"float32"
)
return
dense_input_var
```
标签的定义:
```
python
def
label_input
():
label
=
fluid
.
layers
.
data
(
name
=
"click"
,
shape
=
[
1
],
dtype
=
"int64"
)
return
label
```
组合起来,正确的声明他们:
```
python
import
paddle.fluid.incubate.data_generator
as
dg
self
.
sparse_inputs
=
sparse_inputs
()
self
.
dense_input
=
dense_input
()
self
.
label_input
=
label_input
()
self
.
_data_var
.
append
(
self
.
dense_input
)
for
input
in
self
.
sparse_inputs
:
self
.
_data_var
.
append
(
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的环境,因此会有该逻辑判断。
cont_min_
=
[
0
,
-
3
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
]
cont_max_
=
[
20
,
600
,
100
,
50
,
64000
,
500
,
100
,
50
,
500
,
10
,
10
,
10
,
50
]
cont_diff_
=
[
20
,
603
,
100
,
50
,
64000
,
500
,
100
,
50
,
500
,
10
,
10
,
10
,
50
]
hash_dim_
=
1000001
continuous_range_
=
range
(
1
,
14
)
categorical_range_
=
range
(
14
,
40
)
> Paddle的组网中不支持数据输入为`str`类型,`强烈不建议使用明文保存和读取数据`
class
CriteoDataset
(
dg
.
MultiSlotDataGenerator
):
### Criteo Reader写法
```
python
# 引入PaddleRec的Reader基类
from
paddlerec.core.reader
import
Reader
# 引入PaddleRec的读取yaml配置文件的方法
from
paddlerec.core.utils
import
envs
# 定义TrainReader,需要继承 paddlerec.core.reader.Reader
class
TrainReader
(
Reader
):
# 数据预处理逻辑,继承自基类
# 如果无需处理, 使用pass跳过该函数的执行
def
init
(
self
):
self
.
cont_min_
=
[
0
,
-
3
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
]
self
.
cont_max_
=
[
20
,
600
,
100
,
50
,
64000
,
500
,
100
,
50
,
500
,
10
,
10
,
10
,
50
]
self
.
cont_diff_
=
[
20
,
603
,
100
,
50
,
64000
,
500
,
100
,
50
,
500
,
10
,
10
,
10
,
50
]
self
.
hash_dim_
=
envs
.
get_global_env
(
"hyper_parameters.sparse_feature_number"
,
None
,
"train.model"
)
self
.
continuous_range_
=
range
(
1
,
14
)
self
.
categorical_range_
=
range
(
14
,
40
)
# 读取数据方法,继承自基类
# 实现可以迭代的reader函数,逐行处理数据
def
generate_sample
(
self
,
line
):
"""
Read the data line by line and process it as a dictionary
"""
def
reader
():
"""
This function needs to be implemented by the user, based on data format
"""
features
=
line
.
rstrip
(
'
\n
'
).
split
(
'
\t
'
)
dense_feature
=
[]
sparse_feature
=
[]
for
idx
in
continuous_range_
:
for
idx
in
self
.
continuous_range_
:
if
features
[
idx
]
==
""
:
dense_feature
.
append
(
0.0
)
else
:
dense_feature
.
append
(
(
float
(
features
[
idx
])
-
cont_min_
[
idx
-
1
])
/
cont_diff_
[
idx
-
1
])
for
idx
in
categorical_range_
:
(
float
(
features
[
idx
])
-
self
.
cont_min_
[
idx
-
1
])
/
self
.
cont_diff_
[
idx
-
1
])
for
idx
in
self
.
categorical_range_
:
sparse_feature
.
append
(
[
hash
(
str
(
idx
)
+
features
[
idx
])
%
hash_dim_
])
[
hash
(
str
(
idx
)
+
features
[
idx
])
%
self
.
hash_dim_
])
label
=
[
int
(
features
[
0
])]
process_line
=
dense_feature
,
sparse_feature
,
label
feature_name
=
[
"dense_feature"
]
for
idx
in
categorical_range_
:
feature_name
.
append
(
"C"
+
str
(
idx
-
13
))
feature_name
=
[
"D"
]
for
idx
in
self
.
categorical_range_
:
feature_name
.
append
(
"S"
+
str
(
idx
-
13
))
feature_name
.
append
(
"label"
)
yield
zip
(
feature_name
,
[
dense_feature
]
+
sparse_feature
+
[
label
])
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])]`
d
=
CriteoDataset
()
d
.
run_from_stdin
()
### 调试Reader
在Linux下运行时,默认启动
`Dataset`
模式,在Win/Mac下运行时,默认启动
`Dataloader`
模式。
通过在
`config.yaml`
中添加或修改
`reader_debug_mode=True`
打开debug模式,只会结合组网运行reader的部分,读取10条样本,并print,方便您观察格式是否符合预期或隐藏bug。
```
yaml
reader
:
batch_size
:
2
class
:
"
{workspace}/../criteo_reader.py"
train_data_path
:
"
{workspace}/data/train"
reader_debug_mode
:
True
```
### 快速调试Dataset
我们可以脱离组网架构,单独验证Dataset的输出是否符合我们预期。使用命令
`cat 数据文件 | python dataset读取python文件`
进行dataset代码的调试:
修改后,使用paddlerec.run执行该修改后的yaml文件,可以观察输出。
```
bash
cat
train_data/part-0 | python dataset_generator.py
python
-m
paddlerec.run
-m
./models/rank/dnn/config.yaml
-e
single
```
输出的数据格式如下:
### Dataset调试
dataset输出的数据格式如下:
` dense_input:size ; dense_input:value ; sparse_input:size ; sparse_input:value ; ... ; sparse_input:size ; sparse_input:value ; label:size ; label:value `
理想的输出为(截取了一个片段):
基本规律是对于每个变量,会先输出其维度大小,再输出其具体值。
直接debug
`criteo_reader`
理想的输出为(截取了一个片段):
```
bash
...
13 0.05 0.00663349917081 0.05 0.0 0.02159375 0.008 0.15 0.04 0.362 0.1 0.2 0.0 0.04 1 715353 1 817085 1 851010 1 833725 1 286835 1 948614 1 881652 1 507110 1 27346 1 646986 1 643076 1 200960 1 18464 1 202774 1 532679 1 729573 1 342789 1 562805 1 880474 1 984402 1 666449 1 26235 1 700326 1 452909 1 884722 1 787527 1 0
13 0.0 0.00497512437811 0.05 0.08 0.207421875 0.028 0.35 0.08 0.082 0.0 0.4 0.0 0.08 1 737395 1 210498 1 903564 1 286224 1 286835 1 906818 1 90
6116 1 67180 1 27346 1 51086 1 142177 1 95024 1 157883 1 873363 1 600281 1 812592 1 228085 1 35900 1 880474 1 984402 1 100885 1 26235 1 410878 1 798162 1 499868 1 306163 1 0
...
```
可以看到首先输出的是13维的dense参数,随后是分立的sparse参数,最后一个是1维的label,数值为0,输出符合预期。
>使用Dataset的一些注意事项
> - Dataset的基本原理:将数据print到缓存,再由C++端的代码实现读取,因此,我们不能在dataset的读取代码中,加入与数据读取无关的print信息,会导致C++端拿到错误的数据信息。
> - dataset目前只支持在`unbuntu`及`CentOS`等标准Linux环境下使用,在`Windows`及`Mac`下使用时,会产生预料之外的错误,请知悉。
\ No newline at end of file
> - dataset目前只支持在`unbuntu`及`CentOS`等标准Linux环境下使用,在`Windows`及`Mac`下使用时,会产生预料之外的错误,请知悉。
### DataLoader调试
dataloader的输出格式为
`list: [ list[var_1], list[var_2], ... , list[var_3]]`
,每条样本的数据会被放在一个
**list[list]**
中,list[0]为第一个variable。
直接debug
`criteo_reader`
理想的输出为(截取了一个片段):
```
bash
...
[[
0.0, 0.004975124378109453, 0.05, 0.08, 0.207421875, 0.028, 0.35, 0.08, 0.082, 0.0, 0.4, 0.0, 0.08],
[
560746],
[
902436],
[
262029],
[
182633],
[
368411],
[
735166],
[
321120],
[
39572],
[
185732],
[
140298],
[
926671],
[
81559],
[
461249],
[
728372],
[
915018],
[
907965],
[
818961],
[
850958],
[
311492],
[
980340],
[
254960],
[
175041],
[
524857],
[
764893],
[
526288],
[
220126],
[
0]]
...
```
可以看到首先输出的是13维的dense参数的list,随后是分立的sparse参数,各自在一个list中,最后一个是1维的label的list,数值为0,输出符合预期。
doc/rec_background.md
0 → 100644
浏览文件 @
5a0982fa
# 推荐系统背景知识
> 占位
\ No newline at end of file
readme.md
浏览文件 @
5a0982fa
...
...
@@ -69,9 +69,11 @@
<h2
align=
"center"
>
快速启动
</h2>
目前框架内置了多个模型,简单的命令即可使用内置模型开始单机训练和本地1
*
1模拟训练,我们以
`ctr-dnn`
为例介绍PaddleRec的简单使用。
### 一行命令启动训练
### 启动内置模型
目前框架内置了多个模型,简单的命令即可使用内置模型开始单机训练和本地1
*
1模拟训练,我们以
`ctr-dnn`
为例介绍PaddleRec的简单使用。
<h3
align=
"center"
>
单机训练
</h3>
...
...
@@ -97,6 +99,10 @@ python -m fleetrec.run -m fleetrec.models.rank.dnn -d cpu -e local_cluster
python
-m
fleetrec.run
-m
fleetrec.models.rank.dnn
-d
cpu
-e
cluster
```
### 启动自定义模型
若您复用内置模型,更改了超参,重新配置了数据后
<h2
align=
"center"
>
支持模型列表
</h2>
> 部分表格占位待改(大规模稀疏)
...
...
@@ -122,43 +128,32 @@ python -m fleetrec.run -m fleetrec.models.rank.dnn -d cpu -e cluster
<h2
align=
"center"
>
文档
</h2>
### 新手教程
*
[
环境要求
](
#环境要求
)
*
[
安装命令
](
#安装命令
)
*
[
快速开始
](
#一行命令启动训练
)
*
[
推荐系统背景知识
](
doc/
)
### 进阶教程
*
[
自定义数据集及Reader
](
doc/custom_dataset_reader.md
)
*
[
模型调参
](
doc/optimization_model.md
)
*
[
单机训练
](
doc/local_train.md
)
*
[
分布式训练
](
doc/distributed_train.md
)
*
[
离线预测
](
doc/predict.md
)
### 关于PaddleRec性能
*
[
Bench
am
rk
](
doc/benchmark.md
)
*
[
Bench
ma
rk
](
doc/benchmark.md
)
### FAQ
*
[
常见问题FAQ
](
doc/faq.md
)
### 设计文档
*
[
PaddleRec设计文档
](
doc/design.md
)
<h2
align=
"center"
>
社区
</h2>
### 贡献代码
*
[
优化PaddleRec框架
](
doc/contribute.md
)
*
[
新增模型到PaddleRec
](
doc/contribute.md
)
### 反馈
如有意见、建议及使用中的BUG,欢迎在
`GitHub Issue`
提交
### 版本历史
*
[
版本更新
](
#版本更新
)
-
2020.5.14 - PaddleRec v0.1
### 许可证书
本项目的发布受
[
Apache 2.0 license
](
LICENSE
)
许可认证。
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录