未验证 提交 1da984ab 编写于 作者: J juncaipeng 提交者: GitHub

add v2.0.0 doc, test=develop (#2327)

* add v2.0.0 doc, test=develop

* add test result. test=develop
上级 a480952a
---
layout: post
title: 新增op的方法
---
以下以添加argmax为例,详细说明新增op的方法。
## 1. 添加OpParam 结构体以传导 Op 的输入和输出
- 这里命名为 `ArgmaxParam`
-`paddlelite/lite/operators/op_params.h` 中添加 `ArgmaxParam` 结构体,代码如下:
```c++
struct ArgmaxParam {
lite::Tensor* X{};
lite::Tensor* Out{};
int Axis{0};
};
```
## 2. 添加 Argmax Op 并注册
- 在paddlelite/lite/operators/目录下新建argmax_op.h文件,主要代码如下:
```c++
class ArgmaxOpLite : public OpLite {
public:
ArgmaxOpLite() {}
explicit ArgmaxOpLite(const std::string &op_type) : OpLite(op_type) {}
bool CheckShape() const override;
bool InferShape() const override;
bool AttachImpl(const cpp::OpDesc &opdesc, lite::Scope *scope) override;
void AttachKernel(KernelBase *kernel) override { kernel->SetParam(param_); }
std::string DebugString() const override { return "argmax"; }
private:
mutable ArgmaxParam param_;
};
```
`ArgmaxOpLite` 继承 `OpLite` ,成员变量包括 `ArgmaxParam` 结构体,需要实现的接口包括 `CheckShape()``InferShape()``AttachImp()``AttachKernel()``DebugString()` 函数。`AttachKernel()``DebugString() `函数较为简单,此处直接实现;
-`paddlelite/lite/operators/` 目录下新建argmax_op.cc文件,需要具体实现`CheckShape()``InferShape()``AttachImp()`函数。`CheckShape()`函数检查输入是否符合要求,`InferShape()`函数基于输入推断得到输出的维度,`AttachImp()`函数绑定Op的输入输出。然后在argmax_op.cc文件中注册argmax,核心代码如下:
```c++
bool ArgmaxOpLite::CheckShape() const {
CHECK_OR_FALSE(param_.X);
CHECK_OR_FALSE(param_.Out);
CHECK_OR_FALSE(param_.Axis < (param_.X)->dims().size());
return true;
}
bool ArgmaxOpLite::InferShape() const {
auto x_dims = param_.X->dims();
int x_rank = x_dims.size();
int axis = param_.Axis;
if (axis < 0) axis += x_rank;
std::vector<int64_t> out_dims;
for (int64_t i = 0; i < axis; i++) {
out_dims.push_back(x_dims[i]);
}
for (int64_t i = axis + 1; i < x_rank; i++) {
out_dims.push_back(x_dims[i]);
}
// Set output dims
param_.Out->Resize(lite::DDim(out_dims));
return true;
}
bool ArgmaxOpLite::AttachImpl(const cpp::OpDesc &op_desc, lite::Scope *scope) {
auto x = op_desc.Input("X").front();
auto out = op_desc.Output("Out").front();
param_.X = scope->FindVar(x)->GetMutable<lite::Tensor>();
param_.Out = scope->FindVar(out)->GetMutable<lite::Tensor>();
param_.Axis = op_desc.GetAttr<int>("Axis");
return true;
}
REGISTER_LITE_OP(argmax, paddle::lite::operators::ArgmaxOpLite);
```
- 在paddlelite/lite/operators/CMakeLists.txt中添加```add_operator(argmax_op basic SRCS argmax_op.cc DEPS ${op_DEPS})```
## 3. 添加Argmax Kernel并绑定
以下以arm端argmax实现为例说明
- 在paddlelite/lite/kernels/arm/目录下新建argmax_compute.h文件,声明ArgmaxCompute类,并继承KernelLite,主要代码如下:
```c++
class ArgmaxCompute : public KernelLite<TARGET(kARM), PRECISION(kFloat)> {
public:
using param_t = operators::ArgmaxParam;
void Run() override;
virtual ~ArgmaxCompute() = default;
};
```
- 在paddlelite/lite/kernels/arm/目录下新建argmax_compute.cc文件,主要实现Run函数。`Run()`函数调用paddlelite/lite/bachends/arm/math/argmax.h中的`argmax_func()`函数,根据输入计算输出。最后在argmax_compute.cc文件中,我们绑定argmax的输入输出(为tensor的输入参数都需要绑定),代码如下:
```c++
void ArgmaxCompute::Run() {
auto& param = Param<operators::ArgmaxParam>();
lite::Tensor* input = param.X;
lite::Tensor* output = param.Out;
int axis = param.Axis;
lite::arm::math::argmax_func(input, axis, output);
return;
}
REGISTER_LITE_KERNEL(
argmax, kARM, kFloat, kNCHW, paddle::lite::kernels::arm::ArgmaxCompute, def)
.BindInput("X", {LiteType::GetTensorTy(TARGET(kARM))})
.BindOutput("Out", {LiteType::GetTensorTy(TARGET(kARM))})
.Finalize();
```
- 在paddlelite/lite/kernels/arm/CMakeLists.txt中添加
```cmake
add_kernel(argmax_compute_arm ARM basic SRCS argmax_compute.cc DEPS ${lite_kernel_deps} math_arm)
```
## 4. 添加Argmax实现
- 在paddlelite/lite/backends/arm/math/目录下新建argmax.h文件,声明`argmax_func()`函数,代码如下:
```c++
void argmax_func(const lite::Tensor* input, const int axis, lite::Tensor* output);
```
- 在paddlelite/lite/backends/arm/math/目录下新建argmax.cc文件,具体实现`argmax_func()`函数,代码如下:
```c++
void argmax_func(const lite::Tensor *input,
const int axis,
lite::Tensor *output) {
auto input_ddim = input->dims();
auto output_ddim = output->dims();
const int size = input_ddim[axis];
const int in_channel = input_ddim.count(axis, input_ddim.size());
const int out_channel = output_ddim.count(axis, output_ddim.size());
const int in_stride = input_ddim.count(axis + 1, input_ddim.size());
const int out_stride = input_ddim.count(0, axis);
for (int n = 0; n < out_stride; n++) {
for (int k = 0; k < in_stride; k++) {
const float *in_ptr = input->data<float>() + n * in_channel + k;
std::vector<std::pair<float, int>> vec;
vec.resize(size);
for (int i = 0; i < size; i++) {
vec[i] = std::make_pair(in_ptr[i * in_stride], i);
}
// sort
std::partial_sort(vec.begin(),
vec.begin() + 1,
vec.end(),
std::greater<std::pair<float, int>>());
// out
float *out_ptr = output->mutable_data<float>() + n * out_channel + k;
*out_ptr = vec[0].second;
}
}
}
```
- 在paddlelite/lite/backends/arm/math/CMakeFile.txt中的```math_arm library```中添加argmax.cc,在paddlelite/lite/backends/arm/math/funcs.h中添加```#include "lite/arm/math/argmax.h"```
## 5. 添加Argmax单测
- 在paddlelite/lite/tests/kernels目录下新建argmax_compute_test.cc文件,声明并实现ArgmaxComputeTester类;
- ArgmaxComputeTester类中主要包括PrepareOpDesc、PrepareData和RunBaseline函数。PrepareOpDesc函数设定单测op的类型和输入输出参数,PrepareData函数对输入tensor进行初始化,RunBaseline是基于输入计算得到输出,用于和框架计算的输出进行对比;
- 使用gtest添加单测,代码如下:
```c++
TEST(Argmax, precision) {
#ifdef LITE_WITH_ARM
LOG(INFO) << "test argmax arm";
Place place(TARGET(kARM));
for (int axis : {0, 1, 2, 3}) {
for (int n : {1, 3}) {
for (int c : {3, 6}) {
for (int h : {9, 18}) {
for (int w : {9, 18}) {
std::unique_ptr<arena::TestCase> tester(
new ArgmaxComputeTester(place, "def", axis, n, c, h, w));
arena::Arena arena(std::move(tester), place, 2e-5);
arena.TestPrecision();
}
}
}
}
}
#endif
}
```
- 在paddlelite/lite/tests/kernels/CMakeLists.txt中添加
```cmake
lite_cc_test(test_kernel_argmax_compute SRCS argmax_compute_test.cc DEPS arena_framework ${x86_kernels} ${arm_kernels} ${lite_ops} ${host_kernels})
```
# 6. 编译运行
- 在paddlelite目录中,执行```./lite/tools/ci_build.sh build_test_arm```,该脚本会创建手机模拟器,并编译运行所有单测(花费时间较久)。如果运行无误,则表明添加argmax成功。
---
layout: post
title: Paddle-Lite 开发者文档
---
这篇文档会从开发者角度详细介绍开发 Paddle-Lite 需要的相关信息。
## 设计及思考
近年来,各种深度学习预估硬件称出不穷,从手机APP到车载设备,再到音箱,均需要部署深度学习预测,且有如下共性需求:
1. 高性能
2. 硬件支持和扩展容易
3. 轻量级部署
Paddle-Lite 的架构方面便是定向参考如上需求设计实现的,具体地
- 高性能方面
- 通过 MIR(Machine IR) 实现精细复杂的计算图的分析和优化
- 执行期 Kernel 的简单设计,几乎没有额外调度开销
- 适当的硬件层抽象,框架支持各个硬件后端中做特定的调度实现
- 轻量级部署方面
- 拆分分析和执行两个阶段,执行阶段轻量级实现,可以单独部署
- 轻量级 Op 和 Kernel 设计
- 硬件支持和扩展方面
- 通过 MIR 支撑带硬件和执行信息的宏观分析优化
- TypeSystem 抽象带硬件的不同计算模式的表示,实现整个计算图的强类型推导,以及执行状态机的静态分析
Paddle-Lite 的架构尝试从强类型推导的角度建模支持多硬件,多种计算模式(不同量化精度、不同的 data layout等)的混合计算,从而实现宏观上的各异硬件和计算模式的混合。
框架部分已经经过 FPGA,GPU,NPU 等异构硬件的打磨,各项能力也在完善中。
## 重要模块介绍
### OpLite
[OpLite](https://github.com/PaddlePaddle/Paddle-Lite/blob/v2.0.0-beta1-prerel/lite/core/op_lite.h#L52) 是 Paddle-Lite 中的 Operator,用户扩展单个硬件时,最多的就是扩展 Op 和 Kernel。
重要方法如下:
```c++
class OpLite : public Registry {
public:
// Check the shape.
virtual bool CheckShape() const { return true; }
// Inference the outputs' shape.
virtual bool InferShape() const { return true; }
// Link the external execution environ to internal context.
bool AttachImpl(const cpp::OpDesc &opdesc, lite::Scope *scope);
};
```
其中,分析期执行
- `AttachImpl`
执行期执行
- `CheckShape`
- `InferShape`
扩展须知:
1. `CheckShape` 只在第一个 batch 执行,所以耗时不敏感
2. `InferShape` 需要在每个 batch 执行,应该严格耗时
1. 可以通过添加 member variable 的方式,对其中一部分信息增加 cache,比如
```c++
class XXOp : public OpLite {
void InferShape() {
int batch_size = param().input.shape[0];
if (!shape_cache_.empty()) {
shape_cache_[0] = batch_size;
param().output->Resize(shape_cache_);
}
}
private:
shape_t shape_cache_;
}
```
### OpParam
[OpParam](https://github.com/PaddlePaddle/Paddle-Lite/blob/v2.0.0-beta1-prerel/lite/operators/op_params.h) 用于存储执行期 Kernel 需要的各项参数。 所有字段可以直接存储(比如指针或者 `int`),以避免执行中获取参数的延迟。
因为没有需求,OpParam 暂时没有设置基类。
实际例子:
```c++
// For Softmax op
struct SoftmaxParam {
lite::Tensor* x{};
lite::Tensor* output{};
int axis{-1};
};
```
OpLite 的 `AttachImpl` 方法就用于构建 `OpParam` ,复制传递给 `Kernel` 用于执行。
OpParam 是执行期的重要模块,需要严格保证性能,相应的扩展要求:
1. 字段的获取必须是低延迟的,可以直接用指针,或者直接复制值
2. 避免执行无关信息混入,包括 debug 信息
3. 命名需要与 Paddle OpDesc 中的信息严格一致,以降低功能对齐和理解的难度
### 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"; }
TargetType target() const override { return Target; }
PrecisionType precision() const override { return Precision; }
DataLayoutType layout() const override { return DataLayout; }
Place place() const override { return Place{Target, Precision, DataLayout}; }
std::string name() const override;
};
```
由于是执行期的重要概念,因此 Kernel 设计地非常简单高效。
其中,执行期的 `Run` 是其唯一重要的接口,其中包含具体的计算逻辑。
模板中的参数主要用于方便多硬件编译,以及自解释:
- Target: 执行硬件
- Precision: 主要的计算精度
- DataLayout:主要计算的 data layout
这部分信息用于帮助挑选 kernel,具体的值并不严格。
Kernel 的注册需要用到 TypeSystem,不光对 Kernel 本身的特性进行描述,对其输入和输出均进行详尽的定义。
例如 FullyConnected 的注册
```c++
REGISTER_LITE_KERNEL(
fc, kARM, kFloat, kNCHW, paddle::lite::kernels::arm::FcCompute, def)
.BindInput("Input", {LiteType::GetTensorTy(TARGET(kARM), PRECISION(kFloat), LAYOUT(kNCHW))})
.BindInput("Bias", {LiteType::GetTensorTy(TARGET(kARM))})
.BindInput("W", {LiteType::GetTensorTy(TARGET(kARM))})
.BindOutput("Out", {LiteType::GetTensorTy(TARGET(kARM))})
.Finalize();
```
Kernel自身定义是 `kARM` 的,也就是ARM上的kernel,主要的计算精度是 `kFloat`,主要的 Data layout 是 `kNCHW`
接着会对其所有的输入和输出做详细定义,比如看 `Input` 输入的定义是 `LiteType::GetTensorTy(TARGET(kARM), PRECISION(kFloat), LAYOUT(kNCHW))`,也就是声明其 Target 是 `kARM`, PRECISION 是 `kFloat`,Data Layout 是 `kNCHW`
这里的设计思想是类似C++中的函数重载,同一个 Kernel(的名字),在重载了其输入输出的类型之后可以是不同的kernel。
#### 扩展须知
1. 模板参数选用计算中主要的来表示
1. 比如,scale kernel,同时能接受 `float``int` 的输入,但其不算量化 kernel,那应该设置为 `Precision=float`,代表常规的计算精度中使用
2. Kernel 输入输出的定义需要足够精确,是什么类型就是什么类型;框架会根据其输入输出的定义来动态构建状态机,否则会出现分析期和执行期的状态机不一致,造成未定义行为
### MIR
MIR 类似于 LLVM 里的 IR,只是加上了硬件和执行期的信息参与分析优化。
Pass 是MIR中的模块化策略,其输入和输出都是 SSA Graph.
框架会自动基于模型的Program 构建 SSA Graph,之后按 [Optimizer](https://github.com/PaddlePaddle/Paddle-Lite/blob/v2.0.0-beta1-prerel/lite/core/optimizer.h) 中定义的pass的顺序调用一系列 Pass。
#### Op Fusion
MIR 中的 [PatternMacher](https://github.com/PaddlePaddle/Paddle-Lite/blob/v2.0.0-beta1-prerel/lite/core/mir/pattern_matcher.h) 实现了简单有效的基于图的模板识别的算法,相关的 op fusion 的图操作可以基于此实现。
实际的例子可以参考 [fc_fuse_pass.h](https://github.com/PaddlePaddle/Paddle-Lite/blob/v2.0.0-beta1-prerel/lite/core/mir/fusion/fc_fuse_pass.h)
### TypeSystem
TypeSystem 是 Paddle-Lite 中构建复杂计算图的基础模块,核心思想是协助 SSA Graph 构建一个状态机,表示其中不同的状态。
这里的 Type 主要包含下面四组信息,更多的信息可以按需扩展:
- TargetType
- Precision
- DataLayout
- device id,用于表示卡号
状态机的表示:
```python
Tensor0(kARM, kFloat, kNCHW) --pass--> Tensor1(kOpenCL, kFloat, kNCHW)
```
MIR 会识别出,Tensor0 和 Tensor1 的硬件位置不同,因此触发相依的 Pass 插入对应的 cast op 来进行 type cast,比如
```
Tensor0(kARM, kFloat, kNCHW) --pass-> IoCopyOp(kARM, kOpenCL) --pass-> Tensor1(kOpenCL, kFloat, kNCHW)
```
### KernelContext
KernelContext 是硬件支持的核心封装,主要用于为 Kernel 提供执行期的硬件上下文。
KernelContext 的设计类似于 OpParam,两者均没有基类;对于 KernelContext,其假定是,不同的硬件间的接口和逻辑可能完全不同,比如 kARM 和 kCUDA,因此不设定基类,也不需要提供统一的接口来封装不同硬件行为。
不同硬件的 KernelContext 直接与该硬件对应的 Kernel 对接。
KernelContext 的行为可以被 MIR 在分析期确定和调度。
注意事项:
1. 由于是执行期概念,KernelContext 也需要注意性能和轻量化
2. 移动端部署时只会部署执行期,因此 MIR 和 KernelContext 会拆开,因此 KernelContext 相应的设置需要能够序列化到 ProgramDesc 中,以便执行期载入和执行
## 扩展硬件后端
### 扩展现有的硬件后端
主要是扩充 Op 和 Kernel 的工作,如果需要 fuse,则参考 MIR 章节,增加相应的fuse pass便可,具体地,可以参考
- [fc_op](https://github.com/PaddlePaddle/Paddle-Lite/blob/release/v2.0.0-beta1/lite/operators/fc_op.h) 实现类似的 Op
- [fc_compute](https://github.com/PaddlePaddle/Paddle-Lite/blob/release/v2.0.0-beta1/lite/kernels/arm/fc_compute.h) 实现类似的 Kernel
- [fc_fuse_pass](https://github.com/PaddlePaddle/Paddle-Lite/blob/release/v2.0.0-beta1/lite/core/mir/fusion/fc_fuse_pass.h) 实现fuse逻辑,并注册到 [optimizer](https://github.com/PaddlePaddle/Paddle-Lite/blob/release/v2.0.0-beta1/lite/core/optimizer.h)
### 扩展全新硬件后端
需要额外扩充如下模块,让框架能够支撑硬件执行:
- TypeSystem,需要扩充其中相关的 type
- 相关 [enum](https://github.com/PaddlePaddle/Paddle-Lite/blob/release/v2.0.0-beta1/lite/api/paddle_place.h#L44)
- MIR,需要扩展其中的 type cast 相关的 pass
- [TargetType cast pass](https://github.com/PaddlePaddle/Paddle-Lite/blob/release/v2.0.0-beta1/lite/core/mir/type_target_cast_pass.cc) 用于拷贝不同硬件上的tensor
- [Data layout cast pass](https://github.com/PaddlePaddle/Paddle-Lite/blob/release/v2.0.0-beta1/lite/core/mir/type_target_cast_pass.h) 用于转化不同的 data layout
- [Precision cast pass](https://github.com/PaddlePaddle/Paddle-Lite/blob/release/v2.0.0-beta1/lite/core/mir/type_precision_cast_pass.h) 用于转化不同 tensor 的量化精度
- KernelContext,具体地可以参考
- [ARM context](https://github.com/PaddlePaddle/Paddle-Lite/blob/release/v2.0.0-beta1/lite/core/context.h#L91)
- 需要注意的是,硬件 context 的接口只服务于该硬件的 kernel
- context 有分析期和执行期两个阶段,如果分析期没有特殊的优化,则无需考虑;否则,需要注意将分析期的信息整理并序列化到离线模型中,用于执行期直接加载。
\ No newline at end of file
---
layout: post
title: 架构设计
---
Mobile 在这次升级为 Lite 架构, 侧重多硬件、高性能的支持,其主要设计思想如下
- 引入 Type system,强化多硬件、量化方法、data layout 的混合调度能力
- 硬件细节隔离,通过不同编译开关,对支持的任何硬件可以自由插拔
- 引入 MIR(Machine IR) 的概念,强化带执行环境下的优化支持
- 优化期和执行期严格隔离,保证预测时轻量和高效率
架构图如下
![Paddle Inference Refactor1.0](https://user-images.githubusercontent.com/52520497/64949619-26e49580-d8ac-11e9-855a-514feb9b75af.png)
## 编译期和执行期严格隔离设计
- compile time 优化完毕可以将优化信息存储到模型中;execution time 载入并执行
- 两套 API 及对应的预测lib,满足不同场景
- `CxxPredictor` 打包了 `Compile Time``Execution Time`,可以 runtime 在具体硬件上做分析和优化,得到最优效果
- `MobilePredictor` 只打包 `Execution Time`,保持部署和执行的轻量
## `Execution Time` 轻量级设计和实现
- 每个 batch 实际执行只包含两个步骤执行
- `Op.InferShape`
- `Kernel.Run`,Kernel 相关参数均使用指针提前确定,后续无查找或传参消耗
- 设计目标,执行时,只有 kernel 计算本身消耗
- 轻量级 `Op``Kernel` 设计,避免框架额外消耗
- `Op` 只有 `CreateKernels``InferShape` 两个重要职能
- `Kernel` 只有 `Run` 职能
## 多硬件后端支持
- 硬件通用行为,使用 `TargetWrapper` 模块做适配器适配,对上层框架提供一致界面
- 框架上层策略保持硬件无关,如存储优化 (Memory optimize),计算剪枝 (Computation prune) 等,任何硬件接入均可直接复用
- 框架支持了硬件通用行为,特定硬件细节不做过多约束,各硬件可以自行实现并接入框架
- 计算模式上目前支持两种主流模型,一种是类似 X86, ARM CPU 等非异构设备;一种是 GPU,或 FPGA 等异构设备(支持 stream, event异步执行模式以及跨设备拷贝)
---
## 多硬件及算法混合调度支持
`TensorTy` 用来表示 Tensor 类型
```c++
struct TensorTy {
TargetType target;
PrecisionType precision;
DataLayout layout;
int deviceid;
};
```
```c++
enum class TargetType { kARM, kX86, kCUDA, kOpenCL };
enum class PrecisionType { kFP32, kFP16, kInt8, kInt16 };
enum class DataLayout { kNCHW, kNHWC };
```
---
注册 Kernel,确定特定 Kernel 的输入输出特征
```c++
REGISTER_LITE_KERNEL(
mul, kARM, kFloat, kNCHW, arm::MulCompute, def)
.BindInput("X", {LiteType::GetTensorTy(kARM, kFloat, kNCHW)})
.BindInput("Y", {LiteType::GetTensorTy(kARM, kFloat, kNCHW))})
.BindOutput("Out", {LiteType::GetTensorTy(kARM, kFloat, kNCHW)})
.Finalize();
```
---
同一个 Op 的不同 Kernel 类似函数重载
用于支持任意的混合调度:
1. 标记模型中所有 tensor 的 Type
2. 标记 Kernel 的 硬件、执行精度、data layout 等信息
全局做类型推断,当发现 tensor 传递中有类型冲突,采用 type cast 操作,通过插入特定功能 Op 来实现正确的传导
![lite-7](https://user-images.githubusercontent.com/52520497/64949642-395ecf00-d8ac-11e9-8b69-ced1996abc3b.png)
---
## MIR 用于图分析优化
基于 Type System 的 SSA,通过 IR Pass 对计算图进行分析和优化:
- 支持对整个 graph 进行类型推断,发现类型冲突并加入 type cast op,来支持通用混合调度
- 计算剪枝 (Compute prune),比如去掉 scale(1), assign op 等
- 存储优化 (Memory optimize)
- 操作熔合 (Operator fuse)(已经支持 fc, conv_bn, ele_add+act 等6种 fuse 策略)
- 支持量化处理(已支持 Int8预测)
\ No newline at end of file
---
layout: post
title: Benchmark 数据
---
可以参考[benchmark_tools](../benchmark_tools),推荐**一键benchmark**
# 测试环境
* 测试模型
* fp32模型
* mobilenet_v1
* mobilenet_v2
* squeezenet_v1.1
* mnasnet
* shufflenet_v2
* int8模型
* mobilenet_v1
* mobilenet_v2
* resnet50
* 测试机器(android ndk ndk-r17c)
* 骁龙855
* xiaomi mi9, snapdragon 855
* 4xA76(1@2.84GHz + 3@2.4GHz) + 4xA55@1.78GHz
* 骁龙845
* xiaomi mi8, 845
* 2.8GHz(大四核),1.7GHz(小四核)
* 骁龙835
* xiaomi mix2, snapdragon 835
* 2.45GHz(大四核),1.9GHz(小四核)
* 骁龙625
* oppo R9s, snapdragon625
* A53 x 8, big core@2.0GHz
* 骁龙653
* 360 N5, snapdragon 653
* 4 x A73@2.0GHz + 4 x A53@1.4GHz
* 麒麟970
* HUAWEI Mate10
* 测试说明
* commit id: 12c129affaacd476e27a0a82b235a9d547d33f0f
* warmup=10, repeats=30,统计平均时间,单位是ms
* 当线程数为1时,```DeviceInfo::Global().SetRunMode```设置LITE_POWER_HIGH,否者设置LITE_POWER_NO_BIND
* 模型的输入图像的维度是{1, 3, 224, 224},输入图像的每一位数值是1
# 测试数据
## fp32 模型测试数据
### paddlepaddle model
骁龙855|armv8 | | |armv7 | | |
----| ---- | ---- | ---- | ---- |---- |----
threads num|1 |2 |4 |1 |2 |4
mobilenet_v1 |32.19 |18.81 |10.90 |30.92 |18.31 |10.15
mobilenet_v2 |22.91 |13.75 |8.64 |21.15 |12.79 |7.84
shufflenet_v2 |4.67 |3.37 |2.65 |4.43 |3.15 |2.66
squeezenet_v1.1 |25.10 |15.93 |9.68 |23.28 |14.61 |8.71
mnasnet |21.84 |13.14 |7.96 |23.29 |12.46 |7.48
骁龙835|armv8 | | |armv7 | | |
----| ---- | ---- | ---- | ---- |---- |----
threads num|1 |2 |4 |1 |2 |4
mobilenet_v1 |94.13 |52.17 |30.68 |88.28 |47.58 |26.64
mobilenet_v2 |61.24 |34.64 |22.36 |56.66 |32.19 |19.63
shufflenet_v2 |10.87 |6.92 |5.12 |10.41 |6.76 |4.97
squeezenet_v1.1 |73.61 |42.25 |24.44 |64.87 |38.43 |23.06
mnasnet |58.22 |33.43 |20.44 |53.43 |30.20 |18.09
麒麟980|armv8 | | |armv7 | | |
----| ---- | ---- | ---- | ---- |---- |----
threads num|1 |2 |4 |1 |2 |4
mobilenet_v1 |55.11 |28.24 |13.27 |34.24 |17.74 |12.41
mobilenet_v2 |37.03 |19.80 |51.94 |23.64 |12.98 |9.38
shufflenet_v2 |7.26 |4.94 |15.06 |5.32 |3.33 |2.82
squeezenet_v1.1 |42.73 |23.66 |57.39 |26.03 |14.53 |13.66
mnasnet |36.87 |20.15 |46.04 |21.85 |12.06 |8.68
麒麟970|armv8 | | |armv7 | | |
----| ---- | ---- | ---- | ---- |---- |----
threads num|1 |2 |4 |1 |2 |4
mobilenet_v1 |97.80 |52.64 |34.46 |94.51 |49.36 |28.43
mobilenet_v2 |66.55 |38.52 |23.19 |62.89 |34.93 |21.53
shufflenet_v2 |13.78 |8.11 |5.93 |11.95 |7.90 |5.91
squeezenet_v1.1 |77.64 |43.67 |25.72 |69.91 |40.66 |24.62
mnasnet |61.86 |34.62 |22.68 |59.61 |32.79 |19.56
## caffe model
骁龙855|armv8 | | |armv7 | | |
----| ---- | ---- | ---- | ---- |---- |----|
threads num|1 |2 |4 |1 |2 |4 |
mobilenet_v1 |32.42 |18.68 |10.86 |30.92 |18.35 |10.07 |
mobilenet_v2 |29.53 |17.76 |10.89 |27.19 |16.53 |9.75 |
shufflenet_v2 |4.61 |3.29 |2.61 |4.36 |3.11 |2.51 |
骁龙835|armv8 | | |armv7 | | |
----| ---- | ---- | ---- | ---- |---- |----|
threads num|1 |2 |4 |1 |2 |4 |
mobilenet_v1 |92.52 |52.34 |30.37 |88.31 |49.75 |27.29 |
mobilenet_v2 |79.50 |45.67 |28.79 |76.13 |44.01 |26.13 |
shufflenet_v2 |10.94 |7.08 |5.16 |10.64 |6.83 |5.01 |
麒麟980|armv8 | | |armv7 | | |
----| ---- | ---- | ---- | ---- |---- |----|
threads num|1 |2 |4 |1 |2 |4 |
mobilenet_v1 |55.36 |28.18 |13.31 |34.42 |17.93 |12.52 |
mobilenet_v2 |49.17 |26.10 |65.49 |30.50 |16.66 |11.72 |
shufflenet_v2 |8.45 |5.00 |15.65 |4.58 |3.14 |2.83 |
麒麟970|armv8 | | |armv7 | | |
----| ---- | ---- | ---- | ---- |---- |----|
threads num|1 |2 |4 |1 |2 |4 |
mobilenet_v1 |97.85 |53.38 |33.85 |94.29 |49.42 |28.29 |
mobilenet_v2 |87.40 |50.25 |31.85 |85.55 |48.11 |28.24 |
shufflenet_v2 |12.16 |8.39 |6.21 |12.21 |8.33 |6.32 |
## 量化模型
骁龙855|armv8 | | |armv7 | | |
----| ---- | ---- | ---- | ---- |---- |----|
threads num|1 |2 |4 |1 |2 |4 |
mobilenet_v1 |14.01 |8.13 |4.32 |36.80 |21.58 |11.12 |
mobilenet_v2 |17.24 |11.55 |7.82 |28.72 |19.08 |12.49 |
骁龙835|armv8 | | |armv7 | | |
----| ---- | ---- | ---- | ---- |---- |----|
threads num|1 |2 |4 |1 |2 |4 |
mobilenet_v1 |56.57 |29.84 |15.24 |60.76 |32.25 |16.66 |
mobilenet_v2 |47.52 |28.18 |19.24 |49.38 |31.10 |22.07 |
骁龙970|armv8 | | |armv7 | | |
----| ---- | ---- | ---- | ---- |---- |----|
threads num|1 |2 |4 |1 |2 |4 |
mobilenet_v1 |60.86 |30.98 |16.31 |65.95 |34.39 |18.68 |
mobilenet_v2 |65.57 |37.31 |20.87 |68.87 |39.39 |24.43 |
---
layout: post
title: Benchmark
---
* TOC
{:toc}
本文将会介绍,在**Ubuntu:16.04交叉编译环境**下,用安卓手机在终端测试Paddle-Lite的性能,并介绍两种Benchmark方法:
1. **一键Benchmark**:适用于想快速获得常见模型性能的用户,下载预编译好的benchmark可执行文件;
2. **逐步Benchmark**:将**一键Benchmark**流程拆解讲解。
# 环境准备
1. 准备[adb](https://developer.android.com/studio/command-line/adb)等必备软件:
```shell
sudo apt update
sudo apt install -y wget adb
```
2. 检查手机与电脑连接。安卓手机USB连上电脑,打开设置 -> 开启开发者模式 -> 开启USB调试 -> 允许(授权)当前电脑调试手机;
3. 在电脑终端输入`adb devices`命令,查看当前连接到的设备:
```shell
adb devices
```
命令成功执行,显示结果类似下面(序列码略有不同):
```shell
List of devices attached
712QSDSEMMS7C device
```
## 一. 一键Benchmark
执行以下命令,完成Benchmark:
```shell
wget -c https://paddle-inference-dist.bj.bcebos.com/PaddleLite/run_benchmark.sh
sh run_benchmark.sh
```
`run_benchmark.sh`脚本会:
1. 下载模型,并上传手机:包含mobilenetv1/v2、shufflenetv2、squeezenetv1.1、mnasnet;
2. 下载pre-built android-armv7和android-armv8的可执行文件,并上传手机:`benchmark_bin_v7``benchmark_bin_v8`
3. 自动执行另一个脚本`benchmark.sh`(多台手机连接USB,请在`benchmark.sh`脚本中对`adb`命令后加上测试手机的`serial number`);
4. 从手机下载benchmark结果`result_armv7.txt``result_armv8.txt`,到当前目录,并显示Benchmark结果。
## 二. 逐步Benchmark
### 1. 获取benchmark可执行文件
benchmark_bin文件可以测试PaddleLite的性能,有下面两种方式获得。
#### 方式一:下载benchmark_bin可执行文件
```shell
# Download benchmark_bin for android-armv7
wget -c https://paddle-inference-dist.bj.bcebos.com/PaddleLite/benchmark_bin_v7
# Download benchmark_bin for android-armv8
wget -c https://paddle-inference-dist.bj.bcebos.com/PaddleLite/benchmark_bin_v8
```
#### 方式二:由源码编译benchmark_bin文件
根据[源码编译](../source_compile)准备编译环境,拉取PaddleLite最新release发布版代码,并在仓库根目录下,执行:
```shell
###########################################
# Build benchmark_bin for android-armv7 #
###########################################
./lite/tools/ci_build.sh \
--arm_os="android" \
--arm_abi="armv7" \
--arm_lang="gcc " \
build_arm
# build result see: <paddle-lite-repo>/build.lite.android.armv7.gcc/lite/api/benchmark_bin
###########################################
# Build benchmark_bin for android-armv8 #
###########################################
./lite/tools/ci_build.sh \
--arm_os="android" \
--arm_abi="armv8" \
--arm_lang="gcc " \
build_arm
# build result see: <paddle-lite-repo>/build.lite.android.armv8.gcc/lite/api/benchmark_bin
```
> **注意**:为了避免在docker内部访问不到手机的问题,建议编译得到benchmark_bin后退出到docker外面,并且将benchmark_bin文件拷贝到一个临时目录。然后在该临时目录下,按照下面步骤下载模型、拷贝脚本、测试。
### 2. 下载模型
PaddleLite为Benchmark准备好了[常见Benchmark模型](https://paddle-inference-dist.bj.bcebos.com/PaddleLite/benchmark_models.tar.gz)
执行以下命令,下载常见Benchmark模型并解压:
```shell
wget -c https://paddle-inference-dist.bj.bcebos.com/PaddleLite/benchmark_models.tar.gz
tar zxvf benchmark_models.tar.gz
```
| 模型 | 下载地址 |
| --------------- | ------------------------------------------------------------ |
| MobilenetV1 | [下载](https://paddle-inference-dist.bj.bcebos.com/PaddleLite/mobilenet_v1.tar.gz) |
| MobilenetV2 | [下载](https://paddle-inference-dist.bj.bcebos.com/PaddleLite/mobilenet_v2.tar.gz) |
| ShufflenetV2 | [下载](https://paddle-inference-dist.bj.bcebos.com/PaddleLite/shufflenet_v2.tar.gz) |
| Squeezenet_V1.1 | [下载](https://paddle-inference-dist.bj.bcebos.com/PaddleLite/squeezenet_v11.tar.gz) |
| Mnasnet | [下载](https://paddle-inference-dist.bj.bcebos.com/PaddleLite/mnasnet.tar.gz) |
> 注:若要使用测试脚本,**对单个模型测试**,请把单个模型放入 `benchmark_models` 文件夹,并确保测试脚本、`benchmark_models`文件夹在同一级的目录。
注:上述模型都已经使用`model_optimize_tool`进行转化,而且Lite移动端只支持加载转化后的模型。如果需要测试其他模型,请先参考[模型转化方法](../model_optimize_tool)
### 3. benchmark.sh脚本
benchmark测试的执行脚本`benchmark.sh` 位于源码中的`/PaddleLite/lite/tools/benchmark.sh`位置,测试时需要将`benchmark.sh``benchmark_bin``benchmark_models` 文件复制到同一目录下。
### 4. 测试
从终端进入benchmark.sh、可执行文件(benchmark_bin_v7、benchmark_bin_v8)和模型文件(benchmark_models)所在文件夹。
运行 benchmark.sh 脚本执行测试
```shell
# Benchmark for android-armv7
sh benchmark.sh ./benchmark_bin_v7 ./benchmark_models result_armv7.txt
# Benchmark for android-armv8
sh benchmark.sh ./benchmark_bin_v8 ./benchmark_models result_armv8.txt
```
测试结束后,armv7和armv8的结果,分别保存在当前目录下的`result_armv7.txt``result_armv8.txt`文件中。
**查看测试结果**
在当前目录的`result_armv7.txt``result_armv8.txt`文件,查看测试结果。
```shell
run benchmark armv7
--------------------------------------
PaddleLite Benchmark
Threads=1 Warmup=10 Repeats=30
-- mnasnet avg = 159.8427 ms
-- mobilenet_v1 avg = 235.0072 ms
-- mobilenet_v2 avg = 173.0387 ms
-- shufflenet_v2 avg = 76.0040 ms
-- squeezenet_v11 avg = 164.2957 ms
Threads=2 Warmup=10 Repeats=30
-- mnasnet avg = 83.1287 ms
-- mobilenet_v1 avg = 121.6029 ms
-- mobilenet_v2 avg = 86.6175 ms
-- shufflenet_v2 avg = 41.5761 ms
-- squeezenet_v11 avg = 87.8678 ms
Threads=4 Warmup=10 Repeats=30
-- mnasnet avg = 73.3880 ms
-- mobilenet_v1 avg = 119.0739 ms
-- mobilenet_v2 avg = 85.3050 ms
-- shufflenet_v2 avg = 38.0762 ms
-- squeezenet_v11 avg = 64.2201 ms
--------------------------------------
run benchmark armv8
--------------------------------------
PaddleLite Benchmark
Threads=1 Warmup=10 Repeats=30
-- mnasnet avg = 165.3073 ms
-- mobilenet_v1 avg = 306.0188 ms
-- mobilenet_v2 avg = 195.1884 ms
-- shufflenet_v2 avg = 99.3692 ms
-- squeezenet_v11 avg = 156.6971 ms
Threads=2 Warmup=10 Repeats=30
-- mnasnet avg = 90.2290 ms
-- mobilenet_v1 avg = 157.0007 ms
-- mobilenet_v2 avg = 118.1607 ms
-- shufflenet_v2 avg = 68.6804 ms
-- squeezenet_v11 avg = 91.3090 ms
Threads=4 Warmup=10 Repeats=30
-- mnasnet avg = 179.9730 ms
-- mobilenet_v1 avg = 204.0684 ms
-- mobilenet_v2 avg = 181.6486 ms
-- shufflenet_v2 avg = 123.2728 ms
-- squeezenet_v11 avg = 412.9046 ms
--------------------------------------
```
---
layout: post
title: C++ Demo
---
* TOC
{:toc}
## 编译
首先按照[PaddleLite 源码编译](https://github.com/PaddlePaddle/Paddle-Lite/wiki/source_compile)准备交叉编译环境,之后拉取最新[PaddleLite release发布版代码](https://github.com/PaddlePaddle/Paddle-Lite)。下面以Android-ARMv8架构为例,介绍编译过程,并最终在手机上跑通MobilNetv1模型。
进入 Paddle-Lite 目录,运行以下命令编译代码(**需加编译选项`--build_extra=ON`确保完整编译**):
{% highlight shell %}
./lite/tools/build.sh \
--arm_os=android \
--arm_abi=armv8 \
--arm_lang=gcc \
--android_stl=c++_static \
--build_extra=ON \
full_publish
{% endhighlight %}
编译完成后 `./build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/` 文件夹下包含:
- cxx
- include (头文件文件夹)
- lib (库文件文件夹)
- libpaddle_api_full_bundled.a
- libpaddle_api_light_bundled.a
- libpaddle_light_api_shared.so
- libpaddle_full_api_shared.so
- demo
- cxx (C++ demo)
- mobile_light (light api demo)
- mobile_full (full api demo)
- Makefile.def
- include
- third_party (第三方库文件夹)
- gflags
## 准备执行环境
执行环境有两种:使用安卓手机;若没安卓手机,也可在安卓模拟器中执行。
### 环境一:使用安卓手机
将手机连上电脑,在手机上打开选项 -> 开启-开发者模式 -> 开启-USB调试模式。确保 `adb devices` 能够看到相应的设备。
### 环境二:使用安卓模拟器
运行下面命令,分别创建安卓armv8、armv7架构的模拟器。若需在真机测试,将模拟器换成相应架构的真机环境即可。
{% highlight shell %}
# android-armv8
adb kill-server
adb devices | grep emulator | cut -f1 | while read line; do adb -s $line emu kill; done
echo n | avdmanager create avd -f -n paddle-armv8 -k "system-images;android-24;google_apis;arm64-v8a"
echo -ne '\n' | ${ANDROID_HOME}/emulator/emulator -avd paddle-armv8 -noaudio -no-window -gpu off -port 5554 &
sleep 1m
{% endhighlight %}
{% highlight shell %}
# android-armv7
adb kill-server
adb devices | grep emulator | cut -f1 | while read line; do adb -s $line emu kill; done
echo n | avdmanager create avd -f -n paddle-armv7 -k "system-images;android-24;google_apis;armeabi-v7a"
echo -ne '\n' | ${ANDROID_HOME}/emulator/emulator -avd paddle-armv7 -noaudio -no-window -gpu off -port 5554 &
sleep 1m
{% endhighlight %}
## 下载模型并运行示例
{% highlight bash %}
cd inference_lite_lib.android.armv8/demo/cxx/mobile_full
wget http://paddle-inference-dist.bj.bcebos.com/mobilenet_v1.tar.gz
tar zxvf mobilenet_v1.tar.gz
make
adb -s emulator-5554 push mobilenet_v1 /data/local/tmp/
adb -s emulator-5554 push mobilenetv1_full_api /data/local/tmp/
adb -s emulator-5554 shell chmod +x /data/local/tmp/mobilenetv1_full_api
adb -s emulator-5554 shell "/data/local/tmp/mobilenetv1_full_api --model_dir=/data/local/tmp/mobilenet_v1 --optimized_model_dir=/data/local/tmp/mobilenet_v1.opt"
{% endhighlight %}
注:我们也提供了轻量级 API 的 demo,可以执行以下代码运行轻量级 API 示例。
{% highlight bash %}
cd ../mobile_light
make
adb -s emulator-5554 push mobilenetv1_light_api /data/local/tmp/
adb -s emulator-5554 shell chmod +x /data/local/tmp/mobilenetv1_light_api
adb -s emulator-5554 shell "/data/local/tmp/mobilenetv1_light_api --model_dir=/data/local/tmp/mobilenet_v1.opt --threads=1 "
{% endhighlight %}
## Demo 程序运行结果
Demo 运行成功后 ,将在控制台输出预测结果的前10个类别的预测概率:
{% highlight bash %}
Output dim: 1000
Output[0]: 0.000191
Output[100]: 0.000160
Output[200]: 0.000264
Output[300]: 0.000211
Output[400]: 0.001032
Output[500]: 0.000110
Output[600]: 0.004829
Output[700]: 0.001845
Output[800]: 0.000202
Output[900]: 0.000586
{% endhighlight %}
## 如何在代码中使用 API
在C++中使用PaddleLite API非常简单,不需要添加太多额外代码,具体步骤如下:
- 加入头文件引用
{% highlight cpp %}
#include <iostream>
#include <vector>
#include "paddle_api.h"
#include "paddle_use_kernels.h"
#include "paddle_use_ops.h"
#include "paddle_use_passes.h"
{% endhighlight %}
- 通过MobileConfig设置:模型文件位置(model_dir)、线程数(thread)和能耗模式( power mode )。输入数据(input),从 MobileConfig 创建 PaddlePredictor 并执行预测。 (注:Lite还支持从memory直接加载模型,可以通过MobileConfig::set_model_buffer方法实现)
代码示例:
{% highlight cpp %}
// 1. Create MobileConfig
MobileConfig config;
// 2. Load model
config.set_model_dir("path to your model directory"); // model dir
/*load model: Lite supports loading model from file or from memory (naive buffer from optimized model)
//Method One: Load model from memory:
void set_model_buffer(const char* model_buffer,
size_t model_buffer_size,
const char* param_buffer,
size_t param_buffer_size)
//Method Two: Load model from file:
void set_model_dir(const std::string& model_dir) */
// 3. Set MobileConfig (or you can skip this step to use default value):
config.set_power_mode(LITE_POWER_HIGH); // power mode
/*power modes: Lite supports the following power modes
LITE_POWER_HIGH
LITE_POWER_LOW
LITE_POWER_FULL
LITE_POWER_NO_BIND
LITE_POWER_RAND_HIGH
LITE_POWER_RAND_LOW */
config.set_threads("num of threads"); // threads
// 4. Create PaddlePredictor by MobileConfig
std::shared_ptr<PaddlePredictor> predictor =
CreatePaddlePredictor<MobileConfig>(config);
// 5. Prepare input data
std::unique_ptr<Tensor> input_tensor(std::move(predictor->GetInput(0)));
input_tensor->Resize({1, 3, 224, 224});
auto *data = input_tensor -> mutable_data<float>();
for (int i = 0; i < ShapeProduction(input_tensor->shape()); ++i) {
data[i] = 1;
}
// 6. Run predictor
predictor->Run();
// 7. Get output
std::unique_ptr<const Tensor> output_tensor(std::move(predictor->GetOutput(0)));
{% endhighlight %}
## CxxConfig案例: OCR_model的运行
1. OCR 模型文件:
- 我们提供Pb格式的[ocr_attention_mode](https://paddle-inference-dist.cdn.bcebos.com/ocr_attention.tar.gz)l下载
- 也可以从[Paddle/model项目](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/ocr_recognition)中训练出模型
2. 示例代码:
{% highlight cpp %}
#include "paddle_api.h" // NOLINT
#include "paddle_use_kernels.h" // NOLINT
#include "paddle_use_ops.h" // NOLINT
#include "paddle_use_passes.h" // NOLINT
#include <gflags/gflags.h>
#include <stdio.h>
#include <vector>
using namespace paddle::lite_api; // NOLINT
DEFINE_string(model_dir, "", "Model dir path.");
DEFINE_bool(prefer_int8_kernel, false, "Prefer to run model with int8 kernels");
int64_t ShapeProduction(const shape_t &shape) {
int64_t res = 1;
for (auto i : shape)
res *= i;
return res;
}
void RunModel() {
// 1. Set CxxConfig
CxxConfig config;
config.set_model_dir(FLAGS_model_dir);
std::vector<Place> valid_places({Place{TARGET(kARM), PRECISION(kFloat)}});
if (FLAGS_prefer_int8_kernel) {
valid_places.insert(valid_places.begin(),
Place{TARGET(kARM), PRECISION(kInt8)});
}
config.set_valid_places(valid_places);
// 2. Create PaddlePredictor by CxxConfig
std::shared_ptr<PaddlePredictor> predictor =
CreatePaddlePredictor<CxxConfig>(config);
// 3. Prepare input data
// input 0
std::unique_ptr<Tensor> input_tensor(std::move(predictor->GetInput(0)));
input_tensor->Resize(shape_t({1, 1, 48, 512}));
auto *data = input_tensor->mutable_data<float>();
for (int i = 0; i < ShapeProduction(input_tensor->shape()); ++i) {
data[i] = 1;
}
// input1
std::unique_ptr<Tensor> init_ids(std::move(predictor->GetInput(1)));
init_ids->Resize(shape_t({1, 1}));
auto *data_ids = init_ids->mutable_data<float>();
for (int i = 0; i < ShapeProduction(init_ids->shape()); ++i) {
data_ids[i] = 0;
}
lod_t lod_i;
lod_i.push_back({0, 1});
lod_i.push_back({0, 1});
init_ids->SetLoD(lod_i);
// input2
std::unique_ptr<Tensor> init_scores(std::move(predictor->GetInput(2)));
init_scores->Resize(shape_t({1, 1}));
auto *data_scores = init_scores->mutable_data<float>();
for (int i = 0; i < ShapeProduction(init_scores->shape()); ++i) {
data_scores[i] = 0;
}
lod_t lod_s;
lod_s.push_back({0, 1});
lod_s.push_back({0, 1});
init_scores->SetLoD(lod_s);
// 4. Run predictor
predictor->Run();
// 5. Get output
std::unique_ptr<const Tensor> output_tensor(
std::move(predictor->GetOutput(0)));
for (int i = 0; i < ShapeProduction(output_tensor->shape()); i++) {
printf("Output[%d]: %f\n", i, output_tensor->data<float>()[i]);
}
}
int main(int argc, char **argv) {
google::ParseCommandLineFlags(&argc, &argv, true);
RunModel();
return 0;
}
{% endhighlight %}
3. 运行方法:
参考以上代码编译出可执行文件`OCR_DEMO`,模型文件夹为`ocr_attention`。手机以USB调试、文件传输模式连接电脑
在终端中输入以下命令执行OCR model测试:
{% highlight shell %}
#OCR_DEMO为编译出的可执行文件名称,ocr_attention为ocr_attention模型的文件夹名称
adb push OCR_DEMO data/local/tmp
adb push ocr_attention data/local/tmp
adb shell 'cd data/local/tmp && ./OCR_DEMO --model_dir=./OCR_DEMO'
{% endhighlight %}
4. 运行结果
<img src='https://user-images.githubusercontent.com/45189361/64398400-46531580-d097-11e9-9f1c-5aba1dfbc24f.png' align='left' width="150" height="200"/>
---
layout: post
title: Lite基于CUDA的模型预测
---
Lite支持在x86_64,arm64架构上(如:TX2)进行CUDA的编译运行。
## 编译
**NOTE:** 如果是在TX2等NVIDIA嵌入式硬件上编译,请使用最新的[Jetpack](https://developer.nvidia.com/embedded/jetpack) 安装依赖库。
一: 下载代码
```
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
```
二:编译
```
# 进入代码目录
cd Paddle-Lite
# 运行编译脚本
# 编译结束会在本目录下生成 build_cuda 目录
# 编译过程中如果提示找不到CUDA,CUDNN,请在环境变量设置CUDA_TOOLKIT_ROOT_DIR, CUDNN_ROOT
# CUDA_TOOLKIT_ROOT_DIR,CUDNN_ROOT分别表示CUDA,CUDNN的根目录
./lite/tools/build.sh cuda
```
编译结束会在 `build_cuda/inference_lite_lib/python/lib/` 目录下生成 `lite_core.so`
## 运行
以下以Yolov3模型为例,介绍如何在Nvidia GPU硬件上运行模型。
一: 下载darknet_yolov3模型,模型信息请参考[这里](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/yolov3)
```
# 下载模型
wget https://paddle-inference-dist.cdn.bcebos.com/PaddleLite/yolov3_infer.tar.gz
# 下载图片样例
wget https://paddle-inference-dist.cdn.bcebos.com/PaddleLite/kite.jpg
```
二: 运行
**NOTE:**此处示例使用的是python接口,后续会开放C++接口以及示例。
``` python
#-*- coding: utf-8 -*-
from __future__ import print_function
import sys
import numpy as np
import cv2
sys.path.append('build_cuda/inference_lite_lib/python/lib')
from lite_core import *
def read_img(im_path, resize_h, resize_w):
im = cv2.imread(im_path).astype('float32')
im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
h, w, _ = im.shape
im_scale_x = resize_h / float(w)
im_scale_y = resize_w / float(h)
out_img = cv2.resize(im, None, None, fx=im_scale_x, fy=im_scale_y, interpolation=cv2.INTER_CUBIC)
mean = np.array([0.485, 0.456, 0.406]).reshape((1, 1, -1))
std = np.array([0.229, 0.224, 0.225]).reshape((1, 1, -1))
out_img = (out_img / 255.0 - mean) / std
out_img = out_img.transpose((2, 0, 1))
return out_img
# 配置config
a = CxxConfig()
a.set_model_file('./yolov3_infer/__model__') # 指定模型文件路径
a.set_param_file('./yolov3_infer/__params__') # 指定参数文件路径
place_cuda = Place(TargetType.CUDA)
a.set_valid_places([place_cuda])
# 创建predictor
predictor = create_paddle_predictor(a)
# 设置输入
input_tensor = predictor.get_input(0);
height, width = 608, 608
input_tensor.resize([1, 3, height, width])
data = read_img('./kite.jpg', height, width).flatten()
input_tensor.set_float_data(data, TargetType.CUDA)
in2 = predictor.get_input(1);
in2.resize([1, 2])
in2.set_int32_data([height, width], TargetType.CUDA)
# 运行
predictor.run()
# 获取输出
output_tensor = predictor.get_output(0);
print (output_tensor.shape())
# [100L, 6L]
print (output_tensor.target())
# TargetType.Host
print (output_tensor.float_data()[:6])
# [0.0, 0.9862784743309021, 98.51927185058594, 471.2381286621094, 120.73092651367188, 578.33251953125]
```
**NOTE:** 对CUDA的支持还在持续开发中。
\ No newline at end of file
---
layout: post
title: C++ API接口使用指南
---
# C++ API接口使用指南
请参考[源码编译](../source_compile)确保 Lite 可以正确编译,下面用Lite的c++接口加载并执行 MobileNetV1 模型为例,详细说明使用方法。
## 准备模型
Lite支持PaddlePaddle训练好的模型,MobileNetV1模型可以由以下三种方式得到:
- 直接下载训练好的[MobileNetV1模型](https://paddle-inference-dist.bj.bcebos.com/mobilenet_v1.tar.gz)
- 使用[PaddlePaddle](https://paddlepaddle.org.cn/)构建MobileNetV1网络并训练
- 使用[X2Paddle](../x2paddle)对caffe或者tensorflow的MobileNetV1模型进行转换得到
## 模型优化
使用Model Optimize Tool优化模型,使得模型预测过程表现出优异的性能。Model Optimize Tool的具体使用方法请参考[文档](../model_optimize_tool)
- 准备model_optimize_tool
- 使用model_optimize_tool优化模型
- 得到优化后的模型,包括__model__.nb文件和param.nb文件
## 加载模型
加载MobileNetV1网络模型,创建predictor,具体可以参考```paddlelite/lite/api/model_test.cc```文件。
```c++
lite::DeviceInfo::Init();
lite::DeviceInfo::Global().SetRunMode(lite::LITE_POWER_HIGH, thread_num);
lite_api::MobileConfig config;
config.set_model_dir(model_dir);
auto predictor = lite_api::CreatePaddlePredictor(config);
```
## 设定输入
得到input_tensor,设置输入值,此处我们设定为全1
```cpp
// 获取第 j 个 tensor 的句柄
auto input_tensor = predictor->GetInput(j);
input_tensor->Resize(input_shapes[j]);
// 获取数据指针,以塞入数据
auto input_data = input_tensor->mutable_data<float>();
int input_num = 1;
for (int i = 0; i < input_shapes[j].size(); ++i) {
input_num *= input_shapes[j][i];
}
for (int i = 0; i < input_num; ++i) {
input_data[i] = 1.f;
}
```
## 执行并输出
```cpp
predictor.Run()
auto* out = predictor.GetOutput(0);
LOG(INFO) << "dims " << out->dims();
LOG(INFO) << "out data size: " << out->data_size();
```
输出为```dims dims{1000,}, out data size: 1000```
---
layout: post
title: C++ API文档
---
* TOC
{:toc}
# CreatePaddlePredictor
```c++
template <typename ConfigT>
std::shared_ptr<PaddlePredictor> CreatePaddlePredictor(const ConfigT&);
```
`CreatePaddlePredictor`用来根据`MobileConfig`构建预测器。
示例:
```c++
// 设置MobileConfig
MobileConfig config;
config.set_model_dir(FLAGS_model_dir);
// 根据MobileConfig创建PaddlePredictor
std::shared_ptr<PaddlePredictor> predictor = CreatePaddlePredictor<MobileConfig>(config);
```
参数:
- `config(MobileConfig)` - 用于构建Predictor的配置信息。
返回:`PaddlePredictor`指针
返回类型:`std::shared_ptr<PaddlePredictor>`
# MobileConfig
```c++
class MobileConfig;
```
`MobileConfig`用来配置构建轻量级PaddlePredictor的配置信息,如NaiveBuffer格式的模型地址、模型的内存地址(从内存加载模型时使用)、能耗模式、工作线程数等等。
*注意:输入的模型需要使用[Model Optimize Tool](../model_optimize_tool)转化为NaiveBuffer格式的优化模型。*
示例:
```c++
MobileConfig config;
// 设置NaiveBuffer格式模型目录,从文件加载模型时使用
config.set_model_dir(FLAGS_model_dir);
// 设置工作线程数
config.set_threads(4);
// 设置能耗模式
config.set_power_mode(LITE_POWER_HIGH);
// 根据MobileConfig创建PaddlePredictor
std::shared_ptr<PaddlePredictor> predictor = CreatePaddlePredictor<MobileConfig>(config);
```
## `set_model_dir(model_dir)`
设置模型文件夹路径,当需要从磁盘加载模型时使用。
参数:
- `model_dir(std::string)` - 模型文件夹路径
返回:`None`
返回类型:`void`
## `model_dir()`
返回设置的模型文件夹路径。
参数:
- `None`
返回:模型文件夹路径
返回类型:`std::string`
## `set_model_buffer(model_buffer, model_buffer_size, param_buffer, param_buffer_size)`
设置模型、参数的内存地址,当需要从内存加载模型时使用。
示例:
```c++
// 读取模型文件到内存
std::string model_buffer = ReadFile(FLAGS_model_path);
std::string params_buffer = lite::ReadFile(FLAGS_params_path);
// 设置MobileConfig
lite_api::MobileConfig config;
config.set_model_buffer(model_buffer.c_str(), model_buffer.size(),
params_buffer.c_str(), params_buffer.size());
// 根据MobileConfig创建PaddlePredictor
std::shared_ptr<PaddlePredictor> predictor = CreatePaddlePredictor<MobileConfig>(config);
```
参数:
- `model_buffer(const char*)` - 内存中模型结构数据。
- `model_buffer_size(size_t)` - 内存中模型结构数据的大小。
- `param_buffer(const char*)` - 内存中模型参数数据。
- `param_buffer_size(size_t)` - 内存中模型参数数据的大小。
返回:`None`
返回类型:`Void`
## `model_from_memory()`
是否从内存中加载模型,当使用`set_model_buffer`接口时返回`true`
参数:
- `None`
返回:是否从内存加载模型
返回类型:`bool`
## `model_buffer()`
获取内存中模型结构数据。
参数:
- `None`
返回:内存中模型结构数据
返回类型:`const std::string&`
## `param_buffer()`
获取内存中模型参数数据。
参数:
- `None`
返回:内存中模型结构数据
返回类型:`const std::string&`
## `set_power_mode(mode)`
设置CPU能耗模式。若不设置,则默认使用`LITE_POWER_HIGH`
*注意:只在开启`OpenMP`时生效,否则系统自动调度。*
参数:
- `mode(PowerMode)` - CPU能耗模式
返回:`None`
返回类型:`void`
## `power_mode()`
获取设置的CPU能耗模式。
参数:
- `None`
返回:设置的CPU能耗模式
返回类型:`PowerMode`
## `set_threads(threads)`
设置工作线程数。若不设置,则默认使用单线程。
*注意:只在开启`OpenMP`的模式下生效,否则只使用单线程。*
参数:
- `threads(int)` - 工作线程数
返回:`None`
返回类型:`void`
## `threads()`
获取设置的工作线程数。
参数:
- `None`
返回:工作线程数
返回类型:`int`
# PaddlePredictor
```c++
class PaddlePredictor
```
`PaddlePredictor`是Paddle-Lite的预测器,由`CreatePaddlePredictor`根据`MobileConfig`进行创建。用户可以根据PaddlePredictor提供的接口设置输入数据、执行模型预测、获取输出以及获得当前使用lib的版本信息等。
示例:
```c++
int64_t ShapeProduction(const shape_t& shape) {
int64_t res = 1;
for (auto i : shape) res *= i;
return res;
}
// 设置MobileConfig
MobileConfig config;
config.set_model_dir(FLAGS_model_dir);
// 根据MobileConfig创建PaddlePredictor
std::shared_ptr<PaddlePredictor> predictor = CreatePaddlePredictor<MobileConfig>(config);
// 获得模型的输入和输出名称
std::vector<std::string> input_names = predictor->GetInputNames();
for (int i = 0; i < input_names.size(); i ++) {
printf("Input name[%d]: %s\n", i, input_names[i].c_str());
}
std::vector<std::string> output_names = predictor->GetOutputNames();
for (int i = 0; i < output_names.size(); i ++) {
printf("Output name[%d]: %s\n", i, output_names[i].c_str());
}
// 准备输入数据
// (1)根据index获取输入Tensor
std::unique_ptr<Tensor> input_tensor(std::move(predictor->GetInput(0)));
// (2)根据名称获取输入Tensor
// std::unique_ptr<Tensor> input_tensor(std::move(predictor->GetInputByName(input_names[0])));
input_tensor->Resize({1, 3, 224, 224});
auto* data = input_tensor->mutable_data<float>();
for (int i = 0; i < ShapeProduction(input_tensor->shape()); ++i) {
data[i] = 1;
}
// 执行预测
predictor->Run();
// 获取输出
// (1)根据index获取输出Tensor
std::unique_ptr<const Tensor> output_tensor(std::move(predictor->GetOutput(0)));
// (2)根据名称获取输出Tensor
// std::unique_ptr<const Tensor> output_tensor(std::move(predictor->GetOutput(output_names[0])));
printf("Output dim: %d\n", output_tensor->shape()[1]);
for (int i = 0; i < ShapeProduction(output_tensor->shape()); i += 100) {
printf("Output[%d]: %f\n", i, output_tensor->data<float>()[i]);
}
```
## `GetInput(index)`
获取输入Tensor指针,用来设置模型的输入数据。
参数:
- `index(int)` - 输入Tensor的索引
返回:第`index`个输入`Tensor`的指针
返回类型:`std::unique_ptr<Tensor>`
## `GetOutput(index)`
获取输出Tensor的指针,用来获取模型的输出结果。
参数:
- `index(int)` - 输出Tensor的索引
返回:第`index`个输出Tensor`的指针
返回类型:`std::unique_ptr<Tensor>`
## `GetInputNames()`
获取所有输入Tensor的名称。
参数:
- `None`
返回:所有输入Tensor的名称
返回类型:`std::vector<std::string>`
## `GetOutputNames()`
获取所有输出Tensor的名称。
参数:
- `None`
返回:所有输出Tensor的名称
返回类型:`std::vector<std::string>`
## `GetInputByName(name)`
根据名称获取输出Tensor的指针,用来获取模型的输出结果。
参数:
- `name(const std::string)` - 输入Tensor的名称
返回:输入Tensor`的指针
返回类型:`std::unique_ptr<Tensor>`
## `GetTensor(name)`
根据名称获取输出Tensor的指针。
参数:
- `name(const std::string)` - Tensor的名称
返回:指向`const Tensor`的指针
返回类型:`std::unique_ptr<const Tensor>`
## `Run()`
执行模型预测,需要在***设置输入数据后***调用。
参数:
- `None`
返回:`None`
返回类型:`void`
## `GetVersion()`
用于获取当前lib使用的代码版本。若代码有相应tag则返回tag信息,如`v2.0-beta`;否则返回代码的`branch(commitid)`,如`develop(7e44619)`
参数:
- `None`
返回:当前lib使用的代码版本信息
返回类型:`std::string`
# PowerMode
```c++
enum PowerMode;
```
`PowerMode`为ARM CPU能耗模式,用户可以根据应用场景设置能耗模式获得最优的能效比。
示例:
```c++
MobileConfig config;
// 设置NaiveBuffer格式模型目录
config.set_model_dir(FLAGS_model_dir);
// 设置能耗模式
config.set_power_mode(LITE_POWER_HIGH);
// 根据MobileConfig创建PaddlePredictor
std::shared_ptr<PaddlePredictor> predictor = CreatePaddlePredictor<MobileConfig>(config);
```
PowerMode详细说明如下:
| 选项 | 说明 |
| :------------------: | ------------------------------------------------------------ |
| LITE_POWER_HIGH | 绑定大核运行模式。如果ARM CPU支持big.LITTLE,则优先使用并绑定Big cluster。如果设置的线程数大于大核数量,则会将线程数自动缩放到大核数量。如果系统不存在大核或者在一些手机的低电量情况下会出现绑核失败,如果失败则进入不绑核模式。 |
| LITE_POWER_LOW | 绑定小核运行模式。如果ARM CPU支持big.LITTLE,则优先使用并绑定Little cluster。如果设置的线程数大于小核数量,则会将线程数自动缩放到小核数量。如果找不到小核,则自动进入不绑核模式。 |
| LITE_POWER_FULL | 大小核混用模式。线程数可以大于大核数量。当线程数大于核心数量时,则会自动将线程数缩放到核心数量。 |
| LITE_POWER_NO_BIND | 不绑核运行模式(推荐)。系统根据负载自动调度任务到空闲的CPU核心上。 |
| LITE_POWER_RAND_HIGH | 轮流绑定大核模式。如果Big cluster有多个核心,则每预测10次后切换绑定到下一个核心。 |
| LITE_POWER_RAND_LOW | 轮流绑定小核模式。如果Little cluster有多个核心,则每预测10次后切换绑定到下一个核心。 |
# Tensor
```c++
class Tensor
```
Tensor是Paddle-Lite的数据组织形式,用于对底层数据进行封装并提供接口对数据进行操作,包括设置Shape、数据、LoD信息等。
*注意:用户应使用`PaddlePredictor`的`GetInput`和`GetOuput`接口获取输入/输出的`Tensor`。*
示例:
```c++
int64_t ShapeProduction(const shape_t& shape) {
int64_t res = 1;
for (auto i : shape) res *= i;
return res;
}
// 设置MobileConfig
MobileConfig config;
config.set_model_dir(FLAGS_model_dir);
// 根据MobileConfig创建PaddlePredictor
std::shared_ptr<PaddlePredictor> predictor = CreatePaddlePredictor<MobileConfig>(config);
// 准备输入数据, 获取输入Tensor
std::unique_ptr<Tensor> input_tensor(std::move(predictor->GetInput(0)));
// 设置输入Tensor维度信息
input_tensor->Resize({1, 3, 224, 224});
// 设置输入数据
auto* data = input_tensor->mutable_data<float>();
for (int i = 0; i < ShapeProduction(input_tensor->shape()); ++i) {
data[i] = 1;
}
// 执行预测
predictor->Run();
// 获取输出Tensor
std::unique_ptr<const Tensor> output_tensor(std::move(predictor->GetOutput(0)));
// 获取输出Tensor维度
printf("Output dim: %d\n", output_tensor->shape()[1]);
// 获取输出Tensor数据
for (int i = 0; i < ShapeProduction(output_tensor->shape()); i += 100) {
printf("Output[%d]: %f\n", i, output_tensor->data<float>()[i]);
}
```
## `Resize(shape)`
设置Tensor的维度信息。
参数:
- `shape(std::vector<int64_t>)` - 维度信息
返回:`None`
返回类型:`void`
## `shape()`
获取Tensor的维度信息。
参数:
- `None`
返回:Tensor的维度信息
返回类型:`std::vector<int64_t>`
## `data<T>()`
```c++
template <typename T>
const T* data() const;
```
获取Tensor的底层数据的常量指针,根据传入的不同模型类型获取相应数据。用于读取Tensor数据。
示例:
```c++
std::unique_ptr<const Tensor> output_tensor(std::move(predictor->GetOutput(0)));
// 如果模型中输出为float类型
output_tensor->data<float>()
```
参数:
- `None`
返回:`Tensor`底层数据常量指针
返回类型:`const T*`
## `mutable_data<T>()`
```c++
template <typename T>
T* mutable_data() const;
```
获取Tensor的底层数据的指针,根据传入的不同模型类型获取相应数据。用于设置Tensor数据。
示例:
```c++
std::unique_ptr<Tensor> input_tensor(std::move(predictor->GetInput(0)));
// 如果模型中输出为float类型
auto* data = input_tensor->mutable_data<float>();
// 设置Tensor数据
for (int i = 0; i < ShapeProduction(input_tensor->shape()); ++i) {
data[i] = 1;
}
```
参数:
- `None`
返回:`Tensor`底层数据指针
返回类型:`T*`
## `SetLoD(lod)`
设置Tensor的LoD信息。
参数:
- `lod(std::vector<std::vector<uint64_t>>)` - Tensor的LoD信息
返回:`None`
返回类型:`void`
## `lod()`
获取Tensor的LoD信息
参数:
- `None`
返回:`Tensor`的LoD信息
返回类型:`std::vector<std::vector<uint64_t>>`
---
layout: post
title: Debug tools
---
**Lite Model Debug Tool** 是用来检查Paddle-Lite框架与Paddle-Fluid框架运行时tensor(包括variable与weight)之间diff信息的基础工具。
## 工作流程:
1. 运行 `/bin/bash check_model.sh --model_dir=<your_model_path> --build_root_dir=<your_cmake_root_dir> debug_cpp_stage` 获得模型在Paddle-Lite框架下的运行拓扑信息、varibles信息和weights信息。运行后拓扑信息将会存储在默认名为 `topo_file.txt` 的文件中,variables和weights信息将会存储在默认名为 `tensor_cpp.txt` 的文件中。
2. 运行 `/bin/bash check_model.sh --model_dir=<your_model_path> --build_root_dir=<your_cmake_root_dir> debug_py_stage`执行fluid框架预测以获取相同模型在fluid框架下的variable与weight信息(注意:我们使用fluid的python api运行fluid模型,因此您在运行此步之前应确保已正确安装fluid的python api)。然后debug tool将会自动比较Paddle-Lite框架输出的信息和Paddle-Fluid框架输出的信息来检查是否存在运行时diff。 执行Paddle-Fluid框架,输出的信息将会存储在默认名为 `tensor_py.txt` 的文件中,相应的diff信息将会存储在默认名为 `diff.txt`的文件中(默认情况下,只会输出执行拓扑序中第一个有diff的variable相关的信息)。
## 注意事项:
1. 输出的结果是在**执行完一次预测后**输出的相应变量/权重的最终值,因此如果您在预测过程进行过诸如变量复用/子图融合等优化方法,则相应的输出可能会出现偏差。
2. 默认情况下debug tools将以全1作为输入进行比对。
3. 默认情况下,为了保证与Paddle-Fluid框架的结果可比对,debug tool将会禁用掉所有的Paddle-Lite的优化策略。
4. Paddle-Lite框架的执行环境由与您的编译选项有关,比如您开启了LITE_WITH_ARM编译选项,那debug tool的`debug_cpp_stage`也需要在ARM平台下运行。
## Diff信息输出:
如果debug tool检测到diff信息,那么在`diff.txt`中将会输出类似以下结构信息
```c++
>>>>>>>>>>>>>>>>>>DIFF VARIABLE: dropout_0.tmp_0<<<<<<<<<<<<<<<<<<<
dropout (X:pool2d_7.tmp_0) (Mask:dropout_0.tmp_1 Out:dropout_0.tmp_0)
--------------- Tensor File info ---------------
pool2d_7.tmp_0 {1,1536,1,1} 0.749892 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0150336 0.621641 0.147099 0.636727 0.0 0.0 0.00410917 0.784708 0.0 0.0704846 0.233599 0.840123 0.239201 0.112878 0.0 0.155352 0.306906 0.0 0.0 0.860938 0.221037 0.787316 0.256585 ...
dropout_0.tmp_0 {1,1536,1,1} 0.749892 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0150336 0.621641 0.147099 0.636727 0.0 0.0 0.00410917 0.784708 0.0 0.0704846 0.233599 0.840123 0.239201 0.112878 0.0 0.155352 0.306906 0.0 0.0 0.860938 0.221037 0.787316 0.256585 ...
--------------- Fluid Tensor info ---------------
pool2d_7.tmp_0 {1,1536,1,1} 0.7498912 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.015033395 0.6216395 0.14709876 0.63672537 0.0 0.0 0.0041093696 0.7847073 0.0 0.07048465 0.23359808 0.8401219 0.23919891 0.1128789 0.0 0.1553514 0.3069055 0.0 0.0 0.8609365 0.22103554 ...
dropout_0.tmp_0 {1,1536,1,1} 0.599913 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.012026716 0.4973116 0.117679015 0.5093803 0.0 0.0 0.0032874958 0.62776583 0.0 0.056387722 0.18687847 0.67209756 0.19135913 0.090303116 0.0 0.12428112 0.2455244 0.0 0.0 0.68874925 ...
```
其中第二行为op相关信息,标明了执行哪个op出现了diff及其对应的输入输出变量名。Tensor File info为Paddle-Lite框架的输出信息,而Fluid Tensor info为Paddle-Fluid框架的相应输出信息。
示例中的`dropout_0.tmp_1`没有相应的tensor信息是因为工具检测到其在预测的后序流程中未被使用,因此不会对预测结果造成影响,从而将其自动屏蔽掉以保证输出尽量简洁。
## 其他选项:
| Option | Description |
| --------------------------- | ------------------------------------------------------------ |
| --input_file | 输入文件名,不同field以逗号分隔,相同field内以空格分隔, 只有文件中的第一行输入信息会被使用. 如果您不指定input_file,那么所有输入将会被置为1。注意:`debug_py_stage`目前不支持多field输入。 |
| --cpp_topo_file | 存储运行时拓扑信息,由`debug_cpp_stage`写入并且由`debug_py_stage`读取使用。 默认为`topo_file.txt` 。 |
| --cpp_tensor_file | 存储`debug_cpp_stage` 在运行拓扑序下的输出信息,默认为 `tensor_cpp.txt` 。 |
| --tensor_names | 如果此选项不为空,那么只输出由此选项中指定名字的variable/weight信息,名字间用逗号分隔。 |
| --tensor_output_length | 输出数据的长度,默认为全部输出。 |
| --py_threshold | 判断diff发生的阈值,默认为 `1e-5` 。 |
| --py_tensor_file | 存储`debug_py_stage` 在运行拓扑序下的输出信息,默认为`tensor_py.txt`. |
| --py_output_file | diff信息的存储文件,默认为`diff.txt`。 |
| --py_only_output_first_diff | 是否只输出运行时拓扑序中第一个有diff的var/op信息,默认为true |
您可以参考 `check_model.sh` 脚本中的代码以获得更多细节.
## Basic Profiler
Basic profiler 用于 CPU 上kernel 耗时的统计,在 cmake 时添加 `-DLITE_WITH_PROFILER=ON` ,就可以开启相应支持。
在模型执行完毕后,会自动打印类似如下 profiler 的日志
```
kernel average min max count
feed/def/1/4/2 0 0 0 1
conv2d/def/4/1/1 1175 1175 1175 1
conv2d/def/4/1/1 1253 1253 1253 1
depthwise_conv2d/def/4/1/1 519 519 519 1
conv2d/def/4/1/1 721 721 721 1
elementwise_add/def/4/1/1 18 18 18 1
conv2d/def/4/1/1 2174 2174 2174 1
depthwise_conv2d/def/4/1/1 380 380 380 1
conv2d/def/4/1/1 773 773 773 1
elementwise_add/def/4/1/1 2 2 2 1
conv2d/def/4/1/1 1248 1248 1248 1
depthwise_conv2d/def/4/1/1 492 492 492 1
conv2d/def/4/1/1 1150 1150 1150 1
elementwise_add/def/4/1/1 33 33 33 1
elementwise_add/def/4/1/1 3 3 3 1
conv2d/def/4/1/1 1254 1254 1254 1
depthwise_conv2d/def/4/1/1 126 126 126 1
```
---
layout: post
title: 基础须知
---
可以参考 [Paddle 开发者文档](https://www.paddlepaddle.org.cn/documentation/docs/zh/1.5/advanced_usage/development/contribute_to_paddle/local_dev_guide.html)
# 提交PR
需要在 commit message 里加上 `test=develop` 才能触发 CI
# 版本发布检查清单
1. 所有 feature 梳理,确认状态
2. 所有 QA 测试结果梳理,确认版本可靠
3. Release note 确认 review 通过
4. 确认需要 release 的 binary 编译完毕
---
layout: post
title: 基础须知
---
可以参考 [Paddle 开发者文档](https://www.paddlepaddle.org.cn/documentation/docs/zh/1.5/advanced_usage/development/contribute_to_paddle/local_dev_guide.html)
# 提交PR
需要在 commit message 里加上 `test=develop` 才能触发 CI
# 版本发布检查清单
1. 所有 feature 梳理,确认状态
2. 所有 QA 测试结果梳理,确认版本可靠
3. Release note 确认 review 通过
4. 确认需要 release 的 binary 编译完毕
---
layout: post
title: Lite基于fpga的模型预测
---
Paddle Lite支持基于arm的fpga zu3/zu5/zu9的模型预测,提供armv8的交叉编译
Lite基于fpga运行模型需要相应的fpga驱动,目前只支持百度edgeboard开发板
**Lite实现fpga简介**
Lite支持fpga作为后端硬件进行模型推理,其主要特性如下:
- Lite中fpga的kernel(feed、fetch除外)均以FP16、NHWC的格式作为输入输出格式,所有的weights和bias仍为FP32、NCHW的格式,feed的输入和fetch的输出均为FP32、NCHW格式的数据,在提升计算速度的同时能做到用户对数据格式无感知
- 对于fpga暂不支持的kernel,均会切回arm端运行,实现arm+fpga混合布署运行
- 目前fpga成本功耗都较低,Lite基于fpga的模型性能远远好于arm端,可作为边缘设备首选硬件
# 编译
需要提前准备带有fpgadrv.ko的fpga开发板(如edgeboard开发板)和Lite代码
CMAKE编译选项:
- 设置`LITE_WITH_FPGA=ON``LITE_WITH_ARM=ON`
其他编译选项与ARM编译相同,可以参考[“Paddle Lite在Docker下的ARM编译”](../source_compile)
示例如下:
```shell
cmake .. \
-DWITH_GPU=OFF \
-DWITH_MKL=OFF \
-DWITH_LITE=ON \
-DLITE_WITH_CUDA=OFF \
-DLITE_WITH_X86=OFF \
-DLITE_WITH_ARM=ON \
-DLITE_WITH_OPENMP=ON \
-DLITE_WITH_LIGHT_WEIGHT_FRAMEWORK=ON \
-DWITH_TESTING=OFF \
-DLITE_WITH_FPGA=ON \
-DARM_TARGET_OS=armlinux
make publish_inference -j2
```
Lite提供fpga编译脚本,位于lite/tools/build_fpga.sh,在Lite根目录执行该脚本即可编译
# 运行示例
- **运行文件准备**
下面以Resnet50模型为例,介绍如何使用edgeboard开发板实现模型运行
```bash
#连接开发板,并利用screen命令启动 [本机执行]
screen /dev/cu.SLAB_USBtoUART 115200
#查看开发板ip并ssh登录到开发板,假设开发板ip为192.0.1.1 [本机执行]
ssh root@192.0.1.1
#在开发板上建立目录workspace,拷贝fpga驱动fpgadrv.ko到workspace目录 [开发板执行]
mkdir workspace && scp $DRIVER_PATH/fpgadrv.ko workspace
#将Lite中编译好的测试程序拷贝到开发板workspace目录 [本机执行]
scp $LITE_ROOT/build_fpga/lite/api/test_resnet50_fpga root@$EDGEBOARD_IP:workspace/
#把Resnet50的模型和参数scp到开发板workspace目录 [本机执行]
scp -r $LITE_ROOT/build_fpga/lite/third_party/install/resnet50/ root@$EDGEBOARD_IP:workspace/
#在运行模型前需要加载fpga驱动 [开发板执行]
insmod fpgadrv.ko
#给测试程序添加可运行权限 [开发板执行]
chmod +x test_resnet50_fpga
```
- **使用fpga进行模型预测**
```bash
#以下命令均在开发板上运行
#直接运行单测程序
./test_resnet50_fpga --model_dir=resnet50
#如果需要测试性能,可以用repeats参数设置模型运行次数(如1000),同时可以设置预热次数(如10)来让硬件事先运行到稳定水平
./test_resnet50_fpga --model_dir=resnet50 --repeats=1000 --warmup=10
```
# 如何在Code中使用
在Lite中使用fpga与ARM相似,具体的区别如下:
- 由于fpga运行模式为fp16精度、nhwc布局,所以需要修改相应的`valid_place`
- fpga不需要device的初始化和运行模式设置
代码示例:
```cpp
lite::Predictor predictor;
std::vector<Place> valid_places(
{Place{TARGET(kFPGA), PRECISION(kFP16), DATALAYOUT(kNHWC)}});
predictor.Build(model_dir, "", "", valid_places);
auto* input_tensor = predictor.GetInput(0);
input_tensor->Resize(DDim(std::vector<DDim::value_type>({1, 3, 224, 224})));
auto* data = input_tensor->mutable_data<float>();
auto item_size = input_tensor->dims().production();
//假设设置输入数据全为1
for (int i = 0; i < item_size; i++) {
data[i] = 1;
}
predictor.Run();
auto* out = predictor.GetOutput(0);
```
---
layout: post
title: Paddle-Lite文档
---
> 版本:develop
Paddle-Lite 框架是 PaddleMobile 新一代架构,重点支持移动端推理预测,特点**高性能、多硬件、轻量级** 。支持PaddleFluid/TensorFlow/Caffe/ONNX模型的推理部署,目前已经支持 ARM CPU, Mali GPU, Adreno GPU, Huawei NPU 等多种硬件,正在逐步增加 X86 CPU, Nvidia GPU 等多款硬件,相关硬件性能业内领先。
## 简介
- [技术特点]({{site.baseurl}}/v2.0.0/tech_highlights)
- [架构设计]({{site.baseurl}}/v2.0.0/architecture)
- [支持的硬件]({{site.baseurl}}/v2.0.0/support_hardware)
- [Road Map]({{site.baseurl}}/v2.0.0/roadmap)
## Benchmark
- [最新性能]({{site.baseurl}}/v2.0.0/benchmark)
- [测试方法]({{site.baseurl}}/v2.0.0/benchmark_tools)
## 安装
- [源码编译]({{site.baseurl}}/v2.0.0/source_compile)
## 使用
- [使用流程]({{site.baseurl}}/v2.0.0/tutorial)
- [C++实例]({{site.baseurl}}/v2.0.0/cpp_demo)
- [Java实例]({{site.baseurl}}/v2.0.0/java_demo)
- [Android/IOS APP demo](https://github.com/PaddlePaddle/Paddle-Lite-Demo)
- [模型转化方法]({{site.baseurl}}/v2.0.0/model_optimize_tool)
- [根据模型裁剪预测库方法]({{site.baseurl}}/v2.0.0/library_tailoring)
## 进阶
- [通过 X2Paddle 支持 Caffe, TensorFlow , ONNX 模型]({{site.baseurl}}/v2.0.0/x2paddle)
- [X2Paddle 支持模型列表]({{site.baseurl}}/v2.0.0/x2paddle_models_doc)
- [模型量化]({{site.baseurl}}/v2.0.0/model_quantization)
- [支持Op列表]({{site.baseurl}}/v2.0.0/support_operation_list)
- [新增Op方法]({{site.baseurl}}/v2.0.0/add_new_operation)
- [测试工具]({{site.baseurl}}/v2.0.0/debug_tools)
- [调试方法]({{site.baseurl}}/v2.0.0/debug_tools)
- [使用华为NPU]({{site.baseurl}}/v2.0.0/npu)
- [使用Android GPU]({{site.baseurl}}/v2.0.0/opencl)
- [使用FPGA]({{site.baseurl}}/v2.0.0/fpga)
- [使用CUDA]({{site.baseurl}}/v2.0.0/cuda)
## 开发者文档
- [开发基础须知]({{site.baseurl}}/v2.0.0/for-developer)
- [架构详解]({{site.baseurl}}/v2.0.0/architecture-intro)
## API文档
- [C++ API文档]({{site.baseurl}}/v2.0.0/cxx_api_doc)
- [Java API文档]({{site.baseurl}}/v2.0.0/java_api_doc)
## FAQ
- 问题或建议可以[发Issue](https://github.com/PaddlePaddle/Paddle-Lite/issues),为加快问题解决效率,可先检索是否有类似问题,我们也会及时解答!
- 欢迎加入Paddle-Lite百度官方QQ群:696965088
## paddle-mobile
- [paddle-mobile 编译]({{site.baseurl}}/v2.0.0/mobile)
---
layout: post
title: Java API文档
---
* TOC
{:toc}
# MobileConfig
```java
public class MobileConfig extends ConfigBase;
```
`MobileConfig`用来配置构建轻量级PaddlePredictor的配置信息,如NaiveBuffer格式的模型地址、能耗模式、工作线程数等等。
*注意:输入的模型需要使用Model Optimize Tool转化为NaiveBuffer格式的优化模型。*
示例:
```java
MobileConfig config = new MobileConfig();
// 设置NaiveBuffer格式模型目录
config.setModelDir(modelPath);
// 设置能耗模式
config.setPowerMode(PowerMode.LITE_POWER_HIGH);
// 设置工作线程数
config.setThreads(1);
// 根据MobileConfig创建PaddlePredictor
PaddlePredictor predictor = PaddlePredictor.createPaddlePredictor(config);
```
## ``setModelDir(model_dir)``
设置模型文件夹路径。
参数:
- `model_dir(String)` - 模型文件夹路径
返回:`None`
返回类型:`void`
## `getModelDir()`
返回设置的模型文件夹路径。
参数:
- `None`
返回:模型文件夹路径
返回类型:`String`
## `setPowerMode(mode)`
设置CPU能耗模式。若不设置,则默认使用`LITE_POWER_HIGH`
*注意:只在开启`OpenMP`时生效,否则系统自动调度。*
参数:
- `mode(PowerMode)` - CPU能耗模式。
返回:`None`
返回类型:`void`
## `getPowerMode()`
获取设置的CPU能耗模式。
参数:
- `None`
返回:设置的CPU能耗模式
返回类型:`PowerMode`
## `setThreads(threads)`
设置工作线程数。若不设置,则默认使用单线程。
*注意:只在开启`OpenMP`的模式下生效,否则只使用单线程。*
参数:
- `threads(int)` - 工作线程数。默认为1。
返回:`None`
返回类型:`void`
## `getThreads()`
获取设置的工作线程数。
参数:
- `None`
返回:工作线程数
返回类型:`int`
# PaddlePredictor
```java
public class PaddlePredictor;
```
`PaddlePredictor`是Paddle-Lite的预测器。用户可以根据PaddlePredictor提供的接口使用MobileConfig创建新的预测器、设置输入数据、执行模型预测、获取输出以及获得当前使用lib的版本信息等。
示例:
```java
// 设置MobileConfig
MobileConfig config = new MobileConfig();
config.setModelDir(modelPath);
// 创建PaddlePredictor
PaddlePredictor predictor = PaddlePredictor.createPaddlePredictor(config);
// 设置输入数据
long[] dims = {100, 100};
float[] inputBuffer = new float[10000];
for (int i = 0; i < 10000; ++i) {
inputBuffer[i] = i;
}
Tensor input = predictor.getInput(0);
input.resize(dims);
input.setData(inputBuffer);
// 执行预测
predictor.run();
// 获取输出数据
Tensor output = predictor.getOutput(0);
float[] output = result.getFloatData();
for (int i = 0; i < 1000; ++i) {
System.out.println(output[i]);
}
```
## `CreatePaddlePredictor(config)`
```java
public static PaddlePredictor createPaddlePredictor(ConfigBase config);
```
`CreatePaddlePredictor`用来根据`ConfigBase`动态创建预测器,目前Java API支持使用MobileConfig`。框架会根据您在config中指定的模型路径、能耗模型、工作线程数等自动创建一个预测器。
参数:
- `config(ConfigBase,目前应使用MobileConfig)` - 创建预测器的配置信息
返回:根据config创建完成的预测器
返回类型:`PaddlePredictor`
## `getInput(index)`
获取输入Tensor,用来设置模型的输入数据。
参数:
- `index(int)` - 输入Tensor的索引
返回:第`index`个输入`Tensor`
返回类型:`Tensor`
## `getOutput(index)`
获取输出Tensor,用来获取模型的输出结果。
参数:
- `index(int)` - 输出Tensor的索引
返回:第`index`个输出Tensor
返回类型:`Tensor`
## `run()`
执行模型预测,需要在***设置输入数据后***调用。
参数:
- `None`
返回:预测执行状态,成功返回`true`,否则返回`false`
返回类型:`boolean`
## `getVersion()`
用于获取当前lib使用的代码版本。若代码有相应tag则返回tag信息,如`v2.0-beta`;否则返回代码的`branch(commitid)`,如`develop(7e44619)`。
参数:
- `None`
返回:当前lib使用的代码版本信息
返回类型:`String`
# PowerMode
```java
public enum PowerMode;
```
`PowerMode`为ARM CPU能耗模式,用户可以根据应用场景设置能耗模式获得最优的能效比。
示例:
```java
MobileConfig config = new MobileConfig();
// 设置NaiveBuffer格式模型目录
config.setModelDir(modelPath);
// 设置能耗模式
config.setPowerMode(PowerMode.LITE_POWER_HIGH);
// 根据MobileConfig创建PaddlePredictor
PaddlePredictor predictor = PaddlePredictor.createPaddlePredictor(config);
```
PowerMode详细说明如下:
| 选项 | 说明 |
| :------------------: | ------------------------------------------------------------ |
| LITE_POWER_HIGH | 绑定大核运行模式。如果ARM CPU支持big.LITTLE,则优先使用并绑定Big cluster。如果设置的线程数大于大核数量,则会将线程数自动缩放到大核数量。如果系统不存在大核或者在一些手机的低电量情况下会出现绑核失败,如果失败则进入不绑核模式。 |
| LITE_POWER_LOW | 绑定小核运行模式。如果ARM CPU支持big.LITTLE,则优先使用并绑定Little cluster。如果设置的线程数大于小核数量,则会将线程数自动缩放到小核数量。如果找不到小核,则自动进入不绑核模式。 |
| LITE_POWER_FULL | 大小核混用模式。线程数可以大于大核数量。当线程数大于核心数量时,则会自动将线程数缩放到核心数量。 |
| LITE_POWER_NO_BIND | 不绑核运行模式(推荐)。系统根据负载自动调度任务到空闲的CPU核心上。 |
| LITE_POWER_RAND_HIGH | 轮流绑定大核模式。如果Big cluster有多个核心,则每预测10次后切换绑定到下一个核心。 |
| LITE_POWER_RAND_LOW | 轮流绑定小核模式。如果Little cluster有多个核心,则每预测10次后切换绑定到下一个核心。 |
# Tensor
```c++
public class Tensor;
```
Tensor是Paddle-Lite的数据组织形式,用于对底层数据进行封装并提供接口对数据进行操作,包括设置维度、数据等。
*注意:用户应使用`PaddlePredictor`的`getInput`和`getOuput`接口获取输入/输出的`Tensor`。*
示例:
```java
// 设置MobileConfig
MobileConfig config = new MobileConfig();
config.setModelDir(modelPath);
// 创建PaddlePredictor
PaddlePredictor predictor = PaddlePredictor.createPaddlePredictor(config);
// 设置输入数据
long[] dims = {100, 100};
float[] inputBuffer = new float[10000];
for (int i = 0; i < 10000; ++i) {
inputBuffer[i] = i;
}
// 获取输入Tensor
Tensor input = predictor.getInput(0);
// 设置输入维度
input.resize(dims);
// 设置输入数据
input.setData(inputBuffer);
// 执行预测
predictor.run();
// 获取输出Tensor
Tensor output = predictor.getOutput(0);
// 获取输出数据
float[] output = result.getFloatData();
for (int i = 0; i < 1000; ++i) {
System.out.println(output[i]);
}
```
## `resize(dims)`
设置Tensor的维度信息。
参数:
- `dims(long[])` - 维度信息
返回:设置成功返回`true`,否则返回`false`
返回类型:`boolean`
## `shape()`
获取Tensor的维度信息。
参数:
- `None`
返回:Tensor的维度信息
返回类型:`long[]`
## `setData(data)`
设置Tensor数据。
参数:
- `data(float[])` - 需要设置的数据
返回:成功则返回`true`,否则返回`false`
返回类型:`boolean`
## `getFloatData()`
获取Tensor的底层float型数据。
参数:
- `None`
返回:`Tensor`底层数据
返回类型:`float[]`
---
layout: post
title: Java Android Demo
---
* TOC
{:toc}
本节中,Java demo 完整代码位于 [demo/java](https://github.com/PaddlePaddle/Paddle-Lite/tree/develop/lite/demo/java)
要编译和跑起Android demo 程序 PaddlePredictor,你需要准备:
1. 一台能运行安卓程序的安卓手机
2. 一台带有AndroidStudio的开发机
## 编译
首先在PaddleLite的开发 [Docker镜像](../source_compile) 中,拉取最新PaddleLite代码,编译对应你手机架构的预测库,
下面我们以arm8 架构举例。进入paddlelite 目录,运行以下命令:
```shell
./lite/tools/build.sh \
--arm_os=android \
--arm_abi=armv8 \
--arm_lang=gcc \
--android_stl=c++_static \
tiny_publish
```
命令完成后查看要存在
```
./build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/java/so/libpaddle_lite_jni.so
./build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/java/jar/PaddlePredictor.jar
./build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/demo/java/android
```
libpaddle_lite_jni.so为 PaddleLite c++ 动态链接库,PaddlePredictor.jar为 Java jar 包,两者包含 PaddleLite Java API,接下来 Android Java 代码会使用这些api。android文件夹中则是Android demo。
## 准备 demo 需要的其他文件
Demo 除了代码,还需要准备在Android工程目录下配置好JNI .so 库(上节提到的`libpaddle_lite_jni.so`),Java .jar 包(上文提到的`PaddlePredictor.jar` ),和模型文件。我们提供了自动化的脚本和手动拷贝两种方法,用户可以根据自己需要选择:
### 脚本方法
进入 `build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/demo/java/android`,我们准备了一个脚本`prepare_demo.bash`,脚本输入一个参数,为你要拷贝的.so 对应的架构文件夹名。
例如运行
```
bash prepare_demo.bash arm8
```
该脚本自动下载并解压缩模型文件,拷贝了 .jar 包进demo,还有生成的.so包进`PaddlePredictor/app/src/main/jinLibs/架构文件夹下`
在我们这个例子里,armv8 就是架构文件夹。备注:这种方式构建的 demo 在 armv8 手机运行正常。如果要demo 程序在别的手机架构(如 armv7)上也运行正常,需要添加别的架构。
### 手动拷贝方法
接下来我们介绍手动拷贝,如果使用了脚本,那么可以跳过以下手动方法的介绍。
### 把 .so 动态库和 .jar 拷贝进安卓demo程序:
1. 将PaddlePredictor 载入到AndroidStudio。
2.`libpaddle_lite_jni.so`拷贝进 `PaddlePredictor/app/src/main/jinLibs/架构文件夹下` ,比如文件夹arm8里要包含该 .so文件。
3.`PaddlePredictor.jar` 拷贝进 `PaddlePredictor/app/libs`
### 把demo使用到的模型文件拷贝进安卓程序:
下载我们的5个模型文件,并解压缩到 `PaddlePredictor/app/src/main/assets` 这个文件夹中
需要拷贝的模型文件和下载地址:
```
inception_v4_simple_opt.nb http://paddle-inference-dist.bj.bcebos.com/inception_v4_simple_opt.nb.tar.gz
lite_naive_model_opt.nb http://paddle-inference-dist.bj.bcebos.com/lite_naive_model_opt.nb.tar.gz
mobilenet_v1_opt.nb http://paddle-inference-dist.bj.bcebos.com/mobilenet_v1_opt.nb.tar.gz
mobilenet_v2_relu_opt.nb http://paddle-inference-dist.bj.bcebos.com/mobilenet_v2_relu_opt.nb.tar.gz
resnet50_opt.nb http://paddle-inference-dist.bj.bcebos.com/resnet50_opt.nb.tar.gz
```
下载完后,assets文件夹里要包含解压后的上面五个模型文件夹,但demo里不需要保存原压缩.tar.gz 文件。
注意:输入的模型要求为naive buffer存储格式,您可以通过 [**Model Optimize Tool**](../model_optimize_tool) 将fluid模型转为naive buffer存储格式。
## 运行 Android 程序结果
以上准备工作完成,就可以开始Build 、安装、和运行安卓demo程序。当你运行PaddlePredictor 程序时,大概会等10秒,然后看到类似以下字样:
```
lite_naive_model output: 50.213173, -28.872887
expected: 50.2132, -28.8729
inception_v4_simple test:true
time: xxx ms
resnet50 test:true
time: xxx ms
mobilenet_v1 test:true
time: xxx ms
mobilenet_v2 test:true
time: xxx ms
```
该 demo 程序跑我们的 5 个模型,第一个模型结果将真正的头两个数字输出,并在第二行附上期望的正确值。你应该要看到他们的误差小于0.001。后面四个模型如果你看到 `test:true` 字样,说明模型输出通过了我们在 demo 程序里对其输出的测试。time 代表该测试花费的时间。
---
layout: post
title: 裁剪预测库方法
---
* TOC
{:toc}
## 裁剪预测库方法
Paddle-Lite支持**根据模型裁剪预测库**功能。Paddle-Lite的一般编译会将所有已注册的operator打包到预测库中,造成库文件体积膨胀;**裁剪预测库**能针对具体的模型,只打包优化后该模型需要的operator,有效降低预测库文件大小。
## 效果展示
| mobilenet_v1 | libpaddle_full_api_shared.so | libpaddle_light_api_shared.so | libpaddle_lite_jni.so |
| ------------------ | ---------------------------- | ----------------------------- | --------------------- |
| mobilenet_v1 | 14M | 14M | 6.2M |
| 裁剪后mobilenet_v1 | 7.7M | 7.5M | 2.5M |
## 实现过程:
### 1、转化模型时记录优化后模型信息
说明:使用model_optimize_tool转化模型时,选择 `--record_tailoring_info =true` 会将优化后模型的OP和kernel信息保存到输出文件夹,这些信息将用于编译裁剪后的动态库。
注意:需要使用Paddle-Lite 最新版本(release/v2.0.0之后)代码编译出的model_optimize_tool
例如:
```
./model_optimize_tool --model_dir=./mobilenet_v1 --optimize_out_type=naive_buffer --optimize_out=mobilenet_v1NB --record_tailoring_info =true --valid_targets=arm
```
效果:优化后模型使用的OP和kernel信息被保存在 `mobilenet_v1NB`文件夹中的隐藏文件里了
### 2、根据模型信息编译裁剪后的预测库
说明:编译Paddle-Lite时选择`--build_tailor=ON` ,并且用 `–-opt_model_dir=` 指定优化后的模型的地址
例如:
```
./lite/tools/build.sh --arm_os=android --arm_abi=armv7 --arm_lang=gcc --android_stl=c++_static --build_extra=ON --build_tailor=ON --opt_model_dir=../mobilenet_v1NB full_publish
```
**注意**:上面命令中的`../mobilenet_v1NB`是第1步得到的转化模型的输出路径
**效果**:编译出来的动态库文件变小,且可以运行优化后的模型。
| | libpaddle_full_api_shared.so | libpaddle_light_api_shared.so | libpaddle_lite_jni.so |
| ------------------ | ---------------------------- | ----------------------------- | --------------------- |
| mobilenet_v1 | 14M | 14M | 6.2M |
| 裁剪后mobilenet_v1 | 7.7M | 7.5M | 2.5M |
编译出的C++预测库文件位于 :
`build.lite.android.armv7.gcc/inference_lite_lib.android.armv7/cxx/lib/`
编译出的Java预测库文件位于:
`build.lite.android.armv7.gcc/inference_lite_lib.android.armv7/java/so/`
### 3、运行裁剪后的预测库文件
注意:基于某一模型裁剪出的预测库只能支持优化工具转化后的该模型,例如根据mobilenetV1裁剪出的 full_api预测库只能运行以protobuf格式转化出的模型mobilenetV1_opt_nb, 裁剪出的light_api预测库只能运行以naive_buffer格式转化出的模型mobilenetV1_opt_nb, 运行其他模型可能会出现`segementation fault:undifined op or kernel`。 模型转化方法参考:[使用model_optimize_tool转化模型](../model_optimize_tool))。
**示例1**:使用裁剪后的light_api预测库运行mobilenetv1
1、执行第二步编译后,light_api的C++ 示例位于
`/Paddle-Lite/build.lite.android.armv7.gcc/inference_lite_lib.android.armv7/demo/cxx/mobile_light`
输入`make`命令执行编译可编译出可执行文件mobilenetv1_light_api
2、使用adb将mobilenetV1_NB模型和mobilenetv1_light_api传到手机后执行demo:
`./mobilenetv1_light_api --model_dir=./mobilenetV1_NB`
注意:`mobilenetV1_NB`是用`mobilenetV1`模型转化的naive_buffer格式模型(不需要设置` --record_tailoring_info =true`,转化流程参考:[使用model_optimize_tool转化模型](../model_optimize_tool))。
**示例2**:使用裁剪后的full_api预测库运行mobilenetv1
1、执行第二步编译后,full_api的C++ 示例位于
`/Paddle-Lite/build.lite.android.armv7.gcc/inference_lite_lib.android.armv7/demo/cxx/mobile_light`
替换mobilenetv1_full_api.cc代码内容:
```C++
#include <gflags/gflags.h>
#include <stdio.h>
#include <vector>
#include "paddle_api.h" // NOLINT
#include "paddle_use_kernels.h" // NOLINT
#include "paddle_use_ops.h" // NOLINT
#include "paddle_use_passes.h" // NOLINT
using namespace paddle::lite_api; // NOLINT
DEFINE_string(model_dir, "", "Model dir path.");
int64_t ShapeProduction(const shape_t& shape) {
int64_t res = 1;
for (auto i : shape) res *= i;
return res;
}
void RunModel() {
// 1. Set CxxConfig
CxxConfig config;
config.set_model_file(FLAGS_model_dir + "model");
config.set_param_file(FLAGS_model_dir + "params");
std::vector<Place> valid_places{Place{TARGET(kARM), PRECISION(kFloat)}};
config.set_valid_places(valid_places);
// 2. Create PaddlePredictor by CxxConfig
std::shared_ptr<PaddlePredictor> predictor =
CreatePaddlePredictor<CxxConfig>(config);
// 3. Prepare input data
std::unique_ptr<Tensor> input_tensor(std::move(predictor->GetInput(0)));
input_tensor->Resize(shape_t({1, 3, 224, 224}));
auto* data = input_tensor->mutable_data<float>();
for (int i = 0; i < ShapeProduction(input_tensor->shape()); ++i) {
data[i] = 1;
}
// 4. Run predictor
predictor->Run();
// 5. Get output
std::unique_ptr<const Tensor> output_tensor(
std::move(predictor->GetOutput(0)));
printf("Output dim: %d\n", output_tensor->shape()[1]);
for (int i = 0; i < ShapeProduction(output_tensor->shape()); i += 100) {
printf("Output[%d]: %f\n", i, output_tensor->data<float>()[i]);
}
}
int main(int argc, char** argv) {
google::ParseCommandLineFlags(&argc, &argv, true);
RunModel();
return 0;
}
```
2、使用adb将mobilenetV1_PB模型和mobilenetv1_full_api传到手机后执行demo:
`./mobilenetv1_full_api --model_dir=./mobilenetV1_PB`
注意:`mobilenetV1_PB`是用`mobilenetV1`模型转化的protobuf格式模型(不需要设置` --record_tailoring_info =true`,转化流程参考:[使用model_optimize_tool转化模型](../model_optimize_tool))。
---
layout: post
title: paddle mobile 代码编译
---
详情可以参考 [mobile/README](https://github.com/PaddlePaddle/Paddle-Lite/tree/develop/mobile)
要切换 paddle-mobile 编译,cmake 需要加上 **-DWITH_PADDLE_MOBILE=ON** 开关,其余 flag 请参考上面文档添加到后面
所有其他选项跟 paddle-mobile 原始操作完全一致
\ No newline at end of file
---
layout: post
title: 模型转化方法
---
Lite架构在预测过程中表现出来的高性能得益于其丰富的优化组件,其中包括量化、子图融合、混合调度、Kernel优选等等策略。为了使优化过程更加方便易用,我们提供了**Model Optimize Tool**来自动完成优化步骤,输出一个轻量的、最优的可执行模型。具体使用方法介绍如下:
## 准备model_optimize_tool
可以选择下载或者手动编译model_optimize_tool模型优化工具。
### 下载model_optimize_tool
[Paddle-Lite Release](https://github.com/PaddlePaddle/Paddle-Lite/releases/)官网下载最新版本的`model_optimize_tool`
![model_optimize_tool](https://user-images.githubusercontent.com/45189361/65481346-8d2e7100-dec7-11e9-848b-b237a2f4a3ff.png)
注意:运行前需解压model_optimize_tool并添加可执行权限
```
gunzip ./model_optimize_tool.gz
chmod +x model_optimize_tool
```
### 编译model_optimize_tool
1、参照 [编译安装](./source_compile) 进行环境配置和编译
2、进入docker中PaddleLite根目录,```git checkout [release-version-tag]```切换到release分支
3、执行如下命令编译model_optimize_tool
```bash
./lite/tools/build.sh build_optimize_tool
```
4、编译完成,优化工具在```Paddle-Lite/build.model_optimize_tool/lite/api/model_optimize_tool```
## 使用方法
1、准备需要优化的fluid模型
fluid模型有两种形式,combined形式(权重保存为一个param文件)和非combined形式(权重保存为一个一个单独的文件),model_optimize_tool支持对这两种形式的fluid模型进行直接优化。
2、使用model_optimize_tool对模型进行优化(**需要在 x86 PC 端执行**)
```shell
./model_optimize_tool \
--model_dir=<model_param_dir> \
--model_file=<model_path> \
--param_file=<param_path> \
--optimize_out_type=(protobuf|naive_buffer) \
--optimize_out=<output_optimize_model_dir> \
--valid_targets=(arm|opencl|x86) \
--prefer_int8_kernel=(true|false) \
--record_tailoring_info =(true|false)
```
| 选项 | 说明 |
| ------------------- | ------------------------------------------------------------ |
| --model_dir | 待优化的fluid模型(非combined形式)的路径,其中包括网络结构文件和一个一个单独保存的权重文件。|
| --model_file | 待优化的fluid模型(combined形式)的网络结构路径。 |
| --param_file | 待优化的fluid模型(combined形式)的权重文件路径。 |
| --optimize_out_type | 输出模型类型,目前支持两种类型:protobuf和naive_buffer,其中naive_buffer是一种更轻量级的序列化/反序列化实现。若您需要在mobile端执行模型预测,请将此选项设置为naive_buffer。默认为protobuf。 |
| --optimize_out | 优化模型的输出路径。 |
| --valid_targets | 指定模型可执行的backend,目前可支持x86、arm、opencl,您可以同时指定多个backend(以空格分隔),Model Optimize Tool将会自动选择最佳方式。默认为arm。 |
| --prefer_int8_kernel | 若待优化模型为int8量化模型(如量化训练得到的量化模型),则设置该选项为true以使用int8内核函数进行推理加速,默认为false。 |
| --record_tailoring_info | 当使用**根据模型裁剪库文件**功能时,则设置该选项为true,以记录优化后模型含有的kernel和OP信息,默认为false。 |
* 如果待优化的fluid模型是非combined形式,请设置`--model_dir`,忽略`--model_file``--param_file`
* 如果待优化的fluid模型是combined形式,请设置`--model_file``--param_file`,忽略`--model_dir`
* 优化后的模型包括__model__.nb和param.nb文件。
---
layout: post
title: 模型量化
---
* TOC
{:toc}
本文主要介绍使用PaddlePaddle和Paddle-Lite对模型进行端到端的量化训练和推理执行,模型示例为MobileNetV1。
目前,PaddlePaddle框架的量化训练主要针对卷积层(包括二维卷积和Depthwise卷积)、和全连接层,对应算子是conv2d、depthwise_conv2d和mul,更多量化训练的原理请参考[文档](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/tutorial.md#1-quantization-aware-training%E9%87%8F%E5%8C%96%E4%BB%8B%E7%BB%8D)。Paddle-Lite支持运行PaddlePaddle框架量化训练产出的模型,可以进一步加快模型在移动端的执行速度。
温馨提示:如果您是初次接触PaddlePaddle框架,建议首先学习[新人入门](https://www.paddlepaddle.org.cn/documentation/docs/zh/1.5/beginners_guide/index_cn.html)[使用指南](https://www.paddlepaddle.org.cn/documentation/docs/zh/1.5/user_guides/index_cn.html)
## 准备量化模型
您可以选择下载训练好的量化模型,或者使用PaddleSlim模型压缩工具训练得到量化模型。
### 下载量化模型
官方发布了[MobileNetV1量化模型](https://paddle-inference-dist.bj.bcebos.com/int8%2Fpretrain%2Fmobilenet_v1_quant%2Ffloat.zip),直接下载到本地。
```bash
wget https://paddle-inference-dist.bj.bcebos.com/int8%2Fpretrain%2Fmobilenet_v1_quant%2Ffloat.zip
```
### 使用PaddleSlim模型压缩工具训练量化模型
#### 安装PaddlePaddle
根据操作系统、安装方式、Python版本和CUDA版本,按照[官方说明](https://paddlepaddle.org.cn/start)安装PaddlePaddle。例如:
Ubuntu 16.04.4 LTS操作系统,CUDA9,cuDNN7,GPU版本安装:
```bash
pip install paddlepaddle-gpu==1.6.0.post97 -i https://mirrors.aliyun.com/pypi/simple/
```
Ubuntu 16.04.4 LTS操作系统,CPU版本安装:
```bash
pip install paddlepaddle==1.6.0 -i https://mirrors.aliyun.com/pypi/simple/
```
#### 克隆量化训练所需的代码库
克隆[PaddlePaddle/models](https://github.com/PaddlePaddle/models)到本地,并进入models/PaddleSlim路径。
```bash
git clone https://github.com/PaddlePaddle/models.git
cd models/PaddleSlim
```
#### 数据准备
##### 训练数据准备
参考[models/PaddleCV/image_classification](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/image_classification#data-preparation)中的数据准备教程,下载训练数据,并且保存到PaddleSlim/data路径下。
##### 预训练模型准备
参考/models/PaddleSlim/run.sh脚本, 从[models/PaddleCV/image_classification](https://github.com/PaddlePaddle/models/tree/develop/fluid/PaddleCV/image_classification#supported-models-and-performances)下载MobileNetV1的预训练模型,并保存到PaddleSlim/pretrain路径下。
经过以上三步,PaddleSlim目录下的文件结构如下所示:
```bash
.
├── compress.py # 模型压缩任务主脚本,定义了压缩任务需要的模型相关信息
├── configs # 压缩任务的配置文件,包括:蒸馏、int8量化量化、filter剪切和组合策略的配置文件
├── data # 存放训练数据(需要用户自己创建)
│   └── ILSVRC2012
├── pretrain # 存放预训练模型参数,执行run.sh自动生成
│   ├── MobileNetV1_pretrained
│   ├── MobileNetV1_pretrained.tar
│   ├── ResNet50_pretrained
│   └── ResNet50_pretrained.tar
├── docs # 文档目录
├── light_nas
├── models # 模型网络结构的定义,如MobileNetV1
├── quant_low_level_api # 量化训练的底层API, 用于灵活定制量化训练的过程,适用于高阶用户
├── reader.py # 定义数据处理逻辑
├── README.md
├── run.sh # 模型压缩任务启动脚本
└── utility.py # 定义了常用的工具方法
```
#### 压缩脚本介绍
`compress.py`中定义了执行压缩任务需要的所有模型相关的信息,这里对几个关键的步骤进行简要介绍:
##### 目标网络的定义
compress.py的以下代码片段定义了train program, 这里train program只有前向计算操作。
```python
out = model.net(input=image, class_dim=args.class_dim)
cost = fluid.layers.cross_entropy(input=out, label=label)
avg_cost = fluid.layers.mean(x=cost)
acc_top1 = fluid.layers.accuracy(input=out, label=label, k=1)
acc_top5 = fluid.layers.accuracy(input=out, label=label, k=5)
```
然后,通过clone方法得到eval_program, 用来在压缩过程中评估模型精度,如下:
```python
val_program = fluid.default_main_program().clone()
```
定义完目标网络结构,需要对其初始化,并根据需要加载预训练模型。
##### 定义feed_list和fetch_list
对于train program, 定义train_feed_list用于指定从train data reader中取的数据feed给哪些variable。定义train_fetch_list用于指定在训练时,需要在log中展示的结果。如果需要在训练过程中在log中打印accuracy信心,则将('acc_top1', acc_top1.name)添加到train_fetch_list中即可。
```python
train_feed_list = [('image', image.name), ('label', label.name)]
train_fetch_list = [('loss', avg_cost.name)]
```
> 注意: 在train_fetch_list里必须有loss这一项。
对于eval program. 同上定义eval_feed_list和train_fetch_list:
```python
val_feed_list = [('image', image.name), ('label', label.name)]
val_fetch_list = [('acc_top1', acc_top1.name), ('acc_top5', acc_top5.name)]
```
##### Compressor和量化配置文件
`compress.py`主要使用Compressor和yaml文件完成对模型的量化训练工作。Compressor类的定义如下:
```python
class Compressor(object):
def __init__(self,
place,
scope,
train_program,
train_reader=None,
train_feed_list=None,
train_fetch_list=None,
eval_program=None,
eval_reader=None,
eval_feed_list=None,
eval_fetch_list=None,
teacher_programs=[],
checkpoint_path='./checkpoints',
train_optimizer=None,
distiller_optimizer=None):
```
在定义Compressor对象时,需要注意以下问题:
* train program如果带反向operators和优化更新相关的operators, 参数train_optimizer需要设置为None.
* eval_program中parameter的名称需要与train_program中的parameter的名称完全一致。
* 最终保存的量化模型是在eval_program网络基础上进行剪枝保存的。所以,如果用户希望最终保存的模型可以用于inference, 则eval program需要包含推理阶段需要的各种operators.
* checkpoint保存的是float数据类型的模型。
`configs/quantization.yaml`量化配置文件示例如下:
```python
version: 1.0
strategies:
quantization_strategy:
class: 'QuantizationStrategy'
start_epoch: 0
end_epoch: 9
float_model_save_path: './output/float'
mobile_model_save_path: './output/mobile'
int8_model_save_path: './output/int8'
weight_bits: 8
activation_bits: 8
weight_quantize_type: 'abs_max'
activation_quantize_type: 'moving_average_abs_max'
save_in_nodes: ['image']
save_out_nodes: ['fc_0.tmp_2']
compressor:
epoch: 10
checkpoint_path: './checkpoints_quan/'
strategies:
- quantization_strategy
```
其中,可配置参数包括:
- **class:** 量化策略的类名称,目前仅支持`QuantizationStrategy`
- **start_epoch:** 在start_epoch开始之前,量化训练策略会往train_program和eval_program插入量化operators和反量化operators。 从start_epoch开始,进入量化训练阶段。
- **end_epoch:** 在end_epoch结束之后,会保存用户指定格式的模型。注意:end_epoch之后并不会停止量化训练,而是继续训练直到epoch数等于compressor.epoch值为止。举例来说,当start_epoch=0,end_epoch=0,compressor.epoch=2时,量化训练开始于epoch0,结束于epoch1,但保存的模型是epoch0结束时的参数状态。
- **float_model_save_path:** 保存float数据格式的模型路径,即该路径下的模型参数范围为int8范围但参数数据类型为float32。如果设置为None, 则不存储float格式的模型,默认为None。**注意:Paddle-Lite即使用该目录下的模型进行量化模型推理优化,详见本文[使用Paddle-Lite运行量化模型推理](#二使用Paddle-Lite运行量化模型推理)部分。**
- **int8_model_save_path:** 保存int8数据格式的模型路径,即该路径下的模型参数范围为int8范围且参数数据类型为int8。如果设置为None, 则不存储int8格式的模型,默认为None.
- **mobile_model_save_path:** 保存兼容paddle-mobile框架的模型路径。如果设置为None, 则不存储paddle-mobile格式的模型,默认为None。目前paddle-mobile已升级为Paddle-Lite。
- **weight_bits:** 量化weight的bit数,注意偏置(bias)参数不会被量化。
- **activation_bits:** 量化activation的bit数。
- **weight_quantize_type:** weight量化方式,目前量化训练支持`abs_max``channel_wise_abs_max`
- **activation_quantize_type:** activation量化方式,目前量化训练支持`abs_max``range_abs_max``moving_average_abs_max`
- **save_in_nodes:** variable名称列表。在保存量化后模型的时候,需要根据save_in_nodes对eval programg 网络进行前向遍历剪枝。默认为eval_feed_list内指定的variable的名称列表。
- **save_out_nodes:** varibale名称列表。在保存量化后模型的时候,需要根据save_out_nodes对eval programg 网络进行回溯剪枝。默认为eval_fetch_list内指定的variable的名称列表。
> **备注:**
>
> 1)`abs_max`意为在训练的每个step及inference阶段均动态计算量化scale值。`channel_wise_abs_max`与`abs_max`类似,不同点在于它会对卷积权重进行分channel求取量化scale。换言之,`abs_max`属于tensor-wise量化,而`channel_wise_abs_max`属于channel-wise量化,详细说明请猛戳[此处](https://github.com/PaddlePaddle/FluidDoc/blob/develop/doc/fluid/design/quantization/training_quantization_model_format.md)。
>
> 2)`moving_average_abs_max`和`range_abs_max`意为在训练阶段计算出一个静态的量化scale值,并将其用于inference阶段。`moving_average_abs_max`使用窗口滑动平均的方法计算量化scale,而`range_abs_max`则使用窗口绝对值最大值的方式。
>
> 3)**目前,Paddle-Lite仅支持运行weight量化方式使用`abs_max`且activation量化方式使用`moving_average_abs_max`或`range_abs_max`产出的量化模型**。
#### 执行int8量化训练
修改run.sh,即注释掉`# enable GC strategy``# for sensitivity filter pruning`之间的内容并打开`#for quantization`相关的脚本命令(所需打开注释的命令如下所示)。
```bash
# for quantization
#---------------------------
export CUDA_VISIBLE_DEVICES=0
python compress.py \
--batch_size 64 \
--model "MobileNet" \
--pretrained_model ./pretrain/MobileNetV1_pretrained \
--compress_config ./configs/quantization.yaml \
--quant_only True
```
最后,运行`sh run.sh`命令开始int8量化训练。
上述量化训练过程完成后,若按照本文中所述`configs/quantization.yaml`文件内容配置的模型输出路径,则可在models/PaddleSlim/output目录下看到`float``int8``mobile`三个目录,其中:
* float目录: 参数范围为int8范围但参数数据类型为float32的量化模型。Paddle-Lite即使用该目录下的模型文件及参数进行量化模型的部署。
* int8目录: 参数范围为int8范围且参数数据类型为int8的量化模型。
* mobile目录:参数特点与int8目录相同且兼容paddle-mobile的量化模型(目前paddle-mobile已升级为Paddle-Lite)。
## 使用Paddle-Lite运行量化模型推理
### 使用模型优化工具对量化模型进行优化
接下来,使用float目录下的模型文件,生成适合在移动端直接部署的量化模型。
参考[源码编译](../source_compile)配置编译环境,确保可以编译成功。参考[模型转化方法](../model_optimize_tool),首先编译model_optimize_tool工具,然后执行下面命令对量化训练的模型进行优化(注意,需要自行修改model_file、param_file和optimize_out)。
```bash
./model_optimize_tool \
--model_file=mobilenet_v1_quant/float/model \
--param_file=mobilenet_v1_quant/float/weights \
--optimize_out_type=naive_buffer \
--optimize_out=mobilenet_v1_quant_opt \
--valid_targets=arm \
--prefer_int8_kernel=true
```
如前所述,量化训练后,float目录下的模型参数范围为int8,但参数数据类型仍为float32类型,这样确实没有起到模型参数压缩的效果。但是,经过model\_optimize\_tool工具优化后对应的量化参数均会以int8类型重新存储达到参数压缩的效果,且模型结构也被优化(如进行了各种operator fuse操作)。
### 在手机端准备量化模型文件
使用如下命令将mobilenet_v1_quant_opt目录下的量化模型文件导入到手机端:
```bash
adb push mobilenet_v1_quant_opt /data/local/tmp
```
### 使用mobilenetv1\_light\_api运行优化后的量化模型
参考[源码编译](../source_compile)配置编译环境后,在Paddle-Lite执行如下命令获取轻量级API的demo:
```bash
cd /Paddle-Lite/build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/demo/cxx/mobile_light
make clean && make -j
```
执行完上述命令后,可在`Paddle-Lite/build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/demo/cxx/mobile_light/`路径下看到`mobilenetv1_light_api`可执行文件。将`mobilenetv1_light_api`导入到手机端并运行量化模型推理。执行命令如下:
```bash
adb push Paddle-Lite/build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/demo/cxx/mobile_light/mobilenetv1_light_api /data/local/tmp
adb shell chmod +x /data/local/tmp/mobilenetv1_light_api
adb shell /data/local/tmp/mobilenetv1_light_api \
--model_dir=/data/local/tmp/mobilenet_v1_quant_opt
```
**程序运行结果如下:**
```bash
Output dim: 1000
Output[0]: 0.000228
Output[100]: 0.000260
Output[200]: 0.000250
Output[300]: 0.000560
Output[400]: 0.000950
Output[500]: 0.000275
Output[600]: 0.005143
Output[700]: 0.002509
Output[800]: 0.000538
Output[900]: 0.000969
```
在C++中使用Paddle-Lite API的方法请猛戳[此处](../cpp_demo),用户也可参考[mobilenetv1_light_api.cc](https://github.com/PaddlePaddle/Paddle-Lite/blob/develop/lite/demo/cxx/mobile_light/mobilenetv1_light_api.cc)的代码示例。
## FAQ
**问题**:Compiled with WITH_GPU, but no GPU found in runtime
**解答**:检查本机是否支持GPU训练,如果不支持请使用CPU训练。如果在docker进行GPU训练,请使用nvidia_docker启动容器。
**问题**:Inufficient GPU memory to allocation. at [/paddle/paddle/fluid/platform/gpu_info.cc:262]
**解答**:正确设置run.sh脚本中`CUDA_VISIBLE_DEVICES`,确保显卡剩余内存大于需要内存。
---
layout: post
title: Lite支持NPU在线编译
---
Paddle Lite可以在线分析模型特点,在线编译并生成NPU所需要的IR并实时运行。
是首个支持NPU在线模型的预测框架。
也可以离线分析并调优模型后,保存离线模型,直接线上部署使用。
# 编译
只需要提前准备华为DKK库和Lite 代码。
我们也提供了编译NPU的[脚本](https://github.com/PaddlePaddle/Paddle-Lite/blob/develop/lite/tools/build_npu.sh)可以直接使用。
例如:
```shell
$ ./lite/tools/build_npu.sh --arm_os=android --arm_abi=armv8 --arm_lang=gcc --ddk_root=/to/your/ddk_path build
```
## 细节说明
CMAKE编译选项:
- 设置`LITE_WITH_NPU=ON``LITE_WITH_ARM=ON`
- 设置DDK根目录路径 `NPU_DDK_ROOT`
其他编译选项与ARM编译相同,可以参考[“Paddle Lite在Docker下的ARM编译”](../source_compile)
示例如下:
```shell
cmake .. \
-DWITH_GPU=OFF \
-DWITH_MKL=OFF \
-DWITH_LITE=ON \
-DLITE_WITH_CUDA=OFF \
-DLITE_WITH_X86=OFF \
-DLITE_WITH_ARM=ON \
-DWITH_ARM_DOTPROD=ON \
-DLITE_WITH_LIGHT_WEIGHT_FRAMEWORK=ON \
-DWITH_TESTING=ON \
-DLITE_WITH_NPU=ON \
-DANDROID_API_LEVEL=24 \
-DNPU_DDK_ROOT="/path/to/ai_ddk_lib/" \
-DARM_TARGET_OS=android -DARM_TARGET_ARCH_ABI=armv8 -DARM_TARGET_LANG=gcc
make test_mobilenetv1 -j
```
Note: 当前仅支持armv8和gcc编译。
# 运行示例
把MobilenetV1的模型和参数push到指定的`working_dir`.
```shell
working_dir=/data/local/tmp
test_bin=test_npu_pass
model_dir=mobilenet_v1 # as example
repeats=10
batch_size=1
im_channel=3
im_height=224
im_width=224
optimized_model="${model_dir}_opt"
adb shell "mkdir -p ${working_dir}"
adb push $test_bin $working_dir/
adb push $model_dir $working_dir
adb push ai_ddk_lib/lib64/* $working_dir
adb shell chmod +x "${working_dir}/${test_bin}"
adb shell "rm -rf ${working_dir}/${optimized_model}"
adb shell "cd ${working_dir} ; export LD_LIBRARY_PATH=./; ./${test_bin} --model_dir=${model_dir} --optimized_model=${optimized_model} --repeats=${repeats} --batch_size=${batch_size} --im_channel=${im_channel} --im_height=${im_height} --im_width=${im_width}"
```
在华为810的机器上,由运行结果可知单侧通过并且预测速度为6ms左右。
一般第一次的运行时间略长,可以重复多次得到稳定结果。
# 如何在Code中使用
在Lite中使用NPU非常简单,不需要添加太多额外代码。
- 只需要在添加有效place的时候包括`Place{TARGET(kNPU), PRECISION(kFloat)}`即可。
后续的运行和使用没有任何变化。
Note:
唯一需要注意的是,因为构建NPU子图需要提前知晓各个op输入的具体大小,所以生成NPU的`RuntimeProgram`时需要提前初始化输入的大小,主要包括batchsize大小。
如果不提前设置好大小,生成NPU模型时会报错退出。
代码示例:
```cpp
// if want to use NPU
std::vector<Place> valid_places({Place{TARGET(kNPU), PRECISION(kFloat)},
Place{TARGET(kARM), PRECISION(kFloat)}});
DeviceInfo::Init();
DeviceInfo::Global().SetRunMode(LITE_POWER_HIGH, FLAGS_threads);
lite::Predictor predictor;
predictor.Build(model_dir, "", "", valid_places);
auto* input_tensor = predictor.GetInput(0);
input_tensor->Resize(DDim(std::vector<DDim::value_type>({1, 3, 224, 224})));
auto* data = input_tensor->mutable_data<float>();
auto item_size = input_tensor->dims().production();
for (int i = 0; i < item_size; i++) {
data[i] = 1;
}
predictor.Run();
```
# FAQ
## 关于开发板
由于该框架针对的是华为HiAI最新的NPU架构,应该还没有现成的开发板集成了该架构的NPU,所以通常看到的比如海思2359A上的NPU不一样的。
## 关于手机
支持目前最新的是华为810,以及未来要发布的NPU系列手机。
# Note
注意:由于我们的开发是基于华为内部的最新DDK版本编译,如果您的DDK不是最新的,有可能会遇到编译时某个op找不到定义的情况,此时您可以联系我们尝试一起解决。
---
layout: post
title: 基于OpenCL的ARM GPU预测
---
Lite支持在Android系统上运行基于OpenCL的程序,目前支持Ubuntu环境下armv8、armv7的交叉编译。
## 编译
### 编译环境
1. Docker 容器环境;
2. Linux(推荐 Ubuntu 16.04)环境。
详见[ **源码编译指南-环境准备** 章节](./source_compile.md)
### 编译选项
|参数|介绍|值|
|--------|--------|--------|
|--arm_os|代表目标操作系统|目前仅支持且默认为`android`|
|--arm_abi|代表体系结构类型,支持armv8和armv7|默认为`armv8`即arm64-v8a;`armv7`即armeabi-v7a|
|--arm_lang|代表编译目标文件所使用的编译器|默认为gcc,支持 gcc和clang两种|
### 编译范例
注:以Docker容器环境为例,CMake3.10,android-ndk-r17c位于`/opt/`目录下。
```bash
# 假设当前位于处于Lite源码根目录下
# 导入NDK_ROOT变量,注意检查您的安装目录若与本示例不同
export NDK_ROOT=/opt/android-ndk-r17c
# 删除上一次CMake自动生成的.h文件
rm ./lite/api/paddle_use_kernels.h
rm ./lite/api/paddle_use_ops.h
# 根据指定编译参数编译
./lite/tools/ci_build.sh \
--arm_os=android \
--arm_abi=armv8 \
--arm_lang=gcc \
build_test_arm_opencl
```
## 运行示例准备
下面以android、ARMv8、gcc的环境为例,介绍如何在手机上执行基于OpenCL的ARM GPU推理过程。
**注意:** 以下命令均在Lite源码根目录下运行。
```bash
# 在/data/local/tmp目录下创建OpenCL文件目录
adb shell mkdir -p /data/local/tmp/opencl
adb shell mkdir -p /data/local/tmp/opencl/cl_kernel/buffer
adb shell mkdir -p /data/local/tmp/opencl/cl_kernel/image
# 将OpenCL的kernels文件推送到/data/local/tmp/opencl目录下
adb push lite/backends/opencl/cl_kernel/cl_common.h /data/local/tmp/opencl/cl_kernel/
adb push lite/backends/opencl/cl_kernel/buffer/* /data/local/tmp/opencl/cl_kernel/buffer/
adb push lite/backends/opencl/cl_kernel/image/* /data/local/tmp/opencl/cl_kernel/image/
```
### 运行示例1: test_mobilenetv1
- **运行文件准备**
```bash
# 将mobilenet_v1的模型文件推送到/data/local/tmp/opencl目录下
adb shell mkdir -p /data/local/tmp/opencl/mobilenet_v1
adb push build.lite.android.armv8.gcc.opencl/third_party/install/mobilenet_v1/* /data/local/tmp/opencl/mobilenet_v1/
# 将OpenCL单元测试程序test_mobilenetv1,推送到/data/local/tmp/opencl目录下
adb push build.lite.android.armv8.gcc.opencl/lite/api/test_mobilenetv1 /data/local/tmp/opencl
```
- **执行OpenCL推理过程**
使用如下命令运行OpenCL程序。其中:
- `--cl_path`指定了OpenCL的kernels文件即cl\_kernel所在目录;
- `--modle_dir`指定了模型文件所在目录。
```bash
adb shell chmod +x /data/local/tmp/opencl/test_mobilenetv1
adb shell /data/local/tmp/opencl/test_mobilenetv1 \
--cl_path=/data/local/tmp/opencl \
--model_dir=/data/local/tmp/opencl/mobilenet_v1 \
--warmup=1 \
--repeats=1
```
**注意:** 因为权重参数均会在Op Kernel第一次运行时进行加载,所以第一次的执行时间会略长。一般将warmup的值设为1,repeats值设为多次。
### 运行示例2: test_layout_opencl
- **运行文件准备**
```bash
# 将OpenCL单元测试程序test_layout_opencl,推送到/data/local/tmp/opencl目录下
adb push build.lite.android.armv8.gcc.opencl/lite/kernels/opencl/test_layout_opencl /data/local/tmp/opencl/
```
- **执行OpenCL推理过程**
```bash
adb shell chmod +x /data/local/tmp/opencl/test_layout_opencl
adb shell /data/local/tmp/opencl/test_layout_opencl
```
# 如何在Code中使用
Lite支持对ARM CPU和ARM GPU的混调执行,具体描述如下:
- 设置Lite推断执行的有效Places,使其包含ARM CPU(kARM)和ARM GPU(kOpenCL);
- 确保GPU(kOpenCL)在第一位,位置代表Places的重要性和kernel选择有直接关系。
通过以上设置,Lite在推断执行过程中如果发现某一Op存在着基于OpenCL的实现,其会优先选择使用该实现执行Op的计算过程。若发现某一Op没有基于OpenCL实现的Kernel,其会自动选择执行基于ARM CPU的实现。
代码示例(来自`lite/api/mobilenetv1_test.cc`):
```cpp
// 初始化预测实例、CPU线程数、CPU策略
DeviceInfo::Init();
DeviceInfo::Global().SetRunMode(LITE_POWER_HIGH, FLAGS_threads);
lite::Predictor predictor;
// 设置Lite推断执行的硬件信息Places为{kOpenCL, kARM}
std::vector<Place> valid_places({
Place({TARGET(kOpenCL), PRECISION(kFloat)}),
Place({TARGET(kARM), PRECISION(kFloat)})
});
// 根据Place构建模型
predictor.Build(model_dir, "", "", valid_places);
// 设置模型的输入
auto* input_tensor = predictor.GetInput(0);
input_tensor->Resize(DDim(std::vector<DDim::value_type>({1, 3, 224, 224})));
auto* data = input_tensor->mutable_data<float>();
auto item_size = input_tensor->dims().production();
for (int i = 0; i < item_size; i++) {
data[i] = 1;
}
// 执行模型推断
predictor.Run();
// 获取模型的预测结果tensor
// 下面展示如何取出第一个输入tensor,及其维度,元素个数,指针
auto* out0_tensor = predictor.GetOutput(0);
auto out0_dims = out0_tensor->dims();
auto out0_item_size = out0_tensor->dims().production();
auto* out0_pointer = out0_tensor->data<float>();
```
---
layout: post
title:
---
1. [./run_test_on_android.md](https://github.com/PaddlePaddle/paddle-mobile/wiki/%E5%A6%82%E4%BD%95%E5%9C%A8Android%E6%89%8B%E6%9C%BA%E4%B8%8A%E8%BF%90%E8%A1%8C%E5%8D%95%E6%B5%8B)
\ No newline at end of file
---
layout: post
title:
---
# Road map
这篇文档会介绍 Paddle-Lite 近期对外的开源版本和计划。
其中包含的 feature 为最小集合,按最终发布的版本为准。
## 2.0.0-beta1-prerelease
预计发布 *2019-8-26 ~ 2days*
- 完善编译和 benchmark 文档
- 增加第三方依赖代码的离线下载功能,加速编译过程
- 去掉 `tiny_publish` 模式下无关的第三方代码下载,可以不依赖任何第三方
## 2.0.0-beta1
预计发布 *2019-9-1~2days*
- `model_optimize_tool` 从 ARM 上执行修改为 Host 上执行,只从 kernel 分布来确定计算图优化;后续硬件针对优化会发布新的工具;
- Paddle 模型支持参数 composed 的格式
- 增加分层编译来控制常用模型的部署库的大小,分两个模式 `basic`, `extra`;默认 `basic` 模式只发布核心的op 和kernel;将控制流相关的Op和kernel 折叠进 `extra` 按需编译
- 增加 INT8 量化,从 PaddleSlim 训练到 PaddleLite 部署完整案例
- 支持内存中加载模型,以支持 APP 的简易加密
## 2.0.0-rc ?
预计发布 *2019-9-16~7days*
---
layout: post
title: 源码编译指南
---
* TOC
{:toc}
Paddle-Lite 提供了移动端的一键源码编译脚本 `lite/tools/build.sh`,编译流程如下:
1. 环境准备(选择其一):Docker交叉编译环境、Linux交叉编译环境
2. 编译:调用`build.sh`脚本一键编译
## 一、环境准备
目前支持两种编译的环境:
1. Docker 容器环境;
2. Linux(推荐 Ubuntu 16.04)环境。
### 1、 Docker开发环境
[Docker](https://www.docker.com/) 是一个开源的应用容器引擎, 使用沙箱机制创建独立容器,方便运行不同程序。Docker初学者可以参考[Docker使用方法](https://thenewstack.io/docker-station-part-one-essential-docker-concepts-tools-terminology/)正确安装Docker。
#### 准备Docker镜像
有两种方式准备Docker镜像,推荐从Dockerhub直接拉取Docker镜像
```shell
# 方式一:从Dockerhub直接拉取Docker镜像
docker pull paddlepaddle/paddle-lite:2.0.0_beta
# 方式二:本地源码编译Docker镜像
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
cd Paddle-Lite/lite/tools
mkdir mobile_image
cp Dockerfile.mobile mobile_image/Dockerfile
cd mobile_image
docker build -t paddlepaddle/paddle-lite .
# 镜像编译成功后,可用`docker images`命令,看到`paddlepaddle/paddle-lite`镜像。
```
#### 进入Docker容器
在拉取Paddle-Lite仓库代码的上层目录,执行如下代码,进入Docker容器:
```shell
docker run -it \
--name paddlelite_docker \
-v $PWD/Paddle-Lite:/Paddle-Lite \
--net=host \
paddlepaddle/paddle-lite /bin/bash
```
该命令的含义:将容器命名为`paddlelite_docker``<container-name>`,将当前目录下的`Paddle-Lite`文件夹挂载到容器中的`/Paddle-Lite`这个根目录下,并进入容器中。至此,完成Docker环境的准备。
#### Docker常用命令
```shell
# 退出容器但不停止/关闭容器:键盘同时按住三个键:CTRL + q + p
# 启动停止的容器
docker start <container-name>
# 从shell进入已启动的容器
docker attach <container-name>
# 停止正在运行的Docker容器
docker stop <container-name>
# 重新启动正在运行的Docker容器
docker restart <container-name>
# 删除Docker容器
docker rm <container-name>
```
### 2、Linux 开发环境
#### Android
##### 交叉编译环境要求
- gcc、g++、git、make、wget、python、adb
- Java environment
- cmake(建议使用3.10或以上版本)
- Android NDK (建议ndk-r17c)
##### 具体步骤
安装软件部分以 Ubuntu 为例,其他 Linux 发行版类似。
```shell
# 1. Install basic software
apt update
apt-get install -y --no-install-recommends \
gcc g++ git make wget python unzip adb curl
# 2. Prepare Java env.
apt-get install -y default-jdk
# 3. Install cmake 3.10 or above
wget -c https://mms-res.cdn.bcebos.com/cmake-3.10.3-Linux-x86_64.tar.gz && \
tar xzf cmake-3.10.3-Linux-x86_64.tar.gz && \
mv cmake-3.10.3-Linux-x86_64 /opt/cmake-3.10 && \
ln -s /opt/cmake-3.10/bin/cmake /usr/bin/cmake && \
ln -s /opt/cmake-3.10/bin/ccmake /usr/bin/ccmake
# 4. Download Android NDK for linux-x86_64
# Note: Skip this step if NDK installed
# recommand android-ndk-r17c-darwin-x86_64
# ref: https://developer.android.com/ndk/downloads
cd /tmp && curl -O https://dl.google.com/android/repository/android-ndk-r17c-linux-x86_64.zip
cd /opt && unzip /tmp/android-ndk-r17c-linux-x86_64.zip
# 5. Add environment ${NDK_ROOT} to `~/.bashrc`
echo "export NDK_ROOT=/opt/android-ndk-r17c" >> ~/.bashrc
source ~/.bashrc
```
#### ARM Linux
适用于基于 ARMv8 和 ARMv7 架构 CPU 的各种开发板,例如 RK3399,树莓派等,目前支持交叉编译和本地编译两种方式,对于交叉编译方式,在完成目标程序编译后,可通过 scp 方式将程序拷贝到开发板运行。
##### 交叉编译
###### 编译环境要求
- gcc、g++、git、make、wget、python、scp
- cmake(建议使用3.10或以上版本)
###### 具体步骤
安装软件部分以 Ubuntu 为例,其他 Linux 发行版类似。
```shell
# 1. Install basic software
apt update
apt-get install -y --no-install-recommends \
gcc g++ git make wget python unzip
# 2. Install arm gcc toolchains
apt-get install -y --no-install-recommends \
g++-arm-linux-gnueabi gcc-arm-linux-gnueabi \
g++-arm-linux-gnueabihf gcc-arm-linux-gnueabihf \
gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
# 3. Install cmake 3.10 or above
wget -c https://mms-res.cdn.bcebos.com/cmake-3.10.3-Linux-x86_64.tar.gz && \
tar xzf cmake-3.10.3-Linux-x86_64.tar.gz && \
mv cmake-3.10.3-Linux-x86_64 /opt/cmake-3.10 && \
ln -s /opt/cmake-3.10/bin/cmake /usr/bin/cmake && \
ln -s /opt/cmake-3.10/bin/ccmake /usr/bin/ccmake
```
##### 本地编译(直接在RK3399或树莓派上编译)
###### 编译环境要求
- gcc、g++、git、make、wget、python
- cmake(建议使用3.10或以上版本)
###### 具体步骤
安装软件部分以 Ubuntu 为例,其他 Linux 发行版本类似。
```shell
# 1. Install basic software
apt update
apt-get install -y --no-install-recomends \
gcc g++ make wget python unzip
# 2. install cmake 3.10 or above
wget https://www.cmake.org/files/v3.10/cmake-3.10.3.tar.gz
tar -zxvf cmake-3.10.3.tar.gz
cd cmake-3.10.3
./configure
make
sudo make install
```
之后可通过cmake --version查看cmake是否安装成功。
至此,完成 Linux 交叉编译环境的准备。
### 3、Mac OS 开发环境
#### 交叉编译环境要求
- gcc、git、make、curl、unzip、java
- cmake(建议使用3.10或以上版本)
- 编译Android: Android NDK (建议ndk-r17c)
- 编译IOS: XCode(Version 10.1)
#### 具体步骤
```bash
# 1. Install basic software
brew install -y curl gcc git make unzip
# 2. Install cmake 3.10 or above
brew install -y cmake
# 3. Download Android NDK for Mac
# recommand android-ndk-r17c-darwin-x86_64
# ref: https://developer.android.com/ndk/downloads
# Note: Skip this step if NDK installed
cd ~/Documents && curl -O https://dl.google.com/android/repository/android-ndk-r17c-darwin-x86_64.zip
cd ~/Library && unzip ~/Documents/android-ndk-r17c-darwin-x86_64.zip
# 4. Add environment ${NDK_ROOT} to `~/.bash_profile`
echo "export NDK_ROOT=~/Library/android-ndk-r17c" >> ~/.bash_profile
source ~/.bash_profile
# 5. Install Java Environment
brew cask install java
```
至此,完成 Mac 交叉编译环境的准备。
#### Mac下Full Publish编译时需要**注意**
1. cmake版本需要为cmake 3.10
2. Paddle-Lite项目路径中不可以含有中文字符
3. 编译时如果报错: `Too many open files`
解决方法:修改mac电脑的 “程序可打开的最大文件数“,例如:`ulimit -n 1024`
## 二、编译PaddleLite
### 下载代码
```shell
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
cd Paddle-Lite
git checkout <release-version-tag>
```
### 编译模式与参数
编译脚本`./lite/tools/build.sh`,支持三种编译模式:
| 编译模式 | 介绍 | 适用对象 |
|:-------:|-----|:-------:|
| tiny_publish | 编译移动端部署库,无第三方库依赖 | 用户 |
| full_publish | 编译移动端部署库,有第三方依赖如protobuf、glags等,含有可将模型转换为无需protobuf依赖的naive buffer格式的工具,供tiny_publish库使用 | 用户 |
| test | 编译指定`arm_os``arm_abi`下的移动端单元测试 | 框架开发者 |
编译脚本`./lite/tools/build.sh`,追加参数说明:
| 参数 | 介绍 | 值 |
|-----------|-------------|-------------|
| --arm_os |必选,选择安装平台 | `android``ios``ios64``armlinux` |
| --arm_abi |必选,选择编译的arm版本,其中`armv7hf`为ARMLinux编译时选用| `armv8``armv7``armv7hf`|
| --arm_lang |arm_os=android时必选,选择编译器 | `gcc``clang`|
| --android_stl |arm_os=android时必选,选择静态链接STL或动态链接STL | `c++_static``c++_shared`|
| --build_extra | 可选,是否编译控制流相关op、kernel。(**编译demo时必须选择为ON**) | `ON``OFF` |
| target |必选,选择编译模式,`tiny_publish`为编译移动端部署库、`full_publish`为带依赖的移动端部署库、`test`为移动端单元测试、`ios`为编译ios端`tiny_publish` | `tiny_publish``full_publish``test``ios` |
### 编译代码
注意:编译前请删除lite/api目录下的paddle_use_ops.h和paddle_use_kernels.h
#### 编译`tiny publish`动态库
##### Android
```shell
./lite/tools/build.sh \
--arm_os=android \
--arm_abi=armv8 \
--arm_lang=gcc \
--android_stl=c++_static \
tiny_publish
```
##### IOS
```shell
./lite/tools/build.sh \
--arm_os=ios64 \
--arm_abi=armv8 \
ios
```
ios tiny publish支持的编译选项:
* `--arm_os`: 可选ios或者ios64
* `--arm_abi`: 可选armv7和armv8(**注意**:当`arm_os=ios`时只能选择`arm_abi=armv7`
* 如果mac编译过程中报错:"Invalid CMAKE_DEVELOPER_ROOT: does not exist", 运行:
```shell
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
```
##### ARMLinux
```shell
./lite/tools/build.sh \
--arm_os=armlinux \
--arm_abi=armv7hf \
--arm_lang=gcc \
tiny_publish
```
- `--arm_abi`: 树莓派3b使用armv7hf,RK3399使用armv8
#### 编译`full publish`动态库(**Mac OS下不支持**)
##### Android
```shell
./lite/tools/build.sh \
--arm_os=android \
--arm_abi=armv8 \
--arm_lang=gcc \
--android_stl=c++_static \
full_publish
```
##### ARMLinux
```shell
./lite/tools/build.sh \
--arm_os=armlinux \
--arm_abi=armv7hf \
--arm_lang=gcc \
full_publish
```
- `--arm_abi`: 树莓派3b使用armv7hf,RK3399使用armv8
### 编译结果说明
**编译最终产物位置**`build.lite.xxx.xxx.xxx` 下的 `inference_lite_lib.xxx.xxx` ,如 Android 下 ARMv8 的产物位于`inference_lite_lib.android.armv8`
![](https://user-images.githubusercontent.com/45189361/65375706-204e8780-dccb-11e9-9816-ab4563ce0963.png)
**目录内容**(可能)如下:
**Full_publish编译结果:**
![](https://user-images.githubusercontent.com/45189361/65375704-19c01000-dccb-11e9-9650-6856c7a5bf82.png)
**Tiny_publish结果:**
![](https://user-images.githubusercontent.com/45189361/65375726-3bb99280-dccb-11e9-9903-8ce255371905.png)
**IOS编译结果:**
![](https://user-images.githubusercontent.com/45189361/65375726-3bb99280-dccb-11e9-9903-8ce255371905.png)
**具体内容**说明:
1、 `bin`文件夹:可执行工具文件 `paddle_code_generator``test_model_bin`
2、 `cxx`文件夹:包含c++的库文件与相应的头文件
- `include` : 头文件
- `lib` : 库文件
- 打包的静态库文件:
- `libpaddle_api_full_bundled.a` :包含 full_api 和 light_api 功能的静态库
- `libpaddle_api_light_bundled.a` :只包含 light_api 功能的静态库
- 打包的动态态库文件:
- `libpaddle_full_api_shared.so` :包含 full_api 和 light_api 功能的动态库
- `libpaddle_light_api_shared.so`:只包含 light_api 功能的动态库
3、 `demo`文件夹:示例 demo ,包含 C++ demo 和 Java demo。
- `cxx` : C++示例 demo
- `mobile_full` : full_api 的使用示例
- `mobile_light` : light_api的使用示例
- `java` :Java 示例 demo
- `android` : Java的 Android 示例
4、 `java` 文件夹:包含 Jni 的动态库文件与相应的 Jar 包
- `jar` : `PaddlePredictor.jar`
- `so` : Jni动态链接库 `libpaddle_lite_jni.so`
5、 `third_party` 文件夹:第三方库文件`gflags`
**注意:**
1、 只有当`--arm_os=android` 时才会编译出:
- Java库文件与示例:`Java``demo/java`
- 动态库文件:`libpaddle_full_api_shared.so`,`libpaddle_light_api_shared.so`
2、 `tiny_publish`编译结果不包括 C++ demo和 C++ 静态库,但提供 C++ 的 light_api 动态库、 Jni 动态库和Java demo
### 加速第三方依赖库的下载
移动端相关编译所需的第三方库均位于 `<PaddleLite>/third-party` 目录下,默认编译过程中,会利用`git submodule update --init --recursive`链上相关的第三方依赖的仓库。
为加速`full_publish``test`编译模式中对`protobuf`等第三方依赖的下载,`build.sh``ci_build.sh`支持了从国内 CDN 下载第三方依赖的压缩包。
使用方法:`git clone``Paddle-Lite`仓库代码后,手动删除本地仓库根目录下的`third-party`目录:
```shell
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
git checkout <release-version-tag>
cd Paddle-Lite
rm -rf third-party
```
之后再根据本文档,进行后续编译时,便会忽略第三方依赖对应的`submodule`,改为下载第三方压缩包。
---
layout: post
title: 支持硬件列表
---
## ARM CPU
Paddle Lite支持[ARM Cortex-A系列处理器](https://en.wikipedia.org/wiki/ARM_Cortex-A),支持列表如下:
### 32bit(ARMv7a)
- Cortex-A5
- Cortex-A7
- Cortex-A8
- Cortex-A9
- Cortex-A12
- Cortex-A15
- Cortex-A17(RK3288)
- Cortex-A32
### 64bit(ARMv7a, ARMv8a)
- Cortex-A35
- Cortex-A53(树莓派3)
- Cortex-A55
- Cortex-A57(Nvidia tx1,Nvidia tx2, 高通810等)
- Cortex-A72(麒麟95X,高通820, RK3399,树莓派4等)
- Cortex-A73(麒麟960,麒麟970,高通835, 联发科X30等)
- Cortex-A75(高通845等)
- Cortex-A76(麒麟980,麒麟990,高通855,高通730,联发科G90等)
- Cortex-A77
- ARMv8-A compatible(Apple A系列处理器, Nvidia tegra, Qualcomm Kryo, Falkor, Samsung Mongoose)
## 移动端GPU
Paddle Lite支持移动端GPU和Nvidia端上GPU设备,支持列表如下:
- ARM Mali G 系列
- Qualcomm Adreno 系列
- Nvida tegra系列: tx1, tx2, nano, xavier
---
layout: post
title: 支持OP列表
---
## Ops
- affine_channel
- anchor_generator
- arg_max
- assign
- assign_value
- axpy
- batch_norm
- beam_search
- beam_search_decode
- bilinear_interp
- box_clip
- box_coder
- calib
- calib_once
- cast
- concat
- conv2d
- conv2d_transpose
- crop
- decode_bboxes
- density_prior_box
- depthwise_conv2d
- dropout
- elementwise_add
- elementwise_div
- elementwise_max
- elementwise_mul
- elementwise_sub
- equal
- exp
- expand
- fake_dequantize_max_abs
- fake_quantize_moving_average_abs_max
- fake_quantize_range_abs_max
- fc
- feed
- fetch
- fill_constant
- fill_constant_batch_size_like
- flatten
- flatten2
- floor
- fusion_elementwise_add_activation
- fusion_elementwise_div_activation
- fusion_elementwise_max_activation
- fusion_elementwise_mul_activation
- fusion_elementwise_sub_activation
- generate_proposals
- graph_op
- greater_equal
- greater_than
- gru
- gru_unit
- hard_sigmoid
- im2sequence
- increment
- io_copy
- io_copy_once
- is_empty
- layout
- layout_once
- leaky_relu
- less_equal
- less_than
- lod_reset
- log
- logical_and
- logical_not
- logical_or
- logical_xor
- lookup_table
- lrn
- matmul
- mean
- mul
- multiclass_nms
- nearest_interp
- negative
- norm
- notequal
- pad2d
- pool2d
- power
- prelu
- prior_box
- range
- read_from_array
- reduce_max
- reduce_mean
- relu
- relu6
- relu_clipped
- reshape
- reshape2
- roi_align
- scale
- sequence_expand
- sequence_expand_as
- sequence_pool
- sequence_softmax
- shape
- shuffle_channel
- sigmoid
- slice
- softmax
- split
- square
- squeeze
- squeeze2
- stack
- swish
- tanh
- top_k
- transpose
- transpose2
- uniform_random
- unsqueeze
- unsqueeze2
- while
- write_to_array
- yolo_box
## Kernels
### Host kernels
- feed
- fetch
- flatten
- flatten2
- multiclass_nms
- reshape
- reshape2
### ARM kernels
- affine_channel
- anchor_generator
- arg_max
- assign
- assign_value
- axpy
- batch_norm
- beam_search
- beam_search_decode
- bilinear_interp
- box_clip
- box_coder
- cast
- concat
- conv2d
- conv2d_transpose
- crop
- decode_bboxes
- density_prior_box
- depthwise_conv2d
- dropout
- elementwise_add
- elementwise_div
- elementwise_max
- elementwise_mul
- elementwise_sub
- equal
- exp
- expand
- fc
- fill_constant
- floor
- fusion_elementwise_add_activation
- fusion_elementwise_div_activation
- fusion_elementwise_max_activation
- fusion_elementwise_mul_activation
- fusion_elementwise_sub_activation
- generate_proposals
- greater_equal
- greater_than
- gru
- gru_unit
- hard_sigmoid
- im2sequence
- increment
- is_empty
- leaky_relu
- less_equal
- less_than
- lod_reset
- log
- logical_and
- logical_not
- logical_or
- logical_xor
- lookup_table
- lrn
- matmul
- mul
- nearest_interp
- negative
- norm
- not_equal
- pad2d
- pool2d
- power
- prelu
- prior_box
- range
- read_from_array
- reduce_max
- reduce_mean
- relu
- relu6
- relu_clipped
- roi_align
- scale
- sequence_expand
- sequence_pool
- sequence_softmax
- shape
- shuffle_channel
- sigmoid
- slice
- softmax
- split
- squeeze
- squeeze2
- stack
- swish
- tanh
- top_k
- transpose
- transpose2
- unsqueeze
- unsqueeze2
- while
- write_to_array
- yolo_box
### X86 kernels
- concat
- elementwise_add
- elementwise_sub
- fill_constant_batch_size_like
- gru
- matmul
- mul
- relu
- reshape
- reshape2
- scale
- sequence_expand_as
- sequence_pool
- shape
- slice
- softmax
- square
- squeeze
- squeeze2
### OpenCL kernels
- conv2d
- depthwise_conv2d
- elementwise_add
- fc
- fusion_elementwise_add_activation
- io_copy
- io_copy_once
- mul
- pool2d
- relu
---
layout: post
title: 技术特点
---
不同于普通的移动端预测基于类 Caffe 的架构,Lite 架构最早的设计目标来源于 Paddle Server 和 Mobile 两种场景的要求,其中 Server 端需要有完善的图分析和优化能力,而 Mobile 端要求有轻量级部署的能力,两种场景共同的要求是高性能,多硬件支持等。
基于上述要求,Lite 架构完整实现了相应的能力,重点描述如下。
## 多硬件支持
Lite 架构已经验证和完整支持从 Mobile 到 Server 多种硬件的支持需求,包括 ARM CPU, ARM GPU, Huawei NPU, Intel X86 CPU, NV GPU 等。 得益于对不同硬件适度的抽象,在Lite 框架本身清晰的同时支持不同硬件的特殊调度需求,使得Lite架构在框架的清晰程度和硬件的特定调度优化上达到很好的平衡,比如 Nvidia GPU 上复杂的 stream, event 分配,在 Lite 中可以清晰表示。
多种硬件的 Kernel 在代码层和执行层均互不干扰,用户可以自由插拔任何硬件的支持。
## 高性能
高性能来源于两方面,一是 Kernel 优化;二是框架执行。
Kernel 方面,我们对相应硬件上的 Kernel 通过指令集、操作熔合、算法改写等方式进行了深入优化。
框架执行方面,通过简化 Op 和 Kernel 的功能,使得执行期的框架开销极低;此外,框架极大的灵活性可以支持各种硬件的特定调度优化以提升整体效率。
## 量化支持
Lite 支持Paddle Slim 强大的量化训练完毕的模型,因此完整保留了量化计算的高性能以及量化训练的高精度。
## 强大的图分析和优化能力
在图分析优化上,不同于常规的移动端预测引擎基于 Python 脚本工具转化模型, Lite 架构上有完整基于 C++ 开发的 IR 及相应 Pass 集合,以支持操作熔合 (Operator fusion),计算剪枝 (Computation pruning),存储优化 (Memory optimization),量化计算 (Quantitative computation) 等多类计算图优化。
更多的优化策略可以简单通过添加 Pass 的方式模块化支持。
## 轻量级部署
尽管图优化上有复杂的策略,但并不影响移动端的轻量级部署,图分析模块和最终的执行引擎可以拆开使用,最终部署只有一层薄薄的 Kernel 。
## 可支持任意硬件的混合调度
Lite 支持系统可见任意硬件的混合调度,目前已经支持 ARM CPU 和 ARM GPU 的 Kernel 自动混合调度,并验证了 X86 CPU 和 Nvidia GPU 间的混合调度。
支持混合调度的考量有两点:
1. 当系统内同时存在多种硬件可用时,混合调度可以充分利用各类硬件资源
2. 随着支持模型的增多,各硬件对kernel的支持丰富度不一,难免需要混合调度才能跑通
Lite架构通过从底层支持 `Type system` 的方式通用建模各类混合执行的行为,从而能够相对完备地支持混调。
---
layout: post
title: 文档指南
---
Lite是一种轻量级、灵活性强、易于扩展的高性能的深度学习预测框架,它可以支持诸如ARM、OpenCL、NPU等等多种终端,同时拥有强大的图优化及预测加速能力。如果您希望将Lite框架集成到自己的项目中,那么只需要如下几步简单操作即可。
# 一. 准备模型
Lite框架目前支持的模型结构为[PaddlePaddle](https://github.com/PaddlePaddle/Paddle)深度学习框架产出的模型格式。因此,在您开始使用 Lite 框架前您需要准备一个由PaddlePaddle框架保存的模型。
如果您手中的模型是由诸如Caffe2、Tensorflow等框架产出的,那么我们推荐您使用 [X2Paddle](https://github.com/PaddlePaddle/X2Paddle) 工具进行模型格式转换。
# 二. 模型优化
Lite框架拥有强大的加速、优化策略及实现,其中包含诸如量化、子图融合、Kernel优选等等优化手段,为了方便您使用这些优化策略,我们提供了[Model Optimize Tool](../model_optimize_tool)帮助您轻松进行模型优化。优化后的模型更轻量级,耗费资源更少,并且执行速度也更快。
Model Optimize Tool的详细介绍,请您参考 [模型优化方法](../model_optimize_tool)
使用Model Optimize Tool,您只需编译后在开发机上执行以下代码:
``` shell
$ cd <PaddleLite_base_path>
$ cd build.model_optimize_tool/lite/api/
$ ./model_optimize_tool \
--model_dir=<model_param_dir> \
--model_file=<model_path> \
--param_file=<param_path> \
--optimize_out_type=(protobuf|naive_buffer) \
--optimize_out=<output_optimize_model_dir> \
--valid_targets=(arm|opencl|x86) \
--prefer_int8_kernel=(ture|false)
```
其中,optimize_out为您希望的优化模型的输出路径。optimize_out_type则可以指定输出模型的序列化方式,其目前支持Protobuf与Naive Buffer两种方式,其中Naive Buffer是一种更轻量级的序列化/反序列化实现。如果你需要使用Lite在mobile端进行预测,那么您需要设置optimize_out_type=naive_buffer。
# 三. 使用Lite框架执行预测
在上一节中,我们已经通过Model Optimize Tool获取到了优化后的模型,使用优化模型进行预测也十分的简单。为了方便您的使用,Lite进行了良好的API设计,隐藏了大量您不需要投入时间研究的细节。您只需要简单的五步即可使用Lite在移动端完成预测(以C++ API进行说明):
1. 声明MobileConfig。在config中可以设置**从文件加载模型**也可以设置**从memory加载模型**。从文件加载模型需要声明模型文件路径,如 `config.set_model_dir(FLAGS_model_dir)` ;从memory加载模型方法现只支持加载优化后模型的naive buffer,实现方法为:
`void set_model_buffer(model_buffer,model_buffer_size,param_buffer,param_buffer_size) `
2. 创建Predictor。Predictor即为Lite框架的预测引擎,为了方便您的使用我们提供了 `CreatePaddlePredictor` 接口,你只需要简单的执行一行代码即可完成预测引擎的初始化,`std::shared_ptr<PaddlePredictor> predictor = CreatePaddlePredictor(config)`
3. 准备输入。执行predictor->GetInput(0)您将会获得输入的第0个field,同样的,如果您的模型有多个输入,那您可以执行 `predictor->GetInput(i)` 来获取相应的输入变量。得到输入变量后您可以使用Resize方法指定其具体大小,并填入输入值。
4. 执行预测。您只需要执行 `predictor->Run()` 即可使用Lite框架完成预测。
5. 获取输出。与输入类似,您可以使用 `predictor->GetOutput(i)` 来获得输出的第i个变量。您可以通过其shape()方法获取输出变量的维度,通过 `data<T>()` 模板方法获取其输出值。
# 四. Lite API
为了方便您的使用,我们提供了C++与Java两种API,并且提供了相应的api使用的[C++完整示例](../cpp_demo)[Java完整示例](../java_demo),您可以参考示例中的说明快速了解C++/Java的API使用方法,并集成到您自己的项目中去。需要说明的是,为了减少第三方库的依赖、提高Lite预测框架的通用性,在移动端使用Lite API您需要准备Naive Buffer存储格式的模型,具体方法可参考第2节`模型优化`
# 五. 测试工具
为了使您更好的了解并使用Lite框架,我们向有进一步使用需求的用户开放了 [Lite Model Debug Tool](../debug_tools)[Profile Monitor Tool](../debug_tools)。Lite Model Debug Tool可以用来查找Lite框架与PaddlePaddle框架在执行预测时模型中的对应变量值是否有差异,进一步快速定位问题Op,方便复现与排查问题。Profile Monitor Tool可以帮助您了解每个Op的执行时间消耗,其会自动统计Op执行的次数,最长、最短、平均执行时间等等信息,为性能调优做一个基础参考。您可以通过 [相关专题](../debug_tools) 了解更多内容。
---
layout: post
title: 通过 X2Paddle 转换模型
---
[X2Paddle](https://github.com/PaddlePaddle/X2Paddle)支持将Caffe/TensorFlow模型转换为PaddlePaddle模型。目前X2Paddle支持的模型参考[x2paddle_model_zoo](https://github.com/PaddlePaddle/X2Paddle/blob/develop/x2paddle_model_zoo.md)
## 安装
```
pip install x2paddle
```
安装最新版本,可使用如下安装方式
```
pip install git+https://github.com/PaddlePaddle/X2Paddle.git@develop
```
## 使用
### Caffe
```
x2paddle --framework caffe \
--prototxt model.proto \
--weight model.caffemodel \
--save_dir paddle_model
```
### TensorFlow
```
x2paddle --framework tensorflow \
--model model.pb \
--save_dir paddle_model
```
## 转换结果说明
在指定的`save_dir`下生成两个目录
1. inference_model : 模型结构和参数均序列化保存的模型格式
2. model_with_code : 保存了模型参数文件和模型的python代码
## 问题反馈
X2Paddle使用时存在问题时,欢迎您将问题或Bug报告以[Github Issues](https://github.com/PaddlePaddle/X2Paddle/issues)的形式提交给我们,我们会实时跟进。
---
layout: post
title: X2Paddle 支持模型列表
---
## 多框架支持
|模型 | caffe | tensorflow | onnx |
|---|---|---|---|
|mobilenetv1 | Y | Y | |
|mobilenetv2 | Y | Y | Y |
|resnet18 | Y | Y | |
|resnet50 | Y | Y | Y |
|mnasnet | Y | Y | |
|efficientnet | Y | Y | Y |
|squeezenetv1.1 | Y | Y | Y |
|shufflenet | Y | Y | |
|mobilenet_ssd | Y | Y | |
|mobilenet_yolov3 | | Y | |
|inceptionv4 | | | |
|mtcnn | Y | Y | |
|facedetection | Y | | |
|unet | Y | Y | |
|ocr_attention | | | |
|vgg16 | | | |
......@@ -67,6 +67,7 @@ Paddle-Lite 框架是 PaddleMobile 新一代架构,重点支持移动端推理
- [v2.0.0-beta1]({{site.baseurl}}/v2.0.0-beta1/index)
## Develop文档
## 其他文档
- [Develop]({{site.baseurl}}/develop/index)
- [v2.0.0]({{site.baseurl}}/v2.0.0/index)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册