未验证 提交 1a92bbad 编写于 作者: Z Zeng Jinle 提交者: GitHub

polish data reader API for release/1.5 (#891)

上级 f01477b1
......@@ -4,7 +4,7 @@
准备数据
########
使用PaddlePaddle Fluid准备数据分为个步骤:
使用PaddlePaddle Fluid准备数据分为个步骤:
Step1: 自定义Reader生成训练/预测数据
###################################
......@@ -16,32 +16,55 @@ Batch级的Reader每次返回一个Batch的数据,Sample级的Reader每次返
如果您的数据是Sample级的数据,我们提供了一个可以数据预处理和组建batch的工具::code:`Python Reader` 。
Step2: 将数据送入网络进行训练/预测
Step2: 在网络配置中定义数据层变量
###################################
用户需使用 :code:`fluid.layers.data` 在网络中定义数据层变量。定义数据层变量时需指明数据层的名称name、数据类型dtype和维度shape。例如:
Fluid提供两种方式,分别是同步Feed方式或异步py_reader接口方式,具体介绍如下:
.. code-block:: python
import paddle.fluid as fluid
image = fluid.layers.data(name='image', dtype='float32', shape=[28, 28])
label = fluid.layers.data(name='label', dtype='int64', shape=[1])
需要注意的是,此处的shape是单个样本的维度,PaddlePaddle Fluid会在shape第0维位置添加-1,表示batch_size的维度,即此例中image.shape为[-1, 28, 28],
label.shape为[-1, 1]。
若用户不希望框架在第0维位置添加-1,则可通过append_batch_size=False参数控制,即:
.. code-block:: python
import paddle.fluid as fluid
image = fluid.layers.data(name='image', dtype='float32', shape=[28, 28], append_batch_size=False)
label = fluid.layers.data(name='label', dtype='int64', shape=[1], append_batch_size=False)
此时,image.shape为[28, 28],label.shape为[1]。
Step3: 将数据送入网络进行训练/预测
###################################
Fluid提供两种方式,分别是异步PyReader接口方式或同步Feed方式,具体介绍如下:
- 异步PyReader接口方式
用户需要先使用 :code:`fluid.io.PyReader` 定义PyReader对象,然后通过PyReader对象的decorate方法设置数据源。
使用PyReader接口时,数据传入与模型训练/预测过程是异步进行的,效率较高,推荐使用。
- 同步Feed方式
用户需使用 :code:`fluid.layers.data`
配置数据输入层,并在 :code:`fluid.Executor` 或 :code:`fluid.ParallelExecutor`
用户自行构造输入数据,并在 :code:`fluid.Executor` 或 :code:`fluid.ParallelExecutor`
中使用 :code:`executor.run(feed=...)` 传入训练数据。数据准备和模型训练/预测的过程是同步进行的,
效率较低。
- 异步py_reader接口方式
用户需要先使用 :code:`fluid.layers.py_reader` 配置数据输入层,然后使用
:code:`py_reader` 的 :code:`decorate_paddle_reader` 或 :code:`decorate_tensor_provider`
方法配置数据源,再通过 :code:`fluid.layers.read_file` 读取数据。数据传入与模型训练/预测过程是异步进行的,
效率较高。
这两种准备数据方法的比较如下:
======== ================================= =====================================
对比项 同步Feed方式 异步py_reader接口方式
对比项 同步Feed方式 异步PyReader接口方式
======== ================================= =====================================
API接口 :code:`executor.run(feed=...)` :code:`fluid.layers.py_reader`
API接口 :code:`executor.run(feed=...)` :code:`fluid.io.PyReader`
数据格式 Numpy Array或LoDTensor Numpy Array或LoDTensor
数据增强 Python端使用其他库完成 Python端使用其他库完成
速度 慢 快
......@@ -51,7 +74,7 @@ API接口 :code:`executor.run(feed=...)` :code:`fluid.layers.py_reader
Reader数据类型对使用方式的影响
###############################
根据Reader数据类型的不同,上述Step1和Step2的具体操作将有所不同,具体介绍如下:
根据Reader数据类型的不同,上述步骤的具体操作将有所不同,具体介绍如下:
读取Sample级Reader数据
+++++++++++++++++++++
......@@ -71,12 +94,12 @@ Step1. 组建数据
Step2. 送入数据
=================================
若使用同步Feed方式送入数据,请使用DataFeeder接口将Reader数据转换为LoDTensor格式后送入网络,具体请参见 :ref:`cn_api_fluid_DataFeeder`
若使用异步py_reader接口方式送入数据,请调用 :code:`decorate_paddle_reader` 接口完成,具体请参见:
若使用异步PyReader接口方式送入数据,请调用 :code:`decorate_sample_generator` 或 :code:`decorate_sample_list_generator` 接口完成,具体请参见:
- :ref:`user_guides_use_py_reader`
若使用同步Feed方式送入数据,请使用DataFeeder接口将Reader数据转换为LoDTensor格式后送入网络,具体请参见 :ref:`cn_api_fluid_DataFeeder`
读取Batch级Reader数据
+++++++++++++++++++++++
......@@ -88,19 +111,19 @@ Step1. 组建数据
Step2. 送入数据
=================================
若使用同步Feed方式送入数据,具体请参见:
若使用异步PyReader接口方式送入数据,请调用PyReader的 :code:`decorate_batch_generator` 接口完成,具体方式请参见:
.. toctree::
:maxdepth: 1
feeding_data.rst
use_py_reader.rst
若使用异步py_reader接口方式送入数据,请调用py_reader的 :code:`decorate_tensor_provider` 接口完成,具体方式请参见:
若使用同步Feed方式送入数据,具体请参见:
.. toctree::
:maxdepth: 1
use_py_reader.rst
feeding_data.rst
......
......@@ -15,33 +15,22 @@
import paddle.fluid as fluid
py_reader = fluid.layers.py_reader(capacity=64,
shapes=[(-1,784), (-1,1)],
dtypes=['float32', 'int64'],
name='py_reader',
use_double_buffer=True)
image = fluid.layers.data(name='image', dtype='float32', shape=[784])
label = fluid.layers.data(name='label', dtype='int64', shape=[1])
其中,capacity为PyReader对象的缓存区大小;shapes为batch各参量(如图像分类任务中的image和label)的尺寸;dtypes为batch各参量的数据类型;name为PyReader对象的名称;use_double_buffer默认为True,表示使用 :code:`double_buffer_reader` ,建议开启,可提升数据读取速度。
ITERABLE = True
需要注意的是:如果您要创建多个不同PyReader对象(例如训练和预测阶段需创建两个不同的PyReader),则需要必须给不同的PyReader对象指定不同的name。这是因为PaddlePaddle采用不同的变量名区分不同的变量,而且 `Program.clone()` (参见 :ref:`cn_api_fluid_Program_clone` )不能实现PyReader对象的复制。
py_reader = fluid.io.PyReader(feed_list=[image, label], capacity=64, use_double_buffer=True, iterable=ITERABLE)
.. code-block:: python
import paddle.fluid as fluid
train_py_reader = fluid.layers.py_reader(capacity=64,
shapes=[(-1,784), (-1,1)],
dtypes=['float32', 'int64'],
name='train',
use_double_buffer=True)
其中,
test_py_reader = fluid.layers.py_reader(capacity=64,
shapes=[(-1,3,224,224), (-1,1)],
dtypes=['float32', 'int64'],
name='test',
use_double_buffer=True)
- feed_list为需要输入的数据层变量列表;
- capacity为PyReader对象的缓存区大小;
- use_double_buffer默认为True,表示使用 :code:`double_buffer_reader` 。建议开启,可提升数据读取速度;
- iterable默认为True,表示该PyReader对象是可For-Range迭代的。当iterable=True时,PyReader与Program解耦,定义PyReader对象不会改变Program;当iterable=False时,PyReader会在Program中插入数据读取相关的op。
在使用PyReader时,如果需要共享训练阶段和测试阶段的模型参数,您可以通过 :code:`fluid.unique_name.guard()` 的方式来实现。
需要注意的是:`Program.clone()` (参见 :ref:`cn_api_fluid_Program_clone` )不能实现PyReader对象的复制。如果您要创建多个不同PyReader对象(例如训练和预测阶段需创建两个不同的PyReader),则需重定义两个PyReader对象。
若需要共享训练阶段和测试阶段的模型参数,您可以通过 :code:`fluid.unique_name.guard()` 的方式来实现。
注:Paddle采用变量名区分不同变量,且变量名是根据 :code:`unique_name` 模块中的计数器自动生成的,每生成一个变量名计数值加1。 :code:`fluid.unique_name.guard()` 的作用是重置 :code:`unique_name` 模块中的计数器,保证多次调用 :code:`fluid.unique_name.guard()` 配置网络时对应变量的变量名相同,从而实现参数共享。
下面是一个使用PyReader配置训练阶段和测试阶段网络的例子:
......@@ -52,19 +41,11 @@
import paddle.fluid as fluid
import paddle.dataset.mnist as mnist
def network(is_train):
# Create py_reader object and give different names
# when is_train = True and is_train = False
reader = fluid.layers.py_reader(
capacity=10,
shapes=((-1, 784), (-1, 1)),
dtypes=('float32', 'int64'),
name="train_reader" if is_train else "test_reader",
use_double_buffer=True)
# Use read_file() method to read out the data from py_reader
img, label = fluid.layers.read_file(reader)
...
def network():
image = fluid.layers.data(name='image', dtype='float32', shape=[784])
label = fluid.layers.data(name='label', dtype='int64', shape=[1])
reader = fluid.io.PyReader(feed_list=[image, label], capacity=64)
# Here, we omitted the definition of loss of the model
return loss , reader
......@@ -75,7 +56,7 @@
with fluid.program_guard(train_prog, train_startup):
# Use fluid.unique_name.guard() to share parameters with test network
with fluid.unique_name.guard():
train_loss, train_reader = network(True)
train_loss, train_reader = network()
adam = fluid.optimizer.Adam(learning_rate=0.01)
adam.minimize(train_loss)
......@@ -85,66 +66,99 @@
with fluid.program_guard(test_prog, test_startup):
# Use fluid.unique_name.guard() to share parameters with train network
with fluid.unique_name.guard():
test_loss, test_reader = network(False)
test_loss, test_reader = network()
设置PyReader对象的数据源
################################
PyReader对象通过 :code:`decorate_paddle_reader()` 或 :code:`decorate_tensor_provider()` 方法设置其数据源。 :code:`decorate_paddle_reader()` 和 :code:`decorate_tensor_provider()` 均接收Python生成器 :code:`generator` 作为参数, :code:`generator` 内部每次通过yield的方式生成一个batch的数据。
PyReader对象通过 :code:`decorate_sample_generator()` , :code:`decorate_sample_list_generator` 和 :code:`decorate_batch_generator()` 方法设置其数据源。
这三个方法均接收Python生成器 :code:`generator` 作为参数,其区别在于:
- :code:`decorate_sample_generator()` 要求 :code:`generator` 返回的数据格式为[img_1, label_1],其中img_1和label_1为单个样本的Numpy Array类型数据。
:code:`decorate_paddle_reader()` 和 :code:`decorate_tensor_provider()` 方法的区别在于:
- :code:`decorate_sample_list_generator()` 要求 :code:`generator` 返回的数据格式为[(img_1, label_1), (img_2, label_2), ..., (img_n, label_n)],其中img_i和label_i均为每个样本的Numpy Array类型数据,n为batch size。
- :code:`decorate_paddle_reader()` 要求 :code:`generator` 返回的数据格式为[(img_1, label_1), (img_2, label_2), ..., (img_n, label_n)],其中img_i和label_i均为每个样本的Numpy Array类型数据,n为batch size。而 :code:`decorate_tensor_provider()` 要求 :code:`generator` 返回的数据的数据格式为[batched_imgs, batched_labels],其中batched_imgs和batched_labels为batch级的Numpy Array或LoDTensor类型数据。
- :code:`decorate_batch_generator()` 要求 :code:`generator` 返回的数据的数据格式为[batched_imgs, batched_labels],其中batched_imgs和batched_labels为batch级的Numpy Array或LoDTensor类型数据。
- :code:`decorate_tensor_provider()` 要求 :code:`generator` 返回的数据类型、尺寸必须与配置py_reader时指定的dtypes、shapes参数相同,而 :code:`decorate_paddle_reader()` 不要求数据类型和尺寸的严格一致,其内部会完成数据类型和尺寸的转换。
当PyReader的iterable=True(默认)时,必须给这三个方法传 :code:`places` 参数,
指定将读取的数据转换为CPU Tensor还是GPU Tensor。当PyReader的iterable=False时,不需传places参数。
具体方式为:
例如,假设我们有两个reader,其中fake_sample_reader每次返回一个sample的数据,fake_batch_reader每次返回一个batch的数据。
.. code-block:: python
import paddle.batch
import paddle.fluid as fluid
import numpy as np
BATCH_SIZE = 32
# sample级reader
def fake_sample_reader():
for _ in range(100):
sample_image = np.random.random(size=(784, )).astype('float32')
sample_label = np.random.random_integers(size=(1, ), low=0, high=9).astype('int64')
yield sample_image, sample_label
# batch级reader
def fake_batch_reader():
batch_size = 32
for _ in range(100):
batch_image = np.random.random(size=(batch_size, 784)).astype('float32')
batch_label = np.random.random_integers(size=(batch_size, 1), low=0, high=9).astype('int64')
yield batch_image, batch_label
# Case 1: Use decorate_paddle_reader() method to set the data source of py_reader
# The generator yields Numpy-typed batched data
def fake_random_numpy_reader():
image = np.random.random(size=(784, ))
label = np.random.random_integers(size=(1, ), low=0, high=9)
yield image, label
image1 = fluid.layers.data(name='image1', dtype='float32', shape=[784])
label1 = fluid.layers.data(name='label1', dtype='int64', shape=[1])
py_reader1 = fluid.layers.py_reader(
capacity=10,
shapes=((-1, 784), (-1, 1)),
dtypes=('float32', 'int64'),
name='py_reader1',
use_double_buffer=True)
image2 = fluid.layers.data(name='image2', dtype='float32', shape=[784])
label2 = fluid.layers.data(name='label2', dtype='int64', shape=[1])
py_reader1.decorate_paddle_reader(paddle.batch(fake_random_numpy_reader, batch_size=BATCH_SIZE))
image3 = fluid.layers.data(name='image3', dtype='float32', shape=[784])
label3 = fluid.layers.data(name='label3', dtype='int64', shape=[1])
对应的PyReader设置如下:
# Case 2: Use decorate_tensor_provider() method to set the data source of py_reader
# The generator yields Tensor-typed batched data
def fake_random_tensor_provider():
image = np.random.random(size=(BATCH_SIZE, 784)).astype('float32')
label = np.random.random_integers(size=(BATCH_SIZE, 1), low=0, high=9).astype('int64')
yield image_tensor, label_tensor
.. code-block:: python
py_reader2 = fluid.layers.py_reader(
capacity=10,
shapes=((-1, 784), (-1, 1)),
dtypes=('float32', 'int64'),
name='py_reader2',
use_double_buffer=True)
import paddle
import paddle.fluid as fluid
py_reader2.decorate_tensor_provider(fake_random_tensor_provider)
USE_CUDA = True
USE_DATA_PARALLEL = True
if ITERABLE:
# 若PyReader可迭代,则必须设置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:
# 若PyReader不可迭代,则不需要设置places参数
places = None
# 使用sample级的reader作为PyReader的数据源
py_reader1 = fluid.io.PyReader(feed_list=[image1, label1], capacity=10, iterable=ITERABLE)
py_reader1.decorate_sample_generator(fake_sample_reader, batch_size=32, places=places)
# 使用sample级的reader + paddle.batch设置PyReader的数据源
py_reader2 = fluid.io.PyReader(feed_list=[image2, label2], capacity=10, iterable=ITERABLE)
sample_list_reader = paddle.batch(fake_sample_reader, batch_size=32)
sample_list_reader = paddle.reader.shuffle(sample_list_reader) # 还可以进行适当的shuffle
py_reader2.decorate_sample_list_generator(sample_list_reader, places=places)
# 使用batch级的reader作为PyReader的数据源
py_reader3 = fluid.io.PyReader(feed_list=[image3, label3], capacity=10, iterable=ITERABLE)
py_reader3.decorate_batch_generator(fake_batch_reader, places=places)
使用PyReader进行模型训练和测试
################################
使用PyReader进行模型训练和测试的例程如下:
使用PyReader进行模型训练和测试的例程如下。
- 第一步,我们需组建训练网络和预测网络,并定义相应的PyReader对象,设置好PyReader对象的数据源。
.. code-block:: python
......@@ -153,46 +167,45 @@ PyReader对象通过 :code:`decorate_paddle_reader()` 或 :code:`decorate_tensor
import paddle.dataset.mnist as mnist
import six
def network(is_train):
# Create py_reader object and give different names
# when is_train = True and is_train = False
reader = fluid.layers.py_reader(
capacity=10,
shapes=((-1, 784), (-1, 1)),
dtypes=('float32', 'int64'),
name="train_reader" if is_train else "test_reader",
use_double_buffer=True)
img, label = fluid.layers.read_file(reader)
...
ITERABLE = True
def network():
# 创建数据层对象
image = fluid.layers.data(name='image', dtype='float32', shape=[784])
label = fluid.layers.data(name='label', dtype='int64', shape=[1])
# 创建PyReader对象
reader = fluid.io.PyReader(feed_list=[image, label], capacity=64, iterable=ITERABLE)
# Here, we omitted the definition of loss of the model
return loss , reader
# Create main program and startup program for training
# 创建训练的main_program和startup_program
train_prog = fluid.Program()
train_startup = fluid.Program()
# Define train network
# 定义训练网络
with fluid.program_guard(train_prog, train_startup):
# Use fluid.unique_name.guard() to share parameters with test network
# fluid.unique_name.guard() to share parameters with test network
with fluid.unique_name.guard():
train_loss, train_reader = network(True)
train_loss, train_reader = network()
adam = fluid.optimizer.Adam(learning_rate=0.01)
adam.minimize(train_loss)
# Create main program and startup program for testing
# 创建预测的main_program和startup_program
test_prog = fluid.Program()
test_startup = fluid.Program()
# Define test network
# 定义预测网络
with fluid.program_guard(test_prog, test_startup):
# Use fluid.unique_name.guard() to share parameters with train network
with fluid.unique_name.guard():
test_loss, test_reader = network(False)
test_loss, test_reader = network()
place = fluid.CUDAPlace(0)
exe = fluid.Executor(place)
# Run startup program
# 运行startup_program进行初始化
exe.run(train_startup)
exe.run(test_startup)
......@@ -200,33 +213,45 @@ PyReader对象通过 :code:`decorate_paddle_reader()` 或 :code:`decorate_tensor
train_prog = fluid.CompiledProgram(train_prog).with_data_parallel(loss_name=train_loss.name)
test_prog = fluid.CompiledProgram(test_prog).with_data_parallel(share_vars_from=train_prog)
# Set the data source of py_reader using decorate_paddle_reader() method
train_reader.decorate_paddle_reader(
paddle.reader.shuffle(paddle.batch(mnist.train(), 512), buf_size=8192))
# 设置PyReader的数据源
places = fluid.cuda_places() if ITERABLE else None
train_reader.decorate_sample_list_generator(
paddle.reader.shuffle(paddle.batch(mnist.train(), 512), buf_size=1024), places=places)
test_reader.decorate_sample_list_generator(paddle.batch(mnist.test(), 512), places=places)
- 第二步:根据PyReader对象是否iterable,选用不同的方式运行网络。
若iterable=True,则PyReader对象是一个Python的生成器,可直接for-range迭代。for-range返回的结果通过exe.run的feed参数传入执行器。
.. code-block:: python
test_reader.decorate_paddle_reader(paddle.batch(mnist.test(), 512))
def run_iterable(program, exe, loss, py_reader):
for data in py_reader():
loss_value = exe.run(program=program, feed=data, fetch_list=[loss])
print('loss is {}'.format(loss_value))
for epoch_id in six.moves.range(10):
train_reader.start()
try:
while True:
loss = exe.run(program=train_prog, fetch_list=[train_loss])
print 'train_loss', loss
except fluid.core.EOFException:
print 'End of epoch', epoch_id
train_reader.reset()
run_iterable(train_prog, exe, loss, train_reader)
run_iterable(test_prog, exe, loss, test_reader)
若iterable=False,则需在每个epoch开始前,调用 :code:`start()` 方法启动PyReader对象;并在每个epoch结束时,exe.run会抛出 :code:`fluid.core.EOFException` 异常,在捕获异常后调用 :code:`reset()` 方法重置PyReader对象的状态,
以便启动下一轮的epoch。iterable=False时无需给exe.run传入feed参数。具体方式为:
test_reader.start()
.. code-block:: python
def run_non_iterable(program, exe, loss, py_reader):
py_reader.start()
try:
while True:
loss = exe.run(program=test_prog, fetch_list=[test_loss])
print 'test loss', loss
loss_value = exe.run(program=program, fetch_list=[loss])
print('loss is {}'.format(loss_value))
except fluid.core.EOFException:
print 'End of testing'
test_reader.reset()
print('End of epoch')
py_reader.reset()
具体步骤为:
1. 在每个epoch开始前,调用 :code:`start()` 方法启动PyReader对象;
for epoch_id in six.moves.range(10):
run_non_iterable(train_prog, exe, loss, train_reader)
run_non_iterable(test_prog, exe, loss, test_reader)
2. 在每个epoch结束时, :code:`read_file` 抛出 :code:`fluid.core.EOFException` 异常,在捕获异常后调用 :code:`reset()` 方法重置PyReader对象的状态,以便启动下一轮的epoch。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册