异步数据读取

    创建DataLoader对象的方式为:

    其中,

    • feed_list为需要输入的数据层变量列表;
    • capacity为DataLoader对象的缓存区大小,单位为batch数量;
    • use_double_buffer默认为True,表示使用 double_buffer_reader 。建议开启,可提升数据读取速度;
    • iterable默认为True,表示该DataLoader对象是可For-Range迭代的。推荐设置iterable=True。当iterable=True时,DataLoader与Program解耦,定义DataLoader对象不会改变Program;当iterable=False时,DataLoader会在Program中插入数据读取相关的op。

    需要注意的是:Program.clone() (参见 Program )不能实现DataLoader对象的复制。如果您要创建多个不同DataLoader对象(例如训练和预测阶段需创建两个不同的DataLoader),则需重定义两个DataLoader对象。 若需要共享训练阶段和测试阶段的模型参数,您可以通过 fluid.unique_name.guard() 的方式来实现。 注:Paddle采用变量名区分不同变量,且变量名是根据 unique_name 模块中的计数器自动生成的,每生成一个变量名计数值加1。 fluid.unique_name.guard() 的作用是重置 unique_name 模块中的计数器,保证多次调用 fluid.unique_name.guard() 配置网络时对应变量的变量名相同,从而实现参数共享。

    1. import paddle
    2. import paddle.fluid as fluid
    3. import paddle.dataset.mnist as mnist
    4. def network():
    5. image = fluid.data(name='image', dtype='float32', shape=[None, 784])
    6. label = fluid.data(name='label', dtype='int64', shape=[None, 1])
    7. loader = fluid.io.DataLoader.from_generator(feed_list=[image, label], capacity=64)
    8. # Definition of models
    9. fc = fluid.layers.fc(image, size=10)
    10. xe = fluid.layers.softmax_with_cross_entropy(fc, label)
    11. loss = fluid.layers.reduce_mean(xe)
    12. return loss , loader
    13. # Create main program and startup program for training
    14. train_prog = fluid.Program()
    15. train_startup = fluid.Program()
    16. with fluid.program_guard(train_prog, train_startup):
    17. # Use fluid.unique_name.guard() to share parameters with test network
    18. train_loss, train_loader = network()
    19. adam = fluid.optimizer.Adam(learning_rate=0.01)
    20. # Create main program and startup program for testing
    21. test_prog = fluid.Program()
    22. test_startup = fluid.Program()
    23. with fluid.program_guard(test_prog, test_startup):
    24. # Use fluid.unique_name.guard() to share parameters with train network
    25. with fluid.unique_name.guard():
    26. test_loss, test_loader = network()

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

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

    值得注意的是,使用DataLoader做多GPU卡(或多CPU核)训练时,实际的总batch size为用户传入的 generator 的batch size乘以设备数量。

    当DataLoader的iterable=True(默认)时,必须给这三个方法传 places 参数, 指定将读取的数据转换为CPU Tensor还是GPU Tensor。当DataLoader的iterable=False时,不需传places参数。

    对应的DataLoader设置如下:

    1. import paddle
    2. import paddle.fluid as fluid
    3. ITERABLE = True
    4. USE_CUDA = True
    5. USE_DATA_PARALLEL = True
    6. if ITERABLE:
    7. # 若DataLoader可迭代,则必须设置places参数
    8. if USE_DATA_PARALLEL:
    9. # 若进行多GPU卡训练,则取所有的CUDAPlace
    10. places = fluid.cuda_places() if USE_CUDA else fluid.cpu_places(8)
    11. else:
    12. # 若进行单CPU核训练,则取单个CPUPlace,本例中1代表1个CPUPlace
    13. places = fluid.cuda_places(0) if USE_CUDA else fluid.cpu_places(1)
    14. else:
    15. # 若DataLoader不可迭代,则不需要设置places参数
    16. places = None
    17. # 使用sample级的reader作为DataLoader的数据源
    18. data_loader1 = fluid.io.DataLoader.from_generator(feed_list=[image1, label1], capacity=10, iterable=ITERABLE)
    19. data_loader1.set_sample_generator(fake_sample_reader, batch_size=32, places=places)
    20. # 使用sample级的reader + fluid.io.batch设置DataLoader的数据源
    21. data_loader2 = fluid.io.DataLoader.from_generator(feed_list=[image2, label2], capacity=10, iterable=ITERABLE)
    22. sample_list_reader = fluid.io.batch(fake_sample_reader, batch_size=32)
    23. sample_list_reader = fluid.io.shuffle(sample_list_reader, buf_size=64) # 还可以进行适当的shuffle
    24. data_loader2.set_sample_list_generator(sample_list_reader, places=places)
    25. # 使用batch级的reader作为DataLoader的数据源
    26. data_loader3 = fluid.io.DataLoader.from_generator(feed_list=[image3, label3], capacity=10, iterable=ITERABLE)
    27. data_loader3.set_batch_generator(fake_batch_reader, places=places)

    使用DataLoader进行模型训练和测试的例程如下。

    • 第一步,我们需组建训练网络和预测网络,并定义相应的DataLoader对象,设置好DataLoader对象的数据源。
    • 第二步:根据DataLoader对象是否iterable,选用不同的方式运行网络。

    若iterable=True,则DataLoader对象是一个Python的生成器,可直接for-range迭代。for-range返回的结果通过exe.run的feed参数传入执行器。

    1. def run_iterable(program, exe, loss, data_loader):
    2. for data in data_loader():
    3. loss_value = exe.run(program=program, feed=data, fetch_list=[loss])
    4. print('loss is {}'.format(loss_value))
    5. for epoch_id in six.moves.range(10):
    6. run_iterable(train_prog, exe, train_loss, train_loader)