From a0329a8f778c22215d1352cc3a7d5dfd4ea774d1 Mon Sep 17 00:00:00 2001 From: phlrain Date: Mon, 18 May 2020 12:18:23 +0000 Subject: [PATCH] update dygraph doc; test=develop --- .../basic_concept/dygraph/DyGraph.md | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/doc/fluid/beginners_guide/basic_concept/dygraph/DyGraph.md b/doc/fluid/beginners_guide/basic_concept/dygraph/DyGraph.md index a390364f2..89d96a2f1 100644 --- a/doc/fluid/beginners_guide/basic_concept/dygraph/DyGraph.md +++ b/doc/fluid/beginners_guide/basic_concept/dygraph/DyGraph.md @@ -1,3 +1,5 @@ +# 动态图使用教程 + 从编程范式上说,飞桨兼容支持声明式编程和命令式编程,通俗地讲即动态图和静态图。其实飞桨本没有图的概念,在飞桨的设计中,把一个神经网络定义成一段类似程序的描述,也就是用户在写程序的过程中,就定义了模型表达及计算。在声明式编程模式的控制流实现方面,飞桨借助自己实现的控制流OP而不是python原生的if else和for循环,这使得在飞桨中的定义的program即一个网络模型,可以有一个内部的表达,是可以全局优化编译执行的。考虑对开发者来讲,更愿意使用python原生控制流,飞桨也做了支持,并通过解释方式执行,这就是命令式编程模式。但整体上,这两种编程范式是相对兼容统一的。飞桨将持续发布更完善的命令式编程模式功能,同时保持更强劲的性能。 飞桨平台中,将神经网络抽象为计算表示**Operator**(算子,常简称OP)和数据表示**Variable**(变量),如 图1 所示。神经网络的每层操作均由一个或若干**Operator**组成,每个**Operator**接受一系列的**Variable**作为输入,经计算后输出一系列的**Variable**。 @@ -5,14 +7,13 @@
图1 Operator和Variable关系示意图
- 根据**Operator**解析执行方式不同,飞桨支持如下两种编程范式: * **声明式编程模式模式(动态图)**:先编译后执行的方式。用户需预先定义完整的网络结构,再对网络结构进行编译优化后,才能执行获得计算结果。 * **命令式编程模式模式(静态图)**:解析式的执行方式。用户无需预先定义完整的网络结构,每写一行网络代码,即可同时获得计算结果。 举例来说,假设用户写了一行代码:y=x+1,在声明式编程模式模式下,运行此代码只会往计算图中插入一个Tensor加1的**Operator**,此时**Operator**并未真正执行,无法获得y的计算结果。但在命令式编程模式模式下,所有**Operator**均是即时执行的,运行完此代码后**Operator**已经执行完毕,用户可直接获得y的计算结果。 -# 为什么命令式编程模式模式越来越流行? +## 为什么命令式编程模式模式越来越流行? 声明式编程模式模式作为较早提出的一种编程范式,提供丰富的 API ,能够快速的实现各种模型;并且可以利用全局的信息进行图优化,优化性能和显存占用;在预测部署方面也可以实现无缝衔接。 但具体实践中声明式编程模式模式存在如下问题: 1. 采用先编译后执行的方式,组网阶段和执行阶段割裂,导致调试不方便。 @@ -33,7 +34,7 @@ 5. 命令式编程模式模式常见的使用技巧,如中间变量值/梯度打印、断点调试、阻断反向传递,以及某些场景下如何改写为声明式编程模式模式运行。 -# 1. 开启命令式编程模式模式 +## 1. 开启命令式编程模式模式 目前飞桨默认的模式是声明式编程模式,采用基于 context (上下文)的管理方式开启命令式编程模式模式: ``` @@ -119,7 +120,7 @@ with fluid.dygraph.guard(): * 命令式编程模式模式下,所有操作在运行时就已经完成,更接近我们平时的编程方式,可以随时获取每一个操作的执行结果。 * 声明式编程模式模式下,过程中并没有实际执行操作,上述例子中可以看到只能打印声明的类型,最后需要调用执行器来统一执行所有操作,计算结果需要通过执行器统一返回。 -# 2. 使用命令式编程模式进行模型训练 +## 2. 使用命令式编程模式进行模型训练 接下来我们以一个简单的手写体识别任务为例,说明如何使用飞桨的命令式编程模式来进行模型的训练。包括如下步骤: * 2.1 定义数据读取器:读取数据和预处理操作。 @@ -135,7 +136,7 @@ with fluid.dygraph.guard(): 有关该任务和数据集的详细介绍,可参考:[初识飞桨手写数字识别模型](https://aistudio.baidu.com/aistudio/projectdetail/224342) -## 2.1 定义数据读取器 +### 2.1 定义数据读取器 飞桨提供了多个封装好的数据集API,本任务我们可以通过调用 [paddle.dataset.mnist](https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/data/dataset_cn.html) 的 train 函数和 test 函数,直接获取处理好的 MNIST 训练集和测试集;然后调用 [paddle.batch](https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/io_cn/batch_cn.html#batch) 接口返回 reader 的装饰器,该 reader 将输入 reader 的数据打包成指定 BATCH_SIZE 大小的批处理数据。 @@ -171,7 +172,7 @@ test_reader = paddle.batch( Download finished -## 2.2 定义模型和优化器 +### 2.2 定义模型和优化器 本节我们采用如下网络模型,该模型可以很好的完成“手写数字识别”的任务。模型由卷积层 -> 池化层 -> 卷积层 -> 池化层 -> 全连接层组成,池化层即降采样层。 @@ -317,7 +318,7 @@ with fluid.dygraph.guard(): adam = AdamOptimizer(learning_rate=0.001, parameter_list=mnist.parameters()) ``` -## 2.3 训练 +### 2.3 训练 当我们定义好上述网络结构之后,就可以进行训练了。 @@ -407,7 +408,7 @@ with fluid.dygraph.guard(): Loss at epoch 2 step 400: [0.0497771] -## 2.4 评估测试 +### 2.4 评估测试 模型训练完成,我们已经保存了训练好的模型,接下来进行评估测试。某些OP(如 dropout、batch_norm)需要区分训练模式和评估模式,以标识不同的执行状态。飞桨中OP默认采用的是训练模式(train mode),可通过如下方法切换: @@ -463,7 +464,7 @@ with fluid.dygraph.guard(): print("Eval avg_loss is: {}, acc is: {}".format(avg_loss_val_mean, acc_val_mean)) ``` -## 2.5 模型参数的保存和加载 +### 2.5 模型参数的保存和加载 在命令式编程模式模式下,模型和优化器在不同的模块中,所以模型和优化器分别在不同的对象中存储,使得模型参数和优化器信息需分别存储。 因此模型的保存需要单独调用模型和优化器中的 state_dict() 接口,同样模型的加载也需要单独进行处理。 @@ -497,7 +498,7 @@ with fluid.dygraph.guard(): ``` -# 3. 多卡训练 +## 3. 多卡训练 针对数据量、计算量较大的任务,我们需要多卡并行训练,以提高训练效率。目前命令式编程模式模式可支持GPU的单机多卡训练方式,在命令式编程模式中多卡的启动和单卡略有不同,命令式编程模式多卡通过 Python 基础库 subprocess.Popen 在每一张 GPU 上启动单独的 Python 程序的方式,每张卡的程序独立运行,只是在每一轮梯度计算完成之后,所有的程序进行梯度的同步,然后更新训练的参数。 @@ -640,7 +641,7 @@ if fluid.dygraph.parallel.Env().local_rank == 0: 对模型进行评估测试时,如果需要加载模型,须确保评估和保存的操作在同一个进程中,否则可能出现模型尚未保存完成,即启动评估,造成加载出错的问题。如果不需要加载模型,则没有这个问题,在一个进程或多个进程中评估均可。 -# 4. 模型部署 +## 4. 模型部署 命令式编程模式虽然有非常多的优点,但是如果用户希望使用 C++ 部署已经训练好的模型,会存在一些不便利。比如,命令式编程模式中可使用 Python 原生的控制流,包含 if/else、switch、for/while,这些控制流需要通过一定的机制才能映射到 C++ 端,实现在 C++ 端的部署。 @@ -776,9 +777,9 @@ with fluid.dygraph.guard(): prog_trans.save_inference_model("./mnist_dy2stat", fetch=[0,1]) ``` -# 5. 使用技巧 +## 5. 使用技巧 -## 5.1 中间变量值、梯度打印 +### 5.1 中间变量值、梯度打印 1. 用户想要查看任意变量的值,可以使用 [numpy](https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/fluid_cn/Variable_cn.html#numpy) 接口。 @@ -800,7 +801,7 @@ print(y.gradient()) 可以直接打印反向梯度的值 -## 5.2 断点调试 +### 5.2 断点调试 因为命令式编程模式采用了命令似的编程方式,程序在执行之后,可以立马获取到执行的结果,因此在命令式编程模式中,用户可以利用IDE提供的断点调试功能,通过查 Variable 的 shape、真实值等信息,有助于发现程序中的问题。 @@ -817,7 +818,7 @@ print(y.gradient()) -## 5.3 使用声明式编程模式模式运行 +### 5.3 使用声明式编程模式模式运行 命令式编程模式虽然有友好编写、易于调试等功能,但是命令式编程模式中需要频繁进行 Python 与 C++ 交互,会导致一些任务在命令式编程模式中运行比声明式编程模式慢,根据经验,这类任务中包含了很多小粒度的 OP(指运算量相对比较小的 OP,如加减乘除、sigmoid 等,像 conv、matmul 等属于大粒度的 OP不在此列 )。 @@ -904,7 +905,7 @@ with fluid.program_guard(main_program=main_program, startup_program=startup_prog * 需要对网络进行初始化操作。 * 需要使用执行器执行之前已经定义好的网络,需要调用执行器的run方法执行计算过程。 -## 5.4 阻断反向传递 +### 5.4 阻断反向传递 在一些任务中,只希望拿到正向预测的值,但是不希望更新参数,或者在反向的时候剪枝,减少计算量,阻断反向的传播, Paddle提供了两种解决方案: [detach](https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/fluid_cn/Variable_cn.html#detach) 接口和 [stop_gradient](https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/fluid_cn/Variable_cn.html#stop_gradient) 接口,建议用户使用 detach 接口。 -- GitLab