我接触飞桨框架是在2019年年底左右,到现在已经有大半年了,这段时间里,我通过飞桨框架学习了很多关于机器学习的理论和方法,也跑过不少项目了。但是,在往更深的领域去做时,我发现越来越吃力了,其实问题就在于基础不够扎实,我在找解决办法的时候,翻阅了AI Studio社区里很多大佬的文章。在翻阅这些文章的过程中,我发现我对飞桨确实不够了解,并且飞桨是2016年开源的,有些文章的内容稍微有一点点过时的,所以借此机会,我打算重新开始学习飞桨框架:
1. 关于PaddlePaddle的版本
这里的版本都是指大方向,即出现之前的版本和出现之后的版本
Fluid
出现Fluid前的版本和出现Fluid后的版本做比较,重点介绍出现Fluid以后的版本
最开始的PaddlePaddle还没有Fluid
如果你翻看PaddlePaddle以前的文档,就会发现PaddlePaddle已经有很多版本了,并且最初的版本是没有Fluid这个概念的:
Fluid这个概念是在0.13.0才开始出现的,这里我简单讲讲Fluid出现前的PaddlePaddle是什么样的:
# network config
x = paddle.layer.data(name='x', type=paddle.data_type.dense_vector(2))
y_predict = paddle.layer.fc(input=x, size=1, act=paddle.activation.Linear())
y = paddle.layer.data(name='y', type=paddle.data_type.dense_vector(1))
cost = paddle.layer.mse_cost(input=y_predict, label=y)
定义一层全连接网络,输入x,输出y,拿网络输出的y(y_predict)和期望的y做比较,计算损失cost
再来看一下训练的代码:
# training
trainer.train(
reader=paddle.batch(
train_reader(), batch_size=1),
feeding=feeding,
event_handler=event_handler,
num_passes=100)
训练是通过调用trainer的train方法启动的
Fluid的出现让PaddlePaddle焕发生机
下面是对比图
官方给的区别是从有模型变为无模型,我理解的是,最大的改变就在于程序的编译过程里。
文档的原文是这么描述的:
When a Fluid application program runs, it generates a ProgramDesc protobuf message as an intermediate representation of itself. The C++ class Executor can run this protobuf message as an interpreter.
换句话说,Fluid能让PaddlePaddle的执行速度变的更快
静态图和动态图
PaddlePaddle引入动态图是在1.5之后,即从1.6开始支持动态图
因为静态图和动态图的内容较多,所以我将在下一篇文章内整理
2. Fluid的核心思想
上面的版本其实只是做了一个铺垫,现在使用PaddlePaddle都是需要用到Fluid
Fluid使用一种编译器式的执行流程,分为编译时和运行时两个部分,具体包括:编译器定义 Program ,创建Executor 运行 Program 。
Fluid内部执行流程
-
编译时,用户编写一段python程序,通过调用 Fluid 提供的算子,向一段 Program 中添加变量(Tensor)以及对变量的操作(Operators 或者 Layers)。用户只需要描述核心的前向计算,不需要关心反向计算、分布式下以及异构设备下如何计算。
-
原始的 Program 在平台内部转换为中间描述语言: ProgramDesc。
-
编译期最重要的一个功能模块是 Transpiler。Transpiler 接受一段 ProgramDesc ,输出一段变化后的 ProgramDesc ,作为后端 Executor 最终需要执行的 Fluid Program
-
后端 Executor 接受 Transpiler 输出的这段 Program ,依次执行其中的 Operator(可以类比为程序语言中的指令),在执行过程中会为 Operator 创建所需的输入输出并进行管理。
Executor与Program的设计思想
Program
用户完成网络定义后,一段 Fluid 程序中通常存在 2 段 Program:
-
fluid.default_startup_program:定义了创建模型参数,输入输出,以及模型中可学习参数的初始化等各种操作
default_startup_program 可以由框架自动生成,使用时无需显示地创建
如果调用修改了参数的默认初始化方式,框架会自动的将相关的修改加入default_startup_program -
fluid.default_main_program :定义了神经网络模型,前向反向计算,以及优化算法对网络中可学习参数的更新
使用Fluid的核心就是构建起 default_main_program
Programs and Blocks
Fluid 的 Program 的基本结构是一些嵌套 blocks,形式上类似一段 C++ 或 Java 程序。
blocks中包含:
- 本地变量的定义
- 一系列的operator
block的概念与通用程序一致,例如在下列这段C++代码中包含三个block:
int main(){
//block 0
int i = 0;
if (i<10){
//block 1
for (int j=0;j<10;j++){
//block 2
}
}
return 0;
}
类似的,在下列 Fluid 的 Program 包含3段block:
import paddle.fluid as fluid # block 0
limit = fluid.layers.fill_constant_batch_size_like(
input=label, dtype='int64', shape=[1], value=5.0)
cond = fluid.layers.less_than(x=label, y=limit)
ie = fluid.layers.IfElse(cond)
with ie.true_block(): # block 1
true_image = ie.input(image)
hidden = fluid.layers.fc(input=true_image, size=100, act='tanh')
prob = fluid.layers.fc(input=hidden, size=10, act='softmax')
ie.output(prob)
with ie.false_block(): # block 2
false_image = ie.input(image)
hidden = fluid.layers.fc(
input=false_image, size=200, act='tanh')
prob = fluid.layers.fc(input=hidden, size=10, act='softmax')
ie.output(prob)
prob