未验证 提交 79ce6ed8 编写于 作者: Y yuyang18

Merge branch 'develop' of https://github.com/PaddlePaddle/FluidDoc into pr/20

#################
如何进行基准测试
#################
本文介绍如何给深度学习框架做基准测试。基准测试主要包含验证模型的精度和性能两方面,下文包含搭建测试环境,选择基准测试模型,验证测试结果等几方面内容。
验证深度学习框架,可分为训练和测试两个阶段, 验证指标略有不同,本文只介绍训练阶段的指标验证。训练阶段关注的是模型训练集上的精度,训练集是完备的,因此关注大batch\_size下的训练速度,关注吞吐量,例如图像模型常用的batch\_size=128, 多卡情况下会加大;预测阶段关注的是在测试集上的精度,线上服务测试数据不能提前收集,因此关注小batch\_size下的预测速度,关注延迟,例如预测服务常用的batch\_size=1, 4等。
`Fluid <https://github.com/PaddlePaddle/Paddle>`__ 是PaddlePaddle从0.11.0版本开始引入的设计,本文的基准测试在该版本上完成。
环境搭建
""""""""""""
基准测试中模型精度和硬件、框架无关,由模型结构和数据共同决定;性能方面由测试硬件和框架性能决定。框架基准测试为了对比框架之间的差异,控制硬件环境,系统库等版本一致。下文中的对比实验都在相同的硬件条件和系统环境条件下进行.
不同架构的GPU卡性能差异巨大,在验证模型在GPU上训练性能时,可使用NVIDIA提供的工具:code `nvidia-smi` 检验当前使用的GPU型号,如果测试多卡训练性能,需确认硬件连接是 `nvlink <https://zh.wikipedia.org/zh/NVLink>`__ 或 `PCIe <https://zh.wikipedia.org/zh-hans/PCI_Express>`__ 。 同样地,CPU型号会极大影响模型在CPU上的训练性能。可读取`/proc/cpuinfo`中的参数,确认当前正在使用的CPU型号。
下载GPU对应的Cuda Tool Kit和 Cudnn,或者使用NVIDIA官方发布的nvidia-docker镜像 `nvidia-docker <https://github.com/NVIDIA/nvidia-docker>`__, 镜像内包含了Cuda和Cudnn,本文采用这种方式。 Cuda Tool Kit包含了GPU代码使用到的基础库,影响在此基础上编译出的Fluid二进制运行性能。
准备好Cuda环境后,从github上的下载Paddle并源码编译,会生成对应的最适合当前GPU的sm\_arch二进制\ `sm\_arch <https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html>`__\ 。另外,cudnn对卷积类任务影响巨大,在基准测试中需要小版本一致,例如Cudnn7.0.2与Cudnn7.1.4在Resnet上有5%以上差异。
选择基准模型
""""""""""""
对框架做基准测试,需要覆盖不同训练任务和不同大小的模型,本文中选取了图像和NLP的最为常用的5个模型。
============ ============ ================= ============
任务种类 模型名称 网络结构 数据集
============ ============ ================= ============
图像分类 mnist Lenet mnist
图像分类 VGG VGG-16 Flowers102
图像分类 Resnet Resnet-50 Flowers102
文本分类 Stacked-LSTM Stacked-LSTM IMDB
机器翻译 seq-seq Stacked-LSTM wmt14
============ ============ ================= ============
其中mnist, VGG, Resnet属于CNN模型, stacked-lstm, seq2seq代表RNN模型。
`benchmark <https://github.com/PaddlePaddle/Paddle/tree/develop/benchmark/fluid>`__
基准模型测试脚本中,均跳过了前几个batch的训练过程,原因是加载数据和分配显存受系统当前运行情况影响,会导致统计性能不准确。运行完若干个轮次后,统计对应指标。
基准模型的数据的选择方面,数据量大且验证效果多的公开数据集为首选。图像模型VGG和resnet, 本文选择了 `flowers102 <http://www.robots.ox.ac.uk/~vgg/data/flowers/102/>`__ ,图像大小预处理为和Imagenet相同大小,因此性能可直接对比
NLP模型的公开且影响力大数据集较少,seq2seq模型选择了wmt14数据,stacked-lstm模型中选择了 `imdb <https://www.imdb.com/interfaces/>`__ 数据。
注意,图像模型每条样本大小相同,图像经过变换后大小一致,因此经过的计算路径基本相同,计算速度和显存占用波动较小,可以从若干个batch的数据中采样得到当前的训练性能数据。而NLP模型由于样本长度不定,计算路径和显存占用也不相同,因此只能完整运行若干个轮次后,统计速度和显存消耗。
显存分配是特别耗时的操作,因此Fluid默认会占用所有可用显存空间形成显存池,用以加速计算过程中的显存分配。如果需要统计模型真实显存消耗,可设置环境变量`FLAGS_fraction_of_gpu_memory_to_use=0.0`,观察最大显存开销。
测试过程
""""""""""""
- CPU 单机单线程测试
测试CPU上单线程的性能,先设置CUDA的环境变量为空,``CUDA_VISIBLE_DEVICES=``,并通过环境变量关闭OpenMP和MKL的多线程 ``OMP_NUM_THREADS=1``, ``MKL_NUM_THREADS=1;``。
然后代码中设置为使用CPUPlace,如果使用Paddle代码库中的脚本,只需要命令行参数传入 use_gpu=False即可。
.. code-block:: python
>>> import paddle.fluid as fluid
>>> place = fluid.CPUPlace()
.. code:: bash
docker run -it --name CASE_NAME --security-opt seccomp=unconfined -v $PWD/benchmark:/benchmark paddlepaddle/paddle:latest-dev /bin/bash
- GPU 单机单卡测试
本教程使用了Cuda8, Cudnn7.0.1。来源为:code `nvidia/cuda:8.0-cudnn7-devel-ubuntu16.04`
.. code:: bash
nvidia-docker run -it --name CASE_NAME --security-opt seccomp=unconfined -v $PWD/benchmark:/benchmark -v /usr/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu paddlepaddle/paddle:latest-dev /bin/bash
在单卡上测试,设置CUDA的环境变量使用一块GPU,``CUDA_VISIBLE_DEVICES=0``
然后代码中设置为使用CUDAPlace,如果使用Paddle代码库中的脚本,只需要命令行参数传入 use_gpu=True即可。
.. code-block:: python
>>> import paddle.fluid as fluid
>>> place = fluid.CUDAPlace(0) // 0 指第0块GPU
测试结果
""""""""""""
本教程对比相同环境下的Fluid0.12.0和TensorFlow1.4.0的性能表现。
硬件环境为 CPU: Intel(R) Xeon(R) CPU E5-2660 v4 @ 2.00GHz, GPU: TITAN X(Pascal) 12G x 1, Nvidia-Driver 384.90。
系统环境为Ubuntu 16.04.3 LTS, 本文中采用了docker环境,系统版本为nvidia-docker17.05.0-ce。
测试的Fluid版本为\ `v.0.12.0 <https://github.com/PaddlePaddle/Paddle/releases/tag/v.0.12.0>`__ 。
TensorFlow版本为\ `v.1.4.0-rc1 <https://github.com/tensorflow/tensorflow/tree/v1.4.0-rc1>`__ 。
使用的脚本和配置见\ `benchmark <https://github.com/PaddlePaddle/Paddle/tree/develop/benchmark/fluid>`__ 。
图表中统计单位为samples/秒。
- CPU 单机单线程测试结果
================ ==================== ===================
Speed Fluid CPU TensorFlow CPU
================ ==================== ===================
mnist 1298.75 samples/s 637.57 samples/s
VGG-16 0.4147 images/s 0.1229 images/s
Resnet-50 1.6935 images/s 0.3657 images/s
Stacked-LSTM 472.3225 words/s 48.2293words/s
Seq2Seq 217.1655 words/s 28.6164 words/s
================ ==================== ===================
- GPU 单机单卡测试结果
=============== ===================== =================
Speed Fluid GPU TensorFlow GPU
=============== ===================== =================
mnist 19710.90 samples/s 15576.3 samples/s
VGG-16 59.83327 images/s 40.9967 images/s
Resnet-50 105.84412 97.8923 images/s
Stacked-LSTM 1319.99315 1608.2526 words/s
Seq2Seq 7147.89081 6845.1161 words/s
=============== ===================== =================
......@@ -8,7 +8,8 @@
Complete this guide
.. toctree::
:maxdepth: 2
:maxdepth: 1
deploy/index.rst
development/index.rst
\ No newline at end of file
development/index.rst
benchmark.rst
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid_average:
=============
fluid.average
=============
......
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid_backward:
==============
fluid.backward
==============
......
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid_clip:
==========
fluid.clip
==========
......@@ -41,19 +43,3 @@ GradientClipByGlobalNorm
:members:
:noindex:
.. _api_fluid_clip_append_gradient_clip_ops:
append_gradient_clip_ops
------------------------
.. autofunction:: paddle.fluid.clip.append_gradient_clip_ops
:noindex:
.. _api_fluid_clip_error_clip_callback:
error_clip_callback
-------------------
.. autofunction:: paddle.fluid.clip.error_clip_callback
:noindex:
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid_data_feeder:
=================
fluid.data_feeder
=================
......
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid_executor:
==============
fluid.executor
==============
......@@ -30,12 +32,12 @@ scope_guard
.. autofunction:: paddle.fluid.executor.scope_guard
:noindex:
.. _api_fluid_executor_switch_scope:
.. _api_fluid_executor__switch_scope:
switch_scope
------------
_switch_scope
-------------
.. autofunction:: paddle.fluid.executor.switch_scope
.. autofunction:: paddle.fluid.executor._switch_scope
:noindex:
.. _api_fluid_executor_fetch_var:
......
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid:
=====
fluid
=====
......@@ -65,22 +67,6 @@ program_guard
.. autofunction:: paddle.fluid.program_guard
:noindex:
.. _api_fluid_switch_startup_program:
switch_startup_program
----------------------
.. autofunction:: paddle.fluid.switch_startup_program
:noindex:
.. _api_fluid_switch_main_program:
switch_main_program
-------------------
.. autofunction:: paddle.fluid.switch_main_program
:noindex:
.. _api_fluid_get_var:
get_var
......@@ -114,12 +100,12 @@ scope_guard
.. autofunction:: paddle.fluid.scope_guard
:noindex:
.. _api_fluid_switch_scope:
.. _api_fluid__switch_scope:
switch_scope
------------
_switch_scope
-------------
.. autofunction:: paddle.fluid.switch_scope
.. autofunction:: paddle.fluid._switch_scope
:noindex:
.. _api_fluid_fetch_var:
......@@ -243,6 +229,15 @@ Inferencer
:members:
:noindex:
.. _api_fluid_DistributeTranspiler:
DistributeTranspiler
--------------------
.. autoclass:: paddle.fluid.DistributeTranspiler
:members:
:noindex:
.. _api_fluid_memory_optimize:
memory_optimize
......@@ -374,3 +369,12 @@ DataFeeder
:members:
:noindex:
.. _api_fluid_Scope:
Scope
-----
.. autoclass:: paddle.fluid.Scope
:members:
:noindex:
......@@ -48,7 +48,7 @@ class DocGenerator(object):
!DO NOT EDIT THIS FILE MANUALLY!
''')
self._print_ref_raw_("_".join(self.module_name.split(".")))
self._print_header_(self.module_name, dot='=', is_title=True)
def print_submodule(self, submodule_name):
......@@ -107,9 +107,10 @@ class DocGenerator(object):
self.stream.write('\n')
def _print_ref_(self, name):
self.stream.write(".. _api_{0}_{1}:\n\n".format(
"_".join(self.module_name.split(".")), name
))
self._print_ref_raw_("_".join(self.module_name.split(".") + [name]))
def _print_ref_raw_(self, anchor):
self.stream.write(".. _api_{0}:\n\n".format(anchor))
def main():
......
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid_initializer:
=================
fluid.initializer
=================
......@@ -50,6 +52,15 @@ Bilinear
:members:
:noindex:
.. _api_fluid_initializer_MSRA:
MSRA
----
.. autoclass:: paddle.fluid.initializer.MSRA
:members:
:noindex:
.. _api_fluid_initializer_force_init_on_cpu:
force_init_on_cpu
......@@ -111,3 +122,12 @@ BilinearInitializer
:members:
:noindex:
.. _api_fluid_initializer_MSRAInitializer:
MSRAInitializer
---------------
.. autoclass:: paddle.fluid.initializer.MSRAInitializer
:members:
:noindex:
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid_io:
========
fluid.io
========
......
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid_layers:
============
fluid.layers
============
......@@ -910,6 +912,14 @@ log
.. autofunction:: paddle.fluid.layers.log
:noindex:
.. _api_fluid_layers_crop:
crop
----
.. autofunction:: paddle.fluid.layers.crop
:noindex:
ops
===
......
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid_metrics:
=============
fluid.metrics
=============
......@@ -23,6 +25,24 @@ CompositeMetric
:members:
:noindex:
.. _api_fluid_metrics_Precision:
Precision
---------
.. autoclass:: paddle.fluid.metrics.Precision
:members:
:noindex:
.. _api_fluid_metrics_Recall:
Recall
------
.. autoclass:: paddle.fluid.metrics.Recall
:members:
:noindex:
.. _api_fluid_metrics_Accuracy:
Accuracy
......
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid_nets:
==========
fluid.nets
==========
......
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid_optimizer:
===============
fluid.optimizer
===============
......@@ -59,6 +61,15 @@ DecayedAdagrad
:members:
:noindex:
.. _api_fluid_optimizer_Ftrl:
Ftrl
----
.. autoclass:: paddle.fluid.optimizer.Ftrl
:members:
:noindex:
.. _api_fluid_optimizer_SGDOptimizer:
SGDOptimizer
......@@ -122,6 +133,15 @@ RMSPropOptimizer
:members:
:noindex:
.. _api_fluid_optimizer_FtrlOptimizer:
FtrlOptimizer
-------------
.. autoclass:: paddle.fluid.optimizer.FtrlOptimizer
:members:
:noindex:
.. _api_fluid_optimizer_Adadelta:
Adadelta
......@@ -149,3 +169,12 @@ Optimizer
:members:
:noindex:
.. _api_fluid_optimizer_RMSPropOptimizer:
RMSPropOptimizer
----------------
.. autoclass:: paddle.fluid.optimizer.RMSPropOptimizer
:members:
:noindex:
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid_param_attr:
================
fluid.param_attr
================
......
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid_profiler:
==============
fluid.profiler
==============
......
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid_recordio_writer:
=====================
fluid.recordio_writer
=====================
......
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid_regularizer:
=================
fluid.regularizer
=================
......@@ -13,15 +15,6 @@ append_regularization_ops
.. autofunction:: paddle.fluid.regularizer.append_regularization_ops
:noindex:
.. _api_fluid_regularizer_WeightDecayRegularizer:
WeightDecayRegularizer
----------------------
.. autoclass:: paddle.fluid.regularizer.WeightDecayRegularizer
:members:
:noindex:
.. _api_fluid_regularizer_L1Decay:
L1Decay
......
.. THIS FILE IS GENERATED BY `gen_doc.{py|sh}`
!DO NOT EDIT THIS FILE MANUALLY!
.. _api_fluid_transpiler:
================
fluid.transpiler
================
.. _api_fluid_transpiler_DistributeTranspiler:
DistributeTranspiler
--------------------
.. autoclass:: paddle.fluid.transpiler.DistributeTranspiler
:members:
:noindex:
.. _api_fluid_transpiler_memory_optimize:
memory_optimize
......
# 深度学习入门指南
## 要读的第一本书
基础理论习得的最直接来源就是书本。按机器学习理论、深度学习理论、编程语言三方面划分,这里推荐如下书籍辅助您。
### 机器学习理论
在开启深度学习之前,您需要先行掌握机器学习的理论。深度学习是机器学习中的一个分支,两者内在的理论基础存在强关联。
机器学习理论的书籍教材比较多,这里推荐一本易懂易学的书籍,可以重点关注神经网络部分。
书名:《机器学习》(周志华著,清华大学出版社,2016年版)
### 深度学习理论
打好机器学习的理论功底后,您可以开始钻研深度学习的理论。通常深度学习理论会给人留下抽象难懂的印象,且和数学结合紧密。
为了让您能够顺利入门,这里推荐一份易学易用的教材,无论深度学习理论还是数学理论即可一本搞定。
书名:《Deep Learning(深度学习)》(Goodfellow, Bengio, Courville合著,赵申剑、黎彧君、符天凡和李凯合译,人民邮电出版社,2017年版)
此书电子版在Github上已经开源,详情可参考此链接 [《深度学习》](https://github.com/exacity/deeplearningbook-chinese)
### 编程语言
Python方向:这里推荐您学习Python,一方面各大主流深度学习框架的主力支撑编程语言均为Python;另一方面,对比其他语言,Python较为简单易学。
Python的教材种类较多,这里推荐一本实操和理论性都兼顾的教材,只要完成书中52个习题,跑代码然后发现问题解决,就能逐步上手。
书名:《“笨办法”学Python》(Zed Shaw著,王巍巍译,人民邮电出版社,2014年11月版)
C++方向:C++语言在底层框架中使用较多,您逐步掌握开源框架的基本操作后,在更高阶的框架应用中会用到这个技能点。
同前面提到的Python一样,学习C++时需要多上手操作。这里推荐迅速上手C++的书籍,不但能够学习功能和结构,还提供了解决方案的示例。
书名:《Essential C++》【美】李普曼(Lippman,S.B.)著,侯捷译,电子工业出版社2013年8月版
## 要看的视频公开课
在学习一门新技术的同时,除了看书,如果有老师面对面教授,可以更快更好的学会知识。相比于线下授课,视频公开课能够在省钱省力的同时,达到易学易掌握的效果。
目前深度学习的课程多是公开免费的,通过学习您可以更轻松的理解深度学习中的抽象理论,并在实操方面不绕弯路。
综合课程生动性、可操作性、紧凑性、连续性这些特点,这里推荐如下课程,同步附上网址,便于您查找学习。
### 理论知识详解视频课
[机器学习](http://open.163.com/special/opencourse/machinelearning.html) 斯坦福大学教授吴恩达公开课程,包含相关算法的详细讲解。
[AI技术](https://ai.baidu.com/paddlepaddle/player?id=13) 百度推出的“AI核心技术掌握”课程,每节课在20-30分钟左右,从AI技术到深度学习进行全面细致的解读。
[深度学习](http://speech.ee.ntu.edu.tw/~tlkagk/courses_ML17_2.html) 台湾李宏毅教授的在线课程,其中是英文课程,会结合国外的科研成果,但也适合新手入门和理解深度学习。
[编程语言](https://ai.baidu.com/paddlepaddle/openCourses) Python操作课程,从基础到进阶操作都提供详细说明,每节课时长20分钟左右。
### PaddlePaddle实操视频课
掌握好理论基础,具备编程能力后,您可以开始使用PaddlePaddle Fluid进行实操,从初阶开始学习,向着中高阶努力。
目前已有PaddlePaddle官方视频公开课在官网呈现,内含PaddlePaddle实战、PaddlePaddle应用场景和机器学习模型讲解课程,帮助开发者从零开始使用PaddlePaddle,从简单场景逐步过渡到工业级应用。[点击这里](http://ai.baidu.com/paddlepaddle/openCourses)您即可开始视频课的学习之旅。
......@@ -8,7 +8,8 @@
.. toctree::
:maxdepth: 2
install/index.rst
quick_start/index.rst
basics/index.rst
install/install_doc.rst
quick_start/quick_start.rst
basics/theoretical_background.rst
basics/Guide.md
../../../paddle/doc/fluid/build_and_install/build_from_source_cn.rst
\ No newline at end of file
../../../paddle/doc/fluid/build_and_install/docker_install_cn.rst
\ No newline at end of file
../../../paddle/doc/fluid/build_and_install/index_cn.rst
\ No newline at end of file
.. _how_to_install:
安装说明
^^^^^^^^
您可以使用我们提供的安装包,或使用源代码,安装PaddlePaddle。
.. _install_linux:
在Linux安装PaddlePaddle
--------
推荐您使用 `pip <https://pypi.org/project/pip/>`_
安装,它是Linux系统下最简单的安装方式。
注意事项:
- PaddlePaddle Python API 依赖Python 2.7版本。
执行下面的命令即可在当前机器上安装PaddlePaddle的运行时环境,并自动下载安装依赖软件。
.. code-block:: bash
pip install paddlepaddle
当前的默认版本为0.13.0,cpu_avx_openblas,您可以通过指定版本号来安装其它版本,例如:
.. code-block:: bash
pip install paddlepaddle==0.12.0
如果需要安装支持GPU的版本(cuda9.0_cudnn7_avx_openblas),需要执行:
.. code-block:: bash
pip install paddlepaddle-gpu
当前的默认版本是0.13.0,PaddlePaddle针对不同需求提供了更多版本的安装包,部分列表如下:
================================= ========================================
版本号 版本说明
================================= ========================================
paddlepaddle-gpu==0.13.0 使用CUDA 9.0和cuDNN 7编译的0.13.0版本
paddlepaddle-gpu==0.12.0 使用CUDA 8.0和cuDNN 5编译的0.12.0版本
paddlepaddle-gpu==0.11.0.post87 使用CUDA 8.0和cuDNN 7编译的0.11.0版本
paddlepaddle-gpu==0.11.0.post8 使用CUDA 8.0和cuDNN 5编译的0.11.0版本
paddlepaddle-gpu==0.11.0 使用CUDA 7.5和cuDNN 5编译的0.11.0版本
================================= ========================================
您可以在 `Release History <https://pypi.org/project/paddlepaddle-gpu/#history>`_
中找到paddlepaddle-gpu的各个发行版本。
如果需要获取并安装最新的(开发分支)PaddlePaddle,可以从我们的CI系统中下载最新的whl
安装包和c-api开发包并安装,您可以从下面的表格中找到需要的版本:
如果在点击下面链接时出现如下登陆界面,点击“Log in as guest”即可开始下载:
.. image:: paddleci.png
:scale: 50 %
:align: center
.. csv-table:: 各个版本最新的whl包
:header: "版本说明", "cp27-cp27mu", "cp27-cp27m"
:widths: 1, 3, 3
"cpu_avx_mkl", "`paddlepaddle-latest-cp27-cp27mu-linux_x86_64.whl <https://guest:@paddleci.ngrok.io/repository/download/Manylinux1_CpuAvxCp27cp27mu/.lastSuccessful/paddlepaddle-latest-cp27-cp27mu-linux_x86_64.whl>`__", "`paddlepaddle-latest-cp27-cp27m-linux_x86_64.whl <https://guest:@paddleci.ngrok.io/repository/download/Manylinux1_CpuAvxCp27cp27mu/.lastSuccessful/paddlepaddle-latest-cp27-cp27m-linux_x86_64.whl>`__"
"cpu_avx_openblas", "`paddlepaddle-latest-cp27-cp27mu-linux_x86_64.whl <https://guest:@paddleci.ngrok.io/repository/download/Manylinux1_CpuAvxOpenblas/.lastSuccessful/paddlepaddle-latest-cp27-cp27mu-linux_x86_64.whl>`__", "`paddlepaddle-latest-cp27-cp27m-linux_x86_64.whl <https://guest:@paddleci.ngrok.io/repository/download/Manylinux1_CpuAvxOpenblas/.lastSuccessful/paddlepaddle-latest-cp27-cp27m-linux_x86_64.whl>`__"
"cpu_noavx_openblas", "`paddlepaddle-latest-cp27-cp27mu-linux_x86_64.whl <https://guest:@paddleci.ngrok.io/repository/download/Manylinux1_CpuNoavxOpenblas/.lastSuccessful/paddlepaddle-latest-cp27-cp27mu-linux_x86_64.whl>`__", "`paddlepaddle-latest-cp27-cp27m-linux_x86_64.whl <https://guest:@paddleci.ngrok.io/repository/download/Manylinux1_CpuNoavxOpenblas/.lastSuccessful/paddlepaddle-latest-cp27-cp27m-linux_x86_64.whl>`_"
"cuda8.0_cudnn5_avx_mkl", "`paddlepaddle_gpu-latest-cp27-cp27mu-linux_x86_64.whl <https://guest:@paddleci.ngrok.io/repository/download/Manylinux1_Cuda80cudnn5cp27cp27mu/.lastSuccessful/paddlepaddle_gpu-latest-cp27-cp27mu-linux_x86_64.whl>`__", "`paddlepaddle_gpu-latest-cp27-cp27m-linux_x86_64.whl <https://guest:@paddleci.ngrok.io/repository/download/Manylinux1_Cuda80cudnn5cp27cp27mu/.lastSuccessful/paddlepaddle_gpu-latest-cp27-cp27m-linux_x86_64.whl>`__"
"cuda8.0_cudnn7_avx_mkl", "`paddlepaddle_gpu-latest-cp27-cp27mu-linux_x86_64.whl <https://guest:@paddleci.ngrok.io/repository/download/Manylinux1_Cuda8cudnn7cp27cp27mu/.lastSuccessful/paddlepaddle_gpu-latest-cp27-cp27mu-linux_x86_64.whl>`__", "`paddlepaddle_gpu-latest-cp27-cp27m-linux_x86_64.whl <https://guest:@paddleci.ngrok.io/repository/download/Manylinux1_Cuda8cudnn7cp27cp27mu/.lastSuccessful/paddlepaddle_gpu-latest-cp27-cp27m-linux_x86_64.whl>`__"
"cuda9.0_cudnn7_avx_mkl", "`paddlepaddle_gpu-latest-cp27-cp27mu-linux_x86_64.whl <https://guest:@paddleci.ngrok.io/repository/download/Manylinux1_Cuda90cudnn7avxMkl/.lastSuccessful/paddlepaddle_gpu-latest-cp27-cp27mu-linux_x86_64.whl>`__", "`paddlepaddle_gpu-latest-cp27-cp27m-linux_x86_64.whl <https://guest:@paddleci.ngrok.io/repository/download/Manylinux1_Cuda90cudnn7avxMkl/.lastSuccessful/paddlepaddle_gpu-latest-cp27-cp27m-linux_x86_64.whl>`__"
.. _FAQ:
安装常见问题和解决方法
======================
- paddlepaddle*.whl is not a supported wheel on this platform.
出现这个问题的主要原因是,没有找到和当前系统匹配的paddlepaddle安装包。
请检查Python版本是否为2.7系列。另外最新的pip官方源中的安装包默认是manylinux1标准,
需要使用最新的pip (>9.0.0) 才可以安装。
可以使用下面的命令更新您的pip:
.. code-block:: bash
pip install --upgrade pip
如果仍然存在问题,可以执行:
.. code-block:: bash
python -c "import pip; print(pip.pep425tags.get_supported())"
获取当前系统支持的安装包格式,并检查和需安装的包是否匹配。pypi安装包
可以在 `这里 <https://pypi.python.org/pypi/paddlepaddle/0.10.5>`_ 找到。
如果系统支持的是 linux_x86_64 而安装包是 manylinux1_x86_64 ,需要升级pip版本到最新;
如果系统支持 manylinux1_x86_64 而安装包(本地)是 linux_x86_64,
可以重命名这个whl包为 manylinux1_x86_64 再安装。
.. _install_windows:
在windows安装PaddlePaddle
------------------------------
若您的系统为windows,您可以通过Docker来使用PaddlePaddle。
推荐您下载 `PaddlePaddle快速安装包 <http://paddle-windows.bj.bcebos.com/PaddlePaddle-windows.zip>`_,
该安装包能够帮助您判断、安装适合的Docker,并引导您在Docker中使用PaddlePaddle。
..
todo: windows的安装包要放在百度云上
注意事项:
* 系统要求:windows7&8&10。
* 下载安装包后,请您右键选择“以管理员身份运行”。
* PaddlePaddle不支持在windows使用GPU。
Docker安装完成后,请您执行下面的步骤:
请您右键选择”以管理员身份运行“,来启动Docker客户端
获取Image ID
.. code-block:: bash
docker images
启动Docker
.. code-block:: bash
docker run -d it -t imageid /bin/bash
获取Docker Container
.. code-block:: bash
docker ps -a
进入Container
.. code-block:: bash
docker attach container
.. _others:
其他安装方式
-------------
.. _source:
从源码编译
==========
.. _requirements:
需要的软硬件
"""""""""""""
为了编译PaddlePaddle,我们需要
1. 一台电脑,可以装的是 Linux, Windows 或者 MacOS 操作系统
2. Docker
不需要依赖其他任何软件了。即便是 Python 和 GCC 都不需要,因为我们会把所有编译工具都安装进一个 Docker 镜像里。
.. _build_step:
编译方法
"""""""""""""
PaddlePaddle需要使用Docker环境完成编译,这样可以免去单独安装编译依赖的步骤,可选的不同编译环境Docker镜像
可以在 `这里 <https://hub.docker.com/r/paddlepaddle/paddle_manylinux_devel/tags/>`_ 找到。或者
参考下述可选步骤,从源码中构建用于编译PaddlePaddle的Docker镜像。
如果您选择不使用Docker镜像,则需要在本机安装下面章节列出的 `附录:编译依赖`_ 之后才能开始编译的步骤。
编译PaddlePaddle,需要执行:
.. code-block:: bash
# 1. 获取源码
git clone https://github.com/PaddlePaddle/Paddle.git
cd Paddle
# 2. 可选步骤:源码中构建用于编译PaddlePaddle的Docker镜像
docker build -t paddle:dev .
# 3. 执行下面的命令编译CPU-Only的二进制
docker run -it -v $PWD:/paddle -e "WITH_GPU=OFF" -e "WITH_TESTING=OFF" paddlepaddle/paddle_manylinux_devel:cuda8.0_cudnn5 bash -x /paddle/paddle/scripts/paddle_build.sh build
# 4. 或者也可以使用为上述可选步骤构建的镜像(必须先执行第2步)
docker run -it -v $PWD:/paddle -e "WITH_GPU=OFF" -e "WITH_TESTING=OFF" paddle:dev
注:上述命令把当前目录(源码树根目录)映射为 container 里的 :code:`/paddle` 目录。如果使用自行
构建的镜像(上述第4步)会执行 :code:`Dockerfile` 描述的默认入口程序 :code:`docker_build.sh` 可以省略步骤3中
最后的执行脚本的命令。
编译完成后会在build/python/dist目录下生成输出的whl包,可以选在在当前机器安装也可以拷贝到目标机器安装:
.. code-block:: bash
pip install build/python/dist/*.whl
如果机器中已经安装过PaddlePaddle,有两种方法:
.. code-block:: bash
1. 先卸载之前的版本,再重新安装
pip uninstall paddlepaddle
pip install build/python/dist/*.whl
2. 直接升级到更新的版本
pip install build/python/dist/*.whl -U
.. _run_test:
执行单元测试
"""""""""""""
如果您期望在编译完成后立即执行所有的单元测试,可以按照下面的方法:
设置 :code:`RUN_TEST=ON` 和 :code:`WITH_TESTING=ON` 就会在完成编译之后,立即执行单元测试。
开启 :code:`WITH_GPU=ON` 可以指定同时执行GPU上的单元测试。
.. code-block:: bash
docker run -it -v $PWD:/paddle -e "WITH_GPU=OFF" -e "WITH_TESTING=ON" -e "RUN_TEST=ON" paddlepaddle/paddle_manylinux_devel:cuda8.0_cudnn5 bash -x /paddle/paddle/scripts/paddle_build.sh build
如果期望执行其中一个单元测试,(比如 :code:`test_sum_op` ):
.. code-block:: bash
docker run -it -v $PWD:/paddle -e "WITH_GPU=OFF" -e "WITH_TESTING=ON" -e "RUN_TEST=OFF" paddlepaddle/paddle_manylinux_devel:cuda8.0_cudnn5 bash -x /paddle/paddle/scripts/paddle_build.sh build
cd /paddle/build
ctest -R test_sum_op -V
.. _faq_docker:
常见问题
"""""""""""""
- 什么是 Docker?
如果您没有听说 Docker,可以把它想象为一个类似 virtualenv 的系统,但是虚拟的不仅仅是 Python 的运行环境。
- Docker 还是虚拟机?
有人用虚拟机来类比 Docker。需要强调的是:Docker 不会虚拟任何硬件,Docker container 里运行的编译工具实际上都是在本机的 CPU 和操作系统上直接运行的,性能和把编译工具安装在本机运行一样。
- 为什么用 Docker?
把工具和配置都安装在一个 Docker image 里可以标准化编译环境。这样如果遇到问题,其他人可以复现问题以便帮助。
另外,对于习惯使用Windows和MacOS的开发者来说,使用Docker就不用配置交叉编译环境了。
- 可以选择不用Docker吗?
当然可以。大家可以用把开发工具安装进入 Docker image 一样的方式,把这些工具安装到本机。这篇文档介绍基于 Docker 的开发流程,是因为这个流程比其他方法都更简便。
- 学习 Docker 有多难?
理解 Docker 并不难,大概花十分钟看一下 `这篇文章 <https://zhuanlan.zhihu.com/p/19902938>`_。
这可以帮您省掉花一小时安装和配置各种开发工具,以及切换机器时需要新安装的辛苦。别忘了 PaddlePaddle 更新可能导致需要新的开发工具。更别提简化问题复现带来的好处了。
- 可以用 IDE 吗?
当然可以,因为源码就在本机上。IDE 默认调用 make 之类的程序来编译源码,我们只需要配置 IDE 来调用 Docker 命令编译源码即可。
很多 PaddlePaddle 开发者使用 Emacs。他们在自己的 `~/.emacs` 配置文件里加两行
.. code-block:: bash
(global-set-key "\C-cc" 'compile)
(setq compile-command
"docker run --rm -it -v $(git rev-parse --show-toplevel):/paddle paddle:dev")
就可以按 `Ctrl-C` 和 `c` 键来启动编译了。
- 可以并行编译吗?
是的。我们的 Docker image 运行一个 `Bash 脚本 <https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/paddle/scripts/paddle_build.sh>`_。这个脚本调用 :code:`make -j$(nproc)` 来启动和 CPU 核一样多的进程来并行编译。
- Docker 需要 sudo
如果用自己的电脑开发,自然也就有管理员权限(sudo)了。如果用公用的电脑开发,需要请管理员安装和配置好 Docker。此外,PaddlePaddle 项目在努力开始支持其他不需要 sudo 的集装箱技术,比如 rkt。
- 在 Windows/MacOS 上编译很慢
Docker 在 Windows 和 MacOS 都可以运行。不过实际上是运行在一个 Linux 虚拟机上。可能需要注意给这个虚拟机多分配一些 CPU 和内存,以保证编译高效。具体做法请参考 `这个issue <https://github.com/PaddlePaddle/Paddle/issues/627>`_。
- 磁盘不够
本文中的例子里, :code:`docker run` 命令里都用了 :code:`--rm` 参数,这样保证运行结束之后的 containers 不会保留在磁盘上。可以用 :code:`docker ps -a` 命令看到停止后但是没有删除的 containers。 :code:`docker build` 命令有时候会产生一些中间结果,是没有名字的 images,也会占用磁盘。可以参考 `这篇文章 <https://zaiste.net/posts/removing_docker_containers/>`_ 来清理这些内容。
.. _compile_deps:
附录:编译依赖
"""""""""""""
PaddlePaddle编译需要使用到下面的依赖(包含但不限于),其他的依赖软件,会自动在编译时下载。
.. csv-table:: PaddlePaddle编译依赖
:header: "依赖", "版本", "说明"
:widths: 10, 15, 30
"CMake", ">=3.2", ""
"GCC", "4.8.2", "推荐使用CentOS的devtools2"
"Python", "2.7.x", "依赖libpython2.7.so"
"pip", ">=9.0", ""
"numpy", "", ""
"SWIG", ">=2.0", ""
"Go", ">=1.8", "可选"
.. _build_options:
附录:编译选项
"""""""""""""
PaddlePaddle的编译选项,包括生成CPU/GPU二进制文件、链接何种BLAS库等。
用户可在调用cmake的时候设置它们,详细的cmake使用方法可以参考
`官方文档 <https://cmake.org/cmake-tutorial>`_ 。
在cmake的命令行中,通过使用 ``-D`` 命令设置该类编译选项,例如:
.. code-block:: bash
cmake .. -DWITH_GPU=OFF
.. csv-table:: 编译选项说明
:header: "选项", "说明", "默认值"
:widths: 1, 7, 2
"WITH_GPU", "是否支持GPU", "ON"
"WITH_C_API", "是否仅编译CAPI", "OFF"
"WITH_DOUBLE", "是否使用双精度浮点数", "OFF"
"WITH_DSO", "是否运行时动态加载CUDA动态库,而非静态加载CUDA动态库。", "ON"
"WITH_AVX", "是否编译含有AVX指令集的PaddlePaddle二进制文件", "ON"
"WITH_PYTHON", "是否内嵌PYTHON解释器", "ON"
"WITH_STYLE_CHECK", "是否编译时进行代码风格检查", "ON"
"WITH_TESTING", "是否开启单元测试", "OFF"
"WITH_DOC", "是否编译中英文文档", "OFF"
"WITH_SWIG_PY", "是否编译PYTHON的SWIG接口,该接口可用于预测和定制化训练", "Auto"
"WITH_GOLANG", "是否编译go语言的可容错parameter server", "OFF"
"WITH_MKL", "是否使用MKL数学库,如果为否则是用OpenBLAS", "ON"
BLAS
+++++
PaddlePaddle支持 `MKL <https://software.intel.com/en-us/intel-mkl>`_ 和
`OpenBlAS <http://www.openblas.net/>`_ 两种BLAS库。默认使用MKL。如果使用MKL并且机器含有AVX2指令集,
还会下载MKL-DNN数学库,详细参考 `这里 <https://github.com/PaddlePaddle/Paddle/tree/develop/doc/design/mkldnn#cmake>`_ 。
如果关闭MKL,则会使用OpenBLAS作为BLAS库。
CUDA/cuDNN
+++++++++++
PaddlePaddle在编译时/运行时会自动找到系统中安装的CUDA和cuDNN库进行编译和执行。
使用参数 :code:`-DCUDA_ARCH_NAME=Auto` 可以指定开启自动检测SM架构,加速编译。
PaddlePaddle可以使用cuDNN v5.1之后的任何一个版本来编译运行,但尽量请保持编译和运行使用的cuDNN是同一个版本。
我们推荐使用最新版本的cuDNN。
编译选项的设置
++++++++++++++
PaddePaddle通过编译时指定路径来实现引用各种BLAS/CUDA/cuDNN库。cmake编译时,首先在系统路径( :code:`/usr/lib:/usr/local/lib` )中搜索这几个库,同时也会读取相关路径变量来进行搜索。 通过使用 ``-D`` 命令可以设置,例如
.. code-block:: bash
cmake .. -DWITH_GPU=ON -DWITH_TESTING=OFF -DCUDNN_ROOT=/opt/cudnnv5
**注意:这几个编译选项的设置,只在第一次cmake的时候有效。如果之后想要重新设置,推荐清理整个编译目录(** :code:`rm -rf` )**后,再指定。**
.. _install_docker:
使用Docker安装运行
==================
使用Docker安装和运行PaddlePaddle可以无需考虑依赖环境。
您可以在 `Docker官网 <https://docs.docker.com/get-started/>`_
获得基本的Docker安装和使用方法。
在了解Docker的基本使用方法之后,即可开始下面的步骤:
.. _docker_pull:
获取PaddlePaddle的Docker镜像
""""""""""""""""""""""""""""
执行下面的命令获取最新的PaddlePaddle Docker镜像,版本为cpu_avx_mkl:
.. code-block:: bash
docker pull paddlepaddle/paddle
对于国内用户,我们提供了加速访问的镜像源:
.. code-block:: bash
docker pull docker.paddlepaddlehub.com/paddle
下载GPU版本(cuda8.0_cudnn5_avx_mkl)的Docker镜像:
.. code-block:: bash
docker pull paddlepaddle/paddle:latest-gpu
docker pull docker.paddlepaddlehub.com/paddle:latest-gpu
选择下载使用不同的BLAS库的Docker镜像:
.. code-block:: bash
# 默认是使用MKL的镜像
docker pull paddlepaddle/paddle
# 使用OpenBLAS的镜像
docker pull paddlepaddle/paddle:latest-openblas
下载指定版本的Docker镜像,可以从 `DockerHub网站 <https://hub.docker.com/r/paddlepaddle/paddle/tags/>`_ 获取可选的tag,并执行下面的命令:
.. code-block:: bash
docker pull paddlepaddle/paddle:[tag]
# 比如:
docker pull docker.paddlepaddlehub.com/paddle:0.11.0-gpu
.. _docker_run:
在Docker中执行PaddlePaddle训练程序
"""""""""""""""""""""""""""""""""""
假设您已经在当前目录(比如在/home/work)编写了一个PaddlePaddle的程序 :code:`train.py` (可以参考
`PaddlePaddleBook <http://www.paddlepaddle.org/docs/develop/book/01.fit_a_line/index.cn.html>`_
编写),就可以使用下面的命令开始执行训练:
.. code-block:: bash
cd /home/work
docker run -it -v $PWD:/work paddlepaddle/paddle /work/train.py
上述命令中, :code:`-it` 参数说明容器已交互式运行; :code:`-v $PWD:/work`
指定将当前路径(Linux中$PWD变量会展开为当前路径的绝对路径)挂载到容器内部的 :code:`/work`
目录; :code:`paddlepaddle/paddle` 指定需要使用的容器; 最后 :code:`/work/train.py`
为容器内执行的命令,即运行训练程序。
当然,您也可以进入到Docker容器中,以交互式的方式执行或调试您的代码:
.. code-block:: bash
docker run -it -v $PWD:/work paddlepaddle/paddle /bin/bash
cd /work
python train.py
**注:PaddlePaddle Docker镜像为了减小体积,默认没有安装vim,您可以在容器中执行** :code:`apt-get install -y vim` **安装后,在容器中编辑代码。**
.. _docker_run_book:
使用Docker启动PaddlePaddle Book教程
""""""""""""""""""""""""""""""""""""
使用Docker可以快速在本地启动一个包含了PaddlePaddle官方Book教程的Jupyter Notebook,可以通过网页浏览。
PaddlePaddle Book是为用户和开发者制作的一个交互式的Jupyter Notebook。
如果您想要更深入了解deep learning,PaddlePaddle Book一定是您最好的选择。
大家可以通过它阅读教程,或者制作和分享带有代码、公式、图表、文字的交互式文档。
我们提供可以直接运行PaddlePaddle Book的Docker镜像,直接运行:
.. code-block:: bash
docker run -p 8888:8888 paddlepaddle/book
国内用户可以使用下面的镜像源来加速访问:
.. code-block: bash
docker run -p 8888:8888 docker.paddlepaddlehub.com/book
然后在浏览器中输入以下网址:
.. code-block:: text
http://localhost:8888/
就这么简单,享受您的旅程!
.. _docker_run_gpu:
使用Docker执行GPU训练
""""""""""""""""""""""""""""
为了保证GPU驱动能够在镜像里面正常运行,我们推荐使用
`nvidia-docker <https://github.com/NVIDIA/nvidia-docker>`_ 来运行镜像。
请不要忘记提前在物理机上安装GPU最新驱动。
.. code-block:: bash
nvidia-docker run -it -v $PWD:/work paddlepaddle/paddle:latest-gpu /bin/bash
**注: 如果没有安装nvidia-docker,可以尝试以下的方法,将CUDA库和Linux设备挂载到Docker容器内:**
.. code-block:: bash
export CUDA_SO="$(\ls /usr/lib64/libcuda* | xargs -I{} echo '-v {}:{}') $(\ls /usr/lib64/libnvidia* | xargs -I{} echo '-v {}:{}')"
export DEVICES=$(\ls /dev/nvidia* | xargs -I{} echo '--device {}:{}')
docker run ${CUDA_SO} ${DEVICES} -it paddlepaddle/paddle:latest-gpu
**关于AVX:**
AVX是一种CPU指令集,可以加速PaddlePaddle的计算。最新的PaddlePaddle Docker镜像默认
是开启AVX编译的,所以,如果您的电脑不支持AVX,需要单独
`编译 <./build_from_source_cn.html>`_ PaddlePaddle为no-avx版本。
以下指令能检查Linux电脑是否支持AVX:
.. code-block:: bash
if cat /proc/cpuinfo | grep -i avx; then echo Yes; else echo No; fi
如果输出是No,就需要选择使用no-AVX的镜像
../../../paddle/doc/fluid/build_and_install/paddleci.png
\ No newline at end of file
../../../paddle/doc/fluid/build_and_install/pip_install_cn.rst
\ No newline at end of file
================================
PaddleFluid设计思想和基本使用概念
================================
Paddle Fluid 是用来让用户像 PyTorch 和 Tensorflow Eager Execution 一样执行程序。
在这些系统中,不再有模型这个概念,应用也不再包含一个用于描述 Operator 图或者一系列层的符号描述,
而是像通用程序那样描述训练或者预测的过程。
深度学习平台的演化
================
时至今日,深度学习已成为事实上最流行的机器学习技术。学术界多年研究加上工业界的长期实践提出了若干有效的基本建模单元:
全连接,卷积,循环神经网络等;设计各类训练技巧:初始化方法,跨层连接,各类 norm 技术等;
发明了各种新的优化算法:Adadelta,Adam 等;
各类固定的网络结构:highway, residual, attention 等纷纷涌现,不胜枚举。
学术界工业界多年的付出共同促成了深度学习方法今日的影响力。
学术研究和生产实践中积累了大量的知识,能够很好的解释神经网络中基本模块各自独的学习能力和特性。
基本模块和训练技术的组合能够搭建出千变万化的神经网络模型。
基本模块和训练技术是有限的,但他们的组合却是千变万化,这是深度学习方法的魅力所在,也是难度所在。
正是这样高度的模块化特性,研究者和工程师们都在努力避免重复造轮子以提高研究和生产的效率,
又进一步催生了深度学习平台技术的发展,深度学习框架已演变成为 AI 基础设施中重要的一部分。
从 Theano,到 DistBelief,到 TensorFlow;从 Caffe 到 Caffe2;
从 Torch 到 PyTorch;从 PaddlePaddle 到 PaddleFluid,
深度学习平台技术也经历了两代的演化,并向着第三代平台技术迈进。
站在历史发展的今天,当我们准备切换尝试使用一个新的深度学习平台作为支持自己学习和研究的工具时,
平台技术都发生了哪些演化,能够为我们的带来什么便利呢?
先让我们来看看深度学习框架解决的三大问题:
- 如何描述计算以支持未来潜在会出现的新模型?
- 如何高效利用异构设备最大化算力?
- 如何利用网络中的计算机进行分布式计算来处理千万亿级别的数据?
以上三个问题中的第一个和使用者研究者最为密切相关。
这篇文章我们通过分析 PaddleFluid的设计理念,
来了解一个深度学习框架如何抽象深度学习模型,来看看我们的使用经验如何在不同深度学习平台之间过度和迁移。
如何描述计算
=============
让我们首先来看看 PaddleFluid 如何描述机器学习模型
PaddleFluid之 :code:`Program`
如何描述计算很大程度决定了一个神经网络框架计算功能的完备性。
深度学习模型和方法历经二十多年的发展:“依次执行一组计算的前向,
再以和前向计算相反的顺序执行反向计算,中间无分支无交互”,
这样的模型结构已经无法满足研究者和千千万万框架使用者的想象力。
从 `PaddleFluid 的设计目标 <https://github.com/PaddlePaddle/Paddle/blob/develop/doc/fluid/design/motivation/fluid.md>`_ 来看,
在如何描述机器学习模型这一核心问题上,PaddleFluid 的目标是:
创造一种新的计算描述方式,不但能够描述至今为止人们已知的主流神经网络模型,并且能够支持未来会出现的任意模型。
PaddleFluid 是如何做到支持未来出现的新模型这一目标呢?PaddleFluid 的设计选择是:
对用户来说,用一段 :code:`Program` (在 PaddleFluid 内部会被转化为一种叫作 :code:`ProgramDesc` 的描述语言),
而不是用计算图来描述机器学习模型。 :code:`Program` 用符合用户使用直觉的方式,
提供一种新的描述语言能够描述任意复杂的机器学习模型。
对所有计算机专业同学学习编程语言的第一课一定是建立对“程序语言的三种执行结构:顺序执行,条件选择和循环执行”的认识。
计算机世界的所有可计算逻辑都是由这三种执行结构表示,用这三种结构描述的逻辑是可计算的。那么同样道理,
对一个神经网络框架来说,如果可以和程序语言一样提供对这三种执行结构的支持,那么将可以描述任意复杂的,
可被计算机计算的机器学习模型。PaddleFluid通过提供对这三种执行结构的支持,来做到对任意复杂模型的描述。
具体来说:
1. Fluid 的核心设计理念都可以类比到程序语言,如果已经有写程序的经验,那么使用 Fluid 构建神经网络模型的体验,将非常接近写程序;
2. 在 PaddleFluid 中,用户不会显示地感知“计算图”这样的概念,一个机器学习模型被描述为一个 Fluid :code:`Program` (Fluid 内部称之为 :code:`ProgramDesc` );
- 一个 Fluid :code:`Program` 由一组嵌套的 :code:`Block` 构成。 :code:`Block` 的概念可以类比到 C++ 或是 Java 中的一对大括号,或是 Python 语言中的一个缩进快;
- :code:`Block` 中的计算由顺序执行、条件选择或者循环执行三种方式组合,构成复杂的计算逻辑。
3. Fluid :code:`Program` 中包含对计算和计算对象的描述。计算的描述称之为 Operator;计算作用的对象(或者说 Operator 的输入和输出)被统一为 Tensor。
在描述计算和计算的作用对象这一问题上,各个深度学习框架的选择是相同的,如果有一个平台的使用经验,那么将非常容易在各个平台之间进行迁移。
核心使用概念
=============
下面,我们将更详细地了解核心使用概念在PaddlePaddle的使用方法。
数据表示和计算的对象:Tensor
--------------------------
Tensor 是向量矩阵概念的扩展,是神经网络模型计算操作的基本对象。这在是今天所有主流深度学习平台的共同选择。
可以简单地将 Tensor 理解为一个 N 维向量,它可以有任意多的维度。一个 Tensor 具有两个基本特征:
1. 数据类型:每个 Tensor 的所有元素具有同样的、已知的数据类型;
2. 大小(或者说形状):即维度的个数(rank,阶)以及各维度的长度。
Tensor 某些维度的长度在定义模型阶段可能是未知的,在实际算法执行时才能确定。例如一个 mini-batch 中包含的样本数目(batch size),或者是一个 mini-batch 中序列的最大长度。
PaddleFluid中的Tensor
""""""""""""""""""""""
PaddleFluid 中也使用 Tensor 作为神经网络中输入输出数据的统一表示。Tensor 的概念在今天主流的深度学习平台中都是完全相同,可以在各个深度学习框架之间直接无缝迁移。
在 Fluid 中也同样存在三种特殊的 Tensor:
1. 模型中的可学习参数
模型中的可学习参数生存期和整个训练任务一样长,会接受优化算法的更新。在 PaddleFluid 中同样以 :code:`Variable` 表示;
用户在绝大多数情况下都不需要自己来创建网络中的可学习参数,Fluid 为几乎常见的神经网络基本计算模块都提供了封装。
以最简单的全连接模型为例,下面的代码片段会直接为全连接层创建连接权值 WW 和偏置( :code:`bias` )两个可学习参数,
无需显示地调用 variable 相关接口创建可学习参数。
::
import paddle.fluid as fluid
y = fluid.layers.fc(input=x, size=128, bias_attr=True)
2. 输入输出Tensor
整个神经网络的输入数据也是一个特殊的 Tensor,在这个 Tensor 中,
一些维度的大小在定义模型时无法确定(通常包括:batch size;
如果 mini-batch 之间,数据可变,也会包括序列的最大长度,图片的宽度和高度等),在定义模型时需要占位;
PaddleFluid 中使用 :code:`fluid.layers.data` 来接入输入数据, :code:`fluid.layer.data` 需要提供输入 Tensor 的 形状信息,
当遇到无法确定的维度 时, 相应维度指定为 None ,如下面的代码片段所示:
::
import paddle.fluid as fluid
x = fluid.layers.data(name="x", shape=[2, None, 3], dtype="int64")
3. 常量 Tensor 在 PaddleFluid 中需要通过组合 Tensor 和 :code:`fluid.layers.assign` 来实现。
计算原语:Operation/Operator
----------------------------
Tensor 是今天所有主流深度学习框架的统一数据表示(输入、输出、中间计算结果、模型的可学习参数都是 Tensor)。
另一方面,对数据的操作,在主流深度学习框架中也高度统一为:Operator/Operation。
在中文中,通常我们会习惯将其称之为算子。
注:在 PaddleFluid 中使用 Operator 称呼对 Tensor 的操作。
Operation/Operator 接受多个 Tensor 作为输入,输出若干个 Tensor,表示了从输入到输出的变化。
PaddleFluid中的Operator
""""""""""""""""""""""""
PaddleFluid 支持的所有算子,可以在 `API 帮助文档 <http://www.paddlepaddle.org/docs/develop/api/en/fluid/layers.html>`_ 中查看。
为了便于用户使用,在 Python 端,Fluid 中的 Operator 被进一步封装入 :code:`paddle.fluid.layers` ,
:code:`paddle.fluid.networks` 等模块。这是因为:一些常见的对Tensor的操作可能是有更多基础操作构成,
例如:l2 norm 内部由 reduce、elementwise_add,scale 等多个 Operator 组合计算逻辑完成,
为了提高使用的便利性,框架内部对基础 Operator 进行了一些封装,包括创建 Operator 依赖可学习参数,
可学习参数的初始化细节等,减少用户重复开发的成本。
对所有深度学习框架都面临同样的封装,在绝大多数情况下,用户很少会直接与框架底层的 Operator 直接打交道,而是使用框架提供的 layers,networks 等模块,降低开发的代码量。不论是什么样的概念,他们在各框架之间的本质和作用都是相同的:对 Tensor 的变换。
总结
>>>>>>
不论叫作 Operation、Operator 还是 layers,他们在各深度学习平台中的含义和作用都是相同的:对 Tensor 的变换。是一个深度学习平台提供的基础计算能力。可以在每个平台各自的 API 帮助文档中查到。
在各个深度学习平台都已加入 ONNX 项目的今天,每个深度学习平台提供给大家的基本算子都已趋同,与此同时,每个平台也各有其特点,会提供一些独特的算子,方便某一类任务的开发。
构建模型并执行
--------------
整个训练任务运行方法如下:
Fluid中的Program和Executor
"""""""""""""""""""""""""""
1. Fluid 使用 :code:`Program` 描述神经网络模型,对用户来说,并没有计算图的概念。
用户定义的所有 Tensor 以及对 Tensor 的操作:Operator 都会被加入一段 :code:`Program` 中;
一段 Program 由嵌套的 :code:`Block` 构成,但用户无需显示地创建 :code:`Block` 或是显示地注意到 :code:`Block` 的存在;
在 Fluid 程序中, :code:`Block` 是在调用 :code:`while_op` , :code:`if_op` , :code:`parallel_do` 等特殊 :code:`Operator` 时,由这些 :code:`Operator` 来创建;
对用户使用来说,只需要知道自己正在向一段 Fluid Program 中添加变量( :code:`Tensor` )和操作( :code:`Operator` )即可。
2. Fluid 利用 :code:`Executor` 来执行一段 Fluid :code:`Program` 。
为进一步理解 Fluid 中 :code:`Executor` 的作用,需要先解释一下 Fluid 程序的执行流程。 下图展示单机上,Fluid 程序的执行流程:
.. figure:: fluid_local_train.jpeg
:scale: 50%
:align: center
Figure.1
Fluid本地训练任务执行流程图
1. Fluid 设计思想和灵感非常类似于程序设计语言,和高级编译语言 C++/Java 编写程序的过程非常类似,Fluid 程序执行分为两个重要阶段:编译时和运行时;
2. 编译期,用户通过调用 Fluid 提供的算子,向一段 :code:`Program` 中添加变量(Tensor)以及对变量的操作(Operators 或者 Layers)。用户只需要描述核心的前向计算,不需要关心反向计算,分布式下,异构设备下如何计算;
3. 原始的 :code:`Program` 在平台内部转换为中间描述语言: :code:`ProgramDesc` ;
4. 编译期最重要的一个功能模块是 Transpiler。Transpiler 接受一段 :code:`ProgramDesc` ,输出一段变化后的 :code:`ProgramDesc` ,作为后端 Executor 最终需要执行的 :code:`Fluid Program` ;
最为常用的 Transipler 包括:
1. 内存优化 Transipler:通过对变量读写依赖关系分析,插入内存回收 Operator 以维持运行过程中较小的内存开销;
2. 分布式环境下的 Transpiler:接受用户定义的 local Program ,生成 Parameter Client 和 Parameter Server 执行的两段 :code:`Program` 。
3. 后端 Executor 接受 Transpiler 输出的这段 :code:`Program` ,依次执行其中的 Operator(可以类比为程序语言中的指令),在执行过程中会为 Operator 创建所需的输入输出并进行管理。
从上面的过程中可以看到,Fluid 程序的执行过程分为:编译器的定义 :code:`Program` ,和创建 :code:`Executor` 运行 :code:`Program` 。
:code:`Executor` 执行一段 :code:`Program` 的过程是不可交互和不可中断的。
在 Fluid 中,可以创建多余一段 :code:`Program` 。默认情况,一个 PaddleFluid 程序中存在 2 段 Program:
1. :code:`fluid.framework.default_startup_program` :其中定义了创建模型参数,输入输出,以及模型中可学习参数的初始化等各种操作;
- :code:`default_startup_program` 可以由框架自动生成,使用时无需显示地创建;
- 如果调用修改了参数的默认初始化方式,框架会自动的将相关的修改加入 :code:`default_startup_program` 。
2. :code:`fluid.framework.default_main_program` :定义了神经网络模型,前向反向计算,以及优化算法对网络中可学习参数的更新;
- 使用 Fluid 的核心就是构建起 :code:`default_main_program` 。
3. PaddleFluid 中的 :code:`Scope` 类似于 TensorFlow 中的 collection 这一概念,但在 Fluid 中 :code:`Scope` 是框架后端概念,用户无法直接操作。因此,在使用框架时无需关心。
总结
"""""
Fluid 中通过 Executor 来执行一段用户定义的 Fluid :code:`Program` 。
1. Executor 连接了 Fluid 的前端和后端;
2. Executor 接受用户定义的原始模型(一段 :code:`Program` ),通过调用系统中不同功能更的 :code:`Transpiler` 完成对原始 :code:`Program` 的变化,进行优化。
完整实例:如何完成一个机器学习模型的训练
===================================
这一节,我们以 MNIST 手写数字识别问题 —— 机器学习任务的“Hello World”问题和数据,为例,通过一个可以运行的完整实例,来学习上文介绍的概念如何在PaddleFluid 平台使用。
步骤1:定义数据
----------------
PaddleFluid 中以 :code:`fluid.layers.data` 来接收输入数据。
::
import numpy as np
import paddle.fluid as fluid
import paddle.v2 as paddle
# define the input layers for the network.
x = fluid.layers.data(name="img", shape=[1, 28, 28], dtype="float32")
y_ = fluid.layers.data(name="label", shape=[1], dtype="int64")
Fluid 中 Tensor 的第 0 维度固定为 batch size。在上面代码段中,图像输入 :code:`x` 的形状为:[1, 28, 28]。这三个维度的含义分别是:channel 数目,图像的高度和宽度。
实际上 Fluid 框架内部,一幅图像输入是一个 4-D Tensor,所有 Tensor 的第 0 维固定为 batch size。框架内部会自动为batch size进行填充占位。无需对batch size指定填充占位。
如果除去 batch size(第 0 维度)外,如果 Tensor 某一维度的大小只能在运行时确定,可以在该位置上直接指定 :code:`None` 进行占位。
步骤2:定义模型
--------------
通过调用 Fluid 提供的算子定义含有一个隐层的神经网络。Fluid 模型的分为模型结构和优化方法两部分。这一点与 TensorFlow 程序十分相似似,使用概念可以直接对应进行迁移。
::
# define the network topology.
y = fluid.layers.fc(input=x, size=10, act="softmax")
loss = fluid.layers.cross_entropy(input=y, label=y_)
avg_loss = fluid.layers.mean(loss)
# define the optimization algorithm.
optimizer = fluid.optimizer.Adam(learning_rate=1e-3)
optimizer.minimize(avg_loss)
Fluid 使用 Program 而不是计算图描述模型,一般情况下,用户无需关心 Program 的细节,当调用以上 layers 时,会向一个全局的 Program: :code:`fluid.framework.default_main_program` 中插入变量(Tensor)和对变量的操作(上述代码段中的 layers 和 optimzier)。
步骤3:参数初始化
----------------
如上文介绍,Fluid 程序中的 Executor 是连接 Fluid 前端和后端的接口。
默认一个Fluid模型存在至少两段 Program。用于初始化网络中的可学习参数的那一段 :code:`Program` 叫作 :code:`fluid.default_startup_program()` 。
只有执行器 executor 可以执行 Fluid Program,因此,在初始化网络中的可学习参数之前,需要首先创建一个 Fluid executor。
::
# define the executor.
place = fluid.CPUPlace()
exe = fluid.Executor(place)
exe.run(fluid.default_startup_program())
在以上代码段中, :code:`place` 用于告诉 executor 一段 Fluid Program 在何种设备上执行,
常见的有 :code:`fluid.CPUPlace()` 和 :code:`fluid.CUDAPlace()` 。
步骤4:数据输入 + 执行模型训练
----------------------------
我们在步骤 2 中定义的神经网络模型最终被插入一段叫做 :code:`fluid.framework.default_main_program` 的 Fluid Program 中。
网络可学习参数初始化之后,可以通过让执行器 Executor 执行这段 :code:`fluid.framework.default_main_program` 来进行训练。
::
train_reader = paddle.batch(
paddle.reader.shuffle(paddle.dataset.mnist.train(), buf_size=5000),
batch_size=BATCH_SIZE)
feeder = fluid.DataFeeder(place=place, feed_list=[x, y_])
for pass_id in range(100):
for batch_id, data in enumerate(train_reader()):
loss = exe.run(
fluid.framework.default_main_program(),
feed=feeder.feed(data),
fetch_list=[avg_loss])
print("Cur Cost : %f" % (np.array(loss[0])[0]))
从上面的代码片段中可以看到,Fluid 程序的训练过程和 TensorFlow 程序的训练过程非常接近,
都放在一个 :code:`for` 循环中,循环读取一个 mini-batch 数据,
调用执行器执行 Fluid :code:`default_main_program` :接收 mini-batch 输入,在其上进行前向,反向和参数更新计算。
`注:上面程序使用了 Fluid 内置的 MNIST 数据,和我们提供给 TensorFlow 示例程序的 MNIST 数据完全一样。`
步骤5:观察模型效果
-----------------
以上步骤已经构成了完整的 Tensorflow 模型训练程序,每个 batch 观察一次 loss,可以直观看到模型的迭代效果:
.. figure:: fluid_mnist.png
:scale: 40%
:align: center
Figure.2
Fluid MNIST手写数字识别任务代价下降曲线
附:完整代码
------------
::
import numpy as np
import paddle.fluid as fluid
import paddle.v2 as paddle
def main():
BATCH_SIZE = 128
# define the input layers for the network.
x = fluid.layers.data(name="img", shape=[1, 28, 28], dtype="float32")
y_ = fluid.layers.data(name="label", shape=[1], dtype="int64")
# define the network topology.
y = fluid.layers.fc(input=x, size=10, act="softmax")
loss = fluid.layers.cross_entropy(input=y, label=y_)
avg_loss = fluid.layers.mean(loss)
optimizer = fluid.optimizer.Adam(learning_rate=5e-3)
optimizer.minimize(avg_loss)
# define the executor.
place = fluid.CPUPlace()
exe = fluid.Executor(place)
exe.run(fluid.default_startup_program())
train_reader = paddle.batch(
paddle.reader.shuffle(paddle.dataset.mnist.train(), buf_size=5000),
batch_size=BATCH_SIZE)
feeder = fluid.DataFeeder(place=place, feed_list=[x, y_])
for pass_id in range(100):
for batch_id, data in enumerate(train_reader()):
loss = exe.run(
fluid.framework.default_main_program(),
feed=feeder.feed(data),
fetch_list=[avg_loss])
print("Cur Cost : %f" % (np.array(loss[0])[0]))
if __name__ == "__main__":
main()
.. _user_guide_configure_simple_model:
##############
配置简单的网络
##############
在解决实际问题时,可以先从逻辑层面对问题进行建模,明确模型所需要的 **输入数据类型**、**计算逻辑**、**求解目标** 以及 **优化算法**。PaddlePaddle提供了丰富的算子来实现模型逻辑。下面以一个简单回归任务举例说明如何使用PaddlePaddle构建模型。该例子完整代码参见 `fit_a_line <https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/fluid/tests/book/test_fit_a_line.py>`_。
问题描述及定义
##############
问题描述: 给定一组数据 :math:`<X, Y>`,求解出函数 :math:`f`,使得 :math:`y=f(x)`,其中 :math:`x\subset X` 表示一条样本的特征,为 :math:`13` 维的实数向量;:math:`y \subset Y` 为一实数表示该样本对应的值。
我们可以尝试用回归模型来对问题建模,回归问题的损失函数有很多,这里选择常用的均方误差。为简化问题,这里假定 :math:`f` 为简单的线性变换函数,同时选用随机梯度下降算法来求解模型。
+----------------+----------------------------------------------+
| 输入数据类型 | 样本特征: 13 维 实数 |
+ +----------------------------------------------+
| | 样本标签: 1 维 实数 |
+----------------+----------------------------------------------+
| 计算逻辑 | 使用线性模型,产生 1维实数作为模型的预测输出 |
+----------------+----------------------------------------------+
| 求解目标 | 最小化模型预测输出与样本标签间的均方误差 |
+----------------+----------------------------------------------+
| 优化算法 | 随机梯度下降 |
+----------------+----------------------------------------------+
使用PaddlePadle建模
###################
从逻辑层面明确了输入数据格式、模型结构、损失函数以及优化算法后,需要使用PaddlePaddle提供的API及算子来实现模型逻辑。一个典型的模型主要包含4个部分,分别是:输入数据格式定义,模型前向计算逻辑,损失函数以及优化算法。
数据层
------
PaddlePaddle提供了 :ref:`api_fluid_layers_data` 算子来描述输入数据的格式。
:ref:`api_fluid_layers_data` 算子的输出是一个Variable。这个Variable的实际类型是Tensor。Tensor具有强大的表征能力,可以表示多维数据。为了精确描述数据结构,通常需要指定数据shape以及数值类型type。其中shape为一个整数向量,type可以是一个字符串类型。目前支持的数据类型参考 :ref:`user_guide_paddle_support_data_types` 。 模型训练一般会使用batch的方式读取数据,而batch的size在训练过程中可能不固定。data算子会依据实际数据来推断batch size,所以这里提供shape时不用关心batch size,只需关心一条样本的shape即可,更高级用法请参考 :ref:`user_guide_customize_batch_size_rank`。从上知,:math:`x` 为 :math:`13` 维的实数向量,:math:`y` 为实数,可使用下面代码定义数据层:
.. code-block:: python
x = fluid.layers.data(name='x', shape=[13], dtype='float32')
y = fluid.layers.data(name='y', shape=[1], dtype='float32')
该模型使用的数据比较简单,事实上data算子还可以描述变长的、嵌套的序列数据。也可以使用 :code:`open_files` 打开文件进行训练。更详细的文档可参照 :ref:`user_guide_prepare_data`。
前向计算逻辑
------------
实现一个模型最重要的部分是实现计算逻辑,PaddlePaddle提供了丰富的算子。这些算子的封装粒度不同,通常对应一种或一组变换逻辑。算子输出即为对输入数据执行变换后的结果。用户可以灵活使用算子来完成复杂的模型逻辑。比如图像相关任务中会使用较多的卷积算子、序列任务中会使用LSTM/GRU等算子。复杂模型通常会组合多种算子,以完成复杂的变换。PaddlePaddle提供了非常自然的方式来组合算子,一般地可以使用下面的方式:
.. code-block:: python
op_1_out = fluid.layers.op_1(input=op_1_in, ...)
op_2_out = fluid.layers.op_2(input=op_1_out, ...)
...
其中op_1和op_2表示算子类型,可以是fc来执行线性变换(全连接),也可以是conv来执行卷积变换等。通过算子的输入输出的连接来定义算子的计算顺序以及数据流方向。上面的例子中,op_1的输出是op_2的输入,那么在执行计算时,会先计算op_1,然后计算op_2。更复杂的模型可能需要使用控制流算子,依据输入数据来动态执行,针对这种情况,PaddlePaddle提供了IfElseOp和WhileOp等。算子的文档可参考 :ref:`api_fluid_layers`。具体到这个任务, 我们使用一个fc算子:
.. code-block:: python
y_predict = fluid.layers.fc(input=x, size=1, act=None)
损失函数
--------
损失函数对应求解目标,我们可以通过最小化损失来求解模型。大多数模型使用的损失函数,输出是一个实数值。但是PaddlePaddle提供的损失算子一般是针对一条样本计算。当输入一个batch的数据时,损失算子的输出有多个值,每个值对应一条样本的损失,所以通常会在损失算子后面使用mean等算子,来对损失做归约。模型在一次前向迭代后会得到一个损失值,PaddlePaddle会自动执行链式求导法则计算模型里面每个参数和变量对应的梯度值。这里使用均方误差损失:
.. code-block:: python
cost = fluid.layers.square_error_cost(input=y_predict, label=y)
avg_cost = fluid.layers.mean(cost)
优化方法
--------
确定损失函数后,可以通过前向计算得到损失值,然后通过链式求导法则得到参数的梯度值。获取梯度值后需要更新参数,最简单的算法是随机梯度下降法::math:`w=w - \eta \cdot g`。但是普通的随机梯度下降算法存在一些问题: 比如收敛不稳定等。为了改善模型的训练速度以及效果,学术界先后提出了很多优化算法,包括: :code:`Momentum`、:code:`RMSProp`、:code:`Adam` 等。这些优化算法采用不同的策略来更新模型参数,一般可以针对具体任务和具体模型来选择优化算法。不管使用何种优化算法,学习率一般是一个需要指定的比较重要的超参数,需要通过实验仔细调整。这里采用随机梯度下降算法:
.. code-block:: python
sgd_optimizer = fluid.optimizer.SGD(learning_rate=0.001)
更多优化算子可以参考 :ref:`api_fluid_optimizer` 。
下一步做什么?
##############
使用PaddlePaddle实现模型时需要关注 **数据层**、**前向计算逻辑**、**损失函数** 和 **优化方法**。不同的任务需要的数据格式不同,涉及的计算逻辑不同,损失函数不同,优化方法也不同。PaddlePaddle提供了丰富的模型示例,可以以这些示例为参考来构建自己的模型结构。用户可以访问 `模型库 <https://github.com/PaddlePaddle/models/tree/develop/fluid>`_ 查看官方提供的示例。
......@@ -2,8 +2,11 @@
如何使用PaddlePaddle
####################
.. toctree::
:maxdepth: 2
prepare_data/index
\ No newline at end of file
prepare_data/index
configure_simple_model/index
training/index
......@@ -36,7 +36,10 @@ PaddlePaddle Fluid支持使用 :ref:`api_fluid_layers_data` 配置数据层;
维度的位置的话,请设置 :code:`fluid.layers.data(append_batch_size=False)` 。
请参考进阶使用中的 :ref:`user_guide_customize_batch_size_rank` 。
2. Fluid中用来做类别标签的数据类型是 :code:`int64`,并且标签从0开始。
2. Fluid中用来做类别标签的数据类型是 :code:`int64`,并且标签从0开始。可用数据类型请参考 :ref:`user_guide_paddle_support_data_types`。
.. _user_guide_feed_data_to_executor:
传递训练数据给执行器
####################
......@@ -145,4 +148,22 @@ PaddlePaddle Fluid默认batch size是数据的第一维度,以 :code:`-1` 表
dtype="int64")
这里 :code:`sentence` 的中间维度是batch size。这种数据排布会用在定长的循环神经
网络中。
\ No newline at end of file
网络中。
.. _user_guide_paddle_support_data_types:
Fluid目前支持的数据类型
-----------------------
PaddlePaddle Fluid目前支持的数据类型包括:
* float16: 部分操作支持
* float32: 主要实数类型
* float64: 次要实数类型,支持大部分操作
* int32: 次要标签类型
* int64: 主要标签类型
* uint64: 次要标签类型
* bool: 控制流数据类型
* int16: 次要标签类型
* uint8: 输入数据类型,可用于图像像素
\ No newline at end of file
# Checkpoint功能使用指南
## 背景
单机/多机在训练过程中会由于软件/硬件的问题出现异常,导致训练中断,进而导致训练无结果或结果不可用,浪费大量时间和机器性能。
## 目的
Checkpoint功能能够在训练中途对训练数据中间数据进行保存,出现异常恢复训练的时候能够加载中途保存的数据继续训练, 实现单机/多机的容错训练的功能。
## 说明
### 目前已实现的参数保存:
1. 基于Trainer 0 实现训练过程中的参数保存
2. 基于PServer 实现了```Distribute Lookup Table```相关参数保存
### Fluid Checkpoint 保存数据目录结构:
```
checkpoint_dir (用户定义的checkpoint目录)
├── checkpoint_0 (第一次保存)
│ ├── __lockup_table__ (Distribute Lookup Table 目录)
│ │ ├── table_pserver_0 (Pserver 0 号保存的lookup table 数据)
│ │ └── table_pserver_1
│ ├── __model__ (model 目录)
│ │ └── var.w_1
│ └── trainer_0 (trainer 自有数据保存)
│ ├── epoch_id
│ └── step_id
└── checkpoint_1 (第二次保存)
```
## 使用方法
### 声明Fluid.CheckpointConfig
用户对checkpoint功能的配置,主要是配置对象```Fluid```中的```CheckpointConfig```.
```CheckpointConfig``` 包括4个参数:
| 参数 | 类型 | 说明 |
| - | :-: | - |
| checkpoint_dir | int| checkpoint存储目录 |
| max_num_checkpoints | int | 最大保存的checkpoint副本数 |
| epoch_interval | int | 每隔epoch_interval轮epoch |
| step_interval | int | 每隔step_interval轮step |
### 在Fluid.Trainer对象的声明中加入Fluid.CheckpointConfig的声明
Trainer的__init__方法的参数中包含了对```CheckpointConfig```, 需要传入在声明Trainer前声明的```CheckpointConfig```对象。
如:
```python
config = CheckpointConfig(
checkpoint_dir = "/tmp/ckpt", max_num_checkpoints = 2,
epoch_interval = 2, step_interval = 10)
trainer = Trainer(..., checkpoint_config=config)
```
定义和声明完成后, 训练在运行过程中就会在指定的step和epoch处进行保存,出现异常时,就会自动从最新的checkpoint目录进行参数恢复啦!
## 相关API
[Trainer API 说明](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/fluid/trainer.py)
## 注意
1. 保证每个训练的```checkpoint_dir``` 与其他训练独立。
2. 最大副本数量```max_num_checkpoints```需要根据磁盘容量以及模型的大小进行调整, 保证磁盘的可用性。
3. ```epoch_interval``` 和 ```step_interval``` 不宜过小, 频繁的进行checkpoint会拖慢训练速度。
4. **分布式训练**的过程中:每个Trainer都会在```checkpoint_dir```目录中保存当前Trainer的参数(只有Trainer 0会保存模型的参数),需要**分布式文件系统(HDFS等)**将同```checkpoint_dir```目录的数据进行合并才能得到完整的数据,恢复训练的时候需要用完整的数据进行恢复。
\ No newline at end of file
# Checkpoint User Guide
## Background
In many cases, Stand-alone training and Distributed training can be aborted by the software problem or hardware problem. More seriously, we waste so much time and the performance of the machine but get nothing, which makes us frustrating and we have to restart it again.
## Purpose
The feature of ```Checkpoint``` can save Intermediate model variables, lookup table variable, and other needs data in checkpoint directory. When the exception occurs, we can load these variables from the checkpoint directory immediately.
## Introduce
### Complete Features Currently:
1. The Trainer 0 will save model variables in training.
2. Each of the Trainer will save its own arguments needed.
3. Each of the Parameter Server will save ```Distribute Lookup Table``` variables in training.
### Fluid Checkpoint directory structure:
```
checkpoint_dir (the checkpoint directory user define)
├── checkpoint_0 (the first save directory)
│ ├── __lockup_table__ (Distribute Lookup Table directory)
│ │ ├── table_pserver_0 (Lookup table's data about Pserver 0)
│ │ └── table_pserver_1
│ ├── __model__ (model directory)
│ │ └── var.w_1
│ └── trainer_0 (each trainer will save its own data)
│ ├── epoch_id
│ └── step_id
└── checkpoint_1 (the second save directory)
```
## usage
### Fluid.CheckpointConfig construct
When the user wants to use ```Checkpoint``` feature, the main thing user have to do is declare ```CheckpointConfig``` and construct it.
```CheckpointConfig``` has 4 member variables need to be initialized:
| Member Variable | Type | Comment |
| - | :-: | - |
| checkpoint_dir | int| checkpoint directory |
| max_num_checkpoints | int | Maximum number of checkpoint copies |
| epoch_interval | int | epoch interval times |
| step_interval | int | step interval times |
### Add Fluid.CheckpointConfig's declaration in Fluid.Trainer
Because the initialization of Trainer needs an instance of ```CheckpointConfig```., we should declare ```CheckpointConfig``` in ```Fluid``` first.
For example:
```python
config = CheckpointConfig(
checkpoint_dir = "/tmp/ckpt", max_num_checkpoints = 2,
epoch_interval = 2, step_interval = 10)
trainer = Trainer(..., checkpoint_config=config)
```
After all the things done, the train will save checkpoint at the specified epoch and step, when the train is aborted, the user can restart it, the train will restore from the latest copy.
## Related API
[Related Trainer API](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/fluid/trainer.py)
## Attention
1. Make the ```checkpoint_dir``` only be used by one train job.
2. The number of ```max_num_checkpoints``` need to be adjusted by the disk size and model size.
3. Too frequently to slow down the train speed, so too ```small epoch_interval``` and ```step_interval``` are not suitable.
4. **In distributed train**, each Trainer will save arguments in its ```checkpoint_dir``` (Only Trainer 0 will save model variables). We need **distributed file system (HDFS, etc)** to merge all the ```checkpoint_dir``` to get the whole data.
\ No newline at end of file
.. _cluster_howto
Fluid分布式训练使用手册
====================
分布式训练基本思想
---------------
分布式深度学习训练通常分为两种并行化方法:数据并行,模型并行,参考下图:
.. image:: src/parallelism.png
在模型并行方式下,模型的层和参数将被分布在多个节点上,模型在一个mini-batch的前向和反向训练中,将经过多次跨\
节点之间的通信。每个节点只保存整个模型的一部分;在数据并行方式下,每个节点保存有完整的模型的层和参数,每个节点\
独自完成前向和反向计算,然后完成梯度的聚合并同步的更新所有节点上的参数。Fluid目前版本仅提供数据并行方式,另外\
诸如模型并行的特例实现(超大稀疏模型训练)功能将在后续的文档中予以说明。
在数据并行模式的训练中,Fluid使用了两种通信模式,用于应对不同训练任务对分布式训练的要求,分别为RPC通信和Collective
通信。其中RPC通信方式使用 `gRPC <https://github.com/grpc/grpc/>`_ ,Collective通信方式使用
`NCCL2 <https://developer.nvidia.com/nccl>`_ 。
.. csv-table:: 下面是一个RPC通信和Collective通信的横向对比:
:header: "Feature", "Coolective", "RPC"
"Ring-Based通信", "Yes", "No"
"异步训练", "Yes", "Yes"
"分布式模型", "No", "Yes"
"容错训练", "No", "Yes"
"性能", "Faster", "Fast"
- RPC通信方式的结构:
.. image:: src/dist_train_pserver.png
使用RPC通信方式的数据并行分布式训练,会启动多个pserver进程和多个trainer进程,每个pserver进程\
会保存一部分模型参数,并负责接收从trainer发送的梯度并更新这些模型参数;每个trainer进程会保存一份\
完整的模型,并使用一部分数据进行训练,然后向pserver发送梯度,最后从pserver拉取更新后的参数。
pserver进程可以在和trainer完全不同的计算节点上,也可以和trainer公用节点。一个分布式任务所需要的\
pserver进程个数通常需要根据实际情况调整,已达到最佳的性能,然而通常来说pserver的进程不会比trainer\
更多。
在使用GPU训练时,pserver可以选择使用GPU或只使用CPU,如果pserver也使用GPU,则会增加一次从CPU拷贝\
接收到的梯度数据到GPU的开销,在某些情况下会导致整体训练性能降低。
- NCCL2通信方式的结构:
.. image:: src/dist_train_nccl2.png
使用NCCL2(Collective通信方式)进行分布式训练,是不需要启动pserver进程的,每个trainer进程都保存\
一份完整的模型参数,在完成计算梯度之后通过trainer之间的相互通信,Reduce梯度数据到所有节点的所有设备\
然后每个节点在各自完成参数更新。
使用parameter server方式的训练
------------------------------
使用 :code:`trainer` API,程序可以自动的通过识别环境变量决定是否已分布式方式执行。
.. csv-table:: 需要在您的分布式环境中配置的环境变量包括:
:header: "环境变量", "说明"
"PADDLE_TRAINING_ROLE", "当前进程的角色,可以是PSERVER或TRAINER"
"PADDLE_PSERVER_PORT", "parameter使用的端口"
"PADDLE_PSERVER_IPS", "parameter server的IP地址列表,用逗号分开"
"PADDLE_TRAINERS", "分布式任务中trainer节点的个数"
"PADDLE_CURRENT_IP", "当前节点的IP"
"PADDLE_TRAINER_ID", "trainer节点的id,从0~n-1,不能有重复"
使用更加底层的 :code:`transpiler` API可以提供自定义的分布式训练的方法,比如可以在同一台机器上,
启动多个pserver和trainer进行训练,使用底层API的方法可以参考下面的样例代码:
.. code-block:: python
role = "PSERVER"
trainer_id = 0
pserver_endpoints = "127.0.0.1:6170,127.0.0.1:6171"
current_endpoint = "127.0.0.1:6170"
trainers = 4
t = fluid.DistributeTranspiler()
t.transpile(trainer_id, pservers=pserver_endpoints, trainers=trainers)
if role == "PSERVER":
pserver_prog = t.get_pserver_program(current_endpoint)
pserver_startup = t.get_startup_program(current_endpoint,
pserver_prog)
exe.run(pserver_startup)
exe.run(pserver_prog)
elif role == "TRAINER":
train_loop(t.get_trainer_program())
选择同步或异步训练
++++++++++++++++++
Fluid分布式任务可以支持同步训练或异步训练,在同步训练方式下,所有的trainer节点,会在每个mini-batch
同步地合并所有节点的梯度数据并发送给parameter server完成更新,在异步训练方式下,每个trainer没有相互\
同步等待的过程,可以独立的parameter server的参数。通常情况下,使用异步训练方式,可以在trainer节点\
更多的时候比同步训练方式有更高的总体吞吐量。
在调用 :code:`transpile` 函数时,默认会生成同步训练的分布式程序,通过指定 :code:`sync_mode=False`
参数即可生成异步训练的程序:
.. code-block:: python
t.transpile(trainer_id, pservers=pserver_endpoints, trainers=trainers, sync_mode=False)
选择参数分布方法
++++++++++++++++
参数 :code:`split_method` 可以指定参数在parameter server上的分布方式。
Fluid默认使用 `RoundRobin <https://en.wikipedia.org/wiki/Round-robin_scheduling>`_
方式将参数分布在多个parameter server上。此方式在默认未关闭参数切分的情况下,参数会较平均的分布在所有的
parameter server上。如果需要使用其他,可以传入其他的方法,目前可选的方法有: :code:`RoundRobin` 和
:code:`HashName` 。也可以使用自定义的分布方式,只需要参考
`这里 <https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/fluid/transpiler/ps_dispatcher.py#L44>`_
编写自定义的分布函数。
关闭切分参数
++++++++++++
参数 :code:`slice_var_up` 指定是否将较大(大于8192个元素)的参数切分到多个parameter server已均衡计算负载,默认为开启。
当模型中的可训练参数体积比较均匀或者使用自定义的参数分布方法是参数均匀分布在多个parameter server上,
可以选择关闭切分参数,这样可以降低切分和重组带来的计算和拷贝开销:
.. code-block:: python
t.transpile(trainer_id, pservers=pserver_endpoints, trainers=trainers, slice_var_up=False)
使用NCCL2通信方式的训练
--------------------
注NCCL2模式目前仅支持trainer API,NCCL2方式并没有很多可选项,也没有"transpiler",所以并没有底层API。
使用NCCL2方式同样需要配置每个节点的环境变量,此处与parameter server模式有所不同,并不需要启动独立的\
parameter server的进程,只需要启动多个trainer进程即可。
.. csv-table:: NCCL2模式环境变量说明:
:header: "环境变量", "说明"
"PADDLE_TRAINER_IPS", "所有Trainer节点的IP列表,用逗号分隔"
"PADDLE_TRAINER_ID", "trainer节点的id,从0~n-1,不能有重复"
"PADDLE_PSERVER_PORT", "一个端口,用于在NCCL2初始化时,广播NCCL ID"
"PADDLE_CURRENT_IP", "当前节点的IP"
目前使用NCCL2进行分布式训练仅支持同步训练方式。使用NCCL2方式的分布式训练,更适合模型体积较大,并需要使用\
同步训练和GPU训练,如果硬件设备支持RDMA和GPU Direct,可以达到很高的分布式训练性能。
注意如果系统中有多个网络设备,需要手动指定NCCL2使用的设备,
假设需要使用 :code:`eth2` 为通信设备,需要设定如下环境变量:
.. code-block:: bash
export NCCL_SOCKET_IFNAME=eth2
另外NCCL2提供了其他的开关环境变量,比如指定是否开启GPU Direct,是否使用RDMA等,详情可以参考
`ncclknobs <https://docs.nvidia.com/deeplearning/sdk/nccl-developer-guide/index.html#ncclknobs>`_ 。
.. _cluster_quick_start:
分布式训练快速开始
==================
准备工作
--------
在本篇文章中,我们将会在介绍如何快速在一个集群中启动一个 PaddlePaddle
的分布式训练任务,在开始之前,请按如下步骤做些准备工作:
1. 准备一个至少4个节点的集群,并且保证网络可以联通,在本文中我们使用
``*.paddlepaddle.com`` 来表示每个节点的主机名称,您可以根据集群的实际情况来修改它。
2. 在开始之前确保已经阅读过 :ref:`how_to_install`
并且可以在集群的所有节点上可以正常运行 PaddlePaddle。
启动集群训练任务
----------------
在启动集群训练脚本时,需要在不同的节点上指定不同的环境变量,具体如下:
+-----------------+-----------------+-----------------+---------------------+
| 环境变量 | 数据类型 | 样例 | 描述 |
+=================+=================+=================+=====================+
| PADDLE_TRAINING | str | PSERVER,TRAINER | 训练节点的角色 |
| _ROLE | | | |
+-----------------+-----------------+-----------------+---------------------+
| PADDLE_PSERVER_ | str | ps0.paddlepaddl | 所有 pserver |
| IPS | | e.com,ps1.paddl | 节点的 IP |
| | | epaddle.com… | 地址或 |
| | | | hostname, |
| | | | 用“,”分隔 |
+-----------------+-----------------+-----------------+---------------------+
| PADDLE_PSERVER_ | int | 6174 | pserver |
| PORT | | | 节点监听的端口 |
+-----------------+-----------------+-----------------+---------------------+
| PADDLE_TRAINERS | int | 2 | 训练任务中 |
| | | | trainer |
| | | | 节点的数量 |
+-----------------+-----------------+-----------------+---------------------+
| PADDLE_CURRENT_ | str | ps0.paddlepaddl | 当前 pserver |
| IP | | e.com | 节点的 IP |
| | | | 地址或 hostanme |
+-----------------+-----------------+-----------------+---------------------+
| PADDLE_TRAINER_ | int | 0 | 当前 trainer |
| ID | | | 节点的唯一 ID, |
| | | | 取值范围为从0开始到 |
| | | | PADDLE_TRAINERS-1 |
+-----------------+-----------------+-----------------+---------------------+
样例代码
~~~~~~~~
将下面程序代码保存为 ``fluid_dist.py``
.. code:: python
import paddle
import paddle.fluid as fluid
import contextlib
import numpy
import unittest
# train reader
BATCH_SIZE = 20
train_reader = paddle.batch(
paddle.reader.shuffle(
paddle.dataset.uci_housing.train(), buf_size=500),
batch_size=BATCH_SIZE)
test_reader = paddle.batch(
paddle.reader.shuffle(
paddle.dataset.uci_housing.test(), buf_size=500),
batch_size=BATCH_SIZE)
def train_program():
y = fluid.layers.data(name='y', shape=[1], dtype='float32')
x = fluid.layers.data(name='x', shape=[13], dtype='float32')
y_predict = fluid.layers.fc(input=x, size=1, act=None)
loss = fluid.layers.square_error_cost(input=y_predict, label=y)
avg_loss = fluid.layers.mean(loss)
return avg_loss
def optimizer_func():
return fluid.optimizer.SGD(learning_rate=0.001)
def train(use_cuda, train_program):
place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()
trainer = fluid.Trainer(
train_func=train_program, place=place, optimizer_func=optimizer_func)
def event_handler(event):
if isinstance(event, fluid.EndStepEvent):
if event.step == 10:
test_metrics = trainer.test(
reader=test_reader, feed_order=['x', 'y'])
print("step {0}, loss: {1}".format(event.step, test_metrics))
trainer.stop()
trainer.train(
reader=train_reader,
num_epochs=100,
event_handler=event_handler,
feed_order=['x', 'y'])
train(False, train_program)
启动trainer节点和pserver节点
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. list-table::
:header-rows: 1
* - 启动节点
- 启动命令
- 说明
* - ps0.paddlepaddle.com
- :code:`PADDLE_TRAINING_ROLE=PSERVER PADDLE_CURRENT_IP=ps0.paddlepaddle.com PADDLE_PSERVER_IPS=ps0.paddlepaddle.com,ps1.paddlepaddle.com PADDLE_TRAINERS=2 PADDLE_PSERVER_PORT=6174 python fluid_dist.py`
- 启动 pserver 节点
* - ps1.paddlepaddle.com
- :code:`PADDLE_TRAINING_ROLE=PSERVER PADDLE_CURRENT_IP=ps1.paddlepaddle.com PADDLE_PSERVER_IPS=ps0.paddlepaddle.com,ps1.paddlepaddle.com PADDLE_TRAINERS=2 PADDLE_PSERVER_PORT=6174 python fluid_dist.py`
- 启动 pserver 节点
* - trainer0.paddlepaddle.com
- :code:`PADDLE_TRAINING_ROLE=TRAINER PADDLE_PSERVER_IPS=ps0.paddlepaddle.com,ps1.paddlepaddle.com PADDLE_TRAINERS=2 PADDLE_TRAINER_ID=0 PADDLE_PSERVER_PORT=6174 python fluid_dist.py`
- 启动第0号 trainer 节点
* - trainer1.paddlepaddle.com
- :code:`PADDLE_TRAINING_ROLE=TRAINER PADDLE_PSERVER_IPS=ps0.paddlepaddle.com,ps1.paddlepaddle.com PADDLE_TRAINERS=2 PADDLE_TRAINER_ID=1 PADDLE_PSERVER_PORT=6174 python fluid_dist.py`
- 启动第1号 trainer 节点
**注意**
- 需要先启动pserver节点再启动trainer节点
- 看到trainer节点输出如下日志表示训练任务执行正确
.. code:: bash
step 10, loss: [258.2326202392578]
############
训练神经网络
############
PaddlePaddle Fluid支持单机训练,和多节点训练。每种训练模式下,都支持多种训练方法。
.. toctree::
:maxdepth: 2
single_node
multi_node
save_load_variables
########
多机训练
########
.. toctree::
:maxdepth: 2
cluster_quick_start.rst
cluster_howto.rst
.. _user_guide_save_load_vars:
##################
保存与载入模型变量
##################
模型变量分类
############
在PaddlePaddle Fluid中,所有的模型变量都用 :ref:`api_fluid_Variable` 作为基类进行表示。
在该基类之下,模型变量主要可以分为以下几种类别:
1. 模型参数
模型参数是深度学习模型中被训练和学习的变量,在训练过程中,训练框架根据反向传播算法计算出每一个模型参数当前的梯度,
并用优化器根据梯度对参数进行更新。模型的训练过程本质上可以看做是模型参数不断迭代更新的过程。
在PaddlePaddle Fluid中,模型参数用 :code:`fluid.framework.Parameter` 来表示,
这是一个 :ref:`api_fluid_Variable` 的派生类,除了 :ref:`api_fluid_Variable` 具有的各项性质以外,
:code:`fluid.framework.Parameter` 还可以配置自身的初始化方法、更新率等属性。
2. 长期变量
长期变量指的是在整个训练过程中持续存在、不会因为一个迭代的结束而被销毁的变量,例如动态调节的全局学习率等。
在PaddlePaddle Fluid中,长期变量通过将 :ref:`api_fluid_Variable` 的 :code:`persistable`
属性设置为 :code:`True` 来表示。所有的模型参数都是长期变量,但并非所有的长期变量都是模型参数。
3. 临时变量
不属于上面两个类别的所有模型变量都是临时变量,这种类型的变量只在一个训练迭代中存在,在每一个迭代结束后,
所有的临时变量都会被销毁,然后在下一个迭代开始之前,又会先构造出新的临时变量供本轮迭代使用。
一般情况下模型中的大部分变量都属于这一类别,例如输入的训练数据、一个普通的layer的输出等等。
如何保存模型变量
################
根据用途的不同,我们需要保存的模型变量也是不同的。例如,如果我们只是想保存模型用来进行以后的预测,
那么只保存模型参数就够用了。但如果我们需要保存一个checkpoint以备将来恢复训练,
那么我们应该将各种长期变量都保存下来,甚至还需要记录一下当前的epoch和step的id。
因为一些模型变量虽然不是参数,但对于模型的训练依然必不可少。
因此,根据需求的不同,我们提供了两套API来分别进行模型的参数和checkpoint的保存。
保存模型用于对新样本的预测
==========================
如果我们保存模型的目的是用于对新样本的预测,那么只保存模型参数就足够了。我们可以使用
:ref:`api_fluid_io_save_params` 接口来进行模型参数的保存。
例如:
.. code-block:: python
import paddle.fluid as fluid
exe = fluid.Executor(fluid.CPUPlace())
param_path = "./my_paddle_model"
prog = fluid.default_main_program()
fluid.io.save_params(executor=exe, dirname=param_path, main_program=None)
上面的例子中,通过调用 :code:`fluid.io.save_params` 函数,PaddlePaddle Fluid会对默认
:ref:`api_fluid_Program` 也就是 :code:`prog` 中的所有模型变量进行扫描,
筛选出其中所有的模型参数,并将这些模型参数保存到指定的 :code:`param_path` 之中。
保存checkpoint用于将来恢复训练
==============================
在训练过程中,我们可能希望在一些节点上将当前的训练状态保存下来,
以便在将来需要的时候恢复训练环境继续进行训练。这一般被称作“checkpoint”。
想要保存checkpoint,可以使用 :ref:`api_fluid_io_save_checkpoint` 接口。
例如:
.. code-block:: python
import paddle.fluid as fluid
exe = fluid.Executor(fluid.CPUPlace())
path = "./checkpoints"
prog = fluid.default_main_program()
trainer_args = {"epoch_id": 200,
"step_id": 20} # just an example
fluid.io.save_checkpoint(executor=exe,
checkpoint_dir=path,
trainer_id=0,
trainer_args=trainer_args,
main_program=prog,
max_num_checkpoints=3)
上面的例子中,通过调用 :code:`fluid.io.save_checkpoint` 函数,PaddlePaddle Fluid会对默认
:ref:`api_fluid_Program` 也就是 :code:`prog` 中的所有模型变量进行扫描,
根据一系列内置的规则自动筛选出其中所有需要保存的变量,并将他们保存到指定的 :code:`path` 目录下。
:code:`fluid.io.save_checkpoint` 的各个参数中, :code:`trainer_id` 在单机情况下设置为0即可; :code:`trainer_args`
为一个Python dict,用于给定当前的epoch_id和step_id;
:code:`max_num_checkpoints` 用于表示的最大checkpoint数量,
如果目录中已经存在的checkpoint数量超过这个值,那最早的checkpoint将被删除。
如何载入模型变量
################
与模型变量的保存相对应,我们提供了两套API来分别载入模型的参数和载入模型的checkpoint。
载入模型用于对新样本的预测
==========================
对于通过 :code:`fluid.io.save_params` 保存的模型,可以使用 :code:`fluid.io.load_params`
来进行载入。
例如:
.. code-block:: python
import paddle.fluid as fluid
exe = fluid.Executor(fluid.CPUPlace())
param_path = "./my_paddle_model"
prog = fluid.default_main_program()
fluid.io.load_params(executor=exe, dirname=param_path,
main_program=prog)
上面的例子中,通过调用 :code:`fluid.io.load_params` 函数,PaddlePaddle Fluid会对
:code:`prog` 中的所有模型变量进行扫描,筛选出其中所有的模型参数,
并尝试从 :code:`param_path` 之中读取加载它们。
需要格外注意的是,这里的 :code:`prog` 必须和调用 :code:`fluid.io.save_params`
时所用的 :code:`prog` 中的前向部分完全一致,且不能包含任何参数更新的操作。如果两者存在不一致,
那么可能会导致一些变量未被正确加载;如果错误地包含了参数更新操作,那可能会导致正常预测过程中参数被更改。
这两个 :ref:`api_fluid_Program` 之间的关系类似于训练 :ref:`api_fluid_Program`
和测试 :ref:`api_fluid_Program` 之间的关系,详见: :ref:`user_guide_test_while_training`。
另外,需特别注意运行 :code:`fluid.default_startup_program()` 必须在调用 :code:`fluid.io.load_params`
之前。如果在之后运行,可能会覆盖已加载的模型参数导致错误。
载入checkpoint用于恢复训练
==========================
对于通过 :code:`fluid.io.save_checkpoint` 保存的模型,可以使用 :code:`fluid.io.load_checkpoint`
来进行载入。
例如:
.. code-block:: python
import paddle.fluid as fluid
exe = fluid.Executor(fluid.CPUPlace())
path = "./checkpoints"
prog = fluid.default_main_program()
fluid.io.load_checkpoint(executor=exe, checkpoint_dir=path,
serial=9, main_program=prog)
上面的例子中,通过调用 :code:`fluid.io.save_checkpoint` 函数,PaddlePaddle Fluid会对
:code:`prog` 中的所有模型变量进行扫描,根据内置规则自动筛选出需要加载的变量,
并尝试从 :code:`path` 之中加载它们。
参数 :code:`serial` 用来标记具体要加载的checkpoint的版本号。在保存checkpoint的时候,
一个checkpoint会被保存在一个子目录中,并在目录名上体现出自己的版本号。
一般越大的版本号表示这个checkpoint越新。
这里的 :code:`prog` 必须和调用 :code:`fluid.io.save_checkpoint` 时所用的 :code:`prog`
完全一致,否则会导致变量加载错误或者未加载。另外,与 :code:`fluid.io.save_params` 类似,
运行 :code:`fluid.default_startup_program()` 也必须在 :code:`fluid.io.load_checkpoint`
之前进行。
多机checkpoint保存
##################
.. toctree::
:maxdepth: 2
checkpoint_doc_cn.md
\ No newline at end of file
########
单机训练
########
准备工作
########
要进行PaddlePaddle Fluid单机训练,需要先 :ref:`user_guide_prepare_data` 和
:ref:`user_guide_configure_simple_model` 。当\
:ref:`user_guide_configure_simple_model` 完毕后,可以得到两个\
:ref:`api_fluid_Program`, :code:`startup_program` 和 :code:`main_program`。
默认情况下,可以使用 :ref:`api_fluid_default_startup_program` 与\ :ref:`api_fluid_default_main_program` 获得全局的 :ref:`api_fluid_Program`。
例如:
.. code-block:: python
import paddle.fluid as fluid
image = fluid.layers.data(name="image", shape=[784])
label = fluid.layers.data(name="label", shape=[1])
hidden = fluid.layers.fc(input=image, size=100, act='relu')
prediction = fluid.layers.fc(input=hidden, size=10, act='softmax')
loss = fluid.layers.mean(
fluid.layers.cross_entropy(
input=prediction,
label=label
)
)
sgd = fluid.optimizer.SGD(learning_rate=0.001)
sgd.minimize(loss)
# Here the fluid.default_startup_program() and fluid.default_main_program()
# has been constructed.
在上述模型配置执行完毕后, :code:`fluid.default_startup_program()` 与\
:code:`fluid.default_main_program()` 配置完毕了。
初始化参数
##########
参数随机初始化
==============
用户配置完模型后,参数初始化操作会被写入到\
:code:`fluid.default_startup_program()` 中。使用 :ref:`api_fluid_Executor` 运行
这一程序,即可在全局 :ref:`api_fluid_global_scope` 中随机初始化参数。例如:
.. code-block:: python
exe = fluid.Executor(fluid.CUDAPlace(0))
exe.run(program=fluid.default_startup_program())
值得注意的是: 如果使用多GPU训练,参数需要先在GPU0上初始化,再经由\
:ref:`api_fluid_ParallelExecutor` 分发到多张显卡上。
载入预定义参数
==============
在神经网络训练过程中,经常会需要载入预定义模型,进而继续进行训练。\
如何载入预定义参数,请参考 :ref:`user_guide_save_load_vars`。
单卡训练
########
执行单卡训练可以使用 :ref:`api_fluid_Executor` 中的 :code:`run()` 方法,运行训练\
:ref:`api_fluid_Program` 即可。在运行的时候,用户可以通过 :code:`run(feed=...)`\
参数传入数据;用户可以通过 :code:`run(fetch=...)` 获取持久的数据。例如:\
.. code-block:: python
...
loss = fluid.layers.mean(...)
exe = fluid.Executor(...)
# the result is an numpy array
result = exe.run(feed={"image": ..., "label": ...}, fetch_list=[loss])
这里有几点注意事项:
1. feed的数据格式,请参考文章 :ref:`user_guide_feed_data_to_executor`。
2. :code:`Executor.run` 的返回值是 :code:`fetch_list=[...]` 的variable值。被fetch\
的Variable必须是persistable的。 :code:`fetch_list` 可以传入Variable的列表,\
也可以传入Variable的名字列表。:code:`Executor.run` 返回Fetch结果列表。
3. 如果需要取回的数据包含序列信息,可以设置
:code:`exe.run(return_numpy=False, ...)` 直接返回 :ref:`api_guide_lod_tensor`
。用户可以直接访问 :ref:`api_guide_lod_tensor` 中的信息。
多卡训练
########
执行多卡训练可以使用 :ref:`api_fluid_ParallelExecutor` 运行训练
:ref:`api_fluid_Program`。例如:
.. code-block:: python
train_exe = fluid.ParallelExecutor(use_cuda=True, loss_name=loss.name,
main_program=fluid.default_main_program())
train_exe.run(fetch_list=[loss.name], feed={...})
这里有几点注意事项:
1. :code:`ParallelExecutor` 的构造函数需要指明要执行的 :ref:`api_fluid_Program` ,
并在执行过程中不能修改。默认值是 :ref:`api_fluid_default_main_program` 。
2. :code:`ParallelExecutor` 需要明确指定是否使用 CUDA 显卡进行训练。在显卡训练\
模式下会占用全部显卡。用户可以配置 `CUDA_VISIBLE_DEVICES <http://www.acceleware.com/blog/cudavisibledevices-masking-gpus>`_ 来修改占用\
的显卡。
进阶使用
########
.. toctree::
:maxdepth: 2
test_while_training
save_load_variables
.. _user_guide_test_while_training:
##################
训练过程中评测模型
##################
模型的测试评价与训练的 :ref:`api_fluid_Program` 不同。在测试评价中:
1. 评价测试不进行反向传播,不优化更新参数。
2. 评价测试执行的操作可以不同。
* 例如 BatchNorm 操作,在训练和测试时执行不同的算法。
* 评价模型与训练相比可以是完全不同的模型。
生成测试 :ref:`api_fluid_Program`
#################################
通过克隆训练 :ref:`api_fluid_Program` 生成测试 :ref:`api_fluid_Program`
=======================================================================
:code:`Program.clone()` 方法可以复制出新的 :ref:`api_fluid_Program` 。 通过设置
:code:`Program.clone(for_test=True)` 复制含有用于测试的操作Program。简单的使用方法如下:
.. code-block:: python
import paddle.fluid as fluid
img = fluid.layers.data(name="image", shape=[784])
prediction = fluid.layers.fc(
input=fluid.layers.fc(input=img, size=100, act='relu'),
size=10,
act='softmax'
)
label = fluid.layers.data(name="label", shape=[1], dtype="int64")
loss = fluid.layers.mean(fluid.layers.cross_entropy(input=prediction, label=label))
acc = fluid.layers.accuracy(input=prediction, label=label)
test_program = fluid.default_main_program().clone(for_test=True)
adam = fluid.optimizer.Adam(learning_rate=0.001)
adam.minimize(loss)
在使用 :code:`Optimizer` 之前,将 :code:`fluid.default_main_program()` 复制\
成一个 :code:`test_program` 。之后使用测试数据运行 :code:`test_program`,\
就可以做到运行测试程序,而不影响训练结果。
分别配置训练 :ref:`api_fluid_Program` 和测试 :ref:`api_fluid_Program`
=====================================================================
如果训练程序和测试程序相差较大时,用户也可以通过完全定义两个不同的
:ref:`api_fluid_Program`,分别进行训练和测试。在PaddlePaddle Fluid中,\
所有的参数都有名字。如果两个不同的操作,甚至两个不同的网络使用了同样名字的参数,\
那么他们的值和内存空间都是共享的。
PaddlePaddle Fluid中使用 :code:`fluid.unique_name` 包来随机初始化用户未定义的\
参数名称。通过 :code:`fluid.unique_name.guard` 可以确保多次调用某函数\
参数初始化的名称一致。
例如:
.. code-block:: python
import paddle.fluid as fluid
def network(is_test):
file_obj = fluid.layers.open_files(filenames=["test.recordio"] if is_test else ["train.recordio"], ...)
img, label = fluid.layers.read_file(file_obj)
hidden = fluid.layers.fc(input=img, size=100, act="relu")
hidden = fluid.layers.batch_norm(input=hidden, is_test=is_test)
...
return loss
with fluid.unique_name.guard():
train_loss = network(is_test=False)
sgd = fluid.optimizer.SGD(0.001)
sgd.minimize(train_loss)
test_program = fluid.Program()
with fluid.unique_name.guard():
with fluid.program_gurad(test_program, fluid.Program()):
test_loss = network(is_test=True)
# fluid.default_main_program() is the train program
# fluid.test_program is the test program
执行测试 :ref:`api_fluid_Program`
#################################
使用 :code:`Executor` 执行测试 :ref:`api_fluid_Program`
=======================================================
用户可以使用 :code:`Executor.run(program=...)` 来执行测试
:ref:`api_fluid_Program`。
例如
.. code-block:: python
exe = fluid.Executor(fluid.CPUPlace())
test_acc = exe.run(program=test_program, feed=test_data_batch, fetch_list=[acc])
print 'Test accuracy is ', test_acc
使用 :code:`ParallelExecutor` 执行测试 :ref:`api_fluid_Program`
===============================================================
用户可以使用训练用的 :code:`ParallelExecutor` 与测试 :ref:`api_fluid_Program`
一起新建一个测试的 :code:`ParallelExecutor` ;再使用测试
:code:`ParallelExecutor.run` 来执行测试。
例如:
.. code-block:: python
train_exec = fluid.ParallelExecutor(use_cuda=True, loss_name=loss.name)
test_exec = fluid.ParallelExecutor(use_cuda=True, share_vars_from=train_exec,
main_program=test_program)
test_acc = test_exec.run(fetch_list=[acc], ...)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册