PaddlePaddle核心架构深入解读

2020年5月20日,Imagination 加入百度飞桨硬件生态圈,2021年2月1日,Imagination Technologies 宣布:在 Imagination 和百度飞桨(PaddlePaddle)的合作中,其 IMGDNN API 已集成到 Paddle Lite 中,丰富了全球开发者发展人工智能(AI)的生态系统。


PaddlePaddle(中文名:飞桨,PArallel Distributed Deep LEarning 并行分布式深度学习)是一个深度学习平台,具有易用、高效、灵活和可伸缩等特点,它是中国第一个开源深度学习开发框架。

飞桨框架的核心技术,主要包括前端语言、组网编程范式、核心架构、算子库以及高效率计算核心五部分。

下边分别分析这几部分。

核心架构

飞桨核心架构采用分层设计,如下图所示,前端应用层考虑灵活性,采用Python实现,包括了组网 API、IO API、OptimizerAPI和执行 API等完备的开发接口;框架底层充分考虑性能,采用C++来实现。

框架内核部分,主要包含执行器、存储管理和中间表达优化;内部表示方面,包含网络表示(ProgramDesc)、数据表示(Variable)和计算表示(Operator)几个层面。框架向下对接各种芯片架构,可以支持深度学习模型在不同异构设备上的高效运行。

PaddlePaddle核心架构深入解读

前端语言

为了方便用户使用,飞桨选择Python作为模型开发和执行调用的主要前端语言,并提供了丰富的编程接口API。Python作为一种解释型编程语言,代码修改不需要重新编译就可以直接运行,使用和调试非常方便,并且拥有丰富的第三方库和语法糖,拥有众多的用户群体。

同时为了保证框架的执行效率,飞桨底层实现采用C++。对于预测推理,为方便部署应用,则同时提供了C++和Java API。

组网编程范式

飞桨中同时兼容命令式编程(动态图)与声明式编程(静态图)两种编程范式,以程序化“Program”的形式动态描述神经网络模型计算过程,并提供对顺序、分支和循环三种执行结构的支持,可以组合描述任意复杂的模型,并可在内部自动转化为中间表示的描述语言。

“Program”的定义过程就像在写一段通用程序,使用声明式编程时,相当于将“Program”先编译再执行,可类比静态图模式。

首先根据网络定义代码构造“Program”,然后将“Program”编译优化,最后通过执行器执行“Program”,具备高效性能;同时由于存在静态的网络结构信息,能够方便地完成模型的部署上线。

而命令式编程,相当于将“Program”解释执行,可视为动态图模式,更加符合用户的编程习惯,代码编写和调试也更加方便。

飞桨后面会增强静态图模式下的调试功能,方便开发调试;同时提升动态图模式的运行效率,加强动态图自动转静态图的能力,快速完成部署上线;同时更加完善接口的设计和功能,整体提升框架易用性。

PaddlePaddle核心架构深入解读

显存管理

飞桨为用户提供简单易用、兼顾显存回收与复用的显存优化策略,在很多模型上的表现优异。

显存分配机制

原生的CUDA系统调用(cudaMalloc)和释放(cudaFree)均是同步操作,非常耗时。为了加速显存分配,飞桨实现了显存预分配的策略,具体方式如下图所示。

设置一个显存池chunk,定义其大小为chunk_size。若分配需求requested_size不超过chunk_size,则框架会预先分配chunk_size大小的显存池chunk,并从中分出requested_size大小的块返回。

之后每次申请显存都会从chunk中分配。若requested_size大于chunk_size,则框架会调用cudaMalloc分配requested_size大小的显存。chunk_size一般依据初始可用显存大小按比例确定。

同时飞桨也支持按实际显存占用大小的动态自增长的显存分配方式,可以更精准地控制显存使用,以节省对显存占用量,方便多任务同时运行。

PaddlePaddle核心架构深入解读

显存垃圾及时回收机制

显存垃圾及时回收机制GC(Garbage Collection)的原理是在网络运行阶段释放无用变量的显存空间,达到节省显存的目的。

GC策略会积攒一定大小的显存垃圾后再统一释放。GC内部会根据变量占用的显存大小,对变量进行降序排列,且仅回收前面满足占用大小阈值以上的变量显存。GC策略默认生效于使用Executor或Parallel Executor做模型训练预测时。

Operator内部显存复用机制

Operator内部显存复用机制(Inplace)的原理是Operator的输出复用Operator输入的显存空间。例如,数据整形(reshape)操作的输出和输入可复用同一片显存空间。

Inplace策略可通过构建策略(BuildStrategy)设置生效于Parallel Executor的执行过程中。

算子库

飞桨算子库目前提供了500余个算子,并在持续增加,能够有效支持自然语言处理、计算机视觉、语音等各个方向模型的快速构建。同时提供了高质量的中英文文档,更方便国内外开发者学习使用。文档中对每个算子都进行了详细描述,包括原理介绍、计算公式、论文出处,详细的参数说明和完整的代码调用示例。

飞桨的算子库覆盖了深度学习相关的广泛的计算单元类型。比如提供了多种循环神经网络(Recurrent Neural Network,RNN),多种卷积神经网络(Convolutional Neural Networks, CNN)及相关操作,如深度可分离卷积(Depthwise Deparable Convolution)、空洞卷积(Dilated Convolution)、可变形卷积(Deformable Convolution)、池化兴趣区域池化及其各种扩展、分组归一化、多设备同步的批归一化。

另外涵盖多种损失函数和数值优化算法,可以很好地支持自然语言处理的语言模型、阅读理解、对话模型、视觉的分类、检测、分割、生成、光学字符识别(Optical Character Recognition,OCR)、OCR检测、姿态估计、度量学习、人脸识别、人脸检测等各类模型。

飞桨的算子库除了在数量上进行扩充之外,还在功能性、易用性、便捷开发上持续增强。

例如针对图像生成任务,支持生成算法中的梯度惩罚功能,即支持算子的二次反向能力;而对于复杂网络的搭建,将会提供更高级的模块化算子,使模型构建更加简单的同时也能获得更好的性能;对于创新型网络结构的需求,将会进一步简化算子的自定义实现方式,支持Python算子实现,对性能要求高的算子提供更方便的、与框架解耦的C++实现方式,可使得开发者快速实现自定义的算子,验证算法。

高效率计算核心

飞桨对核心计算的优化,主要体现在以下两个层面。

Operator粒度层面

飞桨提供了大量不同粒度的Operator(Op)实现。细粒度的Op能够提供更好的灵活性,而粗粒度的Op则能提供更好的计算性能。

飞桨提供了诸如softmax_with_cross_entropy等组合功能Op,也提供了像fusion_conv_inception、fused_elemwise_activation等融合类Operator。

其中大部分普通Op,用户可以直接通过Python API配置使用,而很多融合的Op,执行器在计算图优化的时候将会自动进行子图匹配和替换。

核函数实现层面

飞桨主要通过两种方式来实现对不同硬件的支持:人工调优的核函数实现和集成供应商优化库。

针对CPU平台,飞桨一方面提供了使用指令Intrinsic函数和借助于xbyak JIT汇编器实现的原生Operator,深入挖掘编译时和运行时性能。

另一方面,飞桨通过引入OpenBLAS、Intel® MKL、Intel® MKL-DNN 和nGraph,对Intel CXL等新型芯片提供了性能保证。

针对GPU平台,飞桨既为大部分Operator用CUDA C实现了经过人工精心优化的核函数,也集成了cuBLAS、cuDNN等供应商库的新接口、新特性。

来源:OSC开源社区

最新文章