Paddle 的数据读取

准备数据的步骤

在 Paddle 静态图中,准备数据需要三步。

Step1. 自定义 Reader 生成数据

该步骤由用户自己定义一个数据读取方法,比如从一个指定的文件夹不断的加载图片。根据返回的数据形式不同,可分为 Sample 级和 Batch 级的 Reader。

Step2. 在网络中定义数据层变量

每个网络都需要输入数据,在 TensorFlow 中,使用 Placeholder 来定义输入数据,而在 Paddle 中,使用 fluid.data 来定义输入数据:

1
2
3
4
from paddle import fluid

sentences = fluid.data(name="sentences", dtype="int64", shape=[None, 512, 768])
label = fluid.data(name="label", dtype="int64", shape=[None, 1])

Step3. 将数据送入网络进行训练

Paddle 支持两种数据读取方式,分别是异步数据读取和同步数据读取。

  • 异步数据读取。先使用 fluid.io.DataLoader 定义 DataLoader 对象,然后给 DataLoader 对象设置数据源。使用 DataLoader 接口时,数据传入与模型训练过程是异步进行的,效率较高,推荐使用。
  • 同步数据读取。用户自行构造输入数据,并使用 exe.run(feed=...) 传入训练数据。数据准备和模型训练的过程是同步进行的,效率较低。

两种 Reader 数据类型对应两种使用方式

用户自己定义的 Reader 可以是 Sample 级,也可以是 Batch 级,在使用时均需要传入 Batch 级的数据。

  • Sample 级使用方式
    1. 若使用异步数据读取方式。则通过 set_sample_generatorset_sample_list_generator 来设置数据源
    2. 若使用同步数据读取方式。首先通过 fluid.io.batch 将 Sample 级的 Reader 转为 Batch 级的 Reader,再使用 fluid.DataFeeder 将数据转换为 Python 的字典
  • Batch 级使用方式
    1. 若使用异步数据读取方式。则通过 set_batch_generator 来设置数据源
    2. 若使用同步数据读取方式。构建输入数据的字典将输入喂入模型

两种数据读取方式

异步数据读取

异步数据读取需要在定义完数据层变量后创建一个 DataLoader 对象:

1
2
3
4
5
6
7
from paddle import fluid

sentences = fluid.data(name="sentences", dtype="int64", shape=[None, 512, 768])
label = fluid.data(name="label", dtype="int64", shape=[None, 1])

data_loader = fluid.io.DataLoader.from_generator(
feed_list=[sentences, label], capacity=64, use_double_buffer=True, iterable=True)

Program.clone() 不能实现 DataLoader 对象的复制。一般情况下,需重定义多个 DataLoader 对象,例如训练和测试阶段需要不同的 DataLoader 对象。

DataLoader对象通过 set_sample_generator()set_sample_list_generatorset_batch_generator() 方法设置其数据源。这三个方法均接收 Python 生成器 generator 作为参数,其区别在于:

  • set_sample_generator() 要求 generator 返回的数据格式为 [sent_1, label_1],其中 sent_1 和 label_1 为单个样本的 Numpy Array 类型数据。
  • set_sample_list_generator() 要求 generator 返回的数据格式为 [(sent_1, label_1), (sent_2, label_2), …, (sent_n, label_n)],其中 sent_i 和 label_i 均为每个样本的 Numpy Array 类型数据,n 为 batch size。
  • set_batch_generator() 要求 generator 返回的数据的数据格式为 [batched_sents, batched_labels],其中 batched_sents 和 batched_labels 为 batch 级的 Numpy Array 或 LoDTensor 类型数据。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from paddle import fluid
import numpy as np

sentences = fluid.data(name="sentences", dtype="int64", shape=[None, 512, 768])
label = fluid.data(name="label", dtype="int64", shape=[None, 1])

data_loader = fluid.io.DataLoader.from_generator(
feed_list=[sentences, label], capacity=64, use_double_buffer=True, iterable=True)

# 1. Sample 级 Reader
def sample_reader(param1, param2):
def __reader():
while True:
sample_sentence = np.random.random(size=(512, 768)).astype("int64")
sample_label = np.random.random_integers(size=(1, ), low=0, high=9).astype("int64")
yield sample_sentence, sample_label
return __reader

# 2. Batch 级 Reader
def batch_reader(batch_size, param2):
def __reader():
while True:
batch_sentence = np.random.random(size=(batch_size, 512, 768)).astype("int64")
batch_label = np.random.random_integers(size=(batch_size, 1), low=0, high=9).astype("int64")
yield batch_sentecne, batch_label
return __reader

ITERABLE = True
USE_CUDA = True
USE_DATA_PARALLEL = True

if ITERABLE:
# 若 DataLoader 可迭代,则必须设置 places 参数
if USE_DATA_PARALLEL:
# 若进行多 GPU 卡训练,则取所有的 CUDAPlace
# 若进行多 CPU 核训练,则取多个 CPUPlace,本例中取了 8 个 CPUPlace
places = fluid.cuda_places() if USE_CUDA else fluid.cpu_places(8)
else:
# 若进行单 GPU 卡训练,则取单个 CUDAPlace,本例中 0 代表 0 号 GPU 卡
# 若进行单 CPU 核训练,则取单个 CPUPlace,本例中 1 代表 1 个 CPUPlace
places = fluid.cuda_places(0) if USE_CUDA else fluid.cpu_places(1)
else:
# 若 DataLoader 不可迭代,则不需要设置 places 参数
places = None

# 1. 使用 Sample 级的 Reader 作为 DataLoader 的数据源
data_loader.set_sample_generator(sample_reader(param1, param2), batch_size=32, places=places)

# 2. 使用 Sample 级的 Reader + fluid.io.batch 设置 DataLoader 的数据源
sample_list_reader = fluid.io.batch(sample_reader(param1, param2), batch_size=32)
sample_list_reader = fluid.io.shuffle(sample_list_reader, buf_size=64) # 还可以进行适当的 shuffle
data_loader.set_sample_list_generator(sample_list_reader, places=places)

# 3. 使用 Batch 级的 Reader 作为 DataLoader 的数据源
data_loader.set_batch_generator(batch_reader(32, param2), places=places)

同步数据读取

同步数据读取直接在运行时指定 feed 的参数为字典,字典的键为数据层变量名,值为 Numpy Array 或者 LoDTensor。当需要分别设置 ParallelExecutor 中每个设备的训练数据时,直接将一个列表传递给 feed 参数,列表内为若干个字典,每个字典对应每个设备上的数据。

参考