<!doctype html><html lang="zh-cmn-Hans-CN"><head><meta charset="utf-8"/><!--[if IE]><link rel="shortcut icon" href="https://paddlepaddle-org-cn.cdn.bcebos.com/paddle-site-front/favicon.ico"><![endif]--><link rel="icon" href="https://paddlepaddle-org-cn.cdn.bcebos.com/paddle-site-front/favicon-48.png" sizes="48x48"><link rel="icon" href="https://paddlepaddle-org-cn.cdn.bcebos.com/paddle-site-front/favicon-64.png" sizes="64x64"><link rel="icon" href="https://paddlepaddle-org-cn.cdn.bcebos.com/paddle-site-front/favicon-128.png" sizes="128x128"><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#fff"/><link rel="manifest" href="https://paddlepaddle-org-cn.cdn.bcebos.com/paddle-site-front/manifest.json"/><title>飞桨PaddlePaddle-源于产业实践的开源深度学习平台</title><meta name="keywords" content="开源深度学习平台,PaddlePaddle Fluid,飞桨PaddlePaddle官网,飞桨PaddlePaddle教程,飞桨PaddlePaddle框架,飞桨PaddlePaddle使用,飞桨PaddlePaddle book,飞桨,百度飞桨,PaddlePaddle"/><meta name="description" content="飞桨致力于让深度学习技术的创新与应用更简单。具有以下特点:同时支持动态图和静态图,兼顾灵活性和效率;精选应用效果最佳算法模型并提供官方支持;真正源于产业实践,提供业界最强的超大规模并行深度学习能力;推理引擎一体化设计,提供训练到多端推理的无缝对接;唯一提供系统化技术服务与支持的深度学习平台"/><script>if(void 0===_historyPolyfill){var _historyPolyfill=function(t){var n=history[t];function e(t){var n;return"function"==typeof Event?n=new Event(t):(n=document.createEvent("Event")).initEvent(t,!0,!0),n}return function(){var i=n.apply(this,arguments),o=t.toLowerCase(),r="on"+o,a=e(o);return a.arguments=arguments,window.dispatchEvent(a),"function"==typeof window[r]&&window[r].apply(this,arguments),i}};history.pushState=_historyPolyfill("pushState"),history.replaceState=_historyPolyfill("replaceState")}</script><script>var _hmt=_hmt||{};!function(){var e=function(){_hmt=_hmt||{};var e=document.getElementById("baidu-tj");e&&e.parentNode.removeChild(e);var t=document.createElement("script");t.id="baidu-tj",t.src="https://hm.baidu.com/hm.js?89be97848720f62fa00a07b1e0d83ae6";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(t,n),setTimeout((function(){"function"!=typeof _hmt.push&&(_hmt=void 0)}),0)};e(),window.addEventListener("popstate",e),window.addEventListener("pushstate",e),window.addEventListener("replacestate",e)}()</script><script>!function(){var t=function(){var t=document.getElementById("baidu-push");t&&t.parentNode.removeChild(t);var e=document.createElement("script");e.id="baidu-push";var n=window.location.protocol.split(":")[0];e.src="https"===n?"https://zz.bdstatic.com/linksubmit/push.js":"http://push.zhanzhang.baidu.com/push.js";var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(e,a)};t(),window.addEventListener("popstate",t),window.addEventListener("pushstate",t),window.addEventListener("replacestate",t)}()</script><style>.paddle-s-e-o{position:absolute;overflow:hidden;height:0;width:0}</style><link href="https://paddlepaddle-org-cn.cdn.bcebos.com/paddle-site-front/static/css/0.6d26e789.chunk.css" rel="stylesheet"><link href="https://paddlepaddle-org-cn.cdn.bcebos.com/paddle-site-front/static/css/1.5ef1cb33.chunk.css" rel="stylesheet"><link href="https://paddlepaddle-org-cn.cdn.bcebos.com/paddle-site-front/static/css/document.d8b47223.chunk.css" rel="stylesheet"></head><body class="wy-body-for-nav"><style>.paddle-logo{position:absolute;overflow:hidden;height:0;width:0}</style><div class="paddle-logo"><img src="https://paddlepaddle-org-cn.cdn.bcebos.com/paddle-site-front/static/media/paddlelogo.0b483fa7.png"/></div><noscript>You need to enable JavaScript to run this app.</noscript><div style="height:0;overflow:hidden">\u200E</div><div id="root"></div><script>(function(){window.pageData={"navData":[{"title":"开始使用","href":"/start","items":[]},{"title":"特性","href":"/feature","items":[]},{"title":"文档","href":"","items":[{"title":"API","href":"/documentation/docs/zh/1.5/api_cn/index_cn.html?from=paddlenav","items":[]},{"title":"使用指南","href":"/documentation/docs/zh/1.5/user_guides/index_cn.html?from=paddlenav","items":[]}]},{"title":"工具平台","href":"","items":[{"title":"工具","href":"","items":[{"title":"AutoDL","href":"https://github.com/PaddlePaddle/AutoDL/tree/master/AutoDL%20Design"}]}]}],"seo":{"title":"飞桨PaddlePaddle-源于产业实践的开源深度学习平台","keywords":"开源深度学习平台,PaddlePaddle Fluid,飞桨PaddlePaddle官网,飞桨PaddlePaddle教程,飞桨PaddlePaddle框架,飞桨PaddlePaddle使用,飞桨PaddlePaddle book,飞桨,百度飞桨,PaddlePaddle","description":"飞桨致力于让深度学习技术的创新与应用更简单。具有以下特点:同时支持动态图和静态图,兼顾灵活性和效率;精选应用效果最佳算法模型并提供官方支持;真正源于产业实践,提供业界最强的超大规模并行深度学习能力;推理引擎一体化设计,提供训练到多端推理的无缝对接;唯一提供系统化技术服务与支持的深度学习平台"},"pageData":null}})();</script><script>window.docInfo={};</script><script>(function(){window.docInfo.lang="zh";})();</script><script>(function(){window.docInfo.version="1.5";})();</script><div class="paddle-s-e-o"><div class="header"><ul><li><a href="/start">开始使用</a></li><li><a href="/feature">特性</a></li><li>文档<ul><li><a href="/documentation/docs/zh/1.5/api_cn/index_cn.html?from=paddlenav">API</a></li><li><a href="/documentation/docs/zh/1.5/user_guides/index_cn.html?from=paddlenav">使用指南</a></li></ul></li><li>工具平台<ul><li>工具<ul><li><a href="https://github.com/PaddlePaddle/AutoDL/tree/master/AutoDL%20Design">AutoDL</a></li></ul></li></ul></li></ul></div></div><div class="paddle-s-e-o"><div class="docheader"><ul id="doc_version" class="doc-version"><li><a href="/change-version?version=develop">develop</a></li><li><a href="/change-version?version=2.1">2.1</a></li><li><a href="/change-version?version=2.0">2.0</a></li><li><a href="/change-version?version=1.8">1.8</a></li><li><a href="/change-version?version=1.7">1.7</a></li><li><a href="/change-version?version=1.6">1.6</a></li><li class="current"><a href="/change-version?version=1.5">1.5</a></li><li><a href="/change-version?version=1.4">1.4</a></li><li><a href="/change-version?version=1.3">1.3</a></li><li><a href="/change-version?version=1.2">1.2</a></li><li><a href="/change-version?version=1.1">1.1</a></li><li><a href="/change-version?version=1.0">1.0</a></li><li><a href="/change-version?version=0.15.0">0.15.0</a></li><li><a href="/change-version?version=0.14.0">0.14.0</a></li><li><a href="/change-version?version=0.13.0">0.13.0</a></li><li><a href="/change-version?version=0.12.0">0.12.0</a></li><li><a href="/change-version?version=0.11.0">0.11.0</a></li><li><a href="/change-version?version=0.10.0">0.10.0</a></li></ul><ul id="doc_lang" class="doc-lang"><li class="current"><a href="/change-lang?lang=zh">中文(简)</a></li><li><a href="/change-lang?lang=en">English(En)</a></li></ul></div><div id="document_content"># 开发者文档 ## 基本概念 ### Place `Place`类确定了kernel运行时的上下文信息,其中包含了kernel运行时所在的平台,执行运算数据的精度以及数据的布局等信息,使得MIR的分析更加清晰准确。它主要的成员变量如下: * `TargetType target`: kernel运行时所在的平台,如X86/CUDA/ARM等; * `PrecisionType precision`: kernel执行运算的数据的精度,如Float, Int8, Fp16等; * `DataLayoutType layout`: kernel执行运算的数据的布局,如NCHW, NHWC等; ### OpLite `Oplite`类负责协助kernel计算,本身不具备计算功能,主要的接口功能包括: * `CheckShape`: 用于检查op的输入/输出参数维度、类型是否合法,以及属性信息是否符合设计; * `InferShape`: 用于设置输出Tensor的形状信息; * `CreateKernels`: 创建相关的kernel; * `Attach`: 用于从`Scope`和`OpDesc`中获取参数的指针,并传递给kernel; 重要方法及声明如下: ```c++ class OpLite : public Registry { public: OpLite() = default; explicit OpLite(const std::string &type) : op_type_(type) {} explicit OpLite(const std::vector<Place> &valid_places) : valid_places_(valid_places) {} void SetValidPlaces(const std::vector<Place> &places) { VLOG(3) << "valid places " << valid_places_.size(); valid_places_ = places; } // Set supported places const std::vector<Place> &valid_places() const { return valid_places_; } // Check the shape. virtual bool CheckShape() const { return true; } // Inference the outputs' shape. virtual bool InferShape() const { return true; } // Run this operator. virtual bool Run(); // Link the external execution environ to internal context. bool Attach(const cpp::OpDesc &opdesc, lite::Scope *scope); // Create all the kernels for the valid targets. std::vector<std::unique_ptr<KernelBase>> CreateKernels( const std::vector<Place> &places, const std::string &kernel_type = ""); // Assign op param to kernel. virtual void AttachKernel(KernelBase *kernel) = 0; }; ``` ### KernelLite 为了提升kernel对`Target`, `Precision`, `DataLayout`等多种执行模式的支持,引入了`KernelLite`的概念,它主要有以下特点: * 可以通过模版特化不同`Place`和kernel的实现,加强对不同执行模式的支持; * 轻量级,`KernelLite`类似functor,只有执行的职能,执行效率更高; * 每个kernel有明确执行的模式,并且可以在analysis time参与分析; * 依赖简单,便于部署到mobile执行; * 硬件调度信息等`context`跟具体的kernel绑定,方便定制不同kernel的行为。 重要的方法及声明如下: ```c++ template <TargetType Target, PrecisionType Precision, DataLayoutType DataLayout = DataLayoutType::kNCHW> class KernelLite : public KernelBase { public: // Run the kernel. virtual void Run() { CHECK(false) << "Not Implemented"; } // Set target TargetType target() const override { return Target; } // Set precision PrecisionType precision() const override { return Precision; } // Set data layout DataLayoutType layout() const override { return DataLayout; } Place place() const override { return Place{Target, Precision, DataLayout}; } void Touch() {} KernelLite() = default; virtual ~KernelLite() = default; }; ``` ## 架构简介 Mobile 在这次升级为 lite 架构, 侧重多硬件、高性能的支持,其主要设计思想如下 - 引入 Type system,强化多硬件、量化方法、data layout 的混合调度能力 - 硬件细节隔离,通过不同编译开关,对支持的任何硬件可以自由插拔 - 引入 MIR(Machine IR) 的概念,强化带执行环境下的优化支持 - 优化期和执行期严格隔离,保证预测时轻量和高效率 架构图如下  ## 增加新 Kernel的方法 下面主要介绍op新增kernel如何写,简单总结新增kernel的实现需要包含如下内容: - kernel实现:继承自`KernelLite`类的对应op的Compute类定义与实现,根据输入的数据类型,数据布局,数据所在的设备以及运行时所调用的第三方库的不同实现不同的kernel;server端CPU kernel实现在.h文件中。 - kernel注册:server端CPU kernel注册实现在.cc文件。 ## 实现C++类 以mul op的CPU Kernel实现为例,mul kernel执行运算的矩阵乘法的公式为*Out* = *X* * *Y*, 可见该计算由两个输入,一个输出组成; 输入输出参数分别从OP的param中获取,如mul op的param定义如下: ```c++ struct MulParam { const lite::Tensor* x{}; const lite::Tensor* y{}; lite::Tensor* output{}; int x_num_col_dims{1}; int y_num_col_dims{1}; }; ``` 下面开始定义`MulCompute`类的实现: ```c++ template <typename T> class MulCompute : public KernelLite<TARGET(kX86), PRECISION(kFloat)> { public: using param_t = operators::MulParam; void Run() override { auto& context = ctx_->As<X86Context>(); auto& param = *param_.get_mutable<operators::MulParam>(); CHECK(context.x86_device_context()); //1. 为output分配内存 param.output->template mutable_data<T>(); // 2. 获取计算用的输入输出 auto* x = ¶m.x->raw_tensor(); auto* y = ¶m.y->raw_tensor(); auto* z = ¶m.output->raw_tensor(); //3. 对输入输出数据进行需要的处理... Tensor x_matrix, y_matrix; if (x->dims().size() > 2) { x_matrix = framework::ReshapeToMatrix(*x, param.x_num_col_dims); } else { x_matrix = *x; } //4. 调用数学库进行矩阵的运算... auto blas = paddle::operators::math::GetBlas<platform::CPUDeviceContext, T>( *context.x86_device_context()); blas.MatMul(x_matrix, y_matrix, z); } virtual ~MulCompute() = default; }; ``` `MulCompute`类继承自`kernelLite`, 带有下面两个模版参数: - `TARGET(kX86)`: `Target`代表的是硬件信息,如CUDA/X86/ARM/…,表示该kernel运行的硬件平台,在该示例中我们写的是kX86,表示mul这个kernel运行在X86平台上; - `PRECISION(kFloat)`:`Precision`代表该kernel运算支持的数据精度信息,示例中写的是`kFloat`, 表示mul这个kernel支持Float数据的运算; 需要为`MulCompute`类重写`Run`接口, kernel 的输入和输出分别通过`MulParam`获得,输入/输出的变量类型是`lite::Tensor`。 到此,前向mul kernel的实现完成,接下来需要在.cc文件中注册该kernel。 ## 注册kernel 在.cc文件中注册实现的kernel: ```c++ REGISTER_LITE_KERNEL(mul, kX86, kFloat, kNCHW, paddle::lite::kernels::x86::MulCompute<float>, def) .BindInput("X", {LiteType::GetTensorTy(TARGET(kX86))}) .BindInput("Y", {LiteType::GetTensorTy(TARGET(kX86))}) .BindOutput("Out", {LiteType::GetTensorTy(TARGET(kX86))}) .Finalize(); ``` 在上面的代码中; - `REGISTER_LITE_KERNEL`: 注册MulCompute类,并特化模版参数为float类型, 类型名为mul, 运行的平台为X86, 数据精度为float, 数据布局为NCHW; - 在运行时,框架系统根据输入数据所在的设备,输入数据的类型,数据布局等信息静态的选择合适的kernel执行运算。 ## 开发环境 ### Mobile端开发和测试 我们提供了移动端开发所需的docker镜像环境,在`paddle/fluid/lite/tools/Dockerfile.mobile`,可以直接通过 `docker build --file paddle/fluid/lite/tools/Dockerfile.mobile --tag paddle-lite-mobile:latest . `生成镜像文件。 该镜像中提供了 - Android端的交叉编译环境 - ARM Linux端的交叉编译环境 - Android端的模拟器环境 - 开发所需的格式检查工具 #### 相关的cmake选项 目前支持如下的编译配置,以生成不同目标上的程序。 - `ARM_TARGET_OS` 代表目标操作系统, 目前支持 "android" "armlinux", 默认是Android - `ARM_TARGET_ARCH_ABI` 代表ARCH,支持输入"armv8"和"armv7",针对OS不一样选择不一样。 - `-DARM_TARGET_OS="android"` 时 - "armv8", 等效于 "arm64-v8a"。 default值为这个。 - "armv7", 等效于 "armeabi-v7a"。 - `-DARM_TARGET_OS="armlinux"` 时 - "armv8", 等效于 "arm64"。 default值为这个。 - "armv7hf", 等效于使用`eabihf`且`-march=armv7-a -mfloat-abi=hard -mfpu=neon-vfpv4 `。 - "armv7", 等效于使用`eabi`且`-march=armv7-a -mfloat-abi=softfp -mfpu=neon-vfpv4`。 - `ARM_TARGET_LANG` 代表目标编译的语言, 默认为gcc,支持 gcc和clang两种。 注意: ARM Linux当前仅支持在armv8上编译并测试。 #### 开发 添加新的ARM端kernel,主要分为3部分: 1. 添加具体的数学计算,在`paddle/fluid/lite/arm/math`中添加对应的数学函数,侧重点在于代码本身的优化,充分利用NEON指令发挥其优势。 2. 添加kernel声明和调用实例,在`paddle/fluid/lite/kernels/arm`中添加对应kernel的框架声明和调用,侧重点在于每种kernel严格对应输入输出的类型。 3. 添加单元测试,在`paddle/fluid/lite/kernels/arm`中添加相应的单元测试,并保持其在模拟器或者真机中可以通过。 #### 测试 我们在镜像开发环境中添加了`arm64-v8a`和`armeabi-v7a`的Android模拟环境,在没有真机环境下,可以很方便的用于测试对应平台上的单元测试。 常用步骤如下 ```shell # 创建Android avd (armv8) $ echo n | avdmanager create avd -f -n paddle-armv8 -k "system-images;android-24;google_apis;arm64-v8a" # 启动Android armv8 emulator $ ${ANDROID_HOME}/emulator/emulator -avd paddle-armv8 -noaudio -no-window -gpu off -verbose & # 其他正常测试步骤 # 关闭所有模拟器 $ adb devices | grep emulator | cut -f1 | while read line; do adb -s $line emu kill; done ``` </div></div><script>!function(e){function t(t){for(var n,l,p=t[0],a=t[1],f=t[2],c=0,s=[];c<p.length;c++)l=p[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(i&&i(t);s.length;)s.shift()();return u.push.apply(u,f||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,p=1;p<r.length;p++){var a=r[p];0!==o[a]&&(n=!1)}n&&(u.splice(t--,1),e=l(l.s=r[0]))}return e}var n={},o={58:0},u=[];function l(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,l),r.l=!0,r.exports}l.m=e,l.c=n,l.d=function(e,t,r){l.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,t){if(1&t&&(e=l(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(l.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)l.d(r,n,function(t){return e[t]}.bind(null,n));return r},l.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(t,"a",t),t},l.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},l.p="https://paddlepaddle-org-cn.cdn.bcebos.com/paddle-site-front/";var p=this["webpackJsonppaddle-site-front"]=this["webpackJsonppaddle-site-front"]||[],a=p.push.bind(p);p.push=t,p=p.slice();for(var f=0;f<p.length;f++)t(p[f]);var i=a;r()}([])</script><script src="https://paddlepaddle-org-cn.cdn.bcebos.com/paddle-site-front/static/js/0.381d6006.chunk.js"></script><script src="https://paddlepaddle-org-cn.cdn.bcebos.com/paddle-site-front/static/js/1.7178e4d4.chunk.js"></script><script src="https://paddlepaddle-org-cn.cdn.bcebos.com/paddle-site-front/static/js/document.2d1c02c2.chunk.js"></script></body></html>