Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
PaddlePaddle
FluidDoc
提交
5628dd82
F
FluidDoc
项目概览
PaddlePaddle
/
FluidDoc
通知
5
Star
2
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
23
列表
看板
标记
里程碑
合并请求
111
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
F
FluidDoc
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
23
Issue
23
列表
看板
标记
里程碑
合并请求
111
合并请求
111
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
5628dd82
编写于
7月 02, 2019
作者:
C
chengduo
提交者:
xsrobin
7月 02, 2019
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add best practice (#982)
* add best practice * polish doc
上级
1b62d999
变更
2
显示空白变更内容
内联
并排
Showing
2 changed file
with
214 addition
and
0 deletion
+214
-0
doc/fluid/advanced_usage/best_practice/index_cn.rst
doc/fluid/advanced_usage/best_practice/index_cn.rst
+1
-0
doc/fluid/advanced_usage/best_practice/training_best_practice.rst
...d/advanced_usage/best_practice/training_best_practice.rst
+213
-0
未找到文件。
doc/fluid/advanced_usage/best_practice/index_cn.rst
浏览文件 @
5628dd82
...
...
@@ -7,4 +7,5 @@
cpu_train_best_practice.rst
dist_training_gpu.rst
training_best_practice.rst
memory_optimize.rst
doc/fluid/advanced_usage/best_practice/training_best_practice.rst
0 → 100644
浏览文件 @
5628dd82
.. training_best_practice:
#####################
单机训练最佳实践
#####################
开始优化您的单机训练任务
-------------------------
PaddlePaddle Fluid可以支持在现代CPU、GPU平台上进行训练。如果您发现Fluid进行单机训练的速度较慢,您可以根据这篇文档的建议对您的Fluid程序进行优化。
神经网络训练代码通常由三个步骤组成:网络构建、数据准备、模型训练。这篇文档将分别从这三个方向介绍Fluid训练中常用的优化方法。
1. 网络构建过程中的配置优化
=============
这部分优化与具体的模型有关,在这里,我们列举出一些优化过程中遇到过的一些示例。
1.1 cuDNN操作的选择
^^^^^^^^^^^^^^^^
cuDNN是NVIDIA提供的深度神经网络计算库,其中包含了很多神经网络中常用算子,Paddle中的部分Op底层调用的是cuDNN库,例如 :code:`conv2d` :
.. code-block:: python
paddle.fluid.layers.conv2d(input,
num_filters,
filter_size,
stride=1,
padding=0,
dilation=1,
groups=None,
param_attr=None,
bias_attr=None,
use_cudnn=True,
act=None,
name=None)
在 :code:`use_cudnn=True` 时,框架底层调用的是cuDNN中的卷积操作。
通常cuDNN库提供的操作具有很好的性能表现,其性能明显优于Paddle原生的CUDA实现,比如 :code:`conv2d` 。但是cuDNN中有些操作的性能较差,比如: :code:`conv2d_transpose` 在 :code:`batch_size=1` 时、:code:`pool2d` 在 :code:`global_pooling=True` 时等,这些情况下,cuDNN实现的性能差于Paddle的CUDA实现,建议手动设置 :code:`use_cudnn=False` 。
1.2 使用融合功能的API
^^^^^^^^^^^^^^^^
Paddle提供一些粗粒度的API,这些API融合了多个细粒度API的计算,比如:
.. code-block:: python
logits = fluid.layers.softmax(logits)
loss = fluid.layers.cross_entropy(logits, label, ignore_index=255)
和
.. code-block:: python
loss = fluid.layers.softmax_with_cross_entropy(logits, label, ignore_index=255, numeric_stable_mode=True)
用户网络配置中使用融合功能的API,通常能取得更好的计算性能。
2. 数据准备优化
=============
2.1 分析数据准备部分的耗时
^^^^^^^^^^^^^^^^
数据准备部分通常分为两个部分:数据读取部分和预处理部分。
- 数据读取部分:用户需要在Python端从磁盘中加载数据,然后将数据feed到Fluid的执行器中。
- 数据预处理部分:用户需要在Python端进行数据预处理,比如图像任务通常需要进行数据增强、裁剪等。
Fluid提供了两种数据读取方式:**同步数据读取** 和 **异步数据读取**,详情请参考文档 `如何准备数据 <http://paddlepaddle.org/documentation/docs/zh/1.5/user_guides/howto/prepare_data/index_cn.html>`_ 。
2.1.1 同步数据读取
>>>>>>>>>>>>>>>
同步数据读取是一种简单并且直观的数据准备方式,代码示例如下:
.. code-block:: python
# 读取数据
end = time.time()
for batch_id, batch in enumerate(train_reader):
data_time = time.time() - end
# 训练网络
executor.run(feed=[...], fetch_list=[...])
batch_time = time.time() - end
end = time.time()
用户通过调用自己编写的reader函数,reader每次输出一个batch的数据,并将数据传递给执行器。因此数据准备和执行是顺序进行的,用户可通过加入Python计时函数 time.time() 来统计数据准备部分和执行部分所占用的时间。
2.1.2 异步数据读取
>>>>>>>>>>>>>>>
Paddle里面使用py_reader接口来实现异步数据读取,代码示例如下:
.. code-block:: python
# 启动py_reader
train_py_reader.start()
batch_id = 0
try:
end = time.time()
while True:
print("queue size: ", train_py_reader.queue.size())
loss, = executor.run(fetch_list=[...])
# ...
batch_time = time.time() - end
end = time.time()
batch_id += 1
except fluid.core.EOFException:
train_py_reader.reset()
使用异步数据读取时,Paddle的C++端会维护一个数据队列,Python端通过单独的线程向C++端的数据队列传入数据。用户可以在训练过程中输出数据队列中数据的个数,如果queue size始终不为空,表明Python端数据准备的速度比模型执行的速度快,这种情况下Python端的数据读取可能不是瓶颈。
此外,Paddle提供的一些FLAGS也能很好的帮助分析性能,比如通过设置 :code:`export FLAGS_reader_queue_speed_test_mode=True` ,数据队列中的训练数据在被读取之后,不会从数据队列中弹出,这样能够保证数据队列始终不为空,这样就能够很好的评估出数据读取所占的开销。**注意,FLAGS_reader_queue_speed_test_mode只能在分析的时候打开,正常训练模型时需要关闭**。
2.2 优化数据准备速度的方法
^^^^^^^^^^^^^^^^
- 为降低训练的整体时间,建议用户使用异步数据读取的方式,并开启 :code:`use_double_buffer` 。此外,用户可根据模型的实际情况设置数据队列的大小。
- 如果数据准备的时间大于模型执行的时间,或者出现了数据队列为空的情况,这时候需要考虑对Python的用户reader进行加速。常用的方法为:**使用Python多进程准备数据**。一个简单的使用多进程准备数据的示例,请参考 `YOLOv3 <https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/yolov3/reader.py>`_ 。
- Python端的数据预处理,都是使用CPU完成。如果Paddle提供了相应功能的API,可将这部分预处理功能写到模型配置中,如此Paddle就可以使用GPU来完成该预处理功能,这样也可以减轻CPU预处理数据的负担,提升总体训练速度。
3. 模型训练相关优化
=============
3.1 执行器介绍
^^^^^^^^^^^^^^^^
目前Paddle中有两个执行器, :code:`Executor` 和 :code:`ParallelExecutor` ,这两个执行器的区别:
执行调度器
>>>>>>>>>>>>>>>
.. csv-table::
:header: "执行器 ", "执行对象", "执行策略"
:widths: 3, 3, 5
":code:`Executor`", ":code:`Program`", "根据 :code:`Program` 中Operator定义的先后顺序依次运行。"
":code:`ParallelExecutor`", "SSA Graph", "根据Graph中各个节点之间的依赖关系,通过多线程运行。"
为了更好的分析模型, :code:`ParallelExecutor` 内部首先会将输入的 :code:`Program` 转为SSA Graph,然后根据 :code:`build_strategy` 中的配置,通过一系列的Pass对Graph进行优化,比如:memory optimize,operator fuse等优化。最后根据 :code:`execution_strategy` 中的配置执行训练任务。
此外, :code:`ParallelExecutor` 支持数据并行,即单进程多卡和多进程多卡,关于 :code:`ParallelExecutor` 的具体介绍请参考 `文档 <http://www.paddlepaddle.org.cn/documentation/docs/zh/1.5/api_guides/low_level/parallel_executor.html>`_ 。
为了统一 :code:`ParallelExecutor` 接口和 :code:`Executor` 接口,Paddle提供了 :code:`fluid.compiler.CompiledProgram` 接口,在数据并行模式下,该接口底层调用的是 :code:`ParallelExecutor` 。
3.2 BuildStrategy中参数配置说明
^^^^^^^^^^^^^^^^
BuildStrategy配置选项
>>>>>>>>>>>>>>>
.. csv-table::
:header: "选项", "类型", "默认值", "说明"
:widths: 3, 3, 3, 5
":code:`reduce_strategy`", ":code:`fluid.BuildStrategy.ReduceStrategy`", ":code:`fluid.BuildStrategy.ReduceStrategy.AllReduce`", "使用数据并行训练模型时选用 :code:`AllReduce` 模式训练还是 :code:`Reduce` 模式训练."
":code:`enable_backward_optimizer_op_deps`", "bool", "FALSE", "在反向操作和参数更新操作之间添加依赖,保证在所有的反向操作都运行结束之后才开始运行参数更新操作."
":code:`fuse_all_optimizer_ops`", "bool", "FALSE", "对模型中的参数更新算法进行融合."
":code:`fuse_all_reduce_ops`", "bool", "FALSE", "多卡训练时,将all_reduce Op进行融合."
":code:`fuse_relu_depthwise_conv`", "bool", "FALSE", "如果模型中存在relu和depthwise_conv,并且是连接的,即relu->depthwise_conv,该选项可以将这两个操作合并为一个."
":code:`fuse_broadcast_ops`", "bool", "FALSE", "在 :code:`Reduce` 模式下,对最后的多个Broadcast操作融合为一个."
":code:`mkldnn_enabled_op_types`", "list", "{}", "如果是CPU训练,可以用 :code:`mkldnn_enabled_op_types` 指明模型中的那些操作可以使用MKLDNN库,如果不进行设置,模型可以使用MKLDNN库的所有操作都会使用MKLDNN库."
说明:
- 关于 :code:`reduce_strategy` ,在 :code:`ParallelExecutor` 对于数据并行支持两种参数更新模式: :code:`AllReduce` 和 :code:`Reduce` 。在 :code:`AllReduce` 模式下,各个节点上计算得到梯度之后,调用 :code:`AllReduce` 操作,梯度在各个节点上聚合,然后各个节点分别进行参数更新。在 :code:`Reduce` 模式下,参数的更新操作被均匀的分配到各个节点上,即各个节点计算得到梯度之后,将梯度在指定的节点上进行 :code:`Reduce` ,然后在该节点上,最后将更新之后的参数Broadcast到其他节点。即:如果模型中有100个参数需要更新,训练时使用的是4个节点,在 :code:`AllReduce` 模式下,各个节点需要分别对这100个参数进行更新;在 :code:`Reduce` 模式下,各个节点需要分别对这25个参数进行更新,最后将更新的参数Broadcast到其他节点上.
- 关于 :code:`enable_backward_optimizer_op_deps` ,在多卡训练时,打开该选项可能会提升训练速度。
- 关于 :code:`fuse_all_optimizer_ops` ,目前只支持SGD、Adam和Momentum算法。**注意:目前不支持sparse参数梯度** 。
- 关于 :code:`fuse_all_reduce_ops` ,多GPU训练时,可以对 :code:`AllReduce` 操作进行融合,以减少 :code:`AllReduce` 的调用次数。默认情况下会将同一layer中参数的梯度的 :code:`AllReduce` 操作合并成一个,比如对于 :code:`fluid.layers.fc` 中有Weight和Bias两个参数,打开该选项之后,原本需要两次 :code:`AllReduce` 操作,现在只用一次 :code:`AllReduce` 操作。此外,为支持更大粒度的参数梯度融合,Paddle提供了 :code:`FLAGS_fuse_parameter_memory_size` 选项,用户可以指定融合AllReduce操作之后,每个 :code:`AllReduce` 操作的梯度字节数,比如希望每次 :code:`AllReduce` 调用传输64MB的梯度,:code:`export FLAGS_fuse_parameter_memory_size=64` 。**注意:目前不支持sparse参数梯度**。
- 关于 :code:`mkldnn_enabled_op_types` ,支持mkldnn库的Op有:transpose, sum, softmax, requantize, quantize, pool2d, lrn, gaussian_random, fc, dequantize, conv2d_transpose, conv2d, conv3d, concat, batch_norm, relu, tanh, sqrt, abs.
3.3 ExecutionStrategy中的配置参数
^^^^^^^^^^^^^^^^
ExecutionStrategy配置选项
>>>>>>>>>>>>>>>
.. csv-table::
:header: "选项", "类型", "默认值", "说明"
:widths: 3, 3, 5, 5
":code:`num_iteration_per_drop_scope`", "INT", "1", "经过多少次迭代之后清理一次local execution scope"
":code:`num_threads`", "INT", "对于CPU:2*dev_count;对于GPU:4*dev_count. (这是一个经验值)", ":code:`ParallelExecutor` 中执行所有Op使用的线程池大小"
说明:
- 关于 :code:`num_iteration_per_drop_scope` ,框架在运行过程中会产生一些临时变量,这些变量被放在local execution scope中。通常每经过一个batch就要清理一下local execution scope中的变量,但是由于GPU是异步设备,在清理local execution scope之前需要对所有的GPU调用一次同步操作,因此耗费的时间较长。为此我们在 :code:`execution_strategy` 中添加了 :code:`num_iteration_per_drop_scope` 选项。用户可以指定经过多少次迭代之后清理一次local execution scope。
- 关于 :code:`num_threads` ,:code:`ParallelExecutor` 根据Op之间的依赖关系确定Op的执行顺序,即:当Op的输入都已经变为ready状态之后,该Op会被放到一个队列中,等待被执行。 :code:`ParallelExecutor` 内部有一个任务调度线程和一个线程池,任务调度线程从队列中取出所有Ready的Op,并将其放到线程队列中。 :code:`num_threads` 表示线程池的大小。根据以往的经验,对于CPU任务,:code:`num_threads=2*dev_count` 时性能较好,对于GPU任务,:code:`num_threads=4*dev_count` 时性能较好。**注意:线程池不是越大越好**。
执行策略配置推荐
>>>>>>>>>>>>>>>
- 在显存足够的前提下,建议将 :code:`exec_strategy.num_iteration_per_drop_scope` 设置成一个较大的值,比如设置 :code:`exec_strategy.num_iteration_per_drop_scope=100` ,这样可以避免反复地申请和释放内存。该配置对于一些模型的优化效果较为明显。
- 对于一些较小的模型,比如mnist、language_model等,多个线程乱序调度op的开销大于其收益,因此推荐设置 :code:`exec_strategy.num_threads=1` 。
CPU训练设置
>>>>>>>>>>>>>>>
- 如果使用CPU做数据并行训练,需要指定环境变量CPU_NUM,这个环境变量指定程序运行过程中使用的 :code:`CPUPlace` 的个数。
- 如果使用CPU进行数据并行训练,并且 :code:`build_strategy.reduce_strategy` = :code:`fluid.BuildStrategy.ReduceStrategy.Reduce` ,所有 :code:`CPUPlace` 上的参数是共享的,因此对于一些使用CPU进行数据并行训练的模型,选用 :code:`Reduce` 模式可能会更快一些。
4. 运行时FLAGS设置
=============
Fluid中有一些FLAGS可以有助于性能优化:
- FLAGS_fraction_of_gpu_memory_to_use表示每次分配GPU显存的最小单位,取值范围为[0, 1)。由于CUDA原生的显存分配cuMalloc和释放cuFree操作均是同步操作,非常耗时,因此将FLAGS_fraction_of_gpu_memory_to_use设置成一个较大的值,比如0.92(默认值),可以显著地加速训练的速度。
- FLAGS_cudnn_exhaustive_search表示cuDNN在选取conv实现算法时采取穷举搜索策略,因此往往能选取到一个更快的conv实现算法,这对于CNN网络通常都是有加速的。但穷举搜索往往也会增加cuDNN的显存需求,因此用户可根据模型的实际情况选择是否设置该变量。
- FLAGS_enable_cublas_tensor_op_math表示是否使用TensorCore加速计算cuBLAS。这个环境变量只在Tesla V100以及更新的GPU上适用,且可能会带来一定的精度损失。
5. 使用Profile工具进行性能分析
=============
为方便用户更好的发现程序中的性能瓶颈,Paddle提供了多种Profile工具,这些工具的详细介绍和使用说明请参考 `性能调优 <http://www.paddlepaddle.org.cn/documentation/docs/zh/1.5/advanced_usage/development/profiling/index_cn.html>`_ 。
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录