前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >static compiler 1

static compiler 1

原创
作者头像
用户7357251
修改2020-05-20 17:27:58
8450
修改2020-05-20 17:27:58
举报
文章被收录于专栏:joe compilerjoe compiler

本文主要介绍静态图的主体设计思想和基本概念。

1. Program

Fluid将神经网络描述为Program这一数据结构。

Program由Block组成,即 Program = List[Block]

Block由Operator和Variable组成,即 Block = List[Operator] + List[Variable]

与编程语言类比,我们可以将Program理解为程序,Block对应程序的控制流分支结构,如条件分支、循环分支等。Fluid的控制流Op(conditional_block,while,recurrent等)均通过Block表达。

不同Block里的变量可以重名。若父Block与子Block中存在同名变量,那么子Block的Operator运行时会优先找到子Block中的变量。

代码语言:txt
复制
int func(int n) {
    // Main Block
    int a = 3;
    int b = 4;
    int c = a * b;
    int d = c / 10;
    
    ...
    
    int sum = 0;
    if (n > 0) { // sub-block
        for (int i = 0; i < 10; i++) {
            sum += i; // sub-sub-block
        }
    } else { // sub-block
        for (int i = 0; i < (-n); i++) {
            sum -= i; // sub-sub-block
        }
    }
    
    std::cout << d << " " << sum << std::endl;
    
    return 0;
}

在组建神经网络的过程中会涉及两个Program,即startup program和main program。

startup program对应TensorFlow中的tf.global_initializer(),包含参数、learning rate、Optimizer Momentum等变量的初始化Op。

main program对应神经网络的主体结构。因此,在运行神经网络训练/预测时,我们需首先跑一次startup program进行初始化,然后跑多次main program进行训练/预测。

Fluid定义了全局默认的startup program和main program,即 fluid.default_startup_program()fluid.default_main_program() ,调用 fluid.layers.xxx API时,均会往全局默认的program中插入op。

若要切换全局的startup program和main program,可使用 fluid.program_guard() ,例如:

代码语言:txt
复制
import paddle.fluid as fluid
startup_program = fluid.Program()
main_program = fluid.Program()

assert startup_program != fluid.default_startup_program()
assert main_program != fluid.default_main_program()

with fluid.program_guard(main_program, startup_program):
    assert startup_program == fluid.default_startup_program()
    assert main_program == fluid.default_main_program()
    ...
    
assert startup_program != fluid.default_startup_program()
assert main_program != fluid.default_main_program()

Python端的Program,Block,Operator,Variable分别对应于C++端的ProgramDesc,BlockDesc,OpDesc,VarDesc。

值得注意的是,Program是对神经网络的静态描述,其底层是Protobuf Description,因此在运行网络以前所有变量、Op均不存在。

2. C++ Place

Place表示设备,可以是GPU设备或CPU设备。

代码语言:txt
复制
using Place = boost::variant<CUDAPlace, CPUPlace, CUDAPinnedPlace>;

boost::variant类似于C++的union,是一种类型安全的union,即multi-type, one-value。

同一设备的内存/显存的Place相同,即相同Place的Tensor的内存/显存空间在同一设备上。

3. C++ DeviceContext

DeviceContext表示设备,可以理解为是一个虚拟设备的概念,包含CPUDeviceContext和CUDADeviceContext等。理论上,一个Place可对应多个DeviceContext。

DeviceContext中包含一些额外的设备信息,例如cudaStream_t, cudnnHolder_t, cublasHandle_t等。

在目前Fluid的设计中,我们维护了一个全局的DeviceContextPool,记录了Place到DeviceContext的map(单映射,非multimap)。即在全局DeviceContextPool中,Place和DeviceContext是一一对应的。目前Fluid的所有单设备Op均运行在全局的DeviceContext中。

4. C++ Variable

C++ Variable是一个类似于std::any的结构,可以存储任意类型的变量。最常见的,Variable里存储LoDTensor,但Variable还可以存储SelectedRows,ReaderHolder等。

5. C++ Scope

Scope用于存储变量,Scope主要数据成员为:

代码语言:txt
复制
class Scope {
 std::unordered_map<std::string, std::unique_ptr<Variable>> vars_; // Scope中存储的变量
 const Scope *parent_; // 父Scope
 std::list<Scope *> kids_; // 子Scope
};

Scope与编程语言的作用域类似,当调用Scope::FindVar时,会首先在当前Scope中查找变量是否存在,若存在则直接返回,否则递归地从父Scope里寻找该变量。

6. Operator

Op的成员

Op主要包含4个信息:

  • type: std::string类型,表示Op的名称,如"matmul","conv2d","reshape"等。
  • inputs: std::map<std::string, std::vector<std::string>>类型,表示Op的输入变量,map的key为slot名称,对应于OpProtoAndCheckerMaker中定义的名称;map的value为实际的变量名,对应于Scope中的变量名。
  • outputs: std::map<std::string, std::vector<std::string>>类型,表示Op的输出变量。key和value的含义与inputs类似。
  • attributes: std::map<std::string, boost::variant<...>>类型,表示Op的属性,例如transpose选择哪些维度进行转置等。

OperatorBase

OperatorBase是所有Op的基类,其Run方法的声明为:

代码语言:txt
复制
void OperatorBase::Run(const Scope &scope, const platform::Place& place) {...}

运行Op时,需指明Scope和Place。Op运行过程中,会首先从Scope中获取输入输出变量,然后从Place中获取设备信息,进行计算。

OpKernel

OperatorWithKernel继承自OperatorBase,我们称继承自OperatorWithKernel的Op为有Kernel的Op。

Kernel的目的是为了区分不同的运行设备(CPU/GPU)、数据类型(float/double/int)、库(MKLDNN/CUDNN)、layout(NCHW/NHWC)等。

一个Op可以有多个Kernel实现,Kernel实现应继承自OpKernel<T>。

OperatorWithKernel重写了OperatorBase的RunImpl方法,进行了以下操作:

  • 根据inputs和outputs,从Scope中找出所有输入输出变量,形成map<string, vector<Variable *>>,构造出ExecutionContext。
  • 根据inputs Tensor的设备、layout等信息,判断是否需要对Tensor进行设备转换、Layout转换等。例如,若前一个Op的输出Tensor的CPU上,当前Op需要运行在GPU上,需要将当前Op的输入Tensor copy到GPU上。在转换过程中,会从当前Scope中new一个新的Scope,并在新Scope中创建同名变量进行Transfer。
  • 调用OperatorWithKernel::InferShape方法推导输出变量的shape信息。
  • 根据inputs Tensor的设备、layout、数据类型等信息,从所有的Kernel中选择合适的Kernel,将ExecutionContext传入OpKernel<T>::Compute方法进行计算。

7. 编译期过程简介

在Python端组网过程中,即调用fluid.layers.xxx API组网时,亦称为编译期,会往Program中插入Op,具体为:

  • 若Op包含参数,在default_startup_program的block 0中插入参数的初始化Op,然后将参数copy到default_main_program中。
  • 在default_main_program中插入相应的Op。

每次往Python端Program插入Op时,均会走以下步骤:

  • 若Op没有Kernel,则不进行任何操作。
  • 若Op有Kernel,则:
    • 若Op有注册InferVarTypeInference,则调用InferVarTypeInference推导输出变量的类型(LoDTensor还是SelectedRows等)。
    • 调用OperatorWithKernel::InferShape方法,根据输入变量推导输出变量的shape。

在调用optimizer.minimize()的过程中,会发生以下几个动作:

  • 调用每个前向Op的GradOpDescMaker创建反向Op,并插入到default_main_program中。
  • 在Program中为每个参数插入各自的optimizer op。

8. 运行期过程简介

当Python端的Program构建完毕后,Executor::Run会取出Program的Block 0中的所有OpDesc,调用OpRegistry::CreateOp方法根据OpDesc创建OperatorBase,然后调用OperatorBase::Run()方法运行所有Op,具体方式为:

代码语言:txt
复制
void Executor::Run(const ProgramDesc &program, const Scope &scope, const platform::Place &place) {
    std::vector<std::unique_ptr<OperatorBase>> ops;
    
    for (auto &op_desc : program.Block(0).AllOps()) {
        auto op = OpRegistry::CreateOp(op_desc);
        ops.emplace_back(std::move(op));
    }
    
    for (auto &op : ops) {
        op->Run(scope, place);
    }
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. Program
  • 2. C++ Place
  • 3. C++ DeviceContext
  • 4. C++ Variable
  • 5. C++ Scope
  • 6. Operator
    • Op的成员
      • OperatorBase
        • OpKernel
        • 7. 编译期过程简介
        • 8. 运行期过程简介
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档