赣州新闻网

首页 / 科技

马云大神开推——最擅长抓危机中的机遇,世界总是他擅长的舞台

2020-03-02 · 赣州新闻网 · 科技

Fluid程序代码可以分为编译时代码与运行时代码。

编译时的代码可以理解为:用户在编写一段PaddlePaddle程序时,描述模型前向计算的代码。它包括以下几个方面的内容。

  • 创建变量与描述变量。
  • 创建算子与描述算子。
  • 创建算子的属性。
  • 推断变量的类型和形状,进行静态检查。
  • 规划变量的内存复用。
  • 定义反向计算。
  • 添加与优化相关的算子。
  • 添加多机多卡相关的算子,生成在多机多卡上运行的程序。

要定义反向计算,代码如下。

x = fluid.layers.data(name=\'x\',shape=, dtype=\'float32\')
y_predict = fluid.layers.fc(input=x, size=1, act=None)
y = fluid.layers.data(name=\'y\', shape=, dtype=\'float32\')
cost = fluid.layers.square_error_cost(input=y_predict, label=y)
avg_cost = fluid.layers.mean(x=cost)

要添加优化算法,代码如下。

learning_rate = 0.01
sgd_optimizer = fluid.optimizer.SGD(learning_rate)
sgd_optimizer.minimize(avg_cost)

编译时的代码可以理解为计算并控制运行时计算的代码,它包括以下几个方面的内容:

  • 创建框架执行器(executor)。
  • 为将要执行的计算过程创建变量操作域。
  • 创建块,并依次执行块。

要读入数据,代码如下。

train_reader = paddle.batch(
paddle.reader.shuffle(paddle.dataset.uci_housing.train(), buf_size=500),
batch_size=20)
feeder = fluid.DataFeeder(place=place, feed_list=)

要定义执行程序的设备,代码如下。

place = fluid.CPUPlace()
feeder = fluid.DataFeeder(place=place,feed_list=)

要创建执行器,并执行default_startup_program,代码如下。

exe = fluid.Executor(place)
exe.run(fluid.default_startup_program())

要执行训练程序default_main_program,代码如下。

PASS_NUM = 100
for pass_id in range(PASS_NUM):
fluid.io.save_persistables(exe, \"./fit_a_line.model/\")
fluid.io.load_persistables(exe, \"./fit_a_line.model/\")
for data in train_reader():
avg_loss_value, = exe.run(fluid.default_main_program(),
feed=feeder.feed(data),
fetch_list=)
print(avg_loss_value)

1.2 Fluid内部执行流程

Fluid使用一种编译器式的执行流程,分为编译时和运行时两个部分,具体包括编译器定义Program、创建框架执行器和运行Program。

本地训练任务执行流程如图1-1所示。

百度深度学习框架飞桨PaddlePaddle设计思想与核心技术

图1-1

(1)在编译时,用户编写一段Python程序,通过调用Fluid提供的算子,向一段Program中添加变量(在Fluid中用户输入的变量通常以张量表示)以及对变量的操作(算子或者层)。用户只需要描述核心的前向计算,不需要关心反向计算以及分布式环境下和异构设备下是如何计算的。

(2)原始的Program在平台内部转换为中间描述语言ProgramDesc。

(3)在编译期间重要的一个功能模块是编译转换器。编译转换器接受一段 ProgramDesc,输出一段变化后的 ProgramDesc,作为后端框架执行器最终需要执行的是Fluid Program。

(4)后端框架执行器接受编译转换器输出的这段Program,依次执行其中的算子(可以类比为程序语言中的指令),在执行过程中会为算子创建所需的输入/输出并进行管理。

1.3 Program设计简介

PaddlePaddle使用了一个Program的概念,那么什么是Program?

Program本质是对多个数据(通常以变量表示)和操作逻辑的描述。一个深度学习任务中的训练或者预测都可以被描述为一段Program。

多个变量和一系列的算子不仅可以组合为Program,还可以组合成Block,而多个Block可以相互嵌套、并列,最终组合为Program。

用户完成网络定义后,1个Fluid程序中通常存在两段Program。

  • fluid.default_startup_program:定义了创建模型参数、输入/输出,以及模型中可学习参数的初始化等各种操作。default_startup_program可以由框架自动生成,使用时无须手动创建。如果调用修改了参数的默认初始化方式,框架会自动将相关的修改加入default_startup_program。
  • fluid.default_main_program:也由框架自动生成,负责存储定义的神经网络模型、前向/反向计算,以及优化算法对网络中可学习参数的更新。如果在指定程序操作时未指定在哪个Program中操作,Fluid默认自动在fluid.default_main_program操作。

使用Fluid的核心就是构建default_main_program。

可通过图1-2所示的代码,输出ProgramDesc中的内容。

百度深度学习框架飞桨PaddlePaddle设计思想与核心技术

图1-2

上面框出的两行代码的输出内容如图1-3所示。

百度深度学习框架飞桨PaddlePaddle设计思想与核心技术

百度深度学习框架飞桨PaddlePaddle设计思想与核心技术

图1-3

1.4 Block简介

Block的概念与通用程序一致,例如,下面这段C++代码包含3个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=, 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 = ie()

1.5 Block和Program的设计细节

用户描述的Block与Program信息在Fluid中以Protobuf 格式保存,所有的Protobuf信息定义在framework.proto中,在Fluid中称为BlockDesc和ProgramDesc。ProgramDesc和BlockDesc的概念类似于一个抽象语法树。

BlockDesc中包含一系列本地变量(在代码中用vars表示)的定义和一系列的算子(在代码中用ops表示)。

 message BlockDesc {
required int32 parent = 1;
repeated VarDesc vars = 2;
repeated OpDesc ops = 3;
}

parent表示父级Block,因此Block中的操作符既可以引用本地定义的变量,也可以引用祖先块中定义的变量。

Program中的每个Block都被压平并存储在数组中。Block ID是这个数组中Block的索引。

message ProgramDesc {
repeated BlockDesc blocks = 1;
}

在1.4节介绍的例子中,IfElse这个算子包含了两个Block——true分支和false分支。

下述OpDesc的定义过程描述了一个算子可以包含哪些属性。

message OpDesc {
AttrDesc attrs = 1;
...
}

属性可以是Block的类型,实际上就是上面描述的Block ID。

message AttrDesc {
required string name = 1;

enum AttrType {
INT = 1,
STRING = 2,
...
BLOCK = ...
}
required AttrType type = 2;

optional int32 block = 10; // 当type == BLOCK时
...
}

1.6 框架执行器设计思想

在编译时,用户首先在Python前端对网络进行表述,这个表述按照Program、Block的层级关系进行定义。转换器在接收到用户的表述后,将Program编译成ProgramDesc以备运行时使用。在运行时,框架执行器将接受一个ProgramDesc、一个block_id和一个变量操作域。执行以下操作。

(1)框架执行器为每一个Block创建一个变量操作域,Block是可嵌套的,因此变量操作域也是可嵌套的。

(2)创建变量操作域中的所有变量。

(3)按顺序创建并执行所有算子。

编译、执行的具体过程如图1-4表示。

百度深度学习框架飞桨PaddlePaddle设计思想与核心技术

图1-4

1.6.1 代码示例

框架执行器的C++实现代码如下。

class Executor{
public:
void Run(const ProgramDesc& pdesc,
Scope* scope,
int block_id) {
auto& block = pdesc.Block(block_id);

//创建所有变量
for (auto& var : block.AllVars())
scope->Var(Var->Name());
}

//创建算子并按顺序执行
for (auto& op_desc : block.AllOps()){
auto op = CreateOp(*op_desc);
op->Run(*local_scope, place_);
}
}
};

1.6.2 创建框架执行器

Fluid中使用fluid.Executor(place)创建框架执行器,place属性由用户定义,代表程序将在哪里执行。

下例代码表示创建一个框架执行器,它在CPU内运行。

cpu=fluid.CPUPlace()
exe = fluid.Executor(cpu)

1.6.3 运行框架执行器

Fluid使用Executor.run来运行程序。定义中通过feed映射获取数据,通过fetch_list获取结果。

...
x = numpy.random.random(size=(10, 1)).astype(\'float32\')
outs = exe.run(
feed={\'X\': x},
fetch_list=)

1.7 示例

线性回归是最简单的模型之一,可以把它作为一个优化问题来研究。该问题可通过最小化均方误差求解。

本节通过简单的线性回归例子,介绍上述内容如何在代码中实现。

1.7.1 定义Program

用户可以随意定义自己的数据和网络结构,定义的结果都将作为一段Program被Fluid接收,Program的基本结构是Block,本节的Program仅包含Block 0。

#加载函数库
import paddle.fluid as fluid #Block0
import numpy

#定义数据
train_data=numpy.array([,,,]).astype(\'float32\')
y_true = numpy.array([,,,]).astype(\'float32\')
#定义网络
x = fluid.layers.data(name=\"x\",shape=,dtype=\'float32\')
y = fluid.layers.data(name=\"y\",shape=,dtype=\'float32\')
y_predict = fluid.layers.fc(input=x,size=1,act=None)
#定义损失函数
cost = fluid.layers.square_error_cost(input=y_predict,label=y)
avg_cost = fluid.layers.mean(cost)
#定义优化方法
sgd_optimizer = fluid.optimizer.SGD(learning_rate=0.01)
sgd_optimizer.minimize(avg_cost)

完成上述定义,也就是完成了fluid.default_main_program的构建过程,fluid.default_main_program负责神经网络模型、前向/反向计算,以及优化算法对网络中可学习参数的更新。

此时可以输出这段 Program,观察定义好的网络形态。

print(fluid.default_main_program().to_string(True))

完整的ProgramDesc可以在本地查看,这里仅节选前3个变量。

blocks {
idx: 0
parent_idx: -1
vars {
name: \"mean_1.tmp_0\"
type {
type: LOD_TENSOR
lod_tensor {
tensor {
data_type: FP32
dims: 1
}
}
}
persistable: false
}
vars {
name: \"square_error_cost_1.tmp_1\"
type {
type: LOD_TENSOR
lod_tensor {
tensor {
data_type: FP32
dims: -1
dims: 1
}
lod_level: 0
}
}
persistable: false
}
vars {
name: \"square_error_cost_1.tmp_0\"
type {
type: LOD_TENSOR
lod_tensor {
tensor {
data_type: FP32
dims: -1
dims: 1
}
lod_level: 0
}
}
persistable: false
...

从输出结果中可以看到,整个定义过程在框架内部转化为了一段ProgramDesc,以Block idx为索引。本次线性回归模型中仅有1个Block,ProgramDesc中也仅有Block 0这一段BlockDesc。

BlockDesc中包含定义的变量和一系列的算子,以输入x为例,在Python代码中定义x是一个数据类型为“float 32”的一维数据。

x = fluid.layers.data(name=\"x\",shape=,dtype=\'float32\')

在BlockDesc中,变量x被描述为:

vars {
name: \"x\"
type {
type: LOD_TENSOR
lod_tensor {
tensor {
data_type: FP32
dims: -1
dims: 1
}
lod_level: 0
}
}
persistable: false

在Fluid中所有的数据类型都为LoD Tensor,对于不存在序列信息的数据(如此处的变量x),其lod_level=0。

dims表示数据的维度,这里表示x的维度为,其中−1是批的维度,在无法确定具体数值时,Fluid自动用−1表示。

参数persistable表示该变量在整个训练过程中是否为持久化变量。

1.7.2 创建框架执行器

Fluid使用框架执行器来执行网络训练。作为使用者,实际上不必了解内部机制。

创建框架执行器只需要调用 fluid.Executor(place) 即可,在此之前请依据训练场所定义place变量。

 #在CPU内执行训练
cpu = fluid.CPUPlace()
#创建框架执行器
exe = fluid.Executor(cpu)

1.7.3 运行框架执行器

Fluid使用Executor.run来运行一段Program。

在正式进行网络训练前,要先进行参数初始化。其中 default_startup_program 中定义了创建模型参数、输入/输出,以及模型中可学习参数的初始化等操作。

 #参数初始化
exe.run(fluid.default_startup_program())

由于传入的数据与传出的数据存在多列,因此 Fluid 通过 feed 映射定义数据的传输数据,通过 fetch_list 取出期望结果:

#开始训练
outs = exe.run(
feed={\'x\':train_data,\'y\':y_true},
fetch_list=)

上述代码段中定义了train_data用于传入x变量,y_true用于传入y变量,输出y的预测值和最后一轮样本中cost的均值。

输出结果如下。

[array([,
,
,
], dtype=float32), array(, dtype=float32)]


百度深度学习框架飞桨PaddlePaddle设计思想与核心技术

-END-

喜欢的朋友,欢迎转发到朋友圈

关注【异步图书】微信号,回复“VIP”获取异步社区VIP会员卡,近千门电子书、专栏视频课免费学

宁都视频代表内容信息喜欢设计马云大神