“93e0b462e67274ed3482fdb5d232e275fc4286fe”上不存在“x2paddle/optimizer/git@gitcode.net:paddlepaddle/X2Paddle.git”
提交 c31afacb 编写于 作者: alinag's avatar alinag

Merge branch 'temp' into reduce_sum

# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
\ No newline at end of file
# 如何增加Layout
Paddle-Lite中Place包含了Target、Layout、Precision信息,用来注册和选择模型中的具体Kernel。下面以增加Place中的layout:`ImageDefault``ImageFolder``ImageNW`为例,讲解如何增加新Layout。
根据在`lite/core/``lite/api`目录下以`NHWC`为关键词检索代码,发现需要分别在以下的文件中加入Layout内容:
1. lite/api/paddle_place.h
2. lite/api/paddle_place.cc
3. lite/api/python/pybind/pybind.cc
4. lite/core/op_registry.h
5. lite/core/op_registry.cc
## 1. lite/api/paddle_place.h
`enum class DataLayoutType`中加入对应的Layout,注意已有的Layout不能改变值,增加新Layout递增即可:
```cpp
enum class DataLayoutType : int {
kUnk = 0,
kNCHW = 1,
kNHWC = 3,
kImageDefault = 4, // for opencl image2d
kImageFolder = 5, // for opencl image2d
kImageNW = 6, // for opencl image2d
kAny = 2, // any data layout
NUM = 7, // number of fields.
};
```
## 2. lite/api/paddle_place.cc
本文件有3处修改,注意在` DataLayoutToStr`函数中加入对应Layout的字符串名,顺序为`lite/api/paddle_place.h`中枚举值的顺序:
```cpp
// 该文件第1处
const std::string& DataLayoutToStr(DataLayoutType layout) {
static const std::string datalayout2string[] = {
"unk", "NCHW", "any", "NHWC", "ImageDefault", "ImageFolder", "ImageNW"};
auto x = static_cast<int>(layout);
CHECK_LT(x, static_cast<int>(DATALAYOUT(NUM)));
return datalayout2string[x];
}
// 该文件第2处
const std::string& DataLayoutRepr(DataLayoutType layout) {
static const std::string datalayout2string[] = {"kUnk",
"kNCHW",
"kAny",
"kNHWC",
"kImageDefault",
"kImageFolder",
"kImageNW"};
auto x = static_cast<int>(layout);
CHECK_LT(x, static_cast<int>(DATALAYOUT(NUM)));
return datalayout2string[x];
}
// 该文件第3处
std::set<DataLayoutType> ExpandValidLayouts(DataLayoutType layout) {
static const std::set<DataLayoutType> valid_set({DATALAYOUT(kNCHW),
DATALAYOUT(kAny),
DATALAYOUT(kNHWC),
DATALAYOUT(kImageDefault),
DATALAYOUT(kImageFolder),
DATALAYOUT(kImageNW)});
if (layout == DATALAYOUT(kAny)) {
return valid_set;
}
return std::set<DataLayoutType>({layout});
}
```
## 3. lite/api/python/pybind/pybind.cc
```cpp
// DataLayoutType
py::enum_<DataLayoutType>(*m, "DataLayoutType")
.value("NCHW", DataLayoutType::kNCHW)
.value("NHWC", DataLayoutType::kNHWC)
.value("ImageDefault", DataLayoutType::kImageDefault)
.value("ImageFolder", DataLayoutType::kImageFolder)
.value("ImageNW", DataLayoutType::kImageNW)
.value("Any", DataLayoutType::kAny);
```
## 4. lite/core/op_registry.h
找到KernelRegister final中的`using any_kernel_registor_t =`,加入下面修改信息:
```cpp
// 找到KernelRegister final中的`using any_kernel_registor_t =`
// 加入如下内容:
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kFP16),
DATALAYOUT(kNCHW)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kFP16),
DATALAYOUT(kNHWC)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kFP16),
DATALAYOUT(kImageDefault)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kFP16),
DATALAYOUT(kImageFolder)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kFP16),
DATALAYOUT(kImageNW)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kFloat),
DATALAYOUT(kImageDefault)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kFloat),
DATALAYOUT(kImageFolder)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kFloat),
DATALAYOUT(kImageNW)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kAny),
DATALAYOUT(kImageDefault)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kAny),
DATALAYOUT(kImageFolder)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kAny),
DATALAYOUT(kImageNW)> *, //
```
## 5. lite/core/op_registry.cc
该文件有2处修改:
```cpp
// 该文件第1处
#define CREATE_KERNEL1(target__, precision__) \
switch (layout) { \
case DATALAYOUT(kNCHW): \
return Create<TARGET(target__), \
PRECISION(precision__), \
DATALAYOUT(kNCHW)>(op_type); \
case DATALAYOUT(kAny): \
return Create<TARGET(target__), \
PRECISION(precision__), \
DATALAYOUT(kAny)>(op_type); \
case DATALAYOUT(kNHWC): \
return Create<TARGET(target__), \
PRECISION(precision__), \
DATALAYOUT(kNHWC)>(op_type); \
case DATALAYOUT(kImageDefault): \
return Create<TARGET(target__), \
PRECISION(precision__), \
DATALAYOUT(kImageDefault)>(op_type); \
case DATALAYOUT(kImageFolder): \
return Create<TARGET(target__), \
PRECISION(precision__), \
DATALAYOUT(kImageFolder)>(op_type); \
case DATALAYOUT(kImageNW): \
return Create<TARGET(target__), \
PRECISION(precision__), \
DATALAYOUT(kImageNW)>(op_type); \
default: \
LOG(FATAL) << "unsupported kernel layout " << DataLayoutToStr(layout); \
}
// 该文件第2处
// 找到文件中的下面的函数
KernelRegistry::KernelRegistry()
: registries_(static_cast<int>(TARGET(NUM)) *
static_cast<int>(PRECISION(NUM)) *
static_cast<int>(DATALAYOUT(NUM)))
// 在该函数中加入新增Layout的下面内容
INIT_FOR(kOpenCL, kFP16, kNCHW);
INIT_FOR(kOpenCL, kFP16, kNHWC);
INIT_FOR(kOpenCL, kFP16, kImageDefault);
INIT_FOR(kOpenCL, kFP16, kImageFolder);
INIT_FOR(kOpenCL, kFP16, kImageNW);
INIT_FOR(kOpenCL, kFloat, kImageDefault);
INIT_FOR(kOpenCL, kFloat, kImageFolder);
INIT_FOR(kOpenCL, kFloat, kImageNW);
INIT_FOR(kOpenCL, kAny, kImageDefault);
INIT_FOR(kOpenCL, kAny, kImageFolder);
INIT_FOR(kOpenCL, kAny, kImageNW);
```
# 新增Pass方法
本文从三个方面介绍了`Lite`中的`Pass`结构:**Pass是什么****Pass的实现与接口****Pass的一般注册流程**。最后以`Fc_fuse_pass`为例介绍了`fusion_pass`的作用与注册方法。
## 前述:Pass是什么?
**CxxPredictor加载模型后,在执行预测前会先优化模型。模型优化过程是通过Pass实现的。**
具体调用关系如下:
![图片](https://user-images.githubusercontent.com/45189361/69638690-20d21880-1096-11ea-8169-1d2c7e1a1609.png)
- `CreatePredictor(CxxConfig)`函数调用了Predictor->Build(CxxConfig)
- CxxPredictor的构建过程(Build)分为两步:
- Predictor->LoadModel() 加载模型文件到program中
- Predicotr->optimizer_.Run() 对Program中的原始图形结构进行优化
- 对图结构的优化是通过调用 `Pass->Apply(const std::unique_ptr<SSAGraph>& graph)`方法实现的。
**每一类Pass定义了一种优化过程**,包括:原模型中的kernel选取、OP融合、冗余OP去除、子图创建、内存优化、类型推导、类型转换等。
## Pass的实现与接口 :Pass基类、PassManager和Pass注册
### 1、Pass基类:`paddle::lite::mir::Pass`
```c++
class Pass {
public:
// Pass的类型,Pass按照作用的不同可以分为三种
enum class Kind { //种类的作用不太清楚
// 1. 修改模型中的图拓扑结构的Pass
kProgramWise = 0,
// 2. 不修改图结构,修改状态的Pass
kStmtWise,
// 3. 不修改 IR,用于搜集信息和可视化信息的Pass.
kDebug,
};
// 主要实现函数:Apply 函数定义了 Pass 运行时执行的操作
virtual void Apply(const std::unique_ptr<SSAGraph>& graph) = 0;
bool is_program_pass() const { return kind_ == Kind::kProgramWise; }
bool is_stmt_pass() const { return kind_ == Kind::kStmtWise; }
virtual ~Pass() = default;
private:
const Kind kind_; // pass 的种类
std::string name_; // pass 的名称
std::set<TargetType> bound_targets_; // 指定了Pass运行的硬件平台,模型优化过程会根据当前硬件平台是否匹配筛选Pass。
std::unordered_map<std::string, std::set<lite_api::Place>> bound_kernels_; // 绑定的kernel
};
// Different kinds.
class ProgramPass : public Pass {
public:
ProgramPass() : Pass(Kind::kProgramWise) {}
};
class StmtPass : public Pass {
public:
StmtPass() : Pass(Kind::kStmtWise) {}
};
class DebugPass : public Pass {
public:
DebugPass() : Pass(Kind::kDebug) {}
};
```
**代码位置**`lite/core/mir/pass.h`
**主要类成员**
`const Kind kind_` : Pass类型。pass 有三种基本基本类型 :修改图结构的`ProgramPass`、修改状态量的`StmtPass`和Debug过程采集信息与控制可视化的`DebugPass`
`std::string name_` :pass 的名称
`std::set<TargetType> bound_targets_` : Pass运行的硬件平台,optimizer.Run()优化过程会根据硬件平台选择匹配的Pass。------根据硬件平台自动选择需要的pass
`std::unordered_map<std::string, std::set<lite_api::Place>> bound_kernels_` : Pass 绑定的kernel (what's this used for)
**主要接口**
`Pass::Apply(const std::unique_ptr& graph)` : Pass优化过程的具体操作,是新注册Pass需要实现的接口。输入为`SSAGraph`型指针,是对模型结构的拓扑表示。
### 2、Pass管理 `paddle::lite::mir::PassManager`
```c++
class PassManager {
public:
// 内部静态变量PassManager,用来存储使用的Pass和图优化操作
static PassManager& Global() {
static PassManager x;
return x;
}
// 执行所有的 Pass
void Run(const std::unique_ptr<SSAGraph>& graph) {
for (auto& pass : passes_) {
LOG(INFO) << "Running MIR pass " << pass->name();
pass->Apply(graph);
}
private:
std::list<std::unique_ptr> passes_; //存储所有的 Pass
std::map<std::string, mir::Pass*> pass_map_; //使用map变量存储 PassName::Pass
}
```
**代码位置**`lite/core/mir/pass_manager.h`
**主要类成员**
`std::list:unique_ptr> passes_;` : List类型,存储了所有已注册Pass。
`std::map<std::string, mir::Pass*> pass_map_; ` : Map类型,存储了所有"Pass名称-Pass类"键对,用于根据名称查找Pass。
**主要接口**
`static PassManager& Global()` 返回PassManager全局静态变量,该变量存储了所有已注册的Pass
` bool AddNewPass(const std::string& name, Pass* pass)` 添加新的Pass到PassManager中
### 3、 Pass 注册 `paddle::lite::mir::PassRegistry`
**代码位置**`lite/core/mir/pass_registry.h`
**主要接口**
`REGISTER_MIR_PASS(name__, class__)` :宏定义函数,用于注册Pass。注册Pass过程实现的是 `PassManager::Global().AddNewPass(name__, class__)`,将新注册Pass添加到全局变量`PassManager`中。
## Pass的一般注册流程与使用方法
### 1. Pass 注册流程
`lite/core/mir`或其子目录下继承`Pass基类`,实现`Pass::Apply`接口,并使用宏`REGISTER_MIR_PASS(name__, class__)`将Pass注册到`PassManager`即完成了新Pass注册。
**以新建 **`new_demo_pass`**为例**,具体流程如下:
(1)在`lite/core/mir`路径下新建`example_pass.cc``new_demo_pass.h` 文件
(2)在`example_pass.h` 文件中继承Pass基类(ProgramPass、StmtPass或DebugPass)定义自己的Pass类。
```c++
#include "lite/core/mir/pass.h"
namespace paddle {
namespace lite {
namespace mir {
class ExamplePass : public ProgramPass {
void Apply(const std::unique_ptr<SSAGraph> &graph) override {}
...
};
} // namespace mir
} // namespace lite
} // namespace paddle
```
(3)在`example_pass.cc` 文件中实现`ExamplePass::Apply()`接口,并注册`ExamplePass`
```c++
#include "lite/core/mir/pass_registry.h"
#include "lite/core/mir/example_pass.h"
namespace paddle {
namespace lite {
namespace mir {
void ExamplePass::Apply(const std::unique_ptr<SSAGraph>& graph) {
...
}
} // namespace mir
} // namespace lite
} // namespace paddle
REGISTER_MIR_PASS(example_pass, paddle::lite::mir::ExamplePass)
.BindTargets({TARGET(kARM)}); // Pass执行的目标硬件平台
// .BindKernel("conv2d"); //Pass绑定的 kernel
```
(4)修改`lite/core/mir/CMakeLists.txt`文件,将`example_pass.cc` 编译到`mir_passes`库中
```cmake
lite_cc_library(mir_passes
SRCS
demo_pass.cc // 新建的Pass文件
...
memory_optimize_pass.cc
DEPS mir_pass types context ${mir_fusers} ${subgraph_passes})
```
### 2. Pass使用流程
将Pass注册到PassManager后不会自动生效。需要在`optimizer->run()` 函数中添加该Pass才会在模型优化过程中调用。
(1)在`paddle_use_passes.h`文件中调用该Pass
```cmake
#include "paddle_lite_factory_helper.h" // NOLINT
...
USE_MIR_PASS(new_demo_pass); //调用 new_demo_pass
```
(2)要想在优化模型时调用该Pass,需要在`optimizer->run()`函数中手动添加调用。
修改`lite/core/optimizer.h`文件,添加`new_demo_pass``Optimizer::Run()`函数;
```c++
class Optimizer {
public:
void Run(...) {
...
if (passes.empty()) {
RunPasses(std::vector<std::string>{
{"new_demo_pass" //将新注册的Pass添加在这里
...
}
...
}
```
(3)只有CxxPredictor才会在模型加载后根据Pass优化模型。
```c++
...
#include "paddle_use_passes.h" // 引用Pass优化模型
void RunModel() {
// 1. 创建 CxxConfig
CxxConfig config;
config.set_model_dir(FLAGS_model_dir);
config.set_valid_places(Place{TARGET(kARM), PRECISION(kFloat)});
// 2. 创建CxxPredictor,该过程包括加载模型和用Pass优化模型
std::shared_ptr> predictor =
Creat<CxxConfig>(config);
}
```
## Fusion Pass的定义与注册
`Fusion Pass`是一种常见图结构优化Pass,可将多个连续OP融合成单个等效OP,减少数据交换并简化图结构。Pass运行时调用`Fuser`自动查找并替换指定图结构,所以注册`FuserPass`时还需要实现对应的Fuser类。
下面以`fc_fuse_pass`为例,详细说明`FusionPass`的效果和注册方法。
### `fc_fuse_pass`的作用
将相邻的`mul`算子和 `element_wise add `算子 融合成一个 `FC` 算子
```c++
mul(X) = X * W
elementwise_add( mul(x) ) = X * W + Bias
//----------> after fusion
FC(X) = X * W +Bias
```
Pass 运行效果如下:
![图片](https://user-images.githubusercontent.com/45189361/69639193-12383100-1097-11ea-9063-21f030414080.png)
mul和elementwise_add的原有参数映射到FC的参数上:
![图片](https://user-images.githubusercontent.com/45189361/69638836-74446680-1096-11ea-9cdc-a961fa995dfe.png)
### `fc_fuse_pass`的注册方法
#### 1、创建FcFuser
(1)在`lite/core/mir/fusion`路径下新建`fc_fuser.cc``fc_fuser.h` 文件
(2)在`fc_fuser.h` 文件中继承`FuseBase`定义自己的Fuser类。
```c++
#include "lite/core/mir/pattern_matcher_high_api.h"
namespace paddle {
namespace lite {
namespace mir {
namespace fusion {
class FcFuser : public FuseBase {
public:
void BuildPattern() override;
void InsertNewNode(SSAGraph* graph, const key2nodes_t& matched) override;
private:
cpp::OpDesc GenOpDesc(const key2nodes_t& matched) override;
};
} // namespace fusion
} // namespace mir
} // namespace lite
} // namespace paddle
```
**主要接口**
`FuseBase::BuildPattern` : 描述需要替换位置的图结构(pattern),Fuser运行时会自动查找并替换该pattern。
`FuseBase::GenOpDesc` : 创建融合后的等效Fused_op。
`FuseBase::InsertNewNode` :用Fused_op替换原始图结构(pattern)。
对于 `FcFuser`:BuildPattern描述的Pattern是`mul+elementwise add`,GenOpDesc创建的FC_op,InsertNewNode函数的效果是用新建的`FC_op`替换模型中的`mul+elementwise add` pattern。
(3) 在`fc_fuser.cc`文件中实现 `BuildPattern()``GenOpDesc()``InsertNewNode() `接口
下面以FcFuser为例介绍三种接口的实现:
```c++
// 1. BuildPattern函数,描述需要替换的图结构
// FcFuser::BuildPattern() 描述了 mul + element_wise add 图结构
void FcFuser::BuildPattern() {
// (1) 用OpNode描述和VarNode
// mul OP
auto* mul = OpNode("mul", "mul");
// mul OP 的输入和输出
auto* x = VarNode("x")->assert_is_op_input("mul", "X");
auto* W = VarNode("W")->assert_is_op_input("mul", "Y");
auto* mul_out = VarNode("mul_out");
// elementwise_add OP
auto* add = OpNode("add", "elementwise_add");
//elementwise_add 的输入
auto* b = VarNode("b")->assert_is_persistable_var();
// elementwise_add OP的输出(最终输出)
auto* Out = VarNode("Out");
//(2) 描述拓扑连接 (Fuse之前mul 和elementwise_add的连接)
std::vector<PMNode*> mul_inputs{W, x};
std::vector<PMNode*> add_inputs{mul_out, b};
mul_inputs >> *mul >> *mul_out;
add_inputs >> *add >> *Out;
//(3) 声明新的拓扑结构中将会被移除的节点,包括被fuse的OP和OP之间的中间变量
mul_out->AsIntermediate();
mul->AsIntermediate();
add->AsIntermediate();
}
// 2. GenOpDesc函数新建等效 Fused_op
// FcFuser::GenOpDesc() 新建了Fc_op
cpp::OpDesc FcFuser::GenOpDesc(const key2nodes_t& matched) {
// (1) 得到第一个OP节点的 OpDesc ,并清空输入输出信息
cpp::OpDesc op_desc = *matched.at("mul")->stmt()->op_info();
op_desc.mutable_inputs()->clear();
op_desc.mutable_outputs()->clear();
// (2) 修改OpDesc , 将OpType设置为 "fc" (FC OP 的OP_type),
op_desc.SetType("fc");
// (3) 设置OpDesc中的Input、Output、Attrbute。分别连接到BuildPattern()函数中创建的VarNode
op_desc.SetInput("Input", {matched.at("x")->arg()->name});
op_desc.SetInput("W", {matched.at("W")->arg()->name});
op_desc.SetInput("Bias", {matched.at("b")->arg()->name});
op_desc.SetOutput("Out", {matched.at("Out")->arg()->name});
op_desc.SetAttr(
"in_num_col_dims",
matched.at("mul")->stmt()->op_info()->GetAttr<int>("x_num_col_dims"));
return op_desc;
}
// 3. InsertNewNode函数用Fused OP 替换模型图中的原始 Pattern
// FcFuser::InsertNewNode() 用Fc_OP替换原始模型图中的 " mul + element_wise add "
void FcFuser::InsertNewNode(SSAGraph* graph, const key2nodes_t& matched) {
// (1) 创建FC OP的参数(OpDesc)
auto op_desc = GenOpDesc(matched);
// 创建一个 FC OP
auto fc_op = LiteOpRegistry::Global().Create("fc");
// 找到原拓扑结构中的scope (作用域)和 valid_places (可支持设备类型)
auto mul = matched.at("mul")->stmt()->op();
auto* scope = mul->scope();
auto& valid_places = mul->valid_places();
// (2) 将 FC OP的 scope和 valid_places设置与fuse前相同,并在图中创建该节点(node)
fc_op->Attach(op_desc, scope);
auto* new_op_node = graph->GraphCreateInstructNode(fc_op, valid_places);
// (3) 将FC节点连接到输入输出(var_node)
IR_NODE_LINK_TO(matched.at("W"), new_op_node);
IR_NODE_LINK_TO(matched.at("x"), new_op_node);
IR_NODE_LINK_TO(matched.at("b"), new_op_node);
IR_NODE_LINK_TO(new_op_node, matched.at("Out"));
}
```
#### 2、注册fc_fuse_pass
(1)在`lite/core/mir/fusion`路径下新建`fc_fuse_pass.cc``fc_fuse_pass.h` 文件
(2)在`fc_fuse_pass.h` 文件中,继承`ProgramPass`定义`FcFusePass`
```c++
#include "lite/core/mir/pass.h"
namespace paddle {
namespace lite {
namespace mir {
class FcFusePass : public ProgramPass {
public:
void Apply(const std::unique_ptr<SSAGraph>& graph) override; namespace mir namespace lite namespace paddle
```
(3)在`fc_fuse_pass.cc` 文件中实现`FcFusePass::Apply()`接口,并注册`FcFusePass`
```c++
#include "lite/core/mir/pass_registry.h"
#include "lite/core/mir/example_pass.h"
namespace paddle {
namespace lite {
namespace mir {
void FcFusePass::Apply(const std::unique_ptr<SSAGraph>& graph) {
fusion::FcFuser fuser;
fuser(graph.get());namespace mir
} // namespace lite
} // namespace paddle
REGISTER_MIR_PASS(lite_fc_fuse_pass, paddle::lite::mir::FcFusePass)
.BindTargets({TARGET(kAny)}) // FcFusePass 可以在任何硬件平台执行
.BindKernel("fc"); // FcFusePass 绑定 fc_kernel
```
(4)修改`lite/core/mir/fusion/CMakeLists.txt`文件,将`fc_fuser.cc` 编译到`mir_fusers`
```cmake
lite_cc_library(fuse_fc
SRCS fc_fuser.cc
DEPS pattern_matcher_high_api)
set(mir_fusers
fuse_fc
...
CACHE INTERNAL "fusers")
```
(5)修改`lite/core/mir/CMakeLists.txt`文件,将`fc_fuse_pass.cc` 编译到`mir_pass`
```cmake
lite_cc_library(mir_passes
SRCS
fusion/fc_fuse_pass.cc
...
DEPS mir_pass types context ${mir_fusers} ${subgraph_passes})
```
#### 3、使用 fc_fuse_pass
(1) `lite/api/paddle_use_passes.h`使用`USE_LITE_PASS`宏来引入新加入的pass
```c++
USE_MIR_PASS(lite_fc_fuse_pass);
```
(2) 在`lite/core/optimizer.h`文件的`Optimizer::Run()`函数中添加新注册的pass
```C++
class Optimizer {
public:
void Run(Program&& program,
const std::vector<Place>& valid_places,
core::KernelPickFactor kernel_pick_factor,
const std::vector<std::string>& passes = {}) {
...
if (passes.empty()) {
RunPasses(std::vector<std::string>{
{"lite_fc_fuse_pass", // the newly registered pass
...
"argument_type_display_pass"}});
} else {
RunPasses(passes);
}
exec_scope_ = program.exec_scope();
}
```
(3) 以上修改完成后,在CreatePredictor(CxxConfig)创建CxxPredictor时,模型优化过程会调用`lite_fc_fuse_pass `,扫描`mul + element_wise add`结构并替换为等效的Fc_OP。
# CV 图像预处理API接口介绍
请把编译脚本`Paddle-Lite/lite/too/build.sh``BUILD_CV`变量设置为`ON`, 其他编译参数设置请参考[源码编译](../source_compile), 以确保 Lite 可以正确编译。这样`CV`图像的加速库就会编译进去,且会生成`paddle_image_preprocess.h`的API文件
- 硬件平台: `ARM`
- 操作系统:`MAC``LINUX`
## CV 图像预处理功能
Lite 支持不同颜色空间的图像相互转换 `Convert` 、缩放 `Resize` 、翻转 `Flip`、旋转 `Rotate` 和图像数据转换为 `Tensor` 存储`ImageToTensor` 功能,下文将详细介绍每个功能的API接口。
### CV 枚举变量和结构体变量
- 颜色空间
```cpp
enum ImageFormat {
RGBA = 0,
BGRA,
RGB,
BGR,
GRAY,
NV21 = 11,
NV12,
};
```
- 翻转参数
```cpp
enum FlipParam {
X = 0, // flip along the X axis
Y, // flip along the Y axis
XY // flip along the XY axis
};
```
- 转换参数
```cpp
typedef struct {
int ih; // input height
int iw; // input width
int oh; // outpu theight
int ow; // output width
FlipParam flip_param; // flip, support x, y, xy
float rotate_param; // rotate, support 90, 180, 270
} TransParam;
```
### ImagePreprocess 类的成员变量
`ImagePreprocess` 类含有以下三个私有成员变量,通过构造函数进行初始化。
```cpp
private:
ImageFormat srcFormat_; // input image color format
ImageFormat dstFormat_; // output image color format
TransParam transParam_; // image transform parameter
// init
ImagePreprocess::ImagePreprocess(ImageFormat srcFormat, ImageFormat dstFormat, TransParam param) {
this->srcFormat_ = srcFormat;
this->dstFormat_ = dstFormat;
this->transParam_ = param;
}
```
### 颜色空间转换 Convert
`Convert` 函数支持颜色空间:GRAY、NV12(NV21)、RGB(BGR)和RGBA(BGRA)
+ 目前支持以下颜色空间的相互转换:
- GRAY2BGR
- GRAY2RGB
- BGR2RGB
- BGRA2BGR
- BGRA2RGB
- RGBA2RGB
- RGBA2BGR
- BGRA2RGBA
+ 目前支持以下颜色空间的单向转换:
- NV12—BGR
- NV21—BGR
- NV12—RGB
- NV21—RGB
- NV12—BGRA
- NV21—BGRA
- NV12—RGBA
- NV21—RGBA
+ `Convert` 功能的API接口
```cpp
// 方法一
void ImagePreprocess::imageCovert(const uint8_t* src, uint8_t* dst);
// 方法二
void ImagePreprocess::imageCovert(const uint8_t* src,
uint8_t* dst, ImageFormat srcFormat, ImageFormat dstFormat);
```
+ 第一个 `imageCovert` 接口,缺省参数来源于 `ImagePreprocess` 类的成员变量。故在初始化 `ImagePreprocess` 类的对象时,必须要给以下成员变量赋值:
- param srcFormat:`ImagePreprocess` 类的成员变量`srcFormat_`
- param dstFormat:`ImagePreprocess` 类的成员变量`dstFormat_`
- 第二个`imageCovert` 接口,可以直接使用
### 缩放 Resize
`Resize` 功能支持颜色空间:GRAY、NV12(NV21)、RGB(BGR)和RGBA(BGRA)
`Resize` 功能目前支持的方法:`bilinear`
+ `Resize` 功能的API接口
```cpp
// 方法一
void ImagePreprocess::imageResize(const uint8_t* src, uint8_t* dst);
// 方法二
void ImagePreprocess::imageResize(const uint8_t* src, uint8_t* dst, ImageFormat srcFormat, ImageFormat srcFormat, int srcw, int srch, int dstw, int dsth);
```
+ 第一个`imageResize` 接口,缺省参数来源于`ImagePreprocess` 类的成员变量。故在初始化`ImagePreprocess` 类的对象时,必须要给以下成员变量赋值:
- param srcFormat:`ImagePreprocess` 类的成员变量`dstFormat_`
- param srcw:`ImagePreprocess` 类的成员变量`transParam_.iw`
- param srch:`ImagePreprocess` 类的成员变量`transParam_.ih`
- param dstw:`ImagePreprocess` 类的成员变量`transParam_.ow`
- param dsth:`ImagePreprocess` 类的成员变量`transParam_.ow`
- 第二个`imageResize` 接口,可以直接使用
### 旋转 Rotate
`Rotate` 功能支持颜色空间:GRAY、RGB(BGR)和RGBA(BGRA)
`Rotate` 功能目前支持的角度:90、180 和 270
+ `Rotate` 功能的API接口
```cpp
// 方法一
void ImagePreprocess::imageRotate(const uint8_t* src, uint8_t* dst);
// 方法二
void ImagePreprocess::imageRotate(const uint8_t* src, uint8_t* dst, ImageFormat srcFormat, ImageFormat srcFormat, int srcw, int srch, float degree);
```
+ 第一个`imageRotate` 接口,缺省参数来源于`ImagePreprocess` 类的成员变量。故在初始化`ImagePreprocess` 类的对象时,必须要给以下成员变量赋值:
- param srcFormat:`ImagePreprocess` 类的成员变量`dstFormat_`
- param srcw:`ImagePreprocess` 类的成员变量`transParam_.ow`
- param srch:`ImagePreprocess` 类的成员变量`transParam_.oh`
- param degree:`ImagePreprocess` 类的成员变量`transParam_.rotate_param`
- 第二个`imageRotate` 接口,可以直接使用
### 翻转 Flip
`Flip` 功能支持颜色空间:GRAY、RGB(BGR)和RGBA(BGRA)
`Flip` 功能目前支持的功能:沿X轴翻转、沿Y轴翻转和沿XY轴翻转
+ `Flip` 功能的API接口
```cpp
// 方法一
void ImagePreprocess::imageFlip(const uint8_t* src, uint8_t* dst);
// 方法二
void ImagePreprocess::imageFlip(const uint8_t* src, uint8_t* dst, ImageFormat srcFormat, ImageFormat srcFormat, int srcw, int srch, FlipParam flip_param);
```
+ 第一个`imageFlip` 接口,缺省参数来源于`ImagePreprocess` 类的成员变量。故在初始化`ImagePreprocess` 类的对象时,必须要给以下成员变量赋值:
- param srcFormat:`ImagePreprocess` 类的成员变量`dstFormat_`
- param srcw:`ImagePreprocess` 类的成员变量`transParam_.ow`
- param srch:`ImagePreprocess` 类的成员变量`transParam_.oh`
- param flip_param:`ImagePreprocess` 类的成员变量`transParam_.flip_param`
- 第二个`imageFlip` 接口,可以直接使用
### Image2Tensor
`Image2Tensor` 功能支持颜色空间:RGB(BGR)和RGBA(BGRA)
`Image2Tensor` 功能目前支持的Layout:`NCHW``NHWC`
`Image2Tensor` 不仅完成图像转换为`Tensor`数据处理,而且还完成了图像数据的归一化处理
+ `Image2Tensor` 功能的API接口
```cpp
// 方法一
void ImagePreprocess::image2Tensor(const uint8_t* src, Tensor* dstTensor, LayoutType layout, float* means, float* scales);
// 方法二
void ImagePreprocess::image2Tensor(const uint8_t* src, Tensor* dstTensor, ImageFormat srcFormat, srcw, int srch, LayoutType layout, float* means, float* scales;
```
+ 第一个`image2Tensor` 接口,缺省参数来源于`ImagePreprocess` 类的成员变量。故在初始化`ImagePreprocess` 类的对象时,必须要给以下成员变量赋值:
- param srcFormat:`ImagePreprocess` 类的成员变量`dstFormat_`
- param srcw:`ImagePreprocess` 类的成员变量`transParam_.ow`
- param srch:`ImagePreprocess` 类的成员变量`transParam_.oh`
- 第二个`image2Tensor` 接口,可以直接使用
## CV 图像预处理 Demo 示例
例子:输入 `1920x1080` 大小的 `NV12` 图像src,输出 `960x540` 大小 `RGB` 格式的图像dst;然后,完成 `90` 度旋转和沿 `X` 轴翻转功能;最后,用 `NHWC` 格式存储在Tensor里。
定义 `ImagePreprocess` 类的对象,初始化成员变量
```cpp
// init
srcFormat = ImageFormat::NV12;
dstFormat = ImageFormat::RGB;
srch = 1920;
srcw = 1080;
dsth = 960;
dstw = 540;
flip_param = FlipParam::X;
degree = 90;
layout = LayoutType::NHWC
// 方法一:
TransParam tparam;
tparam.ih = srch;
tparam.iw = srcw;
tparam.oh = dsth;
tparam.ow = dstw;
tparam.flip_param = flip_param;
tparam.rotate_param = degree;
ImagePreprocess image_preprocess(srcFormat, dstFormat, tparam);
// 方法二:
ImagePreprocess image_preprocess();
```
### imageConvert Demo
```cpp
// 方法一:
image_preprocess.imageCovert(src, lite_dst);
// 方法二:
image_preprocess.imageCovert(src, lite_dst, (ImageFormat)srcFormat, (ImageFormat)dstFormat);
```
### imageResize Demo
```cpp
// 方法一:
image_preprocess.imageResize(lite_dst, resize_tmp);
// 方法二:
image_preprocess.imageResize(lite_dst,resize_tmp, (ImageFormat)dstFormat, srcw,
srch, dstw, dsth);
```
### imageRotate Demo
```cpp
// 方法一:
image_preprocess.imageRotate(resize_tmp, tv_out_ratote);
// 方法二:
image_preprocess.imageRotate(resize_tmp,tv_out_ratote, (ImageFormat)dstFormat, dstw, dsth, degree);
```
### imageFlip Demo
```cpp
// 方法一:
image_preprocess.imageFlip(tv_out_ratote, tv_out_flip);
// 方法二:
image_preprocess.imageFlip(tv_out_ratote, tv_out_flip, (ImageFormat)dstFormat dstw, dsth, flip_param);
```
### image2Tensor Demo
```cpp
// 方法一:
image_preprocess.image2Tensor(tv_out_flip, &dst_tensor, layout, means, scales);
// 方法二:
image_preprocess.image2Tensor(tv_out_flip, &dst_tensor,(ImageFormat)dstFormat, dstw, dsth, layout, means, scales);
```
# 使用X86预测库
Paddle-Lite 支持在Docker或Linux环境编译x86预测库。环境搭建参考[环境准备](../installation/source_compile)
(注意:非docker Linux环境需要是Ubuntu16.04)
## 编译
1、 下载代码
```bash
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
#需要切换到 release/v2.0.0之后版本
git checkout <release_tag>
```
2、 源码编译
```bash
cd Paddle-Lite
./lite/tools/build.sh x86
```
## 编译结果说明
x86编译结果位于 `build.lite.x86/inference_lite_lib`
**具体内容**说明:
1、 `bin`文件夹:可执行工具文件 `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、 `third_party` 文件夹:第三方库文件
## x86预测API使用示例
```c++
#include <gflags/gflags.h>
#include <iostream>
#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.");
DEFINE_string(optimized_model_dir, "", "Optimized model dir.");
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_file(FLAGS_model_dir + "model");
config.set_param_file(FLAGS_model_dir + "params");
config.set_valid_places({
lite_api::Place{TARGET(kX86), PRECISION(kFloat)}
});
// 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)));
std::cout << "Output dim: " << output_tensor->shape()[1] << std::endl;
for (int i = 0; i < ShapeProduction(output_tensor->shape()); i += 100) {
std::cout << "Output[" << i << "]:" << output_tensor->data<float>()[i] << std::endl;
}
}
int main(int argc, char** argv) {
google::ParseCommandLineFlags(&argc, &argv, true);
RunModel();
return 0;
}
```
# C++ API文档
## 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>`
## CxxConfig
```c++
class CxxConfig;
```
`CxxConfig`用来配置构建CxxPredictor的配置信息,如protobuf格式的模型地址、能耗模式、工作线程数、place信息等等。
示例:
```c++
config = CxxConfig()
# 设置模型目录,加载非combined模型时使用
config.set_model_dir(<your_model_dir_path>)
# 设置工作线程数
config.set_threads(4);
# 设置能耗模式
config.set_power_mode(PowerMode.LITE_POWER_NO_BIND)
# 设置valid places
places = [Place(TargetType.ARM, PrecisionType.FP32)]
config.set_valid_places(places)
# 根据CxxConfig创建CxxPredictor
predictor = create_paddle_predictor(config)
```
### `set_model_dir(model_dir)`
设置模型文件夹路径,当需要从磁盘加载非combined模型时使用。
参数:
- `model_dir(str)` - 模型文件夹路径
返回:`None`
返回类型:`None`
### `model_dir()`
返回设置的模型文件夹路径。
参数:
- `None`
返回:模型文件夹路径
返回类型:`str`
### `set_model_file(model_file)`
设置模型文件路径,加载combined形式模型时使用。
参数:
- `model_file(str)` - 模型文件路径
返回类型:`None`
### `model_file()`
获取设置模型文件路径,加载combined形式模型时使用。
参数:
- `None`
返回:模型文件路径
返回类型:`str`
### `set_param_file(param_file)`
设置模型参数文件路径,加载combined形式模型时使用。
参数:
- `param_file(str)` - 模型文件路径
返回类型:`None`
### `param_file()`
获取设置模型参数文件路径,加载combined形式模型时使用。
参数:
- `None`
返回:模型参数文件路径
返回类型:`str`
### `set_valid_places(valid_places)`
设置可用的places列表。
参数:
- `valid_places(list)` - 可用place列表。
返回类型:`None`
示例:
```c++
config = CxxConfig()
# 设置模型目录,加载非combined模型时使用
config.set_model_dir(<your_model_dir_path>)
# 设置valid places
# 注意,valid_places列表中Place的排序表明了用户对Place的偏好程度,如用户想优先使用ARM上Int8精度的
# kernel,则应把Place(TargetType.ARM, PrecisionType.INT8)置于valid_places列表的首位。
places = [Place(TargetType.ARM, PrecisionType.INT8),
Place(TargetType.ARM, PrecisionType.FP32)]
config.set_valid_places(places)
# 根据CxxConfig创建CxxPredictor
predictor = create_paddle_predictor(config)
```
### `set_power_mode(mode)`
设置CPU能耗模式。若不设置,则默认使用`PowerMode.LITE_POWER_HIGH`
*注意:只在开启`OpenMP`时生效,否则系统自动调度。此函数只在使用`LITE_WITH_ARM`编译选项下生效。*
参数:
- `mode(PowerMode)` - CPU能耗模式
返回:`None`
返回类型:`None`
### `power_mode()`
获取设置的CPU能耗模式。
*注意:此函数只在使用`LITE_WITH_ARM`编译选项下生效。*
参数:
- `None`
返回:设置的CPU能耗模式
返回类型:`PowerMode`
### `set_threads(threads)`
设置工作线程数。若不设置,则默认使用单线程。
*注意:只在开启`OpenMP`的模式下生效,否则只使用单线程。此函数只在使用`LITE_WITH_ARM`编译选项下生效。*
参数:
- `threads(int)` - 工作线程数
返回:`None`
返回类型:`None`
### `threads()`
获取设置的工作线程数。
*注意:此函数只在使用`LITE_WITH_ARM`编译选项下生效。*
参数:
- `None`
返回:工作线程数
返回类型:`int`
### `set_x86_math_library_num_threads(threads)`
设置CPU Math库线程数,CPU核心数支持情况下可加速预测。默认为1,并且仅在x86下有效。
参数:
- `threads(int)` - CPU Math库线程数。
返回:`None`
返回类型:`None`
### `x86_math_library_num_threads()`
返回CPU Math库线程数,CPU核心数支持情况下可加速预测。仅在x86下有效。
参数:
- `None`
返回:CPU Math库线程数。
返回类型:`int`
## 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_from_file(model_dir)`
设置模型文件,当需要从磁盘加载模型时使用。
参数:
- `model_dir(std::string)` - 模型文件路径
返回:`None`
返回类型:`void`
### `set_model_dir(model_dir)`
**注意**:Lite模型格式在release/v2.3.0之后修改,本接口为加载老格式模型的接口,将在release/v3.0.0废弃。建议替换为`set_model_from_file`接口。
设置模型文件夹路径,当需要从磁盘加载模型时使用。
参数:
- `model_dir(std::string)` - 模型文件夹路径
返回:`None`
返回类型:`void`
### `model_dir()`
返回设置的模型文件夹路径。
参数:
- `None`
返回:模型文件夹路径
返回类型:`std::string`
### `set_model_from_buffer(model_buffer)`
设置模型的内存数据,当需要从内存加载模型时使用。
参数:
- `model_buffer(std::string)` - 内存中的模型数据
返回:`None`
返回类型:`void`
### `set_model_buffer(model_buffer, model_buffer_size, param_buffer, param_buffer_size)`
**注意**:Lite模型格式在release/v2.3.0之后修改,本接口为加载老格式模型的接口,将在release/v3.0.0废弃。建议替换为`set_model_from_buffer`接口。
设置模型、参数的内存地址,当需要从内存加载模型时使用。
示例:
```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的指针。
**注意**`GetTensor`接口是为开发者设计的调试接口,可以输出[转化](../model_optimize_tool)后模型中的任一节点。如果出现`GetTensor(InputName)`返回值为空`Tensor`,可能原因是以该`InputName`命名的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`
## TargetType
```c++
class TargetType;
```
`TargetType`为目标设备硬件类型,用户可以根据应用场景选择硬件平台类型。
枚举型变量`TargetType`的所有可能取值包括:
`{X86, CUDA, ARM, OpenCL, FPGA, NPU}`
## PrecisionType
```c++
class PrecisionType {FP32};
```
`PrecisionType`为模型中Tensor的数据精度,默认值为FP32(float32)。
枚举型变量`PrecisionType`的所有可能取值包括:
`{FP32, INT8, INT32, INT64}`
## DataLayoutType
```c++
class DataLayoutType {NCHW};
```
`DataLayoutType`为Tensor的数据格式,默认值为NCHW(number, channel, height, weigth)。
枚举型变量`DataLayoutType`的所有可能取值包括:
` {NCHW, NHWC}`
## Place
```c++
class Place{
TargetType target;
PrecisionType precision{FP32};
DataLayoutType layout{NCHW}
}
```
`Place``TargetType``PrecisionType``DataLayoutType`的集合,说明运行时的设备类型、数据精度和数据格式。
示例:
```C++
Place{TargetType(ARM), PrecisionType(FP32), DataLayoutType(NCHW)}
```
## 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>>`
...@@ -34,13 +34,21 @@ Welcome to Paddle-Lite's documentation! ...@@ -34,13 +34,21 @@ Welcome to Paddle-Lite's documentation!
:caption: 使用指南 :caption: 使用指南
:name: sec-user-guides :name: sec-user-guides
user_guides/model_optimize_tool
user_guides/library_tailoring
user_guides/cuda
user_guides/opencl
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
:caption: 进阶使用指南 :caption: 进阶使用指南
advanced_user_guides/support_operation_list advanced_user_guides/support_operation_list
advanced_user_guides/add_operation advanced_user_guides/add_operation
advanced_user_guides/add_layout
advanced_user_guides/model_quantization advanced_user_guides/model_quantization
advanced_user_guides/add_new_pass
advanced_user_guides/x86
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
...@@ -50,6 +58,8 @@ Welcome to Paddle-Lite's documentation! ...@@ -50,6 +58,8 @@ Welcome to Paddle-Lite's documentation!
:maxdepth: 1 :maxdepth: 1
:caption: API文档 :caption: API文档
api_reference/cxx_api_doc
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
:caption: FAQ :caption: FAQ
......
# 预测库说明
Paddle-Lite的编译结果为预测库文件(包括静态库和动态库),具体编译过程参考[源码编译](./source_compile)
Lite预测库分为**基础预测库****全量预测库**:基础预测库只打包了基础模型需要的基础算子,预测库体积较小;全量预测库打包了所有的Lite算子,可以支持更多的模型,但是预测库的体积也更大。 编译时由编译选项 `build_extra`(默认为OFF)控制,`--build_extra=OFF`时编译基础预测库,`--build_extra=ON`时编译全量的预测库。
## 基础预测库
### 编译方法
编译时设置`--build_extra=OFF` (默认值) 或不指定即可编译出基础预测库。例如:
```
./lite/tools/build.sh --arm_os=android --arm_abi=armv8 --arm_lang=gcc --android_stl=c++_static tiny_publish
```
### 基础预测库支持的功能
(1)支持基础CV模型
(2)支持基础的in8量化模型
(3)支持[benchmark测试](../benchmark/benchmark)
### 基础预测库支持的基础模型:
1. fluid基础模型(paddle model 提供的基础模型9个)
```
mobileNetV1 mnasnet yolov3 ssd_mobilenetv1 shufflenet_v2
mobileNetV2 resnet50 unet squeezenet_v11
```
2. int8量化模型模型
```
mobilenet_v1 mobilenet_v2 resnet50
```
### 特点
轻量级预测库,体积更小,支持常用的基础模型。
## 全量预测库
### 编译方法
编译时设置`--build_extra=ON` 即可编译出全量预测库。例如:
```
./lite/tools/build.sh --arm_os=android --arm_abi=armv8 --arm_lang=gcc --android_stl=c++_static --build_extra=ON tiny_publish
```
### 全量预测库功能
(1) 基础预测库所有功能
(2)支持所有Paddle-Lite中注册的所有算子
### 特点
支持更多的硬件平台和算子,可以支持更多模型但体量更大。
# 源码编译 # 源码编译
Paddle-Lite 提供了移动端的一键源码编译脚本 `lite/tools/build.sh`,编译流程如下:
1. 环境准备(选择其一):Docker交叉编译环境、Linux交叉编译环境
2. 编译:调用`build.sh`脚本一键编译
## 一、环境准备
目前支持三种编译的环境: 目前支持三种编译的环境:
1. Docker 容器环境, 1. Docker 容器环境,
2. Linux(推荐 Ubuntu 16.04)环境, 2. Linux(推荐 Ubuntu 16.04)环境,
3. Mac OS 环境。 3. Mac OS 环境。
### 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(Android编译请使用3.10版本,IOS编译请使用3.15版本)
- 编译Android: Android NDK (建议ndk-r17c)
- 编译IOS: XCode(Version 10.1)
#### 具体步骤
```bash
# 1. Install basic software
brew install curl gcc git make unzip wget
# 2. Install cmake: mac上实现IOS编译和Android编译要求的cmake版本不一致,可以根据需求选择安装。
# (1)在mac环境编译 Paddle-Lite 的Android版本,需要安装cmake 3.10
# mkdir /usr/local/Cellar/cmake/ && cd /usr/local/Cellar/cmake/
# wget https://cmake.org/files/v3.10/cmake-3.10.2-Darwin-x86_64.tar.gz
# tar zxf ./cmake-3.10.2-Darwin-x86_64.tar.gz
# mv cmake-3.10.2-Darwin-x86_64/CMake.app/Contents/ ./3.10.2
# ln -s /usr/local/Cellar/cmake/3.10.2/bin/cmake /usr/local/bin/cmake
# (2)在mac环境编译 Paddle-Lite 的IOS版本,需要安装cmake 3.15
# mkdir /usr/local/Cellar/cmake/ && cd /usr/local/Cellar/cmake/
# cd /usr/local/Cellar/cmake/
# wget https://cmake.org/files/v3.15/cmake-3.15.2-Darwin-x86_64.tar.gz
# tar zxf ./cmake-3.15.2-Darwin-x86_64.tar.gz
# mv cmake-3.15.2-Darwin-x86_64/CMake.app/Contents/ ./3.15.2
# ln -s /usr/local/Cellar/cmake/3.15.2/bin/cmake /usr/local/bin/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
# 6. 编译IOS需要安装XCode(Version 10.1),可以在App Store里安装。安装后需要启动一次并执行下面语句。
# sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
```
至此,完成 Mac 交叉编译环境的准备。
**注意**: Mac上编译Paddle-Lite的full_publish版本时,Paddle-Lite所在路径中不可以含有中文字符
## 二、编译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`(仅`armlinux`支持) |
| --arm_lang |arm_os=android时必选,选择编译器 | `gcc``clang`(`clang`当前暂不支持) |
| --android_stl |arm_os=android时必选,选择静态链接STL或动态链接STL | `c++_static``c++_shared`|
| --build_java | 可选,是否编译java预测库(默认为OFF) | `ON``OFF` |
| --build_extra | 可选,是否编译全量预测库(默认为OFF)。详情可参考[预测库说明](./library.html)。 | `ON``OFF` |
| target |必选,选择编译模式,`tiny_publish`为编译移动端部署库、`full_publish`为带依赖的移动端部署库、`test`为移动端单元测试、`ios`为编译ios端`tiny_publish` | `tiny_publish``full_publish``test``ios` |
### 编译代码
**<font color="orange" >注意</font>**<font color="orange" >:非开发者建议在编译前使用</font>[**“加速第三方依赖库的下载”**](#id22)<font color="orange" >的方法,加速工程中第三方依赖库的下载与编译。 </font>
#### 编译`tiny publish`动态库
##### Android
```shell
./lite/tools/build.sh \
--arm_os=android \
--arm_abi=armv8 \
--build_extra=OFF \
--arm_lang=gcc \
--android_stl=c++_static \
--build_extra=OFF \
tiny_publish
```
##### IOS
```shell
./lite/tools/build.sh \
--arm_os=ios64 \
--arm_abi=armv8 \
--build_extra=OFF \
ios
```
**注意:mac环境编译IOS 时,cmake版本需要高于cmake 3.15;mac环境上编译Android时,cmake版本需要设置为cmake 3.10。**
ios tiny publish支持的编译选项:
* `--arm_os`: 可选ios或者ios64
* `--arm_abi`: 可选armv7和armv8(**注意**:当`arm_os=ios`时只能选择`arm_abi=armv7`,当`arm_os=ios64`时只能选择`arm_abi=armv8`
* 如果mac编译过程中报错:"Invalid CMAKE_DEVELOPER_ROOT: does not exist", 运行:
```shell
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
```
##### ARMLinux
```shell
./lite/tools/build.sh \
--build_extra=OFF \
--arm_os=armlinux \
--arm_abi=armv7hf \
--arm_lang=gcc \
--build_extra=OFF \
tiny_publish
```
- `--arm_abi`: 树莓派3b使用armv7hf,RK3399使用armv8
#### 编译`full publish`动态库
##### Android
```shell
./lite/tools/build.sh \
--arm_os=android \
--arm_abi=armv8 \
--build_extra=OFF \
--arm_lang=gcc \
--android_stl=c++_static \
--build_extra=OFF \
full_publish
```
##### ARMLinux
```shell
./lite/tools/build.sh \
--arm_os=armlinux \
--arm_abi=armv7hf \
--arm_lang=gcc \
--build_extra=OFF \
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`,改为下载第三方压缩包。
# 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
# 如果使用python接口,需要打开build_python选项
./lite/tools/build.sh --build_python=ON 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
tar -zxf 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的支持还在持续开发中。
# 裁剪预测库方法
Paddle-Lite支持**根据模型裁剪预测库**功能。Paddle-Lite的一般编译会将所有已注册的operator打包到预测库中,造成库文件体积膨胀;**裁剪预测库**能针对具体的模型,只打包优化后该模型需要的operator,有效降低预测库文件大小。
## 效果展示(Tiny_publish Android动态预测库体积)
| 测试模型 | 裁剪开关 | **libpaddle_lite_jni.so** |转化后模型中的OP|
| ------------------ | ---------------------------- | -------- |------------------|
| mobilenetv1(armv8) | 裁剪前--build_tailor=OFF | 1.5M | feed,etch,conv2d,depthwise_conv2d,fc,fpool2d,softmax |
| mobilenetv1(armv8) | 裁剪后--build_tailor=ON | 788K |feed,etch,conv2d,depthwise_conv2d,fc,fpool2d,softmax|
| mobilenetv2(armv8) | 裁剪前--build_tailor=OFF | 1.5M | feed,fetch,conv2d,depthwise_conv2d,elementwise_add,fc,pool2d,relu6,softmax |
| mobilenetv2(armv8) | 裁剪后--build_tailor=ON | 912K |feed,fetch,conv2d,depthwise_conv2d,elementwise_add,fc,pool2d,relu6,softmax|
| mobilenetv1(armv7) | 裁剪前--build_tailor=OFF | 938K |feed,fetch,concat,conv2d,dropout,fc,pool2d,softmax|
| mobilenetv1(armv7) | 裁剪后--build_tailor=ON | 607K |feed,fetch,concat,conv2d,dropout,fc,pool2d,softmax|
| mobilenetv2(armv7) | 裁剪前--build_tailor=OFF | 938K | feed,fetch,conv2d,depthwise_conv2d,elementwise_add,fc,pool2d,relu6,softmax |
| mobilenetv2(armv7) | 裁剪后--build_tailor=ON |687K |feed,fetch,conv2d,depthwise_conv2d,elementwise_add,fc,pool2d,relu6,softmax|
## 实现过程:
### 1、转化模型时记录优化后模型信息
说明:使用model_optimize_tool转化模型时,选择 `--record_tailoring_info =true` 会将优化后模型的OP和kernel信息保存到输出文件夹,这些信息将用于编译裁剪后的动态库。
注意:需要使用Paddle-Lite 最新版本(release/v2.0.0之后)代码编译出的model_optimize_tool
例如:
```bash
./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=` 指定优化后的模型的地址
例如:
```bash
./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步得到的转化模型的输出路径
**效果**:编译出来的动态库文件变小,且可以运行优化后的模型。
编译出的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`。 模型转化方法参考:[使用opt转化模型](./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`,转化流程参考:[使用opt转化模型](./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`,转化流程参考:[使用opt转化模型](./model_optimize_tool))。
## 按模型集合裁剪预测库
为了方便用户使用,我们同时提供了按模型集合进行预测库裁剪的功能。用户可以提供一个模型集合,Model Optimize Tool会根据用户所指定的模型集合分析其**优化后的**模型所需要的算子信息对预测库进行裁剪。使用此功能用户根据自己的需要使用模型集合来对预测库中的算子进行任意裁剪。
使用方法如下所示:
```shell
# 非combined模型集合
./model_optimize_tool \
--model_set_dir=<your_model_set_dir> \
--optimize_out_type=naive_buffer \
--optimize_out=<output_model_set_dir> \
--record_tailoring_info=true \
--valid_targets=arm
# combined模型集合
./model_optimize_tool \
--model_set_dir=<your_model_set_dir> \
--optimize_out_type=naive_buffer \
--model_filename=<model_topo_filename> \
--param_filename=<model_param_filename> \
--optimize_out=<output_model_set_dir> \
--record_tailoring_info=true \
--valid_targets=arm
```
经过以上步骤后会在`<output_model_set_dir>`中生成模型集合中各模型对应的NaiveBuffer格式的优化模型。此步会对模型集合中所需算子信息进行搜集并存储到`<output_model_set_dir>`中。下一步编译预测库的流程与使用单模型进行预测库裁剪步骤相同。
**注意:**
1. 模型集合**必须**均为combined参数模型或均为非combined参数模型。
2. 使用非combined参数模型时,模型拓扑文件名应为`__model__`,使用非combined参数模型时,集合中各模型的拓扑与参数名应相同,分别由`--model_filename``--param_filename`指定。
3. 模型集合**必须**均为INT8量化模型或均为非INT8量化模型。
4. 需要使用Paddle-Lite 最新版本(release/v2.1.0之后)代码编译出的model_optimize_tool。
# 模型转化方法
Lite架构在预测过程中表现出来的高性能得益于其丰富的优化组件,其中包括量化、子图融合、混合调度、Kernel优选等等策略。为了使优化过程更加方便易用,我们提供了**opt**来自动完成优化步骤,输出一个轻量的、最优的可执行模型。具体使用方法介绍如下:
**注意**:release/v2.2.0之前的模型转化工具名称为`model_optimize_tool`,从release/v2.3开始模型转化工具名称修改为`opt`
## 准备opt
当前获得opt方法有三种:
1. 我们提供当前develop分支编译结果下载:[opt](https://paddlelite-data.bj.bcebos.com/model_optimize_tool/opt)[opt_mac](https://paddlelite-data.bj.bcebos.com/model_optimize_tool/opt_mac)
release/v2.2.0之前版本的model_optimize_tool: [model_optimize_tool](https://paddlelite-data.bj.bcebos.com/model_optimize_tool/model_optimize_tool)[model_optimize_tool_mac](https://paddlelite-data.bj.bcebos.com/model_optimize_tool/model_optimize_tool_mac)
2. 可以进入Paddle-Lite Github仓库的[release界面](https://github.com/PaddlePaddle/Paddle-Lite/releases),选择release版本下载对应的转化工具`opt`
(release/v2.2.0之前的转化工具为model_optimize_tool、release/v2.3.0之后为opt)
3. 可以下载Paddle-Lite源码,从源码编译出opt工具
```bash
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
cd Paddle-Lite
git checkout <release-version-tag>
./lite/tools/build.sh build_optimize_tool
```
编译结果位于`Paddle-Lite/build.opt/lite/api/opt`
**注意**:从源码编译opt前需要先[安装Paddle-Lite的开发环境](../installation/source_compile)
## 使用opt
opt是x86平台上的可执行文件,需要在PC端运行:包括Linux终端和Mac终端。
### 帮助信息
执行opt时不加入任何输入选项,会输出帮助信息,提示当前支持的选项:
```bash
./opt
```
![](https://paddlelite-data.bj.bcebos.com/doc_images/1.png)
### 功能一:转化模型为Paddle-Lite格式
opt可以将PaddlePaddle支持的模型转化为Paddle-Lite支持的模型格式,期间执行的操作包括:将protobuf格式的模型文件转化为naive_buffer格式的模型文件,有效降低模型体积;执行“量化、子图融合、混合调度、Kernel优选”等图优化操作,提升其在Paddle-Lite上的运行速度、内存占用等性能指标。
模型优化过程:
(1)准备待优化的PaddlePaddle模型
PaddlePaddle模型有两种保存格式:
Combined Param:所有参数信息保存在单个文件`params`中,模型的拓扑信息保存在`__model__`文件中。
![opt_combined_model](https://paddlelite-data.bj.bcebos.com/doc_images%2Fcombined_model.png)
Seperated Param:参数信息分开保存在多个参数文件中,模型的拓扑信息保存在`__model__`文件中。
![opt_seperated_model](https://paddlelite-data.bj.bcebos.com/doc_images%2Fseperated_model.png)
(2) 终端中执行`opt`优化模型
**使用示例**:转化`mobilenet_v1`模型
```
./opt --model_dir=./mobilenet_v1 --valid_targets=arm --optimize_out_type=naive_buffer --optimize_out=mobilenet_v1_opt
```
以上命令可以将`mobilenet_v1`模型转化为arm硬件平台、naive_buffer格式的Paddle_Lite支持模型,优化后的模型文件为`mobilenet_v1_opt.nb`,转化结果如下图所示:
![opt_resulted_model](https://paddlelite-data.bj.bcebos.com/doc_images/2.png)
(3) **更详尽的转化命令**总结:
```shell
./opt \
--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|npu|xpu) \
--prefer_int8_kernel=(true|false) \
--record_tailoring_info =(true|false)
```
| 选项 | 说明 |
| ------------------- | ------------------------------------------------------------ |
| --model_dir | 待优化的PaddlePaddle模型(非combined形式)的路径 |
| --model_file | 待优化的PaddlePaddle模型(combined形式)的网络结构文件路径。 |
| --param_file | 待优化的PaddlePaddle模型(combined形式)的权重文件路径。 |
| --optimize_out_type | 输出模型类型,目前支持两种类型:protobuf和naive_buffer,其中naive_buffer是一种更轻量级的序列化/反序列化实现。若您需要在mobile端执行模型预测,请将此选项设置为naive_buffer。默认为protobuf。 |
| --optimize_out | 优化模型的输出路径。 |
| --valid_targets | 指定模型可执行的backend,默认为arm。目前可支持x86、arm、opencl、npu、xpu,可以同时指定多个backend(以空格分隔),Model Optimize Tool将会自动选择最佳方式。如果需要支持华为NPU(Kirin 810/990 Soc搭载的达芬奇架构NPU),应当设置为npu, arm。 |
| --prefer_int8_kernel | 若待优化模型为int8量化模型(如量化训练得到的量化模型),则设置该选项为true以使用int8内核函数进行推理加速,默认为false。 |
| --record_tailoring_info | 当使用 [根据模型裁剪库文件](./library_tailoring.html) 功能时,则设置该选项为true,以记录优化后模型含有的kernel和OP信息,默认为false。 |
* 如果待优化的fluid模型是非combined形式,请设置`--model_dir`,忽略`--model_file``--param_file`
* 如果待优化的fluid模型是combined形式,请设置`--model_file``--param_file`,忽略`--model_dir`
* 优化后的模型包括__model__.nb和param.nb文件。
### 功能二:统计模型算子信息、判断是否支持
opt可以统计并打印出model中的算子信息、判断Paddle-Lite是否支持该模型。并可以打印出当前Paddle-Lite的算子支持情况。
(1)使用opt统计模型中算子信息
下面命令可以打印出mobilenet_v1模型中包含的所有算子,并判断在硬件平台`valid_targets`下Paddle-Lite是否支持该模型
`./opt --print_model_ops=true --model_dir=mobilenet_v1 --valid_targets=arm`
![opt_print_modelops](https://paddlelite-data.bj.bcebos.com/doc_images/3.png)
(2)使用opt打印当前Paddle-Lite支持的算子信息
`./opt --print_all_ops=true`
以上命令可以打印出当前Paddle-Lite支持的所有算子信息,包括OP的数量和每个OP支持哪些硬件平台:
![opt_print_allops](https://paddlelite-data.bj.bcebos.com/doc_images/4.png)
`./opt ----print_supported_ops=true --valid_targets=x86`
以上命令可以打印出当`valid_targets=x86`时Paddle-Lite支持的所有OP:
![opt_print_supportedops](https://paddlelite-data.bj.bcebos.com/doc_images/5.png)
## 其他功能:合并x2paddle和opt的一键脚本
**背景**:如果想用Paddle-Lite运行第三方来源(tensorflow、caffe、onnx)模型,一般需要经过两次转化。即使用x2paddle工具将第三方模型转化为PaddlePaddle格式,再使用opt将PaddlePaddle模型转化为Padde-Lite可支持格式。
为了简化这一过程,我们提供一键脚本,将x2paddle转化和opt转化合并:
**一键转化脚本**[auto_transform.sh](https://paddlelite-data.bj.bcebos.com/model_optimize_tool/auto_transform.sh)
**环境要求**:使用`auto_transform.sh`脚本转化第三方模型时,需要先安装x2paddle环境,请参考[x2paddle环境安装方法](https://github.com/PaddlePaddle/X2Paddle#环境依赖) 安装x2paddle和其环境依赖项。
**使用方法**
(1)打印帮助帮助信息:` ./auto_transform.sh`
(2)转化模型方法
```bash
USAGE:
auto_transform.sh combines the function of x2paddle and opt, it can
tranform model from tensorflow/caffe/onnx form into paddle-lite naive-buffer form.
----------------------------------------
example:
./auto_transform.sh --framework=tensorflow --model=tf_model.pb --optimize_out=opt_model_result
----------------------------------------
Arguments about x2paddle:
--framework=(tensorflow|caffe|onnx);
--model='model file for tensorflow or onnx';
--prototxt='proto file for caffe' --weight='weight file for caffe'
For TensorFlow:
--framework=tensorflow --model=tf_model.pb
For Caffe:
--framework=caffe --prototxt=deploy.prototxt --weight=deploy.caffemodel
For ONNX
--framework=onnx --model=onnx_model.onnx
Arguments about opt:
--valid_targets=(arm|opencl|x86|npu|xpu); valid targets on Paddle-Lite.
--fluid_save_dir='path to outputed model after x2paddle'
--optimize_out='path to outputed Paddle-Lite model'
----------------------------------------
```
# Lite基于OpenCL的ARM GPU预测
Lite支持在Android系统上运行基于OpenCL的程序,目前支持Ubuntu环境下armv8、armv7的交叉编译。
## 编译
### 编译环境
1. Docker 容器环境;
2. Linux(推荐 Ubuntu 16.04)环境。
详见 **源码编译指南-环境准备** 章节。
### 编译选项
|参数|介绍|值|
|--------|--------|--------|
|--arm_os|代表目标操作系统|目前仅支持且默认为`android`|
|--arm_abi|代表体系结构类型,支持armv8和armv7|默认为`armv8`即arm64-v8a;`armv7`即armeabi-v7a|
|--arm_lang|代表编译目标文件所使用的编译器|默认为gcc,支持 gcc和clang两种|
### 编译Paddle-Lite OpenCL库范例
注:以android-armv8-opencl的目标、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
```
编译产物位于`build.lite.android.armv8.gcc.opencl`下的`inference_lite_lib.android.armv8.opencl`文件夹内,这里仅罗列关键产物:
- `cxx`:该目录是编译目标的C++的头文件和库文件;
- `demo`:该目录包含了两个demo,用来调用使用`libpaddle_api_full_bundled.a``libpaddle_api_light_bundled.a`,分别对应`mobile_full``mobile_light`文件夹。编译对应的demo仅需在`mobile_full``mobile_light`
- `mobile_full`:使用cxx config,可直接加载fluid模型,若使用OpenCL需要在`mobilenetv1_full_api.cc`代码里开启`DEMO_USE_OPENCL`的宏,详细见代码注释;
- `mobile_light`:使用mobile config,只能加载`model_optimize_tool`优化过的模型;
- `opencl`:该目录存放opencl实现的相关kernel。
```bash
.
|-- cxx
| |-- include
| | |-- paddle_api.h
| | |-- paddle_image_preprocess.h
| | |-- paddle_lite_factory_helper.h
| | |-- paddle_place.h
| | |-- paddle_use_kernels.h
| | |-- paddle_use_ops.h
| | `-- paddle_use_passes.h
| `-- lib
| |-- libpaddle_api_full_bundled.a
| |-- libpaddle_api_light_bundled.a
| |-- libpaddle_full_api_shared.so
| `-- libpaddle_light_api_shared.so
|-- demo
| `-- cxx
| |-- Makefile.def
| |-- README.md
| |-- include
| | |-- paddle_api.h
| | |-- paddle_lite_factory_helper.h
| | |-- paddle_place.h
| | |-- paddle_use_kernels.h
| | |-- paddle_use_ops.h
| | `-- paddle_use_passes.h
| |-- mobile_full
| | |-- Makefile
| | `-- mobilenetv1_full_api.cc
| `-- mobile_light
| |-- Makefile
| `-- mobilenetv1_light_api.cc
`-- opencl
`-- cl_kernel
|-- buffer
| |-- depthwise_conv2d_kernel.cl
| |-- elementwise_add_kernel.cl
| |-- fc_kernel.cl
| |-- im2col_kernel.cl
| |-- layout_kernel.cl
| |-- mat_mul_kernel.cl
| |-- pool_kernel.cl
| `-- relu_kernel.cl
|-- cl_common.h
`-- image
|-- channel_add_kernel.cl
|-- elementwise_add_kernel.cl
|-- pool_kernel.cl
`-- relu_kernel.cl
```
调用`libpaddle_api_full_bundled.a``libpaddle_api_light_bundled.a`见下一部分运行示例。
## 运行示例
下面以android、ARMv8、gcc的环境为例,介绍3个示例,分别如何在手机上执行基于OpenCL的ARM GPU推理过程。
**注意:** 以下命令均在Lite源码根目录下运行。在3个示例前,下面这段命令都先要执行用来准备环境:
```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: 编译产物demo示例
```bash
######################################################################
# 编译mobile_full的demo #
######################################################################
# 步骤: #
# 0.确保编译Paddle-Lite时编译了OpenCL; #
# 1.编辑`mobilenetv1_full_api.cc`代码, 开启`DEMO_USE_OPENCL`的宏; #
# 2.在产物目录`demo/cxx/mobile_full`下编译`mobile_full`的demo; #
# 3.上传demo, 模型, opencl kernel文件到手机; #
# 4.运行demo得到预期结果. #
######################################################################
adb shell mkdir /data/local/tmp/opencl/mobilenet_v1
chmod +x ./build.lite.android.armv8.gcc.opencl/inference_lite_lib.android.armv8.opencl/demo/cxx/mobile_full/mobilenetv1_full_api
adb push ./build.lite.android.armv8.gcc.opencl/inference_lite_lib.android.armv8.opencl/demo/cxx/mobile_full/mobilenetv1_full_api /data/local/tmp/opencl/
adb push ./build.lite.android.armv8.gcc.opencl/install/mobilenet_v1/* /data/local/tmp/opencl/mobilenet_v1
# use mobile_full run mobilenet_v1
# `GLOG_v` is log level
adb shell "export GLOG_v=0; \
/data/local/tmp/opencl/mobilenetv1_full_api \
--model_dir=/data/local/tmp/opencl/mobilenet_v1 \
--optimized_model_dir=/data/local/tmp/opencl/full_api_opt_model"
######################################################################
# 编译mobile_light的demo #
######################################################################
# 步骤: #
# 0.确保编译Paddle-Lite时编译了OpenCL; #
# 1.编译model_optimize_tool并对模型优化, `targets`参数为`opencl`; #
# 2.在产物目录`demo/cxx/mobile_light`下编译`mobile_light`的demo; #
# 3.上传demo, 模型, opencl kernel文件到手机; #
# 4.运行demo得到预期结果. #
######################################################################
# use model_optimize_tool to optimize model
./build.model_optimize_tool/lite/api/model_optimize_tool \
--model_dir=./build.lite.android.armv8.gcc.opencl/install/mobilenet_v1/ \
--optimize_out_type=naive_buffer \
--optimize_out=./build.lite.android.armv8.gcc.opencl/install/mobilenet_v1/ \
--valid_targets=opencl
adb shell mkdir /data/local/tmp/opencl/mobilenet_v1
chmod +x ./build.lite.android.armv8.gcc.opencl/inference_lite_lib.android.armv8.opencl/demo/cxx/mobile_light/mobilenetv1_light_api
adb push ./build.lite.android.armv8.gcc.opencl/inference_lite_lib.android.armv8.opencl/demo/cxx/mobile_light/mobilenetv1_light_api /data/local/tmp/opencl/
adb push ./build.lite.android.armv8.gcc.opencl/install/mobilenet_v1/* /data/local/tmp/opencl/mobilenet_v1
# use mobile_light run mobilenet_v1
adb shell "export GLOG_v=5; \
/data/local/tmp/opencl/mobilenetv1_light_api \
--model_dir=/data/local/tmp/opencl/"
```
### 运行示例2: 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值设为多次。
### 运行示例3: 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中使用
见运行示例1的demo代码:
1. [./lite/demo/cxx/mobile_light/mobilenetv1_light_api.cc](https://github.com/PaddlePaddle/Paddle-Lite/blob/develop/lite/demo/cxx/mobile_light/mobilenetv1_light_api.cc);
2. [./lite/demo/cxx/mobile_full/mobilenetv1_full_api.cc](https://github.com/PaddlePaddle/Paddle-Lite/blob/develop/lite/demo/cxx/mobile_full/mobilenetv1_full_api.cc).
注:这里给出的链接会跳转到线上最新develop分支的代码,很可能与您本地的代码存在差异,建议参考自己本地位于`lite/demo/cxx/`目录的代码,查看如何使用。
**NOTE:** 对OpenCL的支持还在持续开发中。
...@@ -232,6 +232,8 @@ if (LITE_WITH_LIGHT_WEIGHT_FRAMEWORK AND LITE_WITH_ARM) ...@@ -232,6 +232,8 @@ if (LITE_WITH_LIGHT_WEIGHT_FRAMEWORK AND LITE_WITH_ARM)
COMMAND cp "${CMAKE_SOURCE_DIR}/lite/demo/cxx/makefiles/mobile_classify/Makefile.${ARM_TARGET_OS}.${ARM_TARGET_ARCH_ABI}" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx/mobile_classify/Makefile" COMMAND cp "${CMAKE_SOURCE_DIR}/lite/demo/cxx/makefiles/mobile_classify/Makefile.${ARM_TARGET_OS}.${ARM_TARGET_ARCH_ABI}" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx/mobile_classify/Makefile"
COMMAND cp -r "${CMAKE_SOURCE_DIR}/lite/demo/cxx/test_cv" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx" COMMAND cp -r "${CMAKE_SOURCE_DIR}/lite/demo/cxx/test_cv" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx"
COMMAND cp "${CMAKE_SOURCE_DIR}/lite/demo/cxx/makefiles/test_cv/Makefile.${ARM_TARGET_OS}.${ARM_TARGET_ARCH_ABI}" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx/test_cv/Makefile" COMMAND cp "${CMAKE_SOURCE_DIR}/lite/demo/cxx/makefiles/test_cv/Makefile.${ARM_TARGET_OS}.${ARM_TARGET_ARCH_ABI}" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx/test_cv/Makefile"
COMMAND cp -r "${CMAKE_SOURCE_DIR}/lite/demo/cxx/mask_detection" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx"
COMMAND cp "${CMAKE_SOURCE_DIR}/lite/demo/cxx/makefiles/mask_detection/Makefile.${ARM_TARGET_OS}.${ARM_TARGET_ARCH_ABI}" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx/mask_detection/Makefile"
) )
add_dependencies(publish_inference_android_cxx_demos logging gflags) add_dependencies(publish_inference_android_cxx_demos logging gflags)
add_dependencies(publish_inference_cxx_lib publish_inference_android_cxx_demos) add_dependencies(publish_inference_cxx_lib publish_inference_android_cxx_demos)
...@@ -251,6 +253,8 @@ if (LITE_WITH_LIGHT_WEIGHT_FRAMEWORK AND LITE_WITH_ARM) ...@@ -251,6 +253,8 @@ if (LITE_WITH_LIGHT_WEIGHT_FRAMEWORK AND LITE_WITH_ARM)
COMMAND cp "${CMAKE_SOURCE_DIR}/lite/demo/cxx/makefiles/mobile_classify/Makefile.${ARM_TARGET_OS}.${ARM_TARGET_ARCH_ABI}" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx/mobile_classify/Makefile" COMMAND cp "${CMAKE_SOURCE_DIR}/lite/demo/cxx/makefiles/mobile_classify/Makefile.${ARM_TARGET_OS}.${ARM_TARGET_ARCH_ABI}" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx/mobile_classify/Makefile"
COMMAND cp -r "${CMAKE_SOURCE_DIR}/lite/demo/cxx/test_cv" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx" COMMAND cp -r "${CMAKE_SOURCE_DIR}/lite/demo/cxx/test_cv" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx"
COMMAND cp "${CMAKE_SOURCE_DIR}/lite/demo/cxx/makefiles/test_cv/Makefile.${ARM_TARGET_OS}.${ARM_TARGET_ARCH_ABI}" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx/test_cv/Makefile" COMMAND cp "${CMAKE_SOURCE_DIR}/lite/demo/cxx/makefiles/test_cv/Makefile.${ARM_TARGET_OS}.${ARM_TARGET_ARCH_ABI}" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx/test_cv/Makefile"
COMMAND cp -r "${CMAKE_SOURCE_DIR}/lite/demo/cxx/mask_detection" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx"
COMMAND cp "${CMAKE_SOURCE_DIR}/lite/demo/cxx/makefiles/mask_detection/Makefile.${ARM_TARGET_OS}.${ARM_TARGET_ARCH_ABI}" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx/mask_detection/Makefile"
) )
add_dependencies(tiny_publish_cxx_lib publish_inference_android_cxx_demos) add_dependencies(tiny_publish_cxx_lib publish_inference_android_cxx_demos)
endif() endif()
......
...@@ -296,10 +296,10 @@ if (LITE_ON_TINY_PUBLISH) ...@@ -296,10 +296,10 @@ if (LITE_ON_TINY_PUBLISH)
endif() endif()
if (LITE_ON_MODEL_OPTIMIZE_TOOL) if (LITE_ON_MODEL_OPTIMIZE_TOOL)
message(STATUS "Compiling model_optimize_tool") message(STATUS "Compiling opt")
lite_cc_binary(model_optimize_tool SRCS model_optimize_tool.cc cxx_api_impl.cc paddle_api.cc cxx_api.cc lite_cc_binary(opt SRCS opt.cc cxx_api_impl.cc paddle_api.cc cxx_api.cc
DEPS gflags kernel op optimizer mir_passes utils) DEPS gflags kernel op optimizer mir_passes utils)
add_dependencies(model_optimize_tool op_list_h kernel_list_h all_kernel_faked_cc supported_kernel_op_info_h) add_dependencies(opt op_list_h kernel_list_h all_kernel_faked_cc supported_kernel_op_info_h)
endif(LITE_ON_MODEL_OPTIMIZE_TOOL) endif(LITE_ON_MODEL_OPTIMIZE_TOOL)
lite_cc_test(test_paddle_api SRCS paddle_api_test.cc DEPS paddle_api_full paddle_api_light lite_cc_test(test_paddle_api SRCS paddle_api_test.cc DEPS paddle_api_full paddle_api_light
......
...@@ -181,6 +181,7 @@ inline MobileConfig jmobileconfig_to_cpp_mobileconfig(JNIEnv *env, ...@@ -181,6 +181,7 @@ inline MobileConfig jmobileconfig_to_cpp_mobileconfig(JNIEnv *env,
MobileConfig config; MobileConfig config;
// set model dir // set model dir
// NOTE: This is a deprecated API and will be removed in latter release.
jmethodID model_dir_method = env->GetMethodID( jmethodID model_dir_method = env->GetMethodID(
mobileconfig_jclazz, "getModelDir", "()Ljava/lang/String;"); mobileconfig_jclazz, "getModelDir", "()Ljava/lang/String;");
jstring java_model_dir = jstring java_model_dir =
...@@ -190,6 +191,27 @@ inline MobileConfig jmobileconfig_to_cpp_mobileconfig(JNIEnv *env, ...@@ -190,6 +191,27 @@ inline MobileConfig jmobileconfig_to_cpp_mobileconfig(JNIEnv *env,
config.set_model_dir(cpp_model_dir); config.set_model_dir(cpp_model_dir);
} }
// set model from file
jmethodID model_file_method = env->GetMethodID(
mobileconfig_jclazz, "getModelFromFile", "()Ljava/lang/String;");
jstring java_model_file =
(jstring)env->CallObjectMethod(jmobileconfig, model_file_method);
if (java_model_file != nullptr) {
std::string cpp_model_file = jstring_to_cpp_string(env, java_model_file);
config.set_model_from_file(cpp_model_file);
}
// set model from buffer
jmethodID model_buffer_method = env->GetMethodID(
mobileconfig_jclazz, "getModelFromBuffer", "()Ljava/lang/String;");
jstring java_model_buffer =
(jstring)env->CallObjectMethod(jmobileconfig, model_buffer_method);
if (java_model_buffer != nullptr) {
std::string cpp_model_buffer =
jstring_to_cpp_string(env, java_model_buffer);
config.set_model_from_buffer(cpp_model_buffer);
}
// set threads // set threads
jmethodID threads_method = jmethodID threads_method =
env->GetMethodID(mobileconfig_jclazz, "getThreads", "()I"); env->GetMethodID(mobileconfig_jclazz, "getThreads", "()I");
......
...@@ -64,6 +64,44 @@ public class MobileConfig extends ConfigBase { ...@@ -64,6 +64,44 @@ public class MobileConfig extends ConfigBase {
return powerMode.value(); return powerMode.value();
} }
/**
* Set model from file.
*
* @return
*/
public void setModelFromFile(String modelFile) {
this.liteModelFile = modelFile;
}
/**
* Returns name of model_file.
*
* @return liteModelFile
*/
public String getModelFile() {
return liteModelFile;
}
/**
* Set model from buffer.
*
* @return
*/
public void setModelFromBuffer(String modelBuffer) {
this.liteModelBuffer = modelBuffer;
}
/**
* Returns model buffer
*
* @return liteModelBuffer
*/
public String getModelBuffer() {
return liteModelBuffer;
}
private PowerMode powerMode = PowerMode.LITE_POWER_HIGH; private PowerMode powerMode = PowerMode.LITE_POWER_HIGH;
private int threads = 1; private int threads = 1;
private String liteModelFile;
private String liteModelBuffer;
} }
...@@ -62,7 +62,7 @@ TEST(CXXApi_LightApi, optim_model) { ...@@ -62,7 +62,7 @@ TEST(CXXApi_LightApi, optim_model) {
TEST(CXXApi_LightApi, save_and_load_model) { TEST(CXXApi_LightApi, save_and_load_model) {
lite::Predictor cxx_api; lite::Predictor cxx_api;
lite::LightPredictor light_api(FLAGS_optimized_model); lite::LightPredictor light_api(FLAGS_optimized_model + ".nb", false);
// CXXAPi // CXXAPi
{ {
......
...@@ -116,7 +116,7 @@ void Run(const std::vector<std::vector<int64_t>>& input_shapes, ...@@ -116,7 +116,7 @@ void Run(const std::vector<std::vector<int64_t>>& input_shapes,
lite_api::MobileConfig config; lite_api::MobileConfig config;
config.set_threads(FLAGS_threads); config.set_threads(FLAGS_threads);
config.set_power_mode(static_cast<PowerMode>(FLAGS_power_mode)); config.set_power_mode(static_cast<PowerMode>(FLAGS_power_mode));
config.set_model_dir(model_dir); config.set_model_from_file(model_dir + ".nb");
auto predictor = lite_api::CreatePaddlePredictor(config); auto predictor = lite_api::CreatePaddlePredictor(config);
......
...@@ -43,6 +43,7 @@ void Predictor::SaveModel(const std::string &dir, ...@@ -43,6 +43,7 @@ void Predictor::SaveModel(const std::string &dir,
LOG(FATAL) << "Unknown model type"; LOG(FATAL) << "Unknown model type";
} }
if (record_info) { if (record_info) {
MkDirRecur(dir);
SaveOpKernelInfo(dir); SaveOpKernelInfo(dir);
} }
} }
...@@ -121,6 +122,7 @@ void Predictor::SaveOpKernelInfo(const std::string &model_dir) { ...@@ -121,6 +122,7 @@ void Predictor::SaveOpKernelInfo(const std::string &model_dir) {
<< kpf_path; << kpf_path;
} }
#ifndef LITE_WITH_FPGA
lite::Tensor *Predictor::GetInput(size_t offset) { lite::Tensor *Predictor::GetInput(size_t offset) {
CHECK(input_names_.size() > offset) CHECK(input_names_.size() > offset)
<< "The network has " << input_names_.size() << " inputs" << "The network has " << input_names_.size() << " inputs"
...@@ -130,6 +132,17 @@ lite::Tensor *Predictor::GetInput(size_t offset) { ...@@ -130,6 +132,17 @@ lite::Tensor *Predictor::GetInput(size_t offset) {
<< " in exec_scope"; << " in exec_scope";
return in_var->GetMutable<lite::Tensor>(); return in_var->GetMutable<lite::Tensor>();
} }
#else
lite::Tensor *Predictor::GetInput(size_t offset) {
auto *_feed_list = exec_scope_->FindVar("feed");
CHECK(_feed_list) << "no feed variable in exec_scope";
auto *feed_list = _feed_list->GetMutable<std::vector<lite::Tensor>>();
if (offset >= feed_list->size()) {
feed_list->resize(offset + 1);
}
return &feed_list->at(offset);
}
#endif
// get inputs names // get inputs names
std::vector<std::string> Predictor::GetInputNames() { return input_names_; } std::vector<std::string> Predictor::GetInputNames() { return input_names_; }
...@@ -167,6 +180,8 @@ void Predictor::PrepareFeedFetch() { ...@@ -167,6 +180,8 @@ void Predictor::PrepareFeedFetch() {
} }
} }
#ifndef LITE_WITH_FPGA
const lite::Tensor *Predictor::GetOutput(size_t offset) const { const lite::Tensor *Predictor::GetOutput(size_t offset) const {
CHECK(output_names_.size() > offset) CHECK(output_names_.size() > offset)
<< "The network has " << output_names_.size() << " outputs" << "The network has " << output_names_.size() << " outputs"
...@@ -186,6 +201,29 @@ std::vector<const lite::Tensor *> Predictor::GetOutputs() const { ...@@ -186,6 +201,29 @@ std::vector<const lite::Tensor *> Predictor::GetOutputs() const {
} }
return outputs; return outputs;
} }
#else
const lite::Tensor *Predictor::GetOutput(size_t offset) const {
auto *_fetch_list = exec_scope_->FindVar("fetch");
CHECK(_fetch_list) << "no fatch variable in exec_scope";
auto &fetch_list = *_fetch_list->GetMutable<std::vector<lite::Tensor>>();
CHECK_LT(offset, fetch_list.size()) << "offset " << offset << " overflow";
return &fetch_list.at(offset);
}
std::vector<const lite::Tensor *> Predictor::GetOutputs() const {
auto *_fetch_list = exec_scope_->FindVar("fetch");
CHECK(_fetch_list) << "no fatch variable in exec_scope";
auto &fetch_list = *_fetch_list->GetMutable<std::vector<lite::Tensor>>();
std::vector<const lite::Tensor *> outputs;
for (auto out : fetch_list) {
outputs.push_back(&out);
}
return outputs;
}
#endif
const cpp::ProgramDesc &Predictor::program_desc() const { const cpp::ProgramDesc &Predictor::program_desc() const {
return program_desc_; return program_desc_;
...@@ -239,7 +277,7 @@ void Predictor::Build(const std::string &model_path, ...@@ -239,7 +277,7 @@ void Predictor::Build(const std::string &model_path,
case lite_api::LiteModelType::kNaiveBuffer: case lite_api::LiteModelType::kNaiveBuffer:
CHECK(!model_path.empty()) CHECK(!model_path.empty())
<< "NaiveBuffer backend only supported combined param"; << "NaiveBuffer backend only supported combined param";
LoadModelNaive(model_path, scope_.get(), &program_desc_); LoadModelNaiveFromFile(model_path, scope_.get(), &program_desc_);
break; break;
default: default:
LOG(FATAL) << "Unknown model type"; LOG(FATAL) << "Unknown model type";
......
...@@ -101,7 +101,7 @@ TEST(CXXApi, save_model) { ...@@ -101,7 +101,7 @@ TEST(CXXApi, save_model) {
TEST(CXXApi, load_model_naive) { TEST(CXXApi, load_model_naive) {
lite::Predictor predictor; lite::Predictor predictor;
std::vector<Place> valid_places({Place{TARGET(kARM), PRECISION(kFloat)}}); std::vector<Place> valid_places({Place{TARGET(kARM), PRECISION(kFloat)}});
predictor.Build(FLAGS_optimized_model + ".naive", predictor.Build(FLAGS_optimized_model + ".naive.nb",
"", "",
"", "",
valid_places, valid_places,
......
...@@ -18,6 +18,17 @@ ...@@ -18,6 +18,17 @@
namespace paddle { namespace paddle {
namespace lite { namespace lite {
void LightPredictor::Build(const std::string& lite_model_file,
bool model_from_memory) {
if (model_from_memory) {
LoadModelNaiveFromMemory(lite_model_file, scope_.get(), &cpp_program_desc_);
} else {
LoadModelNaiveFromFile(lite_model_file, scope_.get(), &cpp_program_desc_);
}
BuildRuntimeProgram(cpp_program_desc_);
PrepareFeedFetch();
}
void LightPredictor::Build(const std::string& model_dir, void LightPredictor::Build(const std::string& model_dir,
const std::string& model_buffer, const std::string& model_buffer,
const std::string& param_buffer, const std::string& param_buffer,
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
*/ */
#pragma once #pragma once
#include <algorithm>
#include <map> #include <map>
#include <memory> #include <memory>
#include <string> #include <string>
...@@ -39,12 +40,22 @@ namespace lite { ...@@ -39,12 +40,22 @@ namespace lite {
*/ */
class LITE_API LightPredictor { class LITE_API LightPredictor {
public: public:
LightPredictor( // constructor function of LightPredictor, `lite_model_file` refers to data in
const std::string& model_dir, // model file or buffer,`model_from_memory` refers to whther to load model
const std::string& model_buffer = "", // from memory.
const std::string& param_buffer = "", LightPredictor(const std::string& lite_model_file,
bool model_from_memory = false, bool model_from_memory = false) {
lite_api::LiteModelType model_type = lite_api::LiteModelType::kProtobuf) { scope_ = std::make_shared<Scope>();
Build(lite_model_file, model_from_memory);
}
// NOTE: This is a deprecated API and will be removed in latter release.
LightPredictor(const std::string& model_dir,
const std::string& model_buffer = "",
const std::string& param_buffer = "",
bool model_from_memory = false,
lite_api::LiteModelType model_type =
lite_api::LiteModelType::kNaiveBuffer) {
scope_ = std::make_shared<Scope>(); scope_ = std::make_shared<Scope>();
Build(model_dir, model_buffer, param_buffer, model_type, model_from_memory); Build(model_dir, model_buffer, param_buffer, model_type, model_from_memory);
} }
...@@ -69,6 +80,10 @@ class LITE_API LightPredictor { ...@@ -69,6 +80,10 @@ class LITE_API LightPredictor {
void PrepareFeedFetch(); void PrepareFeedFetch();
private: private:
void Build(const std::string& lite_model_file,
bool model_from_memory = false);
// NOTE: This is a deprecated API and will be removed in latter release.
void Build( void Build(
const std::string& model_dir, const std::string& model_dir,
const std::string& model_buffer, const std::string& model_buffer,
......
...@@ -23,13 +23,17 @@ namespace lite { ...@@ -23,13 +23,17 @@ namespace lite {
void LightPredictorImpl::Init(const lite_api::MobileConfig& config) { void LightPredictorImpl::Init(const lite_api::MobileConfig& config) {
// LightPredictor Only support NaiveBuffer backend in publish lib // LightPredictor Only support NaiveBuffer backend in publish lib
raw_predictor_.reset( if (config.lite_model_file().empty()) {
new LightPredictor(config.model_dir(), raw_predictor_.reset(
config.model_buffer(), new LightPredictor(config.model_dir(),
config.param_buffer(), config.model_buffer(),
config.model_from_memory(), config.param_buffer(),
lite_api::LiteModelType::kNaiveBuffer)); config.model_from_memory(),
lite_api::LiteModelType::kNaiveBuffer));
} else {
raw_predictor_.reset(new LightPredictor(config.lite_model_file(),
config.model_from_memory()));
}
mode_ = config.power_mode(); mode_ = config.power_mode();
threads_ = config.threads(); threads_ = config.threads();
} }
......
...@@ -23,6 +23,10 @@ ...@@ -23,6 +23,10 @@
#include "lite/core/op_registry.h" #include "lite/core/op_registry.h"
DEFINE_string(optimized_model, "", "optimized_model"); DEFINE_string(optimized_model, "", "optimized_model");
DEFINE_int32(N, 1, "input_batch");
DEFINE_int32(C, 3, "input_channel");
DEFINE_int32(H, 224, "input_height");
DEFINE_int32(W, 224, "input_width");
namespace paddle { namespace paddle {
namespace lite { namespace lite {
...@@ -37,7 +41,8 @@ void TestModel(const std::vector<Place>& valid_places, ...@@ -37,7 +41,8 @@ void TestModel(const std::vector<Place>& valid_places,
predictor.Build(model_dir, "", "", valid_places); predictor.Build(model_dir, "", "", valid_places);
auto* input_tensor = predictor.GetInput(0); auto* input_tensor = predictor.GetInput(0);
input_tensor->Resize(DDim(std::vector<DDim::value_type>({1, 3, 224, 224}))); input_tensor->Resize(DDim(
std::vector<DDim::value_type>({FLAGS_N, FLAGS_C, FLAGS_H, FLAGS_W})));
auto* data = input_tensor->mutable_data<float>(); auto* data = input_tensor->mutable_data<float>();
auto item_size = input_tensor->dims().production(); auto item_size = input_tensor->dims().production();
for (int i = 0; i < item_size; i++) { for (int i = 0; i < item_size; i++) {
...@@ -58,6 +63,8 @@ void TestModel(const std::vector<Place>& valid_places, ...@@ -58,6 +63,8 @@ void TestModel(const std::vector<Place>& valid_places,
predictor.SaveModel(FLAGS_optimized_model); predictor.SaveModel(FLAGS_optimized_model);
} }
LOG(INFO) << "input shape(NCHW):" << FLAGS_N << " " << FLAGS_C << " "
<< FLAGS_H << " " << FLAGS_W;
LOG(INFO) << "================== Speed Report ==================="; LOG(INFO) << "================== Speed Report ===================";
LOG(INFO) << "Model: " << model_dir << ", threads num " << FLAGS_threads LOG(INFO) << "Model: " << model_dir << ", threads num " << FLAGS_threads
<< ", warmup: " << FLAGS_warmup << ", repeats: " << FLAGS_repeats << ", warmup: " << FLAGS_warmup << ", repeats: " << FLAGS_repeats
...@@ -123,10 +130,10 @@ TEST(MobileNetV1, test_arm) { ...@@ -123,10 +130,10 @@ TEST(MobileNetV1, test_arm) {
#ifdef LITE_WITH_OPENCL #ifdef LITE_WITH_OPENCL
TEST(MobileNetV1, test_opencl) { TEST(MobileNetV1, test_opencl) {
std::vector<Place> valid_places({ std::vector<Place> valid_places({
Place{TARGET(kOpenCL), PRECISION(kFP16), DATALAYOUT(kNCHW)}, Place{TARGET(kOpenCL), PRECISION(kFloat), DATALAYOUT(kImageDefault)},
Place{TARGET(kOpenCL), PRECISION(kFP16), DATALAYOUT(kNHWC)},
Place{TARGET(kOpenCL), PRECISION(kFloat), DATALAYOUT(kNCHW)}, Place{TARGET(kOpenCL), PRECISION(kFloat), DATALAYOUT(kNCHW)},
Place{TARGET(kOpenCL), PRECISION(kFloat), DATALAYOUT(kNHWC)}, Place{TARGET(kOpenCL), PRECISION(kAny), DATALAYOUT(kImageDefault)},
Place{TARGET(kOpenCL), PRECISION(kAny), DATALAYOUT(kNCHW)},
TARGET(kARM), // enable kARM CPU kernel when no opencl kernel TARGET(kARM), // enable kARM CPU kernel when no opencl kernel
}); });
......
...@@ -23,6 +23,10 @@ ...@@ -23,6 +23,10 @@
#include "lite/core/op_registry.h" #include "lite/core/op_registry.h"
DEFINE_string(optimized_model, "", "optimized_model"); DEFINE_string(optimized_model, "", "optimized_model");
DEFINE_int32(N, 1, "input_batch");
DEFINE_int32(C, 3, "input_channel");
DEFINE_int32(H, 224, "input_height");
DEFINE_int32(W, 224, "input_width");
namespace paddle { namespace paddle {
namespace lite { namespace lite {
...@@ -38,7 +42,8 @@ void TestModel(const std::vector<Place>& valid_places, ...@@ -38,7 +42,8 @@ void TestModel(const std::vector<Place>& valid_places,
predictor.Build(model_dir, "", "", valid_places); predictor.Build(model_dir, "", "", valid_places);
auto* input_tensor = predictor.GetInput(0); auto* input_tensor = predictor.GetInput(0);
input_tensor->Resize(DDim(std::vector<DDim::value_type>({1, 3, 224, 224}))); input_tensor->Resize(DDim(
std::vector<DDim::value_type>({FLAGS_N, FLAGS_C, FLAGS_H, FLAGS_W})));
auto* data = input_tensor->mutable_data<float>(); auto* data = input_tensor->mutable_data<float>();
auto item_size = input_tensor->dims().production(); auto item_size = input_tensor->dims().production();
for (int i = 0; i < item_size; i++) { for (int i = 0; i < item_size; i++) {
...@@ -59,6 +64,8 @@ void TestModel(const std::vector<Place>& valid_places, ...@@ -59,6 +64,8 @@ void TestModel(const std::vector<Place>& valid_places,
predictor.SaveModel(FLAGS_optimized_model); predictor.SaveModel(FLAGS_optimized_model);
} }
LOG(INFO) << "input shape(NCHW):" << FLAGS_N << " " << FLAGS_C << " "
<< FLAGS_H << " " << FLAGS_W;
LOG(INFO) << "================== Speed Report ==================="; LOG(INFO) << "================== Speed Report ===================";
LOG(INFO) << "Model: " << model_dir << ", threads num " << FLAGS_threads LOG(INFO) << "Model: " << model_dir << ", threads num " << FLAGS_threads
<< ", warmup: " << FLAGS_warmup << ", repeats: " << FLAGS_repeats << ", warmup: " << FLAGS_warmup << ", repeats: " << FLAGS_repeats
...@@ -123,8 +130,11 @@ TEST(MobileNetV2, test_arm) { ...@@ -123,8 +130,11 @@ TEST(MobileNetV2, test_arm) {
#ifdef LITE_WITH_OPENCL #ifdef LITE_WITH_OPENCL
TEST(MobileNetV2, test_opencl) { TEST(MobileNetV2, test_opencl) {
std::vector<Place> valid_places({ std::vector<Place> valid_places({
Place{TARGET(kOpenCL), PRECISION(kFloat)}, Place{TARGET(kOpenCL), PRECISION(kFloat), DATALAYOUT(kImageDefault)},
Place{TARGET(kARM), PRECISION(kFloat)}, Place{TARGET(kOpenCL), PRECISION(kFloat), DATALAYOUT(kNCHW)},
Place{TARGET(kOpenCL), PRECISION(kAny), DATALAYOUT(kImageDefault)},
Place{TARGET(kOpenCL), PRECISION(kAny), DATALAYOUT(kNCHW)},
TARGET(kARM), // enable kARM CPU kernel when no opencl kernel
}); });
TestModel(valid_places); TestModel(valid_places);
......
...@@ -73,7 +73,7 @@ void Run(const std::vector<std::vector<int64_t>>& input_shapes, ...@@ -73,7 +73,7 @@ void Run(const std::vector<std::vector<int64_t>>& input_shapes,
const int repeat, const int repeat,
const int warmup_times = 0) { const int warmup_times = 0) {
lite_api::MobileConfig config; lite_api::MobileConfig config;
config.set_model_dir(model_dir); config.set_model_from_file(model_dir + ".nb");
config.set_power_mode(power_mode); config.set_power_mode(power_mode);
config.set_threads(thread_num); config.set_threads(thread_num);
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#endif #endif
// "supported_kernel_op_info.h", "all_kernel_faked.cc" and "kernel_src_map.h" // "supported_kernel_op_info.h", "all_kernel_faked.cc" and "kernel_src_map.h"
// are created automatically during model_optimize_tool's compiling period // are created automatically during opt's compiling period
#include <iomanip> #include <iomanip>
#include "all_kernel_faked.cc" // NOLINT #include "all_kernel_faked.cc" // NOLINT
#include "kernel_src_map.h" // NOLINT #include "kernel_src_map.h" // NOLINT
...@@ -26,9 +26,11 @@ ...@@ -26,9 +26,11 @@
#include "lite/api/paddle_use_ops.h" #include "lite/api/paddle_use_ops.h"
#include "lite/api/paddle_use_passes.h" #include "lite/api/paddle_use_passes.h"
#include "lite/core/op_registry.h" #include "lite/core/op_registry.h"
#include "lite/core/version.h"
#include "lite/model_parser/compatible_pb.h" #include "lite/model_parser/compatible_pb.h"
#include "lite/model_parser/pb/program_desc.h" #include "lite/model_parser/pb/program_desc.h"
#include "lite/utils/cp_logging.h" #include "lite/utils/cp_logging.h"
#include "lite/utils/io.h"
#include "lite/utils/string.h" #include "lite/utils/string.h"
#include "supported_kernel_op_info.h" // NOLINT #include "supported_kernel_op_info.h" // NOLINT
...@@ -89,13 +91,13 @@ std::vector<Place> ParserValidPlaces() { ...@@ -89,13 +91,13 @@ std::vector<Place> ParserValidPlaces() {
valid_places.emplace_back(TARGET(kARM)); valid_places.emplace_back(TARGET(kARM));
} else if (target_repr == "opencl") { } else if (target_repr == "opencl") {
valid_places.emplace_back( valid_places.emplace_back(
Place{TARGET(kOpenCL), PRECISION(kFP16), DATALAYOUT(kNCHW)}); Place{TARGET(kOpenCL), PRECISION(kFloat), DATALAYOUT(kImageDefault)});
valid_places.emplace_back(
Place{TARGET(kOpenCL), PRECISION(kFP16), DATALAYOUT(kNHWC)});
valid_places.emplace_back( valid_places.emplace_back(
Place{TARGET(kOpenCL), PRECISION(kFloat), DATALAYOUT(kNCHW)}); Place{TARGET(kOpenCL), PRECISION(kFloat), DATALAYOUT(kNCHW)});
valid_places.emplace_back( valid_places.emplace_back(
Place{TARGET(kOpenCL), PRECISION(kFloat), DATALAYOUT(kNHWC)}); Place{TARGET(kOpenCL), PRECISION(kAny), DATALAYOUT(kImageDefault)});
valid_places.emplace_back(
Place{TARGET(kOpenCL), PRECISION(kAny), DATALAYOUT(kNCHW)});
valid_places.emplace_back( valid_places.emplace_back(
TARGET(kARM)); // enable kARM CPU kernel when no opencl kernel TARGET(kARM)); // enable kARM CPU kernel when no opencl kernel
} else if (target_repr == "x86") { } else if (target_repr == "x86") {
...@@ -239,6 +241,7 @@ void PrintOpsInfo(std::set<std::string> valid_ops = {}) { ...@@ -239,6 +241,7 @@ void PrintOpsInfo(std::set<std::string> valid_ops = {}) {
/// Print help information /// Print help information
void PrintHelpInfo() { void PrintHelpInfo() {
// at least one argument should be inputed // at least one argument should be inputed
const std::string opt_version = lite::version();
const char help_info[] = const char help_info[] =
"At least one argument should be inputed. Valid arguments are listed " "At least one argument should be inputed. Valid arguments are listed "
"below:\n" "below:\n"
...@@ -260,7 +263,8 @@ void PrintHelpInfo() { ...@@ -260,7 +263,8 @@ void PrintHelpInfo() {
" `--print_model_ops=true --model_dir=<model_param_dir> " " `--print_model_ops=true --model_dir=<model_param_dir> "
"--valid_targets=(arm|opencl|x86|npu|xpu)`" "--valid_targets=(arm|opencl|x86|npu|xpu)`"
" Display operators in the input model\n"; " Display operators in the input model\n";
std::cout << help_info << std::endl; std::cout << "opt version:" << opt_version << std::endl
<< help_info << std::endl;
exit(1); exit(1);
} }
...@@ -397,6 +401,7 @@ void Main() { ...@@ -397,6 +401,7 @@ void Main() {
return; return;
} }
lite::MkDirRecur(FLAGS_optimize_out);
auto model_dirs = lite::ListDir(FLAGS_model_set_dir, true); auto model_dirs = lite::ListDir(FLAGS_model_set_dir, true);
if (model_dirs.size() == 0) { if (model_dirs.size() == 0) {
LOG(FATAL) << "[" << FLAGS_model_set_dir << "] does not contain any model"; LOG(FATAL) << "[" << FLAGS_model_set_dir << "] does not contain any model";
...@@ -451,7 +456,9 @@ int main(int argc, char** argv) { ...@@ -451,7 +456,9 @@ int main(int argc, char** argv) {
} }
google::ParseCommandLineFlags(&argc, &argv, false); google::ParseCommandLineFlags(&argc, &argv, false);
paddle::lite_api::ParseInputCommand(); paddle::lite_api::ParseInputCommand();
paddle::lite_api::CheckIfModelSupported(); if (FLAGS_model_set_dir == "") {
paddle::lite_api::CheckIfModelSupported();
}
paddle::lite_api::Main(); paddle::lite_api::Main();
return 0; return 0;
} }
...@@ -190,5 +190,27 @@ void ConfigBase::set_threads(int threads) { ...@@ -190,5 +190,27 @@ void ConfigBase::set_threads(int threads) {
#endif #endif
} }
// set model data in combined format, `set_model_from_file` refers to loading
// model from file, set_model_from_buffer refers to loading model from memory
// buffer
void MobileConfig::set_model_from_file(const std::string &x) {
lite_model_file_ = x;
}
void MobileConfig::set_model_from_buffer(const std::string &x) {
lite_model_file_ = x;
model_from_memory_ = true;
}
void MobileConfig::set_model_buffer(const char *model_buffer,
size_t model_buffer_size,
const char *param_buffer,
size_t param_buffer_size) {
LOG(WARNING) << "warning: `set_model_buffer` will be abandened in "
"release/v3.0.0, new method `set_model_from_buffer(const "
"std::string &x)` is recommended.";
model_buffer_ = std::string(model_buffer, model_buffer + model_buffer_size);
param_buffer_ = std::string(param_buffer, param_buffer + param_buffer_size);
model_from_memory_ = true;
}
} // namespace lite_api } // namespace lite_api
} // namespace paddle } // namespace paddle
...@@ -168,22 +168,40 @@ class LITE_API CxxConfig : public ConfigBase { ...@@ -168,22 +168,40 @@ class LITE_API CxxConfig : public ConfigBase {
/// MobileConfig is the config for the light weight predictor, it will skip /// MobileConfig is the config for the light weight predictor, it will skip
/// IR optimization or other unnecessary stages. /// IR optimization or other unnecessary stages.
class LITE_API MobileConfig : public ConfigBase { class LITE_API MobileConfig : public ConfigBase {
// whether to load data from memory. Model data will be loaded from memory
// buffer if model_from_memory_ is true.
bool model_from_memory_{false};
// model data readed from file or memory buffer in combined format.
std::string lite_model_file_;
// NOTE: This is a deprecated variable and will be removed in latter release.
std::string model_buffer_; std::string model_buffer_;
std::string param_buffer_; std::string param_buffer_;
bool model_from_memory_{false};
public: public:
// set model data in combined format, `set_model_from_file` refers to loading
// model from file, set_model_from_buffer refers to loading model from memory
// buffer
void set_model_from_file(const std::string& x);
void set_model_from_buffer(const std::string& x);
// return model data in lite_model_file_, which is in combined format.
const std::string& lite_model_file() const { return lite_model_file_; }
// return model_from_memory_, which indicates whether to load model from
// memory buffer.
bool model_from_memory() const { return model_from_memory_; }
// NOTE: This is a deprecated API and will be removed in latter release.
void set_model_buffer(const char* model_buffer, void set_model_buffer(const char* model_buffer,
size_t model_buffer_size, size_t model_buffer_size,
const char* param_buffer, const char* param_buffer,
size_t param_buffer_size) { size_t param_buffer_size);
model_buffer_ = std::string(model_buffer, model_buffer + model_buffer_size);
param_buffer_ = std::string(param_buffer, param_buffer + param_buffer_size);
model_from_memory_ = true;
}
bool model_from_memory() const { return model_from_memory_; } // NOTE: This is a deprecated API and will be removed in latter release.
const std::string& model_buffer() const { return model_buffer_; } const std::string& model_buffer() const { return model_buffer_; }
// NOTE: This is a deprecated API and will be removed in latter release.
const std::string& param_buffer() const { return param_buffer_; } const std::string& param_buffer() const { return param_buffer_; }
}; };
......
...@@ -72,7 +72,7 @@ TEST(CxxApi, run) { ...@@ -72,7 +72,7 @@ TEST(CxxApi, run) {
#ifdef LITE_WITH_LIGHT_WEIGHT_FRAMEWORK #ifdef LITE_WITH_LIGHT_WEIGHT_FRAMEWORK
TEST(LightApi, run) { TEST(LightApi, run) {
lite_api::MobileConfig config; lite_api::MobileConfig config;
config.set_model_dir(FLAGS_model_dir + ".opt2.naive"); config.set_model_from_file(FLAGS_model_dir + ".opt2.naive.nb");
auto predictor = lite_api::CreatePaddlePredictor(config); auto predictor = lite_api::CreatePaddlePredictor(config);
...@@ -109,16 +109,11 @@ TEST(LightApi, run) { ...@@ -109,16 +109,11 @@ TEST(LightApi, run) {
// Demo2 for Loading model from memory // Demo2 for Loading model from memory
TEST(MobileConfig, LoadfromMemory) { TEST(MobileConfig, LoadfromMemory) {
// Get naive buffer // Get naive buffer
auto model_path = std::string(FLAGS_model_dir) + ".opt2.naive/__model__.nb"; auto model_file = std::string(FLAGS_model_dir) + ".opt2.naive.nb";
auto params_path = std::string(FLAGS_model_dir) + ".opt2.naive/param.nb"; std::string model_buffer = lite::ReadFile(model_file);
std::string model_buffer = lite::ReadFile(model_path);
size_t size_model = model_buffer.length();
std::string params_buffer = lite::ReadFile(params_path);
size_t size_params = params_buffer.length();
// set model buffer and run model // set model buffer and run model
lite_api::MobileConfig config; lite_api::MobileConfig config;
config.set_model_buffer( config.set_model_from_buffer(model_buffer);
model_buffer.c_str(), size_model, params_buffer.c_str(), size_params);
auto predictor = lite_api::CreatePaddlePredictor(config); auto predictor = lite_api::CreatePaddlePredictor(config);
auto input_tensor = predictor->GetInput(0); auto input_tensor = predictor->GetInput(0);
......
...@@ -116,6 +116,8 @@ void BindLiteMobileConfig(py::module *m) { ...@@ -116,6 +116,8 @@ void BindLiteMobileConfig(py::module *m) {
py::class_<MobileConfig> mobile_config(*m, "MobileConfig"); py::class_<MobileConfig> mobile_config(*m, "MobileConfig");
mobile_config.def(py::init<>()) mobile_config.def(py::init<>())
.def("set_model_from_file", &MobileConfig::set_model_from_file)
.def("set_model_from_buffer", &MobileConfig::set_model_from_buffer)
.def("set_model_dir", &MobileConfig::set_model_dir) .def("set_model_dir", &MobileConfig::set_model_dir)
.def("model_dir", &MobileConfig::model_dir) .def("model_dir", &MobileConfig::model_dir)
.def("set_model_buffer", &MobileConfig::set_model_buffer) .def("set_model_buffer", &MobileConfig::set_model_buffer)
......
...@@ -31,11 +31,7 @@ TEST(ResNet50, test) { ...@@ -31,11 +31,7 @@ TEST(ResNet50, test) {
std::vector<Place> valid_places( std::vector<Place> valid_places(
{Place{TARGET(kFPGA), PRECISION(kFP16), DATALAYOUT(kNHWC)}}); {Place{TARGET(kFPGA), PRECISION(kFP16), DATALAYOUT(kNHWC)}});
predictor.Build(FLAGS_model_dir, predictor.Build(FLAGS_model_dir, "", "", valid_places);
"",
"",
Place{TARGET(kFPGA), PRECISION(kFP16), DATALAYOUT(kNHWC)},
valid_places);
auto* input_tensor = predictor.GetInput(0); auto* input_tensor = predictor.GetInput(0);
input_tensor->Resize(DDim(std::vector<DDim::value_type>({1, 3, 224, 224}))); input_tensor->Resize(DDim(std::vector<DDim::value_type>({1, 3, 224, 224})));
......
...@@ -78,6 +78,7 @@ if (NOT HAS_ARM_MATH_LIB_DIR) ...@@ -78,6 +78,7 @@ if (NOT HAS_ARM_MATH_LIB_DIR)
conv3x3s2_depthwise_int8.cc conv3x3s2_depthwise_int8.cc
conv5x5s1_depthwise_int8.cc conv5x5s1_depthwise_int8.cc
conv5x5s1_depthwise_fp32.cc conv5x5s1_depthwise_fp32.cc
conv5x5s2_depthwise_int8.cc
conv5x5s2_depthwise_fp32.cc conv5x5s2_depthwise_fp32.cc
conv3x3_winograd_fp32_c4.cc conv3x3_winograd_fp32_c4.cc
conv_winograd_3x3.cc conv_winograd_3x3.cc
......
...@@ -122,8 +122,7 @@ void conv_compute_6x6_3x3(const float* input, ...@@ -122,8 +122,7 @@ void conv_compute_6x6_3x3(const float* input,
// begin compute // begin compute
for (int ni = 0; ni < num; ++ni) { for (int ni = 0; ni < num; ++ni) {
// trans input to c4 // trans input to c4
#pragma omp parallel for num_threads(threads)
for (int i = 0; i < ic_4; ++i) { for (int i = 0; i < ic_4; ++i) {
prepack_input_nxwc4_dw(input + ni * in_n_stride, prepack_input_nxwc4_dw(input + ni * in_n_stride,
input_c4 + i * new_c_stride, input_c4 + i * new_c_stride,
...@@ -410,8 +409,7 @@ void conv_compute_2x2_3x3(const float* input, ...@@ -410,8 +409,7 @@ void conv_compute_2x2_3x3(const float* input,
// begin compute // begin compute
for (int ni = 0; ni < num; ++ni) { for (int ni = 0; ni < num; ++ni) {
// trans input to c4 // trans input to c4
#pragma omp parallel for num_threads(threads)
for (int i = 0; i < ic_4; ++i) { for (int i = 0; i < ic_4; ++i) {
prepack_input_nxwc4_dw(input + ni * in_n_stride, prepack_input_nxwc4_dw(input + ni * in_n_stride,
input_c4 + i * new_c_stride, input_c4 + i * new_c_stride,
...@@ -672,9 +670,8 @@ void conv_compute_2x2_3x3_small(const float* input, ...@@ -672,9 +670,8 @@ void conv_compute_2x2_3x3_small(const float* input,
// begin compute // begin compute
for (int ni = 0; ni < num; ++ni) { for (int ni = 0; ni < num; ++ni) {
// trans input to c4 // trans input to c4
#pragma omp parallel for num_threads(threads)
for (int i = 0; i < ic_4; ++i) { for (int i = 0; i < ic_4; ++i) {
prepack_input_nxwc4_dw(input + ni * in_n_stride, prepack_input_nxwc4_dw(input + ni * in_n_stride,
input_c4 + i * new_c_stride, input_c4 + i * new_c_stride,
......
...@@ -109,7 +109,7 @@ void conv_depthwise_5x5s1_fp32(float* dout, ...@@ -109,7 +109,7 @@ void conv_depthwise_5x5s1_fp32(float* dout,
tmp_din + omp_get_thread_num() * (pre_in_size + pre_out_size); tmp_din + omp_get_thread_num() * (pre_in_size + pre_out_size);
float* pre_out = pre_din + pre_in_size; float* pre_out = pre_din + pre_in_size;
#else #else
float pre_din = tmp_din; float* pre_din = tmp_din;
float* pre_out = pre_din + pre_in_size; float* pre_out = pre_din + pre_in_size;
#endif #endif
prepack_input_nxwc4_dw( prepack_input_nxwc4_dw(
......
此差异已折叠。
...@@ -189,6 +189,24 @@ void conv_depthwise_5x5s1_int8(Dtype* dout, ...@@ -189,6 +189,24 @@ void conv_depthwise_5x5s1_int8(Dtype* dout,
int padh, int padh,
ARMContext* ctx); ARMContext* ctx);
template <typename Dtype>
void conv_depthwise_5x5s2_int8(Dtype* dout,
const int8_t* din,
const int8_t* weights,
const float* scale,
const float* bias,
bool flag_bias,
bool flag_relu,
int num,
int chin,
int hin,
int win,
int hout,
int wout,
int padw,
int padh,
ARMContext* ctx);
} // namespace math } // namespace math
} // namespace arm } // namespace arm
} // namespace lite } // namespace lite
......
...@@ -880,6 +880,23 @@ void conv_depthwise_5x5_int8_fp32(const void* din, ...@@ -880,6 +880,23 @@ void conv_depthwise_5x5_int8_fp32(const void* din,
pad_w, pad_w,
pad_h, pad_h,
ctx); ctx);
} else if (stride == 2) {
conv_depthwise_5x5s2_int8(reinterpret_cast<float*>(dout),
reinterpret_cast<const int8_t*>(din),
reinterpret_cast<const int8_t*>(weights),
scale,
bias,
flag_bias,
flag_relu,
num,
ch_in,
h_in,
w_in,
h_out,
w_out,
pad_w,
pad_h,
ctx);
} else { } else {
LOG(FATAL) << "unsupport this type 5x5 dw conv int8"; LOG(FATAL) << "unsupport this type 5x5 dw conv int8";
} }
...@@ -922,6 +939,23 @@ void conv_depthwise_5x5_int8_int8(const void* din, ...@@ -922,6 +939,23 @@ void conv_depthwise_5x5_int8_int8(const void* din,
pad_w, pad_w,
pad_h, pad_h,
ctx); ctx);
} else if (stride == 2) {
conv_depthwise_5x5s2_int8(reinterpret_cast<int8_t*>(dout),
reinterpret_cast<const int8_t*>(din),
reinterpret_cast<const int8_t*>(weights),
scale,
bias,
flag_bias,
flag_relu,
num,
ch_in,
h_in,
w_in,
h_out,
w_out,
pad_w,
pad_h,
ctx);
} else { } else {
LOG(FATAL) << "unsupport this type 5x5 dw conv int8"; LOG(FATAL) << "unsupport this type 5x5 dw conv int8";
} }
......
...@@ -922,7 +922,7 @@ void sgemv_trans(const int M, ...@@ -922,7 +922,7 @@ void sgemv_trans(const int M,
/* end */ \ /* end */ \
"4: \n" /* end */ \ "4: \n" /* end */ \
"fmov s1, %w[alpha] \n" /* mov alpha to s1 */ \ "fmov s1, %w[alpha] \n" /* mov alpha to s1 */ \
"fcmp s8, #0 \n" /* cmp with zero*/ \ "fcmp s8, #0.0 \n" /* cmp with zero*/ \
"bge 5f \n" /* if ge zero */ \ "bge 5f \n" /* if ge zero */ \
"fmul s8, s8, s1 \n" /* out * alpha */ \ "fmul s8, s8, s1 \n" /* out * alpha */ \
"5: \n" /* leakey relu label */ \ "5: \n" /* leakey relu label */ \
......
...@@ -46,6 +46,7 @@ void fp32_to_int8(const float* din, ...@@ -46,6 +46,7 @@ void fp32_to_int8(const float* din,
float inv_scale = 1.f / scale[j % axis_size]; float inv_scale = 1.f / scale[j % axis_size];
float32x4_t vzero = vdupq_n_f32(0.f); float32x4_t vzero = vdupq_n_f32(0.f);
float32x4_t vscale = vdupq_n_f32(inv_scale); float32x4_t vscale = vdupq_n_f32(inv_scale);
float32x4_t vmax = vdupq_n_f32(-127.f);
float32x4_t vpoff = vdupq_n_f32(0.5f); float32x4_t vpoff = vdupq_n_f32(0.5f);
float32x4_t vnoff = vdupq_n_f32(-0.5f); float32x4_t vnoff = vdupq_n_f32(-0.5f);
const float* din_c = din + j * inner_size; const float* din_c = din + j * inner_size;
...@@ -63,6 +64,14 @@ void fp32_to_int8(const float* din, ...@@ -63,6 +64,14 @@ void fp32_to_int8(const float* din,
"fmul v5.4s, v1.4s, %[scale].4s \n" "fmul v5.4s, v1.4s, %[scale].4s \n"
"fmul v6.4s, v2.4s, %[scale].4s \n" "fmul v6.4s, v2.4s, %[scale].4s \n"
"fmul v7.4s, v3.4s, %[scale].4s \n" "fmul v7.4s, v3.4s, %[scale].4s \n"
"fcmge v8.4s, v4.4s, %[vmax].4s \n"
"fcmge v9.4s, v5.4s, %[vmax].4s \n"
"fcmge v10.4s, v6.4s, %[vmax].4s \n"
"fcmge v11.4s, v7.4s, %[vmax].4s \n"
"bif v4.16b, %[vmax].16b, v8.16b \n"
"bif v5.16b, %[vmax].16b, v9.16b \n"
"bif v6.16b, %[vmax].16b, v10.16b \n"
"bif v7.16b, %[vmax].16b, v11.16b \n"
"ldp q0, q1, [%[in]], #32 \n" "ldp q0, q1, [%[in]], #32 \n"
"subs %[cnt], %[cnt], #1 \n" "subs %[cnt], %[cnt], #1 \n"
"FCVTAS v8.4s, v4.4s \n" "FCVTAS v8.4s, v4.4s \n"
...@@ -79,7 +88,7 @@ void fp32_to_int8(const float* din, ...@@ -79,7 +88,7 @@ void fp32_to_int8(const float* din,
"str q8, [%[out]], #16 \n" "str q8, [%[out]], #16 \n"
"bne 0b \n" "bne 0b \n"
: [in] "+r"(din_ptr), [out] "+r"(dout_ptr), [cnt] "+r"(cnt_loop) : [in] "+r"(din_ptr), [out] "+r"(dout_ptr), [cnt] "+r"(cnt_loop)
: [scale] "w"(vscale) : [scale] "w"(vscale), [vmax] "w"(vmax)
: "v0", : "v0",
"v1", "v1",
"v2", "v2",
...@@ -104,15 +113,23 @@ void fp32_to_int8(const float* din, ...@@ -104,15 +113,23 @@ void fp32_to_int8(const float* din,
"vcgt.f32 q8, q0, %q[vzero] @ get mask > 0, in0\n" "vcgt.f32 q8, q0, %q[vzero] @ get mask > 0, in0\n"
"vcgt.f32 q9, q1, %q[vzero] @ get mask > 0, in1\n" "vcgt.f32 q9, q1, %q[vzero] @ get mask > 0, in1\n"
"vcgt.f32 q10, q2, %q[vzero] @ get mask > 0, in2\n" "vcgt.f32 q10, q2, %q[vzero] @ get mask > 0, in2\n"
"vcgt.f32 q11, q3, %q[vzero] @ get mask > 0, in3\n"
"vbif.f32 q4, %q[vnoff], q8 @ get right offset\n" "vbif.f32 q4, %q[vnoff], q8 @ get right offset\n"
"vcgt.f32 q8, q3, %q[vzero] @ get mask > 0, in3\n"
"vbif.f32 q5, %q[vnoff], q9 @ get right offset\n" "vbif.f32 q5, %q[vnoff], q9 @ get right offset\n"
"vbif.f32 q6, %q[vnoff], q10 @ get right offset\n" "vbif.f32 q6, %q[vnoff], q10 @ get right offset\n"
"vbif.f32 q7, %q[vnoff], q11 @ get right offset\n" "vbif.f32 q7, %q[vnoff], q8 @ get right offset\n"
"vmla.f32 q4, q0, %q[vscale] @ mul scale\n" "vmla.f32 q4, q0, %q[vscale] @ mul scale\n"
"vmla.f32 q5, q1, %q[vscale] @ mul scale\n" "vmla.f32 q5, q1, %q[vscale] @ mul scale\n"
"vmla.f32 q6, q2, %q[vscale] @ mul scale\n" "vmla.f32 q6, q2, %q[vscale] @ mul scale\n"
"vmla.f32 q7, q3, %q[vscale] @ mul scale\n" "vmla.f32 q7, q3, %q[vscale] @ mul scale\n"
"vcge.f32 q8, q4, %q[vmax] @ q4 >= vmax \n"
"vcge.f32 q9, q5, %q[vmax] @ q4 >= vmax \n"
"vcge.f32 q10, q6, %q[vmax] @ q4 >= vmax \n"
"vbif q4, %q[vmax], q8 @ choose \n"
"vcge.f32 q8, q7, %q[vmax] @ q4 >= vmax \n"
"vbif q5, %q[vmax], q9 @ choose \n"
"vbif q6, %q[vmax], q10 @ choose \n"
"vbif q7, %q[vmax], q8 @ choose \n"
"vcvt.s32.f32 q0, q4 @ cvt to int32\n" "vcvt.s32.f32 q0, q4 @ cvt to int32\n"
"vcvt.s32.f32 q1, q5 @ cvt to int32\n" "vcvt.s32.f32 q1, q5 @ cvt to int32\n"
"vcvt.s32.f32 q2, q6 @ cvt to int32\n" "vcvt.s32.f32 q2, q6 @ cvt to int32\n"
...@@ -133,25 +150,16 @@ void fp32_to_int8(const float* din, ...@@ -133,25 +150,16 @@ void fp32_to_int8(const float* din,
: [vscale] "w"(vscale), : [vscale] "w"(vscale),
[vpoff] "w"(vpoff), [vpoff] "w"(vpoff),
[vnoff] "w"(vnoff), [vnoff] "w"(vnoff),
[vzero] "w"(vzero) [vzero] "w"(vzero),
: "q0", [vmax] "w"(vmax)
"q1", : "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9", "q10");
"q2",
"q3",
"q4",
"q5",
"q6",
"q7",
"q8",
"q9",
"q10",
"q11");
#endif #endif
} }
const float* din_r = din_c + 16 * cnt; const float* din_r = din_c + 16 * cnt;
signed char* dout_r = dout_c + 16 * cnt; signed char* dout_r = dout_c + 16 * cnt;
for (int i = 0; i < remain; ++i) { for (int i = 0; i < remain; ++i) {
dout_r[i] = saturate_cast<int8_t>(roundf(inv_scale * din_r[i])); dout_r[i] = saturate_cast<int8_t>(roundf(inv_scale * din_r[i]));
dout_r[i] = dout_r[i] < -127 ? -127 : dout_r[i];
} }
} }
} }
......
...@@ -151,6 +151,10 @@ class TensorLite { ...@@ -151,6 +151,10 @@ class TensorLite {
size_t offset() const { return offset_; } size_t offset() const { return offset_; }
bool IsInitialized() const { return buffer_->data(); } bool IsInitialized() const { return buffer_->data(); }
void clear() {
buffer_->Free();
offset_ = 0;
}
// Other share data to this. // Other share data to this.
void ShareDataWith(const TensorLite &other); void ShareDataWith(const TensorLite &other);
......
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include <cl_common.h>
__kernel void concat2(__global const CL_DTYPE* x_data0, __global const CL_DTYPE* x_data1, __global CL_DTYPE* out_data,
int size, int axis_size, int pre_size, int post_size, int total, int total0, int total1) {
const int index = get_global_id(0);
if (index < size){
for (int i = 0; i < pre_size; i++){
int offset_out = index * post_size + i * total;
int offset_in = index * post_size + i * total0;
// memcpy(out_data + offset_out, x_data0 + offset_in, post_size);
CL_DTYPE* dst = out_data + offset_out;
CL_DTYPE* src = x_data0 + offset_in;
for (int k = 0; k < post_size; k++){
*dst++ = *src++;
}
}
}else if (index < axis_size){
for (int i = 0; i < pre_size; i++){
int offset_out = index * post_size + i * total;
int offset_in = index * post_size + i * total1;
// memcpy(out_data + offset_out, x_data1 + offset_in, post_size);
CL_DTYPE* dst = out_data + offset_out;
CL_DTYPE* src = x_data1 + offset_in;
for (int k = 0; k < post_size; k++){
*dst++ = *src++;
}
}
}
}
__kernel void concat_mul(__global const CL_DTYPE* x_data, __global CL_DTYPE* out_data,
int axis_size, int pre_size, int post_size, int start, int total, int total0) {
const int index = get_global_id(0);
if (index < axis_size){
for (int i = 0; i < pre_size; i++){
int offset_out = (start + index) * post_size + i * total;
int offset_in = index * post_size + i * total0;
// memcpy(out_data + offset_out, x_data + offset_in, post_size);
CL_DTYPE* dst = out_data + offset_out;
CL_DTYPE* src = x_data + offset_in;
for (int k = 0; k < post_size; k++){
*dst++ = *src++;
}
}
}
}
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include <cl_common.h>
__kernel void sigmoid(__global const CL_DTYPE* x_data, const int count, __global CL_DTYPE* out_data) {
const int index = get_global_id(0);
if (index < count) {
out_data[index] = 1 / (1 + exp(-x_data[index]));
}
}
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include <cl_common.h>
__kernel void concat2(__read_only image2d_t input0,
__read_only image2d_t input1,
__write_only image2d_t output,
int axis_size, int flag, int width) {
const int x = get_global_id(0); // image_width cxw/4
const int y = get_global_id(1); // image_height nxh
const sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE |
CLK_ADDRESS_CLAMP |
CLK_FILTER_NEAREST;
int xx = x / width;
if (flag == 0){
xx = y / width;
}
if (xx < axis_size){
CL_DTYPE4 in = READ_IMG_TYPE(CL_DTYPE_CHAR, input0, sampler, (int2)(x, y));
WRITE_IMG_TYPE(CL_DTYPE_CHAR, output, (int2)(x, y), in);
}else{
int new_val = xx - axis_size;
new_val *= width;
CL_DTYPE4 in = READ_IMG_TYPE(CL_DTYPE_CHAR, input0, sampler, (int2)(new_val, y));
WRITE_IMG_TYPE(CL_DTYPE_CHAR, output, (int2)(x, y), in);
}
// WRITE_IMG_TYPE(CL_DTYPE_CHAR, output, (int2)(x, y), in);
}
__kernel void concat_mul(__read_only image2d_t input0,
__write_only image2d_t output,
int axis_size, int flag, int width, int start) {
const int x = get_global_id(0); // image_width cxw/4
const int y = get_global_id(1); // image_height nxh
const sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE |
CLK_ADDRESS_CLAMP |
CLK_FILTER_NEAREST;
int xx = x / width;
if (flag == 0){
xx = y / width;
}
if (xx < axis_size && xx >= start){
xx -= start;
xx *= width;
CL_DTYPE4 in = READ_IMG_TYPE(CL_DTYPE_CHAR, input0, sampler, (int2)(xx, y));
WRITE_IMG_TYPE(CL_DTYPE_CHAR, output, (int2)(x, y), in);
}
}
...@@ -142,7 +142,7 @@ __kernel void depth_conv2d_3x3(__private const int global_size_dim0, ...@@ -142,7 +142,7 @@ __kernel void depth_conv2d_3x3(__private const int global_size_dim0,
#endif #endif
#ifdef RELU #ifdef RELU
output = activation(output); output = activation_type4(output);
#endif #endif
...@@ -309,8 +309,8 @@ __kernel void depth_conv2d_3x3s1(__private const int ou_ch_blk, ...@@ -309,8 +309,8 @@ __kernel void depth_conv2d_3x3s1(__private const int ou_ch_blk,
#endif #endif
#ifdef RELU #ifdef RELU
output[0] = activation(output[0]); output[0] = activation_type4(output[0]);
output[1] = activation(output[1]); output[1] = activation_type4(output[1]);
#endif #endif
WRITE_IMG_TYPE(CL_DTYPE_CHAR, output_image, (int2)(ou_x, ou_nh_id), output[0]); WRITE_IMG_TYPE(CL_DTYPE_CHAR, output_image, (int2)(ou_x, ou_nh_id), output[0]);
......
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include <cl_common.h>
__kernel void scale(__read_only image2d_t input,
__write_only image2d_t output,
__private float scale,
__private float bias){
const int x = get_global_id(0); // image_width
const int y = get_global_id(1); // image_height
const sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE |
CLK_ADDRESS_CLAMP |
CLK_FILTER_NEAREST;
CL_DTYPE4 in = READ_IMG_TYPE(CL_DTYPE_CHAR, input, sampler, (int2)(x, y));
in = convert_float(scale) * in + convert_float(bias);
WRITE_IMG_TYPE(CL_DTYPE_CHAR, output, (int2)(x, y), in);
}
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include <cl_common.h>
__kernel void sigmoid(__read_only image2d_t input,
__write_only image2d_t output) {
const int x = get_global_id(0); // image_width
const int y = get_global_id(1); // image_height
const sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE |
CLK_ADDRESS_CLAMP |
CLK_FILTER_NEAREST;
CL_DTYPE4 in = READ_IMG_TYPE(CL_DTYPE_CHAR, input, sampler, (int2)(x, y));
CL_DTYPE4 out = 1 / (1 + exp(-in));
WRITE_IMG_TYPE(CL_DTYPE_CHAR, output, (int2)(x, y), out);
}
...@@ -142,14 +142,13 @@ void StrideScal(const T* a, const T* x, T* y, int n, int stride); ...@@ -142,14 +142,13 @@ void StrideScal(const T* a, const T* x, T* y, int n, int stride);
// remain is the product of dimension shapes after the axis dimension // remain is the product of dimension shapes after the axis dimension
template <typename T> template <typename T>
void Softmax(const T* x, T* y, int n, int bs, int remain = 1) { void Softmax(const T* x, T* y, int n, int bs, int remain = 1) {
std::vector<T> entities(bs);
for (int i = 0; i < bs; ++i) { for (int i = 0; i < bs; ++i) {
entities[i] = x[i * n]; T entity = x[i * n];
for (int c = 1; c < n; ++c) { for (int c = 1; c < n; ++c) {
entities[i] = x[i * n + c] > entities[i] ? x[i * n + c] : entities[i]; entity = x[i * n + c] > entity ? x[i * n + c] : entity;
} }
for (int c = 0; c < n; ++c) { for (int c = 0; c < n; ++c) {
y[i * n + c] = x[i * n + c] - entities[i]; y[i * n + c] = x[i * n + c] - entity;
} }
} }
VExp(y, y, n * bs); VExp(y, y, n * bs);
......
...@@ -110,11 +110,7 @@ void set_constant(const lite::Context<Target>& context, ...@@ -110,11 +110,7 @@ void set_constant(const lite::Context<Target>& context,
lite::Tensor* tensor, lite::Tensor* tensor,
float value) { float value) {
TensorSetConstantWithTarget<Target> func(context, tensor, value); TensorSetConstantWithTarget<Target> func(context, tensor, value);
//#ifdef PADDLE_WITH_CUDA
// tensor->target().apply_visitor(func);
//#else
func(); func();
//#endif
} }
template <typename T> template <typename T>
...@@ -123,17 +119,19 @@ struct RowwiseAdd<lite::TargetType::kX86, T> { ...@@ -123,17 +119,19 @@ struct RowwiseAdd<lite::TargetType::kX86, T> {
const lite::Tensor& input, const lite::Tensor& input,
const lite::Tensor& vector, const lite::Tensor& vector,
lite::Tensor* output) { lite::Tensor* output) {
auto in_dims = input.dims(); const auto& in_dims = input.dims();
auto size = input.numel() / in_dims[0]; auto size = input.numel() / in_dims[0];
PADDLE_ENFORCE_EQ(vector.numel(), size); PADDLE_ENFORCE_EQ(vector.numel(), size);
PADDLE_ENFORCE_EQ(output->dims(), in_dims); PADDLE_ENFORCE_EQ(output->dims(), in_dims);
auto in = lite::fluid::EigenMatrix<T>::From(input); const T* input_data = input.data<T>();
auto vec = lite::fluid::EigenVector<T>::Flatten(vector); const T* vector_data = vector.data<T>();
auto out = lite::fluid::EigenMatrix<T>::From(*output); T* output_data = output->mutable_data<T>();
for (int64_t i = 0; i < in_dims[0]; ++i) { for (int64_t i = 0; i < in_dims[0]; ++i) {
out.chip(i, 0) = in.chip(i, 0) + vec; for (int64_t j = 0; j < size; ++j) {
output_data[i * in_dims[0] + j] =
input_data[i * in_dims[0] + j] + vector_data[j];
}
} }
} }
}; };
......
...@@ -29,6 +29,11 @@ void ConvActivationFusePass::Apply(const std::unique_ptr<SSAGraph>& graph) { ...@@ -29,6 +29,11 @@ void ConvActivationFusePass::Apply(const std::unique_ptr<SSAGraph>& graph) {
act_types.push_back("leaky_relu"); act_types.push_back("leaky_relu");
break; break;
} }
if (place.target == TARGET(kARM) && place.precision == PRECISION(kFloat)) {
act_types.push_back("relu6");
act_types.push_back("leaky_relu");
break;
}
} }
for (auto conv_type : {"conv2d", "depthwise_conv2d", "conv2d_transpose"}) { for (auto conv_type : {"conv2d", "depthwise_conv2d", "conv2d_transpose"}) {
for (auto act_type : act_types) { for (auto act_type : act_types) {
......
...@@ -53,6 +53,11 @@ void mir::Node::Stmt::ResetOp(const cpp::OpDesc &op_desc, ...@@ -53,6 +53,11 @@ void mir::Node::Stmt::ResetOp(const cpp::OpDesc &op_desc,
} }
valid_kernels_ = op_->CreateKernels(valid_places); valid_kernels_ = op_->CreateKernels(valid_places);
} }
void mir::Node::Stmt::ResetKernels(const std::vector<Place> &valid_places) {
CHECK(op_) << "change valid place failed, not created op";
valid_kernels_.clear();
valid_kernels_ = op_->CreateKernels(valid_places);
}
mir::Node::Arg &mir::Node::AsArg(const std::string &name, int id) { mir::Node::Arg &mir::Node::AsArg(const std::string &name, int id) {
auto &x = AsArg(); auto &x = AsArg();
......
...@@ -53,6 +53,7 @@ class Node { ...@@ -53,6 +53,7 @@ class Node {
const std::vector<Place>& valid_places, const std::vector<Place>& valid_places,
lite::Scope* scope = nullptr); lite::Scope* scope = nullptr);
void ResetKernels(const std::vector<Place>& valid_places);
std::string op_type() const { return op_info()->Type(); } std::string op_type() const { return op_info()->Type(); }
const OpInfo* op_info() const; const OpInfo* op_info() const;
OpInfo* mutable_op_info(); OpInfo* mutable_op_info();
......
...@@ -157,7 +157,7 @@ std::shared_ptr<lite_api::PaddlePredictor> TestModel( ...@@ -157,7 +157,7 @@ std::shared_ptr<lite_api::PaddlePredictor> TestModel(
lite_api::LiteModelType::kNaiveBuffer); lite_api::LiteModelType::kNaiveBuffer);
// Load optimized model // Load optimized model
lite_api::MobileConfig mobile_config; lite_api::MobileConfig mobile_config;
mobile_config.set_model_dir(optimized_model_dir); mobile_config.set_model_from_file(optimized_model_dir + ".nb");
mobile_config.set_power_mode(lite_api::PowerMode::LITE_POWER_HIGH); mobile_config.set_power_mode(lite_api::PowerMode::LITE_POWER_HIGH);
mobile_config.set_threads(1); mobile_config.set_threads(1);
predictor = lite_api::CreatePaddlePredictor(mobile_config); predictor = lite_api::CreatePaddlePredictor(mobile_config);
...@@ -203,7 +203,7 @@ TEST(Subgraph, generate_model_and_check_precision) { ...@@ -203,7 +203,7 @@ TEST(Subgraph, generate_model_and_check_precision) {
valid_places, valid_places,
input_tensor_shape, input_tensor_shape,
input_tensor_type, input_tensor_type,
FLAGS_optimized_model_dir + "/ref_opt_model"); FLAGS_optimized_model_dir + "_ref_opt_model");
// Generate and run optimized model on NPU/XPU as the target predictor // Generate and run optimized model on NPU/XPU as the target predictor
#ifdef LITE_WITH_NPU #ifdef LITE_WITH_NPU
valid_places.push_back(lite_api::Place{TARGET(kNPU), PRECISION(kFloat)}); valid_places.push_back(lite_api::Place{TARGET(kNPU), PRECISION(kFloat)});
...@@ -217,7 +217,7 @@ TEST(Subgraph, generate_model_and_check_precision) { ...@@ -217,7 +217,7 @@ TEST(Subgraph, generate_model_and_check_precision) {
valid_places, valid_places,
input_tensor_shape, input_tensor_shape,
input_tensor_type, input_tensor_type,
FLAGS_optimized_model_dir + "/tar_opt_model"); FLAGS_optimized_model_dir + "_tar_opt_model");
// Check the difference of the output tensors between reference predictor and // Check the difference of the output tensors between reference predictor and
// target predictor // target predictor
CheckOutputTensors(tar_predictor, ref_predictor, output_tensor_type); CheckOutputTensors(tar_predictor, ref_predictor, output_tensor_type);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册