提交 a12f43bf 编写于 作者: M MyPandaShaoxiang

Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle-Lite into int8

test=develop
Conflicts:
	lite/kernels/arm/conv_compute.cc
......@@ -63,6 +63,16 @@ test/models/
test/images/
*.pyc
# model
*.nb
*.svg
*.dot
# vim intermediate files
*.swp
# Emacs intermediate files
*~
......
......@@ -97,7 +97,7 @@ lite_option(LITE_WITH_FPGA "Enable FPGA support in lite" OFF)
lite_option(LITE_WITH_LIGHT_WEIGHT_FRAMEWORK "Enable light-weight framework" OFF)
lite_option(LITE_WITH_PROFILE "Enable profile mode in lite framework" OFF)
lite_option(LITE_WITH_PRECISION_PROFILE "Enable precision profile in profile mode ON in lite" OFF)
lite_option(LITE_SHUTDOWN_LOG "Shutdown log system or not." OFF)
lite_option(LITE_WITH_LOG "Enable log printing or not." ON)
lite_option(LITE_ON_TINY_PUBLISH "Publish tiny predictor lib." OFF)
lite_option(LITE_ON_MODEL_OPTIMIZE_TOOL "Build the model optimize tool" OFF)
# publish options
......
......@@ -61,7 +61,8 @@ For demands of Apple's GPU Metal and web front end inference, please see `./meta
Paddle Lite has referenced the following open-source projects:
- [ARM compute library](http://agroup.baidu.com/paddle-infer/md/article/%28https://github.com/ARM-software/ComputeLibrary%29)
- [Anakin](https://github.com/PaddlePaddle/Anakin). The optimizations under Anakin has been incorporated into Paddle Lite, and so there will not be any future updates of Anakin. As another high-performance inference project under PaddlePaddle, Anakin has been forward-looking and helpful to the making of Paddle Lite.
- [Anakin](https://github.com/PaddlePaddle/Anakin). The optimizations under Anakin has been incorporated into Paddle Lite, and so there will not be any future updates of Anakin. As another high-performance inference project under PaddlePaddle, Anakin has been forward-looking and helpful to the making of Paddle Lite.
## Feedback and Community Support
......
......@@ -186,8 +186,8 @@ if (LITE_WITH_LIGHT_WEIGHT_FRAMEWORK)
add_definitions("-DLITE_WITH_LIGHT_WEIGHT_FRAMEWORK")
endif()
if (LITE_SHUTDOWN_LOG)
add_definitions("-DLITE_SHUTDOWN_LOG")
if (LITE_WITH_LOG)
add_definitions("-DLITE_WITH_LOG")
endif()
if (LITE_ON_TINY_PUBLISH)
......
......@@ -32,34 +32,3 @@ endif()
message(STATUS "APU_DDK_INC: ${APU_DDK_INC}")
include_directories("${APU_DDK_ROOT}/include")
set(APU_SUB_LIB_PATH "lib64")
if(ARM_TARGET_ARCH_ABI STREQUAL "armv8")
set(APU_SUB_LIB_PATH "lib64")
endif()
find_library(APU_NEURON_FILE NAMES neuron
PATHS ${APU_DDK_ROOT}/${APU_SUB_LIB_PATH})
find_library(APU_NEURON_ADAPTER_FILE NAMES neuron_adapter
PATHS ${APU_DDK_ROOT}/${APU_SUB_LIB_PATH})
if(NOT APU_NEURON_FILE)
message(FATAL_ERROR "Can not find APU_NEURON_FILE in ${APU_DDK_ROOT}")
else()
message(STATUS "Found APU NEURON Library: ${APU_NEURON_FILE}")
add_library(apu_neuron SHARED IMPORTED GLOBAL)
set_property(TARGET apu_neuron PROPERTY IMPORTED_LOCATION ${APU_NEURON_FILE})
endif()
if(NOT APU_NEURON_ADAPTER_FILE)
message(FATAL_ERROR "Can not find APU_NEURON_ADAPTER_FILE in ${APU_DDK_ROOT}")
else()
message(STATUS "Found APU NEURON ADAPTER Library: ${APU_NEURON_ADAPTER_FILE}")
add_library(apu_neuron_adapter SHARED IMPORTED GLOBAL)
set_property(TARGET apu_neuron_adapter PROPERTY IMPORTED_LOCATION ${APU_NEURON_ADAPTER_FILE})
endif()
set(apu_runtime_libs apu_neuron apu_neuron_adapter CACHE INTERNAL "apu runtime libs")
message(STATUS "${apu_runtime_libs}")
......@@ -45,7 +45,7 @@ else()
# we changed the source code to adapt for windows compiling
# git diffs : (1) unsupported/Eigen/CXX11/src/Tensor/TensorBlockV2.h
######################################################################################################
URL https://paddlelite-data.bj.bcebos.com/third_party_libs/eigen-git-mirror-master-9ab917e9db99f5907d086aa73d5f9103.zip
URL http://paddlelite-data.bj.bcebos.com/third_party_libs/eigen-git-mirror-master-9ab917e9db99f5907d086aa73d5f9103.zip
DOWNLOAD_DIR ${EIGEN_SOURCECODE_DIR}
DOWNLOAD_NO_PROGRESS 1
PREFIX ${EIGEN_SOURCE_DIR}
......
......@@ -400,7 +400,7 @@ std::shared_ptr<PaddlePredictor> predictor = CreatePaddlePredictor<MobileConfig>
- `None`
返回:内存中模型结构数据
返回:内存中模型参数数据
返回类型:`const std::string&`
......
# PaddleLite使用百度XPU预测部署
Paddle Lite已支持百度XPU在x86和arm服务器(例如飞腾 FT-2000+/64)上进行预测部署。
目前支持Kernel和子图两种接入方式,其中子图接入方式与之前华为NPU类似,即加载并分析Paddle模型,将Paddle算子转成XTCL组网API进行网络构建,在线生成并执行模型。
## 支持现状
### 已支持的芯片
- 昆仑818-100(推理芯片)
- 昆仑818-300(训练芯片)
### 已支持的设备
- K100/K200昆仑AI加速卡
### 已支持的Paddle模型
- [ResNet50](https://paddlelite-demo.bj.bcebos.com/models/resnet50_fp32_224_fluid.tar.gz)
- [BERT](https://paddlelite-demo.bj.bcebos.com/models/bert_fp32_fluid.tar.gz)
- [ERNIE](https://paddlelite-demo.bj.bcebos.com/models/ernie_fp32_fluid.tar.gz)
- YOLOv3
- Mask R-CNN
- Faster R-CNN
- UNet
- SENet
- SSD
- 百度内部业务模型(由于涉密,不方便透露具体细节)
### 已支持(或部分支持)的Paddle算子(Kernel接入方式)
- scale
- relu
- tanh
- sigmoid
- stack
- matmul
- pool2d
- slice
- lookup_table
- elementwise_add
- elementwise_sub
- cast
- batch_norm
- mul
- layer_norm
- softmax
- conv2d
- io_copy
- io_copy_once
- __xpu__fc
- __xpu__multi_encoder
- __xpu__resnet50
- __xpu__embedding_with_eltwise_add
### 已支持(或部分支持)的Paddle算子(子图/XTCL接入方式)
- relu
- tanh
- conv2d
- depthwise_conv2d
- elementwise_add
- pool2d
- softmax
- mul
- batch_norm
- stack
- gather
- scale
- lookup_table
- slice
- transpose
- transpose2
- reshape
- reshape2
- layer_norm
- gelu
- dropout
- matmul
- cast
- yolo_box
## 参考示例演示
### 测试设备(K100昆仑AI加速卡)
![baidu_xpu](https://paddlelite-demo.bj.bcebos.com/devices/baidu/baidu_xpu.jpg)
### 准备设备环境
- K100/200昆仑AI加速卡[规格说明书](https://paddlelite-demo.bj.bcebos.com/devices/baidu/K100_K200_spec.pdf),如需更详细的规格说明书或购买产品,请联系欧阳剑ouyangjian@baidu.com;
- K100为全长半高PCI-E卡,K200为全长全高PCI-E卡,要求使用PCI-E x16插槽,且需要单独的8针供电线进行供电;
- 安装K100/K200驱动,目前支持Ubuntu和CentOS系统,由于驱动依赖Linux kernel版本,请正确安装对应版本的驱动安装包。
### 准备本地编译环境
- 为了保证编译环境一致,建议参考[源码编译](../user_guides/source_compile)中的Linux开发环境进行配置;
- 由于编译示例程序需要依赖OpenCV和CMake 3.10.3,请执行如下命令进行安装;
```shell
$ sudo apt-get update
$ sudo apt-get install gcc g++ make wget unzip libopencv-dev pkg-config
$ 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
```
### 运行图像分类示例程序
-[https://paddlelite-demo.bj.bcebos.com/devices/baidu/PaddleLite-linux-demo.tar.gz](https://paddlelite-demo.bj.bcebos.com/devices/baidu/PaddleLite-linux-demo.tar.gz)下载示例程序,解压后清单如下:
```shell
- PaddleLite-linux-demo
- image_classification_demo
- assets
- images
- tabby_cat.jpg # 测试图片
- labels
- synset_words.txt # 1000分类label文件
- models
- resnet50_fp32_224_fluid # Paddle fluid non-combined格式的resnet50 float32模型
- __model__ # Paddle fluid模型组网文件,可拖入https://lutzroeder.github.io/netron/进行可视化显示网络结构
- bn2a_branch1_mean # Paddle fluid模型参数文件
- bn2a_branch1_scale
...
- shell
- CMakeLists.txt # 示例程序CMake脚本
- build
- image_classification_demo # 已编译好的,适用于amd64的示例程序
- image_classification_demo.cc # 示例程序源码
- build.sh # 示例程序编译脚本
- run.sh # 示例程序运行脚本
- libs
- PaddleLite
- amd64
- include # PaddleLite头文件
- lib
- libiomp5.so # Intel OpenMP库
- libmklml_intel.so # Intel MKL库
- libxpuapi.so # XPU API库,提供设备管理和算子实现。
- llibxpurt.so # XPU runtime库
- libpaddle_full_api_shared.so # 预编译PaddleLite full api库
- arm64
- include # PaddleLite头文件
- lib
- libxpuapi.so # XPU API库,提供设备管理和算子实现。
- llibxpurt.so # XPU runtime库
- libpaddle_full_api_shared.so # 预编译PaddleLite full api库
```
- 进入PaddleLite-linux-demo/image_classification_demo/shell,直接执行./run.sh amd64即可;
```shell
$ cd PaddleLite-linux-demo/image_classification_demo/shell
$ ./run.sh amd64 # 默认已生成amd64版本的build/image_classification_demo,因此,无需重新编译示例程序就可以执行。
$ ./run.sh arm64 # 需要在arm64(FT-2000+/64)服务器上执行./build.sh arm64后才能执行该命令。
...
AUTOTUNE:(12758016, 16, 1, 2048, 7, 7, 512, 1, 1, 1, 1, 0, 0, 0) = 1by1_bsp(1, 32, 128, 128)
Find Best Result in 150 choices, avg-conv-op-time = 40 us
[INFO][XPUAPI][/home/qa_work/xpu_workspace/xpu_build_dailyjob/api_root/baidu/xpu/api/src/wrapper/conv.cpp:274] Start Tuning: (12758016, 16, 1, 512, 7, 7, 512, 3, 3, 1, 1, 1, 1, 0)
AUTOTUNE:(12758016, 16, 1, 512, 7, 7, 512, 3, 3, 1, 1, 1, 1, 0) = wpinned_bsp(1, 171, 16, 128)
Find Best Result in 144 choices, avg-conv-op-time = 79 us
I0502 22:34:18.176113 15876 io_copy_compute.cc:75] xpu to host, copy size 4000
I0502 22:34:18.176406 15876 io_copy_compute.cc:36] host to xpu, copy size 602112
I0502 22:34:18.176697 15876 io_copy_compute.cc:75] xpu to host, copy size 4000
iter 0 cost: 2.116000 ms
I0502 22:34:18.178530 15876 io_copy_compute.cc:36] host to xpu, copy size 602112
I0502 22:34:18.178792 15876 io_copy_compute.cc:75] xpu to host, copy size 4000
iter 1 cost: 2.101000 ms
I0502 22:34:18.180634 15876 io_copy_compute.cc:36] host to xpu, copy size 602112
I0502 22:34:18.180881 15876 io_copy_compute.cc:75] xpu to host, copy size 4000
iter 2 cost: 2.089000 ms
I0502 22:34:18.182726 15876 io_copy_compute.cc:36] host to xpu, copy size 602112
I0502 22:34:18.182976 15876 io_copy_compute.cc:75] xpu to host, copy size 4000
iter 3 cost: 2.085000 ms
I0502 22:34:18.184814 15876 io_copy_compute.cc:36] host to xpu, copy size 602112
I0502 22:34:18.185068 15876 io_copy_compute.cc:75] xpu to host, copy size 4000
iter 4 cost: 2.101000 ms
warmup: 1 repeat: 5, average: 2.098400 ms, max: 2.116000 ms, min: 2.085000 ms
results: 3
Top0 tabby, tabby cat - 0.689418
Top1 tiger cat - 0.190557
Top2 Egyptian cat - 0.112354
Preprocess time: 1.553000 ms
Prediction time: 2.098400 ms
Postprocess time: 0.081000 ms
```
- 如果需要更改测试图片,可将图片拷贝到PaddleLite-linux-demo/image_classification_demo/assets/images目录下,然后将run.sh的IMAGE_NAME设置成指定文件名即可;
- 如果需要重新编译示例程序,直接运行./build.sh amd64或./build.sh arm64即可。
```shell
$ cd PaddleLite-linux-demo/image_classification_demo/shell
$ ./build.sh amd64 # For amd64
$ ./build.sh arm64 # For arm64(FT-2000+/64)
```
### 更新模型
- 通过Paddle Fluid训练,或X2Paddle转换得到ResNet50 float32模型[resnet50_fp32_224_fluid](https://paddlelite-demo.bj.bcebos.com/models/resnet50_fp32_224_fluid.tar.gz)
- 由于XPU一般部署在Server端,因此将使用PaddleLite的full api加载原始的Paddle Fluid模型进行预测,即采用CXXConfig配置相关参数。
### 更新支持百度XPU的Paddle Lite库
- 下载PaddleLite源码;
```shell
$ git clone https://github.com/PaddlePaddle/Paddle-Lite.git
$ cd Paddle-Lite
$ git checkout <release-version-tag>
```
- 下载xpu_toolchain for amd64 or arm64(FT-2000+/64);
```shell
$ wget <URL_to_download_xpu_toolchain>
$ tar -xvf output.tar.gz
$ mv output xpu_toolchain
```
- 编译full_publish for amd64 or arm64(FT-2000+/64);
```shell
For amd64,如果报找不到cxx11::符号的编译错误,请将gcc切换到4.8版本。
$ ./lite/tools/build.sh --build_xpu=ON --xpu_sdk_root=./xpu_toolchain x86
For arm64(FT-2000+/64)
$ ./lite/tools/build.sh --arm_os=armlinux --arm_abi=armv8 --arm_lang=gcc --build_extra=ON --build_xpu=ON --xpu_sdk_root=./xpu_toolchain --with_log=ON full_publish
```
- 将编译生成的build.lite.x86/inference_lite_lib/cxx/include替换PaddleLite-linux-demo/libs/PaddleLite/amd64/include目录;
- 将编译生成的build.lite.x86/inference_lite_lib/cxx/include/lib/libpaddle_full_api_shared.so替换PaddleLite-linux-demo/libs/PaddleLite/amd64/lib/libpaddle_full_api_shared.so文件;
- 将编译生成的build.lite.armlinux.armv8.gcc/inference_lite_lib.armlinux.armv8.xpu/cxx/include替换PaddleLite-linux-demo/libs/PaddleLite/arm64/include目录;
- 将编译生成的build.lite.armlinux.armv8.gcc/inference_lite_lib.armlinux.armv8.xpu/cxx/lib/libpaddle_full_api_shared.so替换PaddleLite-linux-demo/libs/PaddleLite/arm64/lib/libpaddle_full_api_shared.so文件。
## 其它说明
- 如需更进一步的了解相关产品的信息,请联系欧阳剑ouyangjian@baidu.com;
- 百度昆仑的研发同学正在持续适配更多的Paddle算子,以便支持更多的Paddle模型。
......@@ -48,7 +48,7 @@ cuda的编译结果位于 `build_cuda/inference_lite_lib`
4、 `demo` 文件夹:c++ demo.
如果编译打开了python选项,则会在 `build_cuda/inference_lite_lib/python/lib/` 目录下生成 `lite_core.so`
如果编译打开了python选项,则会在 `build_cuda/inference_lite_lib/python/lib/` 目录下生成 `lite.so`
## 运行
......@@ -66,7 +66,7 @@ wget https://paddle-inference-dist.cdn.bcebos.com/PaddleLite/kite.jpg
二: 运行
**NOTE:**此处示例使用的是python接口。
**NOTE:** 此处示例使用的是python接口。
``` python
#-*- coding: utf-8 -*-
......@@ -75,7 +75,7 @@ import sys
import numpy as np
import cv2
sys.path.append('build_cuda/inference_lite_lib/python/lib')
from lite_core import *
from lite import *
def read_img(im_path, resize_h, resize_w):
im = cv2.imread(im_path).astype('float32')
......
# PaddleLite使用MTK APU预测部署
Paddle Lite已支持MTK APU的预测部署。
其接入原理是与之前华为NPU类似,即加载并分析Paddle模型,将Paddle算子转成MTK的Neuron adapter API(类似Android NN API)进行网络构建,在线生成并执行模型。
## 支持现状
### 已支持的芯片
- [MT8168](https://www.mediatek.cn/products/tablets/mt8168)/[MT8175](https://www.mediatek.cn/products/tablets/mt8175)及其他智能芯片。
### 已支持的设备
- MT8168-P2V1 Tablet。
### 已支持的Paddle模型
- [全量化MobileNetV1](https://paddlelite-demo.bj.bcebos.com/devices/mediatek/mobilenet_v1_int8_224_fluid.tar.gz)
### 已支持(或部分支持)的Paddle算子
- relu
- conv2d
- depthwise_conv2d
- elementwise_add
- elementwise_mul
- fc
- pool2d
- softmax
## 参考示例演示
### 测试设备(MT8168-P2V1 Tablet)
![mt8168_p2v1_tablet_front](https://paddlelite-demo.bj.bcebos.com/devices/mediatek/mt8168_p2v1_tablet_front.jpg)
![mt8168_p2v1_tablet_back](https://paddlelite-demo.bj.bcebos.com/devices/mediatek/mt8168_p2v1_tablet_back.jpg)
### 准备设备环境
- 由于需要依赖特定版本的firmware,感兴趣的同学通过MTK官网[https://www.mediatek.cn/about/contact-us](https://www.mediatek.cn/about/contact-us)提供的联系方式(类别请选择"销售"),获取测试设备和firmware;
### 准备交叉编译环境
- 为了保证编译环境一致,建议参考[源码编译](../user_guides/source_compile)中的Docker开发环境进行配置。
### 运行图像分类示例程序
-[https://paddlelite-demo.bj.bcebos.com/devices/mediatek/PaddleLite-android-demo.tar.gz](https://paddlelite-demo.bj.bcebos.com/devices/mediatek/PaddleLite-android-demo.tar.gz)下载示例程序,解压后清单如下:
```shell
- PaddleLite-android-demo
- image_classification_demo
- assets
- images
- tabby_cat.jpg # 测试图片
- labels
- synset_words.txt # 1000分类label文件
- models
- mobilenet_v1_int8_224_for_cpu.nb # 已通过opt转好的、适合arm cpu的mobilenetv1量化模型
- mobilenet_v1_int8_224_for_apu.nb # 已通过opt转好的、适合mtk apu的mobilenetv1量化模型
- shell # android shell端的示例程序
- CMakeLists.txt # 示例程序CMake脚本
- build
- image_classification_demo # 已编译好的android shell端的示例程序
- image_classification_demo.cc # 示例程序源码
- build.sh # 示例程序编译脚本
- run.sh # 示例程序运行脚本
- apk # 常规android应用程序
- app
- src
- main
- java # java层代码
- cpp # 自定义的jni实现
- app.iml
- build.gradle
- gradle
...
- libs
- PaddleLite
- arm64-v8a
- include # PaddleLite头文件
- lib
- libc++_shared.so
- libpaddle_light_api_shared.so # 预编译PaddleLite库
- OpenCV # OpenCV 4.2 for android
```
- Android shell端的示例程序
- 进入PaddleLite-android-demo/image_classification_demo/shell,直接执行./run.sh即可,注意:run.sh不能在docker环境执行,否则可能无法找到设备;
- 如果需要更改测试图片,可将图片拷贝到PaddleLite-android-demo/image_classification_demo/assets/images目录下,然后将run.sh的IMAGE_NAME设置成指定文件名即可;
- 如果需要重新编译示例程序,直接运行./build.sh即可,注意:build.sh的执行必须在docker环境中,否则可能编译出错;
- 需要说明的是,由于MTK APU暂时只支持NHWC的数据布局格式,而PaddleLite默认使用NCHW的数据布局格式,导致额外增加了预测中输入张量的NCHW到NHWC的转换,大约耗费8~9ms。
```shell
$ cd PaddleLite-android-demo/image_classification_demo/shell
$ ./run.sh
...
warmup: 5 repeat: 10, average: 30.998502 ms, max: 31.049002 ms, min: 30.937002 ms
results: 3
Top0 Egyptian cat - -0.122845
Top1 tabby, tabby cat - -0.122845
Top2 tiger cat - -0.544028
Preprocess time: 3.620000 ms
Prediction time: 30.998502 ms
Postprocess time: 0.069000 ms
[vpuBuffer] vpuMemAllocator::freeMem: type = 1, va = 0x7ed1b00000, pa = 0xfb3f9000, len = 255
[vpuBuffer] vpuMemAllocator::freeMem: type = 1, va = 0x7ed1af8000, pa = 0xfb3fa000, len = 255
[vpuBuffer] vpuMemAllocator::freeMem: type = 1, va = 0x7ed1af7000, pa = 0xf8ffe000, len = 255
[vpuBuffer] vpuMemAllocator::freeMem: type = 1, va = 0x7ed1af6000, pa = 0xf7bfe000, len = 255
[vpuBuffer] vpuMemAllocator::freeMem: type = 1, va = 0x7ed1af5000, pa = 0xf7bfd000, len = 255
[vpuBuffer] vpuMemAllocator::freeMem: type = 1, va = 0x7ed1b0c000, pa = 0xfb3fe000, len = 255
[vpuBuffer] vpuMemAllocator::freeMem: type = 1, va = 0x7ed1b0b000, pa = 0xfb3ff000, len = 255
[vpuBuffer] vpuMemAllocator::freeMem: type = 1, va = 0x7ed1b0a000, pa = 0xf31ff000, len = 255
[vpuBuffer] vpuMemAllocator::freeMem: type = 1, va = 0x7ed1b09000, pa = 0xfb3f6000, len = 255
[vpuBuffer] vpuMemAllocator::freeMem: type = 1, va = 0x7ed1b08000, pa = 0xf7bff000, len = 255
```
- 常规Android应用程序
- 安装Android Studio 3.4
- 打开Android Studio,在"Welcome to Android Studio"窗口点击"Open an existing Android Studio project",在弹出的路径选择窗口中进入"PaddleLite-android-demo/image_classification_demo/apk"目录,然后点击右下角的"Open"按钮即可导入工程;
- 通过USB连接Android手机、平板或开发板;
- 临时关闭selinux模式,允许app调用系统库;
```shell
$ adb root
# setenforce 0
```
- 待工程加载完成后,点击菜单栏的Build->Rebuild Project按钮,如果提示CMake版本不匹配,请点击错误提示中的'Install CMake xxx.xxx.xx'按钮,重新安装CMake,然后再次点击菜单栏的Build->Rebuild Project按钮;
- 待工程编译完成后,点击菜单栏的Run->Run 'App'按钮,在弹出的"Select Deployment Target"窗口选择已经连接的Android设备,然后点击"OK"按钮;
- 等待大约1分钟后(第一次时间比较长,需要耐心等待),app已经安装到设备上。默认使用ARM CPU模型进行预测,由于MT8168的CPU由四核Arm-Cortex A53组成,性能较一般手机的A7x系列要弱很多,如下图所示,只有6fps;
![mt8168_p2v1_tablet_cpu](https://paddlelite-demo.bj.bcebos.com/devices/mediatek/mt8168_p2v1_tablet_cpu.jpg)
- 点击app界面右下角的设置按钮,在弹出的设置页面点击"Choose pre-installed models",选择"mobilenet_v1_int8_for_apu",点击返回按钮后,app将切换到APU模型,如下图所示,帧率提高到14fps。
![mt8168_p2v1_tablet_apu](https://paddlelite-demo.bj.bcebos.com/devices/mediatek/mt8168_p2v1_tablet_apu.jpg)
### 更新模型
- 通过Paddle Fluid训练,或X2Paddle转换得到MobileNetv1 foat32模型[mobilenet_v1_fp32_224_fluid](https://paddlelite-demo.bj.bcebos.com/models/mobilenet_v1_fp32_224_fluid.tar.gz)
- 参考[模型量化-有校准数据训练后量化](../user_guides/post_quant_with_data)使用PaddleSlim对float32模型进行量化(注意:由于MTK APU只支持量化OP,在启动量化脚本时请注意相关参数的设置),最终得到全量化MobileNetV1模型[mobilenet_v1_int8_224_fluid](https://paddlelite-demo.bj.bcebos.com/devices/mediatek/mobilenet_v1_int8_224_fluid.tar.gz)
- 参考[模型转化方法](../user_guides/model_optimize_tool),利用opt工具转换生成MTK APU模型,仅需要将valid_targets设置为apu,arm即可。
```shell
$ ./opt --model_dir=mobilenet_v1_int8_224_fluid \
--optimize_out_type=naive_buffer \
--optimize_out=mobilenet_v1_int8_224_for_apu \
--valid_targets=apu,arm
```
- 注意:opt生成的模型只是标记了MTK APU支持的Paddle算子,并没有真正生成MTK APU模型,只有在执行时才会将标记的Paddle算子转成MTK Neuron adapter API调用实现组网,最终生成并执行模型。
### 更新支持MTK APU的Paddle Lite库
- 下载PaddleLite源码和APU DDK;
```shell
$ git clone https://github.com/PaddlePaddle/Paddle-Lite.git
$ cd Paddle-Lite
$ git checkout <release-version-tag>
$ wget https://paddlelite-demo.bj.bcebos.com/devices/mediatek/apu_ddk.tar.gz
$ tar -xvf apu_ddk.tar.gz
```
- 编译tiny_publish for MT8168-P2V1 Tablet
```shell
$ ./lite/tools/build.sh --arm_os=android --arm_abi=armv8 --arm_lang=gcc --android_stl=c++_shared --build_extra=ON --with_log=ON --build_apu=ON --apu_ddk_root=./apu_ddk tiny_publish
```
- 将编译生成的build.lite.android.armv8.gcc/inference_lite_lib.android.armv8.apu/cxx/include替换PaddleLite-android-demo/libs/PaddleLite/arm64-v8a/include目录;
- 将编译生成的build.lite.android.armv8.gcc/inference_lite_lib.android.armv8.apu/cxx/lib/libpaddle_light_api_shared.so替换PaddleLite-android-demo/libs/PaddleLite/arm64-v8a/lib/libpaddle_light_api_shared.so文件。
## 其它说明
- 由于涉及到License的问题,无法提供用于测试的firmware,我们深感抱歉。如果确实对此非常感兴趣,可以参照之前提到的联系方式,直接联系MTK的销售;
- MTK研发同学正在持续增加用于适配Paddle算子bridge/converter,以便适配更多Paddle模型。
......@@ -110,19 +110,91 @@ $ ./lite/tools/build_npu.sh --arm_os=android --arm_abi=armv7 --arm_lang=gcc --an
## 通过JAVA接口加载并执行NPU模型
**注意:由于华为手机root权限限制,现在仅支持JAVA接口加载和执行NPU模型**
- 使用方法和[Java实例](java_demo)一致,无需额外设置任何参数,只需将模型换成NPU模型即可。[Paddle-Lite-Demo](https://github.com/PaddlePaddle/Paddle-Lite-Demo)中的Image Classification Demo for Android是同时支持CPU和NPU两种模型的图像分类Demo。
注意:在拷贝libpaddle_lite_jni.so的时候,由于依赖HiAI DDK so和libc++_shared.so库,需要将HiAI DDK中ai_ddk_lib/lib或ai_ddk_lib/lib64目录下的所有so和libc++_shared.so,拷到libpaddle_lite_jni.so同级目录下。
## 通过C++接口加载并执行NPU模型
- 使用方法和[C++实例](cpp_demo)一致,同样无需额外设置任何参数,只需将模型换成NPU模型即可。
注意:1)不能使用安卓模拟器,需要使用真实设备,且必须是支持NPU的华为手机。2)在使用adb push命令向手机推送目标程序时,需要将HiAI DDK中ai_ddk_lib/lib或ai_ddk_lib/lib64目录下的所有so和libc++_shared.so,推送到目标程序同级目录下。
## 其它说明
- 华为达芬奇架构的NPU内部大量采用float16进行运算,因此,预测结果会存在偏差,但大部分情况下精度不会有较大损失,可参考[Paddle-Lite-Demo](https://github.com/PaddlePaddle/Paddle-Lite-Demo)中Image Classification Demo for Android对同一张图片CPU与NPU的预测结果。
- 华为Kirin 810/990 Soc搭载的自研达芬奇架构的NPU,与Kirin 970/980 Soc搭载的寒武纪NPU不一样,同样的,与Hi3559A、Hi3519A使用的NNIE也不一样,Paddle Lite只支持华为自研达芬奇架构NPU。
- 我们正在持续增加能够适配HiAI IR的Paddle算子bridge/converter,以便适配更多Paddle模型,同时华为研发同学也在持续对HiAI IR性能进行优化。
## 手动分割子图
### 背景
- Paddle-Lite已经支持了大量的华为NPU的算子,但是仍然不能满足所有模型的需求。对于一个有部分算子不支持的模型,Paddle-Lite会将模型划分为可以跑在NPU上的子图和跑在CPU上的子图,实现NPU和CPU自动调度的功能,通常情况下可以获得比较好的性能。在一些特殊情况下,模型会被自动划分为比较多的子图,导致CPU和NPU的切换开销很大,从而导致整体性能变差。因此,需要手动分割子图的功能来指定一些算子跑在CPU上,避免子图过多。
### 功能
- 通过配置文件来指定需要强制跑在CPU上的算子
### 使用方法
- 1、通过netron打开paddle模型文件,可以查看模型结构,获得算子的类型、输入名称。输出名称。
- 注意:Paddle-Lite会对模型进行优化,模型算子可以改变,需要以优化后的模型算子为准。后面会举例说明。
- 2、生成配置文件 ```split_cfg.txt```,记录需要跑在CPU上的算子信息。
- 每行一条OP记录信息,以冒号":"分隔"op名称","op输入名","op输出名",以逗号","分隔"op输入名"和"op输出名"中的不同var名。
- 可以部分省略输入或者输出名。比如:```op3:in3_var0```表示,指定类型为"op3",输入为"in3_var0"的算子;```op4```表示所有类型为"op4"的算子
- 例子1:
```
op0:in0_var0,in0_var1:out0_var0,out0_var1
op1:in1_var0,in1_var1:out1_var0
op2::out2_var0
op3:in3_var0
op4
```
- 例子2:
```
transpose:conv2d_22.tmp_1:transpose_0.tmp_0
```
![image](https://user-images.githubusercontent.com/50474132/80475316-4a5fda80-897b-11ea-910a-6aee13243387.png)
- 3、使用环境变量```SUBGRAPH_CUSTOM_PARTITION_CONFIG_FILE```指定配置文件的位置。
- 例如:
```
export SUBGRAPH_CUSTOM_PARTITION_CONFIG_FILE=/data/local/tmp/split_sfg.txt
```
- 4、以上步骤完成后,运行的模型中符合条件的算子将被强制跑在CPU上。
### 举例
- 以模型[image](https://paddlelite-demo.bj.bcebos.com/models/ssd_mobilenet_v1_pascalvoc_fp32_300_fluid.tar.gz)为例
- 1、可以使用netron查看模型
- 2、初步分析
- 下图是ssd_mobilenet_v1中的部分结构。其中红色部分暂时不支持在NPU上运行,蓝色部分可能NPU上的性能不理想。此时,如果直接让预测库自动调度的话,可能会分成多个子图,而且整体性能不佳。因此,可以将蓝色部分和绿色部分整体指定在CPU上运行,让其他部分自动运行在NPU上(红色部分会自动在CPU上运行)。
![ssd_mobilenet_v1_example](https://user-images.githubusercontent.com/50474132/80453173-525b5280-895a-11ea-847f-c7dd5b5799de.png)
- 3、使用opt转换模型
- opt转换过程中会打印log信息。在log中搜索```digraph G```和```// end G```可以找到优化后的模型图。
![image](https://user-images.githubusercontent.com/50474132/80454098-145f2e00-895c-11ea-9f16-dde1483a9beb.png)
![image](https://user-images.githubusercontent.com/50474132/80454123-1de89600-895c-11ea-86b9-a62d78a6616d.png)
- 将从```digraph G```开始的,到```// end G```结束的整段模型图信息,保存到```.dot```格式的文件中。可以用```graphviz```打开查看,或者在[网页版](http://dreampuf.github.io/GraphvizOnline/)查看。
![image](https://user-images.githubusercontent.com/50474132/80454841-47ee8800-895d-11ea-9531-5689c5560fcb.png)
- 在此处确认需要被指定的算子是否被优化了。(期望是被指定的算子都还独立存在,如果被融合为了一个算子,需要指定此时融合后的算子)。
- 4、写配置文件
- 在配置文件中指定可以支持NPU但是需要指定在CPU上运行的算子。
```
reshape
transpose
concat
softmax
```
- 由于这些算子都指定在NPU上运行,因此不需要特意配置算子的输入输出名称。
- 5、指定配置文件路径
- 通过```export SUBGRAPH_CUSTOM_PARTITION_CONFIG_FILE=your_split_config_file```的方式实现。
- 6、性能测试
- 设备:华为mate30 5G
- HIAI ddk版本:320
- 性能:CPU约71.8ms,NPU约16.6ms。
......@@ -2,53 +2,57 @@
Lite支持在Android系统上运行基于OpenCL的程序,目前支持Ubuntu环境下armv8、armv7的交叉编译。
## 编译
## 1. 编译
### 编译环境
### 1.1 编译环境
1. Docker 容器环境;
2. Linux(推荐 Ubuntu 16.04)环境。
详见 **源码编译指南-环境准备** 章节。
### 编译Paddle-Lite OpenCL库范例
### 1.2 编译Paddle-Lite OpenCL库范例
注:以android-armv8-opencl的目标、Docker容器的编译开发环境为例,CMake3.10,android-ndk-r17c位于`/opt/`目录下。
注:以android/armv7/opencl的目标、Docker容器的编译开发环境为例,CMake3.10,android-ndk-r17c位于`/opt/`目录下。
#### 针对 Lite 用户的编译命令(无单元测试,有编译产物)
#### 针对 Lite 用户的编译命令(无单元测试,有编译产物,适用于benchmark)
- `arm_os`: `[android]`,目前不支持linux
- `with_opencl`: `[ON | OFF]`,编译OpenCL必选
- `arm_abi`: `[armv7 | armv8]`
- `arm_lang`: `[gcc]`,目前不支持clang
- `build_extra`: `[OFF | ON]`,编译全量op和kernel,体积会大,编译时间长;
- `toolchain`: `[gcc | clang]`
- `build_extra`: `[OFF | ON]`,编译全量op和kernel,包含控制流NLP相关的op和kernel体积会大,编译时间长;
- `build_cv`: `[OFF | ON]`,编译arm cpu neon实现的的cv预处理模块;
- `android_stl`: `[c++_shared | c++_static]`,paddlelite的库以何种方式链接`android_stl`,选择`c++_shared`得到的动态库体积更小,但使用时候记得上传paddlelite所编译版本(armv7或armv8)一致的`libc++_shared.so`(来自Android-NDK);
注:调用`./lite/tools/build.sh`执行编译。
- `android_stl`: `[c++_shared | c++_static | gnu_static | gnu_shared]`,paddlelite的库以何种方式链接`android_stl`,选择`c++_shared`得到的动态库体积更小,但使用时候记得上传paddlelite所编译版本(armv7或armv8)一致的`libc++_shared.so`。默认使用`c++_static`
```bash
# 假设当前位于处于Lite源码根目录下
######################################
# 假设当前位于处于Lite源码根目录下 #
######################################
# 导入NDK_ROOT变量,注意检查您的安装目录若与本示例不同
# 导入NDK_ROOT变量,注意检查NDK安装目录若与本示例是否不同
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/build.sh \
--arm_os=android \
--arm_abi=armv8 \
--arm_lang=gcc \
--build_extra=OFF \
--build_cv=OFF \
--android_stl=c++_shared \
opencl
# 设置编译参数并开始编译
./lite/tools/build_android.sh \
--arch=armv7 \
--toolchain=clang \
--with_cv=OFF \
--with_log=OFF \
--with_extra=OFF \
--with_opencl=ON
# 注:编译帮助请执行: ./lite/tools/build_android.sh help
```
注:该方式的编译产物中的`demo/cxx/mobile_light`适用于做benchmark,该过程不会打印开发中加入的log,注意需要提前转好模型。关于使用,详见下文**运行示例1: 编译产物demo示例**
#### 针对 Lite 开发者的编译命令(有单元测试,编译产物)
注:调用`./lite/tools/ci_build.sh`执行编译,该命令会编译armv7和armv8的opencl库。虽然有编译产物,但因编译单元测试,编译产物包体积可能较大,不推荐使用。
注:调用`./lite/tools/ci_build.sh`执行编译,该命令会编译armv7和armv8的opencl库。虽然有编译产物,但因编译单元测试,编译产物包体积可能较大,生产环境不推荐使用。
```bash
# 假设当前位于处于Lite源码根目录下
......@@ -70,13 +74,13 @@ rm ./lite/api/paddle_use_ops.h
注:如果要调试cl kernel,假设已经完成上述脚本编译(已生成cmake文件)。调试只需要修改`./lite/backends/opencl/cl_kernel/`下对应的kernel文件,保存后在项目根目录执行`python ./lite/tools/cmake_tools/gen_opencl_code.py ./lite/backends/opencl/cl_kernel ./lite/backends/opencl/opencl_kernels_source.cc`,该命令会自动将修改后,再切到build目录下执行`make publish_inference`或者你要编译的单测的可执行文件名,cl kernel文件的内容会随着编译自动打包到产物包如 .so 中或者对应单测可执行文件中。
### 编译产物说明
### 1.3 编译产物说明
编译产物位于`build.lite.android.armv8.gcc.opencl`下的`inference_lite_lib.android.armv8.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_full`:使用cxx config,可直接加载fluid模型,若使用OpenCL需要在`mobilenetv1_full_api.cc`代码里开启`DEMO_USE_OPENCL`的宏,详细见该文件的代码注释;
- `mobile_light`:使用mobile config,只能加载`model_optimize_tool`优化过的模型。
注:`opencl`实现的相关kernel已经打包到动态库中。
......@@ -119,47 +123,48 @@ rm ./lite/api/paddle_use_ops.h
## 运行示例
下面以android、ARMv8、gcc的环境为例,介绍3个示例,分别如何在手机上执行基于OpenCL的ARM GPU推理过程。
## 2. 运行示例
### 运行示例1: 编译产物demo示例
下面以android的环境为例,介绍3个示例,分别如何在手机上执行基于OpenCL的ARM GPU推理过程。
```bash
######################################################################
# 编译mobile_light的demo #
######################################################################
# 步骤: #
# 0.确保编译Paddle-Lite时编译了OpenCL; #
# 1.编译model_optimize_tool并对模型优化, `targets`参数为`opencl`; #
# 2.在产物目录`demo/cxx/mobile_light`下编译`mobile_light`的demo; #
# 3.上传demo, 模型文件到手机; #
# 4.运行demo得到预期结果. #
######################################################################
# 在/data/local/tmp目录下创建OpenCL文件目录
adb shell mkdir -p /data/local/tmp/opencl
### 2.1 运行示例1: 编译产物demo示例和benchmark
# 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/mobilenetv1_opt \
--valid_targets=opencl
需要提前用模型优化工具opt转好模型(下面假设已经转换好模型,且模型名为`mobilenetv1_opencl_fp32_opt_releasev2.6_b8234efb_20200423.nb`)。编译脚本为前文**针对 Lite 用户的编译命令(无单元测试,有编译产物,适用于benchmark)**
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/mobilenetv1_opt.nb /data/local/tmp/opencl/
# use mobile_light run mobilenet_v1
adb shell "export GLOG_v=1; \
/data/local/tmp/opencl/mobilenetv1_light_api \
/data/local/tmp/opencl/mobilenetv1_opt.nb"
```bash
#################################
# 假设当前位于build.xxx目录下 #
#################################
# prepare enviroment on phone
adb shell mkdir -p /data/local/tmp/opencl/
# build demo
cd inference_lite_lib.android.armv7.opencl/demo/cxx/mobile_light/
make
cd -
# push executable binary, library to device
adb push inference_lite_lib.android.armv7.opencl/demo/cxx/mobile_light/mobilenetv1_light_api /data/local/tmp/opencl/
adb shell chmod +x /data/local/tmp/opencl/mobilenetv1_light_api
adb push inference_lite_lib.android.armv7.opencl/cxx/lib/libpaddle_light_api_shared.so /data/local/tmp/opencl/
# push model with optimized(opt) to device
adb push ./mobilenetv1_opencl_fp32_opt_releasev2.6_b8234efb_20200423.nb /data/local/tmp/opencl/
# run demo on device
adb shell "export LD_LIBRARY_PATH=/data/local/tmp/opencl/; \
/data/local/tmp/opencl/mobilenetv1_light_api \
/data/local/tmp/opencl/mobilenetv1_opencl_fp32_opt_releasev2.6_b8234efb_20200423.nb \
1 3 224 224 \
100 10 0" # round=100, warmup=10, print_output_tensor=0
```
**注:** `GLOG_v`是指定需要显示VLOG的日志级别,默认为0。权重参数会在第一次运行时加载,所以第一次执行时间略长。一般将warmup的值设为10,repeats值设为多次。
**注:** 权重参数会在第一次运行时加载,所以第一次执行时间略长。一般将warmup的值设为10,repeats值设为多次。
### 2.2 运行示例2: test_mobilenetv1单元测试
### 运行示例2: test_mobilenetv1单元测试
编译脚本为前文**针对 Lite 开发者的编译命令(有单元测试,编译产物)**
- **运行文件准备**
......@@ -181,27 +186,27 @@ adb push build.lite.android.armv8.gcc.opencl/lite/api/test_mobilenetv1 /data/loc
adb shell chmod +x /data/local/tmp/opencl/test_mobilenetv1
adb shell "export GLOG_v=1; \
/data/local/tmp/opencl-image/test_mobilenetv1 \
--model_dir=/data/local/tmp/opencl-image/mobilenetv1_fluid/ \
/data/local/tmp/opencl/test_mobilenetv1 \
--model_dir=/data/local/tmp/opencl/mobilenetv1_fluid/ \
--warmup=10 \
--repeats=100"
```
### 运行示例3: test_layout_opencl单元测试
### 2.3 运行示例3: test_layout_opencl单元测试
编译脚本为前文**针对 Lite 开发者的编译命令(有单元测试,编译产物)**
```bash
adb shell mkdir -p /data/local/tmp/opencl
adb push build.lite.android.armv8.gcc.opencl/lite/kernels/opencl/test_layout_opencl /data/local/tmp/opencl/
adb shell chmod +x /data/local/tmp/opencl/test_layout_opencl
adb shell "export GLOG_v=4; \
/data/local/tmp/opencl/test_layout_opencl"
```
### 如何在Code中使用
见运行示例1的demo代码:
## 3. 如何在Code中使用
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).
即编译产物`demo/cxx/mobile_light`目录下的代码,在线版参考GitHub仓库[./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);
注:这里给出的链接会跳转到线上最新develop分支的代码,很可能与您本地的代码存在差异,建议参考自己本地位于`lite/demo/cxx/`目录的代码,查看如何使用。
......
# PaddleLite使用RK NPU预测部署
Paddle Lite已支持RK NPU的预测部署。
其接入原理是与之前华为NPU类似,即加载并分析Paddle模型,将Paddle算子转成RK组网API进行网络构建,在线生成并执行模型。
## 支持现状
### 已支持的芯片
- RK1808, RK1806,暂时不支持RK3399Pro。
### 已支持的设备
- RK1808/1806 EVB。
### 已支持的Paddle模型
- [全量化MobileNetV1](https://paddlelite-demo.bj.bcebos.com/devices/rockchip/mobilenet_v1_int8_224_fluid.tar.gz)
### 已支持(或部分支持)的Paddle算子
- relu
- conv2d
- depthwise_conv2d
- pool2d
- fc
- softmax
- batch_norm
- concat
- elementwise_add
- elementwise_sub
- elementwise_mul
- elementwise_div
## 参考示例演示
### 测试设备(RK1808 EVB)
![rk1808_evb_front](https://paddlelite-demo.bj.bcebos.com/devices/rockchip/rk1808_evb_front.jpg)
![rk1808_evb_back](https://paddlelite-demo.bj.bcebos.com/devices/rockchip/rk1808_evb_back.jpg)
### 准备设备环境
- 需要依赖特定版本的firmware,请参照[rknpu_ddk](https://github.com/airockchip/rknpu_ddk)的说明对设备进行firmware的更新;
- 由于RK1808 EVB在刷firmware后,只是一个纯净的Linux系统,无法像Ubuntu那样使用apt-get命令方便的安装软件,因此,示例程序和PaddleLite库的编译均采用交叉编译方式;
- 将MicroUSB线插入到设备的MicroUSB OTG口,就可以使用Android的adb命令进行设备的交互,再也不用配置网络使用ssh或者通过串口的方式访问设备了,这个设计非常赞!
### 准备交叉编译环境
- 为了保证编译环境一致,建议参考[源码编译](../user_guides/source_compile)中的Docker开发环境进行配置。
### 运行图像分类示例程序
-[https://paddlelite-demo.bj.bcebos.com/devices/rockchip/PaddleLite-linux-demo.tar.gz](https://paddlelite-demo.bj.bcebos.com/devices/rockchip/PaddleLite-linux-demo.tar.gz)下载示例程序,解压后清单如下:
```shell
- PaddleLite-linux-demo
- image_classification_demo
- assets
- images
- tabby_cat.jpg # 测试图片
- tabby_cat.raw # 已处理成raw数据的测试图片
- labels
- synset_words.txt # 1000分类label文件
- models
- mobilenet_v1_int8_224_for_cpu.nb # 已通过opt转好的、适合arm cpu的mobilenetv1量化模型
- mobilenet_v1_int8_224_for_rknpu.nb # 已通过opt转好的、适合rknpu的mobilenetv1量化模型
- shell
- CMakeLists.txt # 示例程序CMake脚本
- build
- image_classification_demo # 已编译好的示例程序
- image_classification_demo.cc # 示例程序源码
- convert_to_raw_image.py # 将测试图片保存为raw数据的python脚本
- build.sh # 示例程序编译脚本
- run.sh # 示例程序运行脚本
- libs
- PaddleLite
- arm64
- include # PaddleLite头文件
- lib
- libGAL.so # RK DDK库
- libOpenVX.so
- libVSC.so
- librknpu_ddk.so
- libgomp.so.1 # gnuomp库
- libpaddle_light_api_shared.so # 预编译PaddleLite库
- armhf
- include # PaddleLite头文件
- lib
- libGAL.so
- libOpenVX.so
- libVSC.so
- librknpu_ddk.so
- libgomp.so.1
- libpaddle_light_api_shared.so
```
- 进入PaddleLite-linux-demo/image_classification_demo/shell,直接执行./run.sh arm64即可,注意:run.sh不能在docker环境执行,否则无法找到设备;
```shell
$ cd PaddleLite-linux-demo/image_classification_demo/shell
$ ./run.sh arm64 # For RK1808 EVB
$ ./run.sh armhf # For RK1806 EVB
...
warmup: 5 repeat: 10, average: 6.499500 ms, max: 6.554000 ms, min: 6.468000 ms
results: 3
Top0 Egyptian cat - 0.532328
Top1 tabby, tabby cat - 0.345136
Top2 tiger cat - 0.111146
Preprocess time: 2.414000 ms
Prediction time: 6.499500 ms
Postprocess time: 0.414000 ms
```
- 如果需要更改测试图片,可通过convert_to_raw_image.py工具生成;
- 如果需要重新编译示例程序,直接运行./build.sh即可,注意:build.sh的执行必须在docker环境中,否则可能编译出错。
### 更新模型
- 通过Paddle Fluid训练,或X2Paddle转换得到MobileNetv1 foat32模型[mobilenet_v1_fp32_224_fluid](https://paddlelite-demo.bj.bcebos.com/models/mobilenet_v1_fp32_224_fluid.tar.gz)
- 参考[模型量化-有校准数据训练后量化](../user_guides/post_quant_with_data)使用PaddleSlim对float32模型进行量化(注意:由于RK NPU只支持tensor-wise的全量化模型,在启动量化脚本时请注意相关参数的设置),最终得到全量化MobileNetV1模型[mobilenet_v1_int8_224_fluid](https://paddlelite-demo.bj.bcebos.com/devices/rockchip/mobilenet_v1_int8_224_fluid.tar.gz)
- 参考[模型转化方法](../user_guides/model_optimize_tool),利用opt工具转换生成RKNPU模型,仅需要将valid_targets设置为rknpu,arm即可。
```shell
$ ./opt --model_dir=mobilenet_v1_int8_224_fluid \
--optimize_out_type=naive_buffer \
--optimize_out=mobilenet_v1_int8_224_for_rknpu \
--valid_targets=rknpu,arm
```
- 注意:opt生成的模型只是标记了RKNPU支持的Paddle算子,并没有真正生成RK NPU模型,只有在执行时才会将标记的Paddle算子转成RK NPU组网API,最终生成并执行模型。
### 更新支持RK NPU的Paddle Lite库
- 下载PaddleLite源码和RK DDK;
```shell
$ git clone https://github.com/PaddlePaddle/Paddle-Lite.git
$ cd Paddle-Lite
$ git checkout <release-version-tag>
$ git clone https://github.com/airockchip/rknpu_ddk.git
```
- 编译full_publish and tiny_publish for RK1808 and RK1806 EVB
```shell
For RK1808 EVB
$ ./lite/tools/build.sh --arm_os=armlinux --arm_abi=armv8 --arm_lang=gcc --build_extra=ON --with_log=ON --build_rknpu=ON --rknpu_ddk_root=./rknpu_ddk full_publish
$ ./lite/tools/build.sh --arm_os=armlinux --arm_abi=armv8 --arm_lang=gcc --build_extra=ON --with_log=ON --build_rknpu=ON --rknpu_ddk_root=./rknpu_ddk tiny_publish
For RK1806 EVB
$ ./lite/tools/build.sh --arm_os=armlinux --arm_abi=armv7 --arm_lang=gcc --build_extra=ON --with_log=ON --build_rknpu=ON --rknpu_ddk_root=./rknpu_ddk full_publish
$ ./lite/tools/build.sh --arm_os=armlinux --arm_abi=armv7 --arm_lang=gcc --build_extra=ON --with_log=ON --build_rknpu=ON --rknpu_ddk_root=./rknpu_ddk tiny_publish
```
- 将编译生成的build.lite.armlinux.armv8.gcc/inference_lite_lib.armlinux.armv8.rknpu/cxx/include替换PaddleLite-linux-demo/libs/PaddleLite/arm64/include目录;
- 将编译生成的build.lite.armlinux.armv8.gcc/inference_lite_lib.armlinux.armv8.rknpu/cxx/lib/libpaddle_light_api_shared.so替换PaddleLite-linux-demo/libs/PaddleLite/arm64/lib/libpaddle_light_api_shared.so文件;
- 将编译生成的build.lite.armlinux.armv7.gcc/inference_lite_lib.armlinux.armv7.rknpu/cxx/include替换PaddleLite-linux-demo/libs/PaddleLite/armhf/include目录;
- 将编译生成的build.lite.armlinux.armv7.gcc/inference_lite_lib.armlinux.armv7.rknpu/cxx/lib/libpaddle_light_api_shared.so替换PaddleLite-linux-demo/libs/PaddleLite/armhf/lib/libpaddle_light_api_shared.so文件。
## 其它说明
- RK研发同学正在持续增加用于适配Paddle算子bridge/converter,以便适配更多Paddle模型。
# PaddleLite使用X86预测部署
## 一、Docker或者Linux环境
Paddle-Lite 支持在Docker或Linux环境编译x86预测库。环境搭建参考[环境准备](../user_guides/source_compile)
(注意:非docker Linux环境需要是Ubuntu16.04)
## 编译
### 编译
1、 下载代码
```bash
# 下载Paddle-Lite源码
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
# 切换到release分支
git checkout release/v2.3
git checkout release/v2.6.0
```
2、 源码编译
......@@ -18,9 +21,12 @@ git checkout release/v2.3
```bash
cd Paddle-Lite
./lite/tools/build.sh x86
# 其他可选择编译选项
# --with_log=OFF 关闭LOG信息输出
```
## 编译结果说明
### 编译结果说明
x86编译结果位于 `build.lite.x86/inference_lite_lib`
**具体内容**说明:
......@@ -31,35 +37,68 @@ x86编译结果位于 `build.lite.x86/inference_lite_lib`
- `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 功能的动态库
- 静态库文件:
- `libpaddle_api_full_bundled.a`full_api 静态库
- `libpaddle_api_light_bundled.a`light_api 静态库
- 态库文件:
- `libpaddle_full_api_shared.so`full_api 动态库
- `libpaddle_light_api_shared.so`light_api 动态库
3、 `third_party` 文件夹:第三方库文件
3、 `third_party` 文件夹:依赖的第三方预测库mklml
## x86预测API使用示例
- mklml : Paddle-Lite预测库依赖的mklml数学库
1、我们提供Linux环境下x86 API运行mobilenet_v1的示例:[mobilenet_full_x86demo](https://paddlelite-data.bj.bcebos.com/x86/mobilenet_full_x86demo.zip)。下载解压后内容如下:
4、 `demo/cxx`文件夹:x86预测库的C++ 示例demo
![](https://paddlelite-data.bj.bcebos.com/x86/x86-doc/demo.png)
- `mobilenetv1_full` :使用full_api 执行mobilenet_v1预测的C++ demo
- `mobilenetv1_light` :使用light_api 执行mobilenet_v1预测的C++ demo
`mobilenet_v1`为模型文件、`lib``include`分别是Paddle-Lite的预测库和头文件、`third_party`下是编译时依赖的第三方库`mklml``mobilenet_full_api.cc`是x86示例的源代码、`build.sh`为编译的脚本。
2、demo内容与使用方法
### x86预测API使用示例
1、`mobilenetv1_full`目录结构
```bash
mobilenetv1_full/
|-- CMakeLists.txt
|-- build.sh
`-- mobilenet_full_api.cc
```
本demo使用cmake构建`CMakeLists.txt`为cmake脚本,`mobilenet_full_api.cc`是x86示例的源代码、`build.sh`为编译的脚本。
2、demo使用方法
``` bash
# 1、编译
cd mobilenetv1_full
sh build.sh
```
编译结果为当前目录下的 `mobilenet_full_api `
``` bash
# 2、执行预测
mobilenet_full_api mobilenet_v1
./mobilenet_full_api ./mobilenet_v1
```
下载并解压模型[`mobilenet_v1`](http://paddle-inference-dist.bj.bcebos.com/mobilenet_v1.tar.gz)到当前目录,执行以上命令进行预测。
```bash
# 3、执行demo后输出结果如下,全一输入下mobilenet_v1的预测结果
Output shape 1000
Output[0]: 0.000191312
Output[100]: 0.000159713
Output[200]: 0.000264313
Output[300]: 0.000210793
Output[400]: 0.00103236
Output[500]: 0.000110071
Output[600]: 0.00482924
Output[700]: 0.00184533
Output[800]: 0.000202116
Output[900]: 0.000585591
```
`mobilenet_v1`为当前目录下的模型路径,`mobilenet_full_api`为第一步编译出的可执行文件。
3、示例源码`mobilenet_full_api.cc`
......@@ -121,3 +160,83 @@ int main(int argc, char** argv) {
}
```
## 二、Windows环境
### 环境准备
#### 编译环境需求
- Windows 10 专业版
- 目前Windows暂不支持GPU模式
- *Python 版本 2.7/3.5.1+/3.6/3.7 (64 bit)*
- *pip 或 pip3 版本 9.0.1+ (64 bit)*
- *Visual Studio 2015 Update3*
#### 安装步骤
1. cmake 需要3.15版本, 可在官网[下载](https://cmake.org/download/),并添加到环境变量中。
2. python 需要2.7 及以上版本, 可在官网[下载](https://www.python.org/download/releases/2.7/)
3. git可以在官网[下载](https://gitforwindows.org/),并添加到环境变量中
### 编译
1、 下载代码
```bash
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
# 切换到release分支
git checkout release/v2.3
```
2、 源码编译
```bash
cd Paddle-Lite
lite/tools/build_windows.bat with_extra with_python with_profile
```
编译脚本`lite/tools/build.bat`,追加参数说明:
| 参数 | 介绍 | 值 |
|-----------|-------------|-------------|
| with_extra | 可选,是否编译全量预测库(默认为OFF)。详情可参考[预测库说明](./library.html)。 | `ON``OFF` |
| with_python | 可选,是否编译python预测库(默认为OFF) 。 | `ON``OFF` |
| with_profile | 可选,是否支持分析器模式(默认为OFF) 。 | `ON``OFF` |
### 编译结果
x86编译结果位于 `build.lite.x86/inference_lite_lib`
**具体内容**说明:
1、 `bin`文件夹:可执行工具文件 `test_model_bin`
2、 `cxx`文件夹:包含c++的库文件与相应的头文件
- `include` : 头文件
- `lib` : 库文件
- 打包的静态库文件:
- `libpaddle_api_full_bundled.lib` :full_api 静态库
- `libpaddle_api_light_bundled.lib` :light_api 静态库
3、 `third_party` 文件夹:第三方库文件
### x86预测API使用示例
1、我们提供Windows环境下x86 API运行mobilenet_v1的示例:[mobilenet_full_x86demo](https://paddlelite-data.bj.bcebos.com/x86/mobilenet_full_x86demo.zip)。下载解压后内容如下>:
![](https://paddlelite-data.bj.bcebos.com/x86/x86-doc/demo.png)
`mobilenet_v1`为模型文件、`lib``include`分别是Paddle-Lite的预测库和头文件、`third_party`下是编译时依赖的第三方库`mklml``mobilenet_full_api.cc`是x86示例的源代码、`build.bat`为编译的脚本。
2、demo内容与使用方法
``` bash
# 1、编译(需在vs2015的命令窗口执行该脚本)
build.bat
```
编译结果为当前目录下的 `Release\\mobilenet_full_api.exe`
``` bash
# 2、执行预测
Release\\mobilenet_full_api.exe ..\mobilenet_v1
```
`mobilenet_v1`为模型路径,`mobilenet_full_api.exe`为第一步编译出的可执行文件。
......@@ -54,6 +54,9 @@ Welcome to Paddle-Lite's documentation!
demo_guides/opencl
demo_guides/fpga
demo_guides/npu
demo_guides/baidu_xpu
demo_guides/rockchip_npu
demo_guides/mediatek_apu
.. toctree::
:maxdepth: 1
......
# 编译Android预测库
**注意:本编译方法只适用于release/v2.6.0之后版本(包括 v2.6.0)**
安装了Android的编译环境,可以下载并编译 Paddle-Lite源码
```shell
# 1. 下载Paddle-Lite源码 并切换到release分支
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
cd Paddle-Lite && git checkout release/v2.3
# 2. 编译Paddle-Lite Android预测库 (armv8, gcc编译, 静态链接ndk stl)
./lite/tools/build_android.sh
```
### 编译结果
位于`Paddle-Lite/build.lite.android.armv8.gcc/inference_lite_lib.android.armv8`:
```shell
inference_lite_lib.android.armv8/
|-- cxx C++ 预测库和头文件
| |-- include C++ 头文件
| | |-- 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 C++预测库
| |-- libpaddle_api_light_bundled.a C++静态库
| `-- libpaddle_light_api_shared.so C++动态库
|-- java Java预测库
| |-- jar
| | `-- PaddlePredictor.jar
| |-- so
| | `-- libpaddle_lite_jni.so
| `-- src
|-- demo C++和Java示例代码
| |-- cxx C++ 预测库demo
| `-- java Java 预测库demo
```
### 编译命令
- 默认编译方法: (armv8, gcc, c++_static)
``` shell
./lite/tools/build_android.sh
```
- 打印 help 信息:
```shell
./lite/tools/build_android.sh help
```
- 其他可选编译命令:
```shell
--arch: (armv8|armv7) arm版本,默认为armv8
--toolchain: (gcc|clang) 编译器类型,默认为gcc
--android_stl: (c++_static|c++_shared|gnu_static|gnu_shared) NDK stl库链接方法,默认为静态链接c++_static
--with_java: (OFF|ON) 是否编译Java预测库, 默认为 ON
--with_cv: (OFF|ON) 是否编译CV相关预处理库, 默认为 OFF
--with_log: (OFF|ON) 是否输出日志信息, 默认为 ON
--with_extra: (OFF|ON) 是否编译OCR或NLP相关模型的kernel&OP,默认为OFF,只编译CV模型相关kernel&OP
```
- 裁剪预测库方法(只编译模型中的kernel&OP,降低预测库体积):
```shell
./lite/tools/build_android.sh --with_strip=ON --opt_model_dir=YourOptimizedModelDir
```
```shell
--with_strip: (OFF|ON); 是否根据输入模型裁剪预测库,默认为OFF
--opt_model_dir: 输入模型的绝对路径,需要为opt转化之后的模型
```
详情请参考: [裁剪预测库](https://paddle-lite.readthedocs.io/zh/latest/user_guides/library_tailoring.html)
- 编译 Android npu 预测库方法:
```shell
./lite/tools/build_android.sh --with_huawei_kirin_npu=ON --huawei_kirin_npu_sdk_root=YourNpuSdkPath
```
```shell
--with_huawei_kirin_npu: (OFF|ON); 是否编译编译huawei_kirin_npu 的预测库,默认为OFF
--huawei_kirin_npu_sdk_root: `huawei HiAi DDK`文件的绝对路径,可从下面网址下载:
https://developer.huawei.com/consumer/cn/hiai/
```
详情请参考:[PaddleLite使用NPU(华为)预测部署](https://paddle-lite.readthedocs.io/zh/latest/demo_guides/npu.html)
- 编译Android opencl 预测库方法:(armv8, gcc, c++_static)
```shell
./lite/tools/build_android.sh --with_opencl=ON
```
```shell
--with_opencl: (OFF|ON); 是否编译opencl预测库, 默认为 OFF
```
# 编译Linux预测库
**注意:本编译方法只适用于release/v2.6.0之后版本(包括 v2.6.0)**
**注意:本编译方法暂时只适用于ARM的设备**
安装了ArmLinux的编译环境,可以下载并编译 Paddle-Lite源码
```shell
# 1. 下载Paddle-Lite源码 并切换到release分支
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
cd Paddle-Lite && git checkout release/v2.6
# 2. 编译Paddle-Lite Android预测库 (armv8, gcc编译)
./lite/tools/build_linux.sh
```
### 编译结果
位于 `Paddle-Lite/build.lite.linux.armv8.gcc/inference_lite_lib.armlinux.armv8` :
```shell
inference_lite_lib.armlinux.armv8/
|-- cxx C++ 预测库和头文件
| |-- include C++ 头文件
| | |-- 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 C++预测库
| |-- libpaddle_api_light_bundled.a C++静态库
| `-- libpaddle_light_api_shared.so C++动态库
|
|-- demo
| `-- python python预测库demo
|
|-- python Python预测库(需要打开with_python选项)
| |-- install
| | `-- dist
| | `-- paddlelite-*.whl python whl包
| |-- lib
| `-- lite.so python预测库
```
### 编译命令
- 默认编译方法: (armv8, gcc)
```shell
./lite/tools/build_linux.sh
```
- 打印 help 信息:
```shell
./lite/tools/build_linux.sh help
```
- 其他可选编译命令:
```shell
--arch: (armv8|armv7|armv7hf) arm版本,默认为armv8
--toolchain: (gcc|clang) 编译器类型,默认为gcc
--with_extra: (OFF|ON) 是否编译OCR或NLP相关模型的kernel&OP,默认为OFF,只编译CV模型相关kernel&OP
--with_python: (OFF|ON) 是否编译python预测库, 默认为 OFF
--with_cv: (OFF|ON) 是否编译CV相关预处理库, 默认为 OFF
--with_log: (OFF|ON) 是否输出日志信息, 默认为 ON
```
- 裁剪预测库方法(只编译模型中的kernel&OP,降低预测库体积):
```shell
./lite/tools/build_linux.sh --with_strip=ON --opt_model_dir=YourOptimizedModelDir
```
```shell
--with_strip: (OFF|ON); 是否根据输入模型裁剪预测库,默认为OFF
--opt_model_dir: 输入模型的绝对路径,需要为opt转化之后的模型
```
详情请参考: [裁剪预测库](https://paddle-lite.readthedocs.io/zh/latest/user_guides/library_tailoring.html)
- 使用 rockchip npu 方法:
```shell
--with_rockchip_npu: (OFF|ON); 是否编译编译 huawei_kirin_npu 的预测库,默认为OFF
--rockchip_npu_sdk_root: `rockchip_npu DDK`文件的绝对路径
```
详情请参考:[PaddleLite使用RK NPU预测部署](https://paddle-lite.readthedocs.io/zh/latest/demo_guides/rockchip_npu.html)
- 使用 baidu xpu 方法:
```shell
--with_baidu_xpu: (OFF|ON); 是否编译编译 baidu_xpu 的预测库,默认为OFF
--baidu_xpu_sdk_root: `baidu_xpu DDK`文件的绝对路径
```
详情请参考:[PaddleLite使用百度XPU预测部署](https://paddle-lite.readthedocs.io/zh/latest/demo_guides/baidu_xpu.html)
# 编译iOS预测库
**注意:本编译方法只适用于release/v2.6.0之后版本(包括 v2.6.0)**
安装了iOS的编译环境,可以下载并编译 Paddle-Lite源码
```shell
# 1. 下载Paddle-Lite源码 并切换到release分支
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
cd Paddle-Lite && git checkout release/v2.6.0
# 2. 编译Paddle-Lite Android预测库 (armv8, gcc编译, 静态链接ndk stl)
./lite/tools/build_ios.sh
```
### 编译结果
位于`Paddle-Lite/build.ios.ios64.armv8/inference_lite_lib.ios64.armv8`:
```shell
inference_lite_lib.ios64.armv8 iOS预测库和头文件
|-- include C++头文件
| |-- 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 C++预测库(静态库)
`-- libpaddle_api_light_bundled.a
```
### 编译命令
- 默认编译方法: (armv8)
``` shell
./lite/tools/build_ios.sh
```
- 打印 help 信息:
```shell
./lite/tools/build_ios.sh help
```
- 其他可选编译命令:
```shell
--arch: (armv8|armv7) arm版本,默认为armv8
--with_cv: (OFF|ON) 是否编译CV相关预处理库, 默认为 OFF
--with_log: (OFF|ON) 是否输出日志信息, 默认为 ON
--with_extra: (OFF|ON) 是否编译OCR或NLP相关模型的kernel&OP,默认为OFF,只编译CV模型相关kernel&OP
```
- 裁剪预测库方法(只编译模型中的kernel&OP,降低预测库体积):
```shell
./lite/tools/build_android.sh --with_strip=ON --opt_model_dir=YourOptimizedModelDir
```
```shell
--with_strip: (OFF|ON); 是否根据输入模型裁剪预测库,默认为OFF
--opt_model_dir: 输入模型的绝对路径,需要为opt转化之后的模型
```
详情参考: [裁剪预测库](https://paddle-lite.readthedocs.io/zh/latest/user_guides/library_tailoring.html)
# release/v2.3 源码编译
**说明:release/v2.3 之前版本(包括v2.3版本)的源码编译请参考本文档**
**注意:OpenCL、华为NPU、FPGA、CUDA、X86预测库、CV模块的编译,请见进阶使用指南的对应章节。**
### 下载代码
```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预测库(默认为ON) | `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 \
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 \
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 \
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
......@@ -24,22 +24,29 @@ Paddle-Lite支持**根据模型裁剪预测库**功能。Paddle-Lite的一般编
### 1、转化模型时记录优化后模型信息
说明:使用model_optimize_tool转化模型时,选择 `--record_tailoring_info =true` 会将优化后模型的OP和kernel信息保存到输出文件夹,这些信息将用于编译裁剪后的动态库。
注意:需要使用Paddle-Lite 最新版本(release/v2.0.0之后)代码编译出的model_optimize_tool
说明:使用`opt`转化模型时,选择 `--record_tailoring_info =true` 会将优化后模型的OP和kernel信息保存到输出文件夹,这些信息将用于编译裁剪后的动态库。
例如:
```bash
./model_optimize_tool --model_dir=./mobilenet_v1 --optimize_out_type=naive_buffer --optimize_out=mobilenet_v1NB --record_tailoring_info =true --valid_targets=arm
./opt --model_dir=./mobilenet_v1 --optimize_out_type=naive_buffer --optimize_out=mobilenet_v1NB --record_tailoring_info =true --valid_targets=arm
```
效果:优化后模型使用的OP和kernel信息被保存在 `mobilenet_v1NB`文件夹中的隐藏文件里了
效果:优化后模型使用的`OP``kernel`信息被保存在 `mobilenet_v1NB`文件夹中的隐藏文件里了
### 2、根据模型信息编译裁剪后的预测库
说明:编译Paddle-Lite时选择`--build_tailor=ON` ,并且用 `–-opt_model_dir=` 指定优化后的模型的地址
例如:
**release/v2.6.0以后版本或develop分支使用以下命令**
```bash
./lite/tools/build_android.sh --with_strip=ON --opt_model_dir=../mobilenet_v1NB
```
**release/v2.3之前版本使用以下命令**
```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 tiny_publish
./lite/tools/build.sh --arm_os=android --arm_abi=armv8 --arm_lang=gcc --android_stl=c++_static --build_extra=ON --build_tailor=ON --opt_model_dir=../mobilenet_v1NB tiny_publish
```
**注意**:上面命令中的`../mobilenet_v1NB`是第1步得到的转化模型的输出路径
......@@ -148,13 +155,13 @@ int main(int argc, char** argv) {
## 按模型集合裁剪预测库
为了方便用户使用,我们同时提供了按模型集合进行预测库裁剪的功能。用户可以提供一个模型集合,Model Optimize Tool会根据用户所指定的模型集合分析其**优化后的**模型所需要的算子信息对预测库进行裁剪。使用此功能用户根据自己的需要使用模型集合来对预测库中的算子进行任意裁剪。
为了方便用户使用,我们同时提供了按模型集合进行预测库裁剪的功能。用户可以提供一个模型集合,opt 会根据用户所指定的模型集合分析其**优化后的**模型所需要的算子信息对预测库进行裁剪。使用此功能用户根据自己的需要使用模型集合来对预测库中的算子进行任意裁剪。
使用方法如下所示:
```shell
# 非combined模型集合
./model_optimize_tool \
./opt \
--model_set_dir=<your_model_set_dir> \
--optimize_out_type=naive_buffer \
--optimize_out=<output_model_set_dir> \
......@@ -162,7 +169,7 @@ int main(int argc, char** argv) {
--valid_targets=arm
# combined模型集合
./model_optimize_tool \
./opt \
--model_set_dir=<your_model_set_dir> \
--optimize_out_type=naive_buffer \
--model_filename=<model_topo_filename> \
......@@ -172,7 +179,7 @@ int main(int argc, char** argv) {
--valid_targets=arm
```
经过以上步骤后会在`<output_model_set_dir>`中生成模型集合中各模型对应的NaiveBuffer格式的优化模型。此步会对模型集合中所需算子信息进行搜集并存储到`<output_model_set_dir>`中。下一步编译预测库的流程与使用单模型进行预测库裁剪步骤相同。
经过以上步骤后会在`<output_model_set_dir>`中生成模型集合中各模型对应的`NaiveBuffer`格式的优化模型。此步会对模型集合中所需算子信息进行搜集并存储到`<output_model_set_dir>`中。下一步编译预测库的流程与使用单模型进行预测库裁剪步骤相同。
**注意:**
......
# 模型量化-量化训练
本文主要介绍使用Paddle-Lite加载PaddlePaddle产出的量化模型,并进行推理执行。我们以MobileNetV1模型为示例,首先说明产出量化模型,然后说明预测部署。
本文主要介绍使用Paddle-Lite加载PaddlePaddle产出的量化模型,并进行推理执行。
## 1 简介
量化训练是基于大量训练数据,对训练好的预测模型进行量化。该方法使用模拟量化的思想,在训练阶段更新权重,实现减小量化误差。
量化训练是使用较多练数据,对训练好的预测模型进行量化。该方法使用模拟量化的思想,在训练阶段更新权重,实现减小量化误差。
使用条件:
* 有预训练模型
* 有较多训练数据
* 有较多训练数据(大于5000)
使用步骤:
* 产出量化模型:使用PaddlePaddle调用量化训练接口,产出量化模型
......@@ -23,271 +23,37 @@
建议首先使用“有校准数据训练后量化”对模型进行量化,然后使用使用量化模型进行预测。如果该量化模型的精度达不到要求,再使用“量化训练”。
## 2 产出量化模型
目前,PaddlePaddle框架的量化训练主要针对卷积层(包括二维卷积和Depthwise卷积)、和全连接层,对应算子是conv2d、depthwise_conv2d和mul,更多量化训练的原理请参考[文档](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/tutorial.md#1-quantization-aware-training%E9%87%8F%E5%8C%96%E4%BB%8B%E7%BB%8D)。Paddle-Lite支持运行PaddlePaddle框架量化训练产出的模型,可以进一步加快模型在移动端的执行速度。
目前,PaddleSlim 框架的量化训练主要针对卷积层(包括二维卷积和Depthwise卷积)、和全连接层,对应算子是conv2d、depthwise_conv2d和mul。Paddle-Lite支持运行PaddlePaddle框架量化训练产出的模型,可以进一步加快模型在移动端的执行速度。
温馨提示:如果您是初次接触PaddlePaddle框架,建议首先学习[新人入门](https://www.paddlepaddle.org.cn/documentation/docs/zh/1.5/beginners_guide/index_cn.html)[使用指南](https://www.paddlepaddle.org.cn/documentation/docs/zh/1.5/user_guides/index_cn.html)
您可以选择下载训练好的量化模型,或者使用PaddleSlim模型压缩工具训练得到量化模型。
### 下载量化模型
官方发布了[MobileNetV1量化模型](https://paddle-inference-dist.bj.bcebos.com/int8%2Fpretrain%2Fmobilenet_v1_quant%2Ffloat.zip),直接下载到本地。
```bash
wget https://paddle-inference-dist.bj.bcebos.com/int8%2Fpretrain%2Fmobilenet_v1_quant%2Ffloat.zip
```
### 使用PaddleSlim模型压缩工具训练量化模型
#### 安装PaddlePaddle
根据操作系统、安装方式、Python版本和CUDA版本,按照[官方说明](https://paddlepaddle.org.cn/start)安装PaddlePaddle。例如:
Ubuntu 16.04.4 LTS操作系统,CUDA9,cuDNN7,GPU版本安装:
```bash
pip install paddlepaddle-gpu==1.6.0.post97 -i https://mirrors.aliyun.com/pypi/simple/
```
Ubuntu 16.04.4 LTS操作系统,CPU版本安装:
```bash
pip install paddlepaddle==1.6.0 -i https://mirrors.aliyun.com/pypi/simple/
```
#### 克隆量化训练所需的代码库
克隆[PaddlePaddle/models](https://github.com/PaddlePaddle/models)到本地,并进入models/PaddleSlim路径。
```bash
git clone https://github.com/PaddlePaddle/models.git
cd models/PaddleSlim
```
#### 准备数据和模型
##### 训练数据准备
参考[models/PaddleCV/image_classification](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/image_classification#data-preparation)中的数据准备教程,下载训练数据,并且保存到PaddleSlim/data路径下。
##### 预训练模型准备
参考/models/PaddleSlim/run.sh脚本, 从[models/PaddleCV/image_classification](https://github.com/PaddlePaddle/models/tree/develop/fluid/PaddleCV/image_classification#supported-models-and-performances)下载MobileNetV1的预训练模型,并保存到PaddleSlim/pretrain路径下。
经过以上三步,PaddleSlim目录下的文件结构如下所示:
```bash
.
├── compress.py # 模型压缩任务主脚本,定义了压缩任务需要的模型相关信息
├── configs # 压缩任务的配置文件,包括:蒸馏、int8量化量化、filter剪切和组合策略的配置文件
├── data # 存放训练数据(需要用户自己创建)
│   └── ILSVRC2012
├── pretrain # 存放预训练模型参数,执行run.sh自动生成
│   ├── MobileNetV1_pretrained
│   ├── MobileNetV1_pretrained.tar
│   ├── ResNet50_pretrained
│   └── ResNet50_pretrained.tar
├── docs # 文档目录
├── light_nas
├── models # 模型网络结构的定义,如MobileNetV1
├── quant_low_level_api # 量化训练的底层API, 用于灵活定制量化训练的过程,适用于高阶用户
├── reader.py # 定义数据处理逻辑
├── README.md
├── run.sh # 模型压缩任务启动脚本
└── utility.py # 定义了常用的工具方法
```
##### 压缩脚本介绍
`compress.py`中定义了执行压缩任务需要的所有模型相关的信息,这里对几个关键的步骤进行简要介绍:
**目标网络的定义**
compress.py的以下代码片段定义了train program, 这里train program只有前向计算操作。
```python
out = model.net(input=image, class_dim=args.class_dim)
cost = fluid.layers.cross_entropy(input=out, label=label)
avg_cost = fluid.layers.mean(x=cost)
acc_top1 = fluid.layers.accuracy(input=out, label=label, k=1)
acc_top5 = fluid.layers.accuracy(input=out, label=label, k=5)
```
然后,通过clone方法得到eval_program, 用来在压缩过程中评估模型精度,如下:
```python
val_program = fluid.default_main_program().clone()
```
定义完目标网络结构,需要对其初始化,并根据需要加载预训练模型。
**定义feed_list和fetch_list**
对于train program, 定义train_feed_list用于指定从train data reader中取的数据feed给哪些variable。定义train_fetch_list用于指定在训练时,需要在log中展示的结果。如果需要在训练过程中在log中打印accuracy信心,则将('acc_top1', acc_top1.name)添加到train_fetch_list中即可。
```python
train_feed_list = [('image', image.name), ('label', label.name)]
train_fetch_list = [('loss', avg_cost.name)]
```
> 注意: 在train_fetch_list里必须有loss这一项。
对于eval program. 同上定义eval_feed_list和train_fetch_list:
```python
val_feed_list = [('image', image.name), ('label', label.name)]
val_fetch_list = [('acc_top1', acc_top1.name), ('acc_top5', acc_top5.name)]
```
**Compressor和量化配置文件**
`compress.py`主要使用Compressor和yaml文件完成对模型的量化训练工作。Compressor类的定义如下:
```python
class Compressor(object):
def __init__(self,
place,
scope,
train_program,
train_reader=None,
train_feed_list=None,
train_fetch_list=None,
eval_program=None,
eval_reader=None,
eval_feed_list=None,
eval_fetch_list=None,
teacher_programs=[],
checkpoint_path='./checkpoints',
train_optimizer=None,
distiller_optimizer=None):
```
在定义Compressor对象时,需要注意以下问题:
* train program如果带反向operators和优化更新相关的operators, 参数train_optimizer需要设置为None.
* eval_program中parameter的名称需要与train_program中的parameter的名称完全一致。
* 最终保存的量化模型是在eval_program网络基础上进行剪枝保存的。所以,如果用户希望最终保存的模型可以用于inference, 则eval program需要包含推理阶段需要的各种operators.
* checkpoint保存的是float数据类型的模型。
`configs/quantization.yaml`量化配置文件示例如下:
```python
version: 1.0
strategies:
quantization_strategy:
class: 'QuantizationStrategy'
start_epoch: 0
end_epoch: 9
float_model_save_path: './output/float'
mobile_model_save_path: './output/mobile'
int8_model_save_path: './output/int8'
weight_bits: 8
activation_bits: 8
weight_quantize_type: 'abs_max'
activation_quantize_type: 'moving_average_abs_max'
save_in_nodes: ['image']
save_out_nodes: ['fc_0.tmp_2']
compressor:
epoch: 10
checkpoint_path: './checkpoints_quan/'
strategies:
- quantization_strategy
```
其中,可配置参数包括:
- **class:** 量化策略的类名称,目前仅支持`QuantizationStrategy`
- **start_epoch:** 在start_epoch开始之前,量化训练策略会往train_program和eval_program插入量化operators和反量化operators。 从start_epoch开始,进入量化训练阶段。
- **end_epoch:** 在end_epoch结束之后,会保存用户指定格式的模型。注意:end_epoch之后并不会停止量化训练,而是继续训练直到epoch数等于compressor.epoch值为止。举例来说,当start_epoch=0,end_epoch=0,compressor.epoch=2时,量化训练开始于epoch0,结束于epoch1,但保存的模型是epoch0结束时的参数状态。
- **float_model_save_path:** 保存float数据格式的模型路径,即该路径下的模型参数范围为int8范围但参数数据类型为float32。如果设置为None, 则不存储float格式的模型,默认为None。**注意:Paddle-Lite即使用该目录下的模型进行量化模型推理优化,详见本文[使用Paddle-Lite运行量化模型推理](#二使用Paddle-Lite运行量化模型推理)部分。**
- **int8_model_save_path:** 保存int8数据格式的模型路径,即该路径下的模型参数范围为int8范围且参数数据类型为int8。如果设置为None, 则不存储int8格式的模型,默认为None.
- **mobile_model_save_path:** 保存兼容paddle-mobile框架的模型路径。如果设置为None, 则不存储paddle-mobile格式的模型,默认为None。目前paddle-mobile已升级为Paddle-Lite。
- **weight_bits:** 量化weight的bit数,注意偏置(bias)参数不会被量化。
- **activation_bits:** 量化activation的bit数。
- **weight_quantize_type:** weight量化方式,目前量化训练支持`abs_max``channel_wise_abs_max`
- **activation_quantize_type:** activation量化方式,目前量化训练支持`range_abs_max``moving_average_abs_max`。PaddlePaddle中还支持 `abs_max` 方法对激活进行量化,但是该方法动态计算输入的量化scale,这会增加计算量、减慢模型推理速度,所以lite不支持 `abs_max`激活量化方式。
- **save_in_nodes:** variable名称列表。在保存量化后模型的时候,需要根据save_in_nodes对eval programg 网络进行前向遍历剪枝。默认为eval_feed_list内指定的variable的名称列表。
- **save_out_nodes:** varibale名称列表。在保存量化后模型的时候,需要根据save_out_nodes对eval programg 网络进行回溯剪枝。默认为eval_fetch_list内指定的variable的名称列表。
> **备注:**
>
> 1)`abs_max`意为在训练的每个step及inference阶段均动态计算量化scale值。`channel_wise_abs_max`与`abs_max`类似,不同点在于它会对卷积权重进行分channel求取量化scale。换言之,`abs_max`属于tensor-wise量化,而`channel_wise_abs_max`属于channel-wise量化,详细说明请猛戳[此处](https://github.com/PaddlePaddle/FluidDoc/blob/develop/doc/fluid/design/quantization/training_quantization_model_format.md)。
>
> 2)`moving_average_abs_max`和`range_abs_max`意为在训练阶段计算出一个静态的量化scale值,并将其用于inference阶段。`moving_average_abs_max`使用窗口滑动平均的方法计算量化scale,而`range_abs_max`则使用窗口绝对值最大值的方式。
>
> 3)**目前,Paddle-Lite仅支持运行weight量化方式使用`abs_max`且activation量化方式使用`moving_average_abs_max`或`range_abs_max`产出的量化模型**。
#### 执行量化训练
修改run.sh,即注释掉`# enable GC strategy``# for sensitivity filter pruning`之间的内容并打开`#for quantization`相关的脚本命令(所需打开注释的命令如下所示)。
```bash
# for quantization
#---------------------------
export CUDA_VISIBLE_DEVICES=0
python compress.py \
--batch_size 64 \
--model "MobileNet" \
--pretrained_model ./pretrain/MobileNetV1_pretrained \
--compress_config ./configs/quantization.yaml \
--quant_only True
```
最后,运行`sh run.sh`命令开始int8量化训练。
上述量化训练过程完成后,若按照本文中所述`configs/quantization.yaml`文件内容配置的模型输出路径,则可在models/PaddleSlim/output目录下看到`float``int8``mobile`三个目录,其中:
* float目录: 参数范围为int8范围但参数数据类型为float32的量化模型。Paddle-Lite即使用该目录下的模型文件及参数进行量化模型的部署。
* int8目录: 参数范围为int8范围且参数数据类型为int8的量化模型。
* mobile目录:参数特点与int8目录相同且兼容paddle-mobile的量化模型(目前paddle-mobile已升级为Paddle-Lite)。
使用PaddleSlim模型压缩工具训练量化模型,请参考文档:
* 量化训练[快速开始教程](https://paddlepaddle.github.io/PaddleSlim/quick_start/quant_aware_tutorial.html)
* 量化训练[API接口说明](https://paddlepaddle.github.io/PaddleSlim/api_cn/quantization_api.html)
* 量化训练[Demo](https://github.com/PaddlePaddle/PaddleSlim/tree/release/1.0.1/demo/quant/quant_aware)
## 3 使用Paddle-Lite运行量化模型推理
### 使用模型优化工具对量化模型进行优化
接下来,使用原始的量化模型生成适合在移动端直接部署的模型。
参考[源码编译](source_compile)配置编译环境,确保可以编译成功。参考[模型转化方法](model_optimize_tool),首先编译model_optimize_tool工具,然后执行下面命令对量化训练的模型进行优化(注意,需要自行修改model_file、param_file和optimize_out)。
```bash
./model_optimize_tool \
--model_file=mobilenet_v1_quant/float/model \
--param_file=mobilenet_v1_quant/float/weights \
--optimize_out_type=naive_buffer \
--optimize_out=mobilenet_v1_quant_opt \
--valid_targets=arm \
```
首先,使用PaddleLite提供的模型转换工具(model_optimize_tool)将量化模型转换成移动端预测的模型,然后加载转换后的模型进行预测部署。
如前所述,量化训练后,float目录下的模型参数范围为int8,但参数数据类型仍为float32类型,这样确实没有起到模型参数压缩的效果。但是,经过model\_optimize\_tool工具优化后对应的量化参数均会以int8类型重新存储达到参数压缩的效果,且模型结构也被优化(如进行了各种operator fuse操作)。
### 3.1 模型转换
### 在手机端准备量化模型文件
使用如下命令将mobilenet_v1_quant_opt目录下的量化模型文件导入到手机端:
参考[模型转换](../user_guides/model_optimize_tool)准备模型转换工具,建议从Release页面下载。
参考[模型转换](../user_guides/model_optimize_tool)使用模型转换工具,参数按照实际情况设置。比如在安卓手机ARM端进行预测,模型转换的命令为:
```bash
adb push mobilenet_v1_quant_opt /data/local/tmp
./opt --model_dir=./mobilenet_v1_quant \
--optimize_out_type=naive_buffer \
--optimize_out=mobilenet_v1_quant_opt \
--valid_targets=arm
```
### 使用mobilenetv1\_light\_api运行优化后的量化模型
参考[源码编译](source_compile)配置编译环境后,在Paddle-Lite执行如下命令获取轻量级API的demo:
### 3.2 量化模型预测
```bash
cd /Paddle-Lite/build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/demo/cxx/mobile_light
make clean && make -j
```
执行完上述命令后,可在`Paddle-Lite/build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/demo/cxx/mobile_light/`路径下看到`mobilenetv1_light_api`可执行文件。将`mobilenetv1_light_api`导入到手机端并运行量化模型推理。执行命令如下:
和FP32模型一样,转换后的量化模型可以在Android/IOS APP中加载预测,建议参考[C++ Demo](../demo_guides/cpp_demo)[Java Demo](../demo_guides/java_demo)[Android/IOS Demo](../demo_guides/android_app_demo)
```bash
adb push Paddle-Lite/build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/demo/cxx/mobile_light/mobilenetv1_light_api /data/local/tmp
adb shell chmod +x /data/local/tmp/mobilenetv1_light_api
adb shell /data/local/tmp/mobilenetv1_light_api \
--model_dir=/data/local/tmp/mobilenet_v1_quant_opt
```
**程序运行结果如下:**
```bash
Output dim: 1000
Output[0]: 0.000228
Output[100]: 0.000260
Output[200]: 0.000250
Output[300]: 0.000560
Output[400]: 0.000950
Output[500]: 0.000275
Output[600]: 0.005143
Output[700]: 0.002509
Output[800]: 0.000538
Output[900]: 0.000969
```
在C++中使用Paddle-Lite API的方法请猛戳[此处](../demo_guides/cpp_demo),用户也可参考[mobilenetv1_light_api.cc](https://github.com/PaddlePaddle/Paddle-Lite/blob/develop/lite/demo/cxx/mobile_light/mobilenetv1_light_api.cc)的代码示例。
## FAQ
......
# 模型量化-无校准数据训练后量化
本文首先简单介绍无校准数据训练后量化,然后说明产出量化模型,最阐述量化模型预测。
本文首先简单介绍无校准数据训练后量化,然后说明产出量化模型,最阐述量化模型预测。
## 1 简介
......@@ -18,7 +18,7 @@
* 权重量化成INT8类型,模型精度会受到影响,模型大小为原始的1/4
缺点:
* 暂无
* 只可以减小模型大小,不能加快模型推理
## 2 产出量化模型
......@@ -43,10 +43,15 @@ model_dir = path/to/fp32_model_params
save_model_dir = path/to/save_model_path
weight_quant = WeightQuantization(model_dir=model_dir)
weight_quant.quantize_weight_to_int(save_model_dir=save_model_dir,
weight_bits=16,
quantizable_op_type=['conv2d', 'depthwise_conv2d', 'mul'])
weight_bits=8,
quantizable_op_type=['conv2d', 'mul'],
weight_quantize_type="channel_wise_abs_max",
generate_test_model=False)
```
执行完成后,可以在 `save_model_dir/quantized_model` 目录下得到量化模型。
对于调用无校准数据训练后量化,以下对api接口进行详细介绍。
```python
......@@ -58,24 +63,29 @@ class WeightQuantization(model_dir, model_filename=None, params_filename=None)
* params_filename(str, optional):待量化模型的权重文件名,如果所有权重保存成一个文件,则需要使用params_filename设置权重文件名。
```python
WeightQuantization.quantize_weight_to_int(save_model_dir,
save_model_filename=None,
save_params_filename=None,
quantizable_op_type=['conv2d', 'mul'],
weight_bits=8,
threshold_rate=0.0)
WeightQuantization.quantize_weight_to_int(self,
save_model_dir,
save_model_filename=None,
save_params_filename=None,
quantizable_op_type=["conv2d", "mul"],
weight_bits=8,
weight_quantize_type="channel_wise_abs_max",
generate_test_model=False,
threshold_rate=0.0)
```
参数说明如下:
* save_model_dir(str):保存量化模型的路径。
* save_model_filename(str, optional):如果save_model_filename等于None,则模型的网络结构保存到__model__文件,如果save_model_filename不等于None,则模型的网络结构保存到特定的文件。默认为None。
* save_params_filename(str, optional):如果save_params_filename等于None,则模型的参数分别保存到一系列文件中,如果save_params_filename不等于None,则模型的参数会保存到一个文件中,文件名为设置的save_params_filename。默认为None。
* quantizable_op_type(list[str]): 需要量化的op类型,默认是`['conv2d', 'mul']`,列表中的值可以是任意支持量化的op类型 `['conv2d', 'depthwise_conv2d', 'mul']`
* weight_bits(int, optional):权重量化保存的比特数,可以是8~16,一般设置为8/16。默认为8。
* quantizable_op_type(list[str]): 需要量化的op类型,默认是`['conv2d', 'mul']`,列表中的值可以是任意支持量化的op类型 `['conv2d', 'depthwise_conv2d', 'mul']`。一般不对 `depthwise_conv2d` 量化,因为对减小模型大小收益不大,同时可能影响模型精度。
* weight_bits(int, optional):权重量化保存的比特数,可以是8~16,一般设置为8/16,默认为8。量化为8bit,模型体积最多可以减小4倍,可能存在微小的精度损失。量化成16bit,模型大小最多可以减小2倍,基本没有精度损失。
* weight_quantize_type(str, optional): 权重量化的方式,支持 `channel_wise_abs_max``abs_max`,一般都是 `channel_wise_abs_max`,量化模型精度损失小。
* generate_test_model(bool, optional): 是否产出测试模型,用于测试量化模型部署时的精度。测试模型保存在 `save_model_dir/test_model` 目录下,可以和FP32模型一样使用Fluid加载测试,但是该模型不能用于预测端部署。
## 3 量化模型预测
目前,对于无校准数据训练后量化产出的量化模型,不支持PaddlePaddle加载执行,只能使用PaddleLite进行预测部署。
目前,对于无校准数据训练后量化产出的量化模型,只能使用PaddleLite进行预测部署。
很简单,首先使用PaddleLite提供的模型转换工具(opt)将量化模型转换成移动端预测的模型,然后加载转换后的模型进行预测部署。
......
# 模型量化-有校准数据训练后量化
本文首先简单介绍有校准数据训练后量化,然后说明产出量化模型、量化模型预测,最后给出一个使用示例。
如果想快速上手,大家可以先参考使用示例,再查看详细使用方法。
## 1 简介
有校准数据训练后量化,使用少量校准数据计算量化因子,可以快速得到量化模型。使用该量化模型进行预测,可以减少计算量、降低计算内存、减小模型大小。
......@@ -14,7 +11,7 @@
* 有少量校准数据,比如100~500张图片
使用步骤:
* 产出量化模型:使用PaddlePaddle或者PaddleSlim调用有校准数据训练后量化接口,产出量化模型
* 产出量化模型:使用PaddleSlim调用有校准数据训练后量化接口,产出量化模型
* 量化模型预测:使用PaddleLite加载量化模型进行预测推理
优点:
......@@ -27,11 +24,11 @@
## 2 产出量化模型
大家可以使用PaddlePaddle或者PaddleSlim调用有校准数据训练后量化接口,得到量化模型。本文主要介绍使用PaddlePaddle产出量化模型,使用PaddleSlim可以参考[文档](https://github.com/PaddlePaddle/models/tree/develop/PaddleSlim)
大家可以使用PaddleSlim调用有校准数据训练后量化接口,得到量化模型
### 2.1 安装PaddlePaddle
### 2.1 安装PaddleSlim
参考PaddlePaddle[官网](https://www.paddlepaddle.org.cn/install/quick),安装PaddlePaddle CPU/GPU 1.7版本
参考PaddleSlim[文档](https://paddlepaddle.github.io/PaddleSlim/install.html)进行安装
### 2.2 准备模型和校准数据
......@@ -49,7 +46,7 @@
```python
import paddle.fluid as fluid
from paddle.fluid.contrib.slim.quantization import PostTrainingQuantization
from paddleslim.quant import quant_post
exe = fluid.Executor(fluid.CPUPlace())
model_dir = path/to/fp32_model_params
......@@ -69,75 +66,23 @@ batch_size = 10
batch_nums = 10
algo = "KL"
quantizable_op_type = ["conv2d", "depthwise_conv2d", "mul"]
ptq = PostTrainingQuantization(
executor=exe,
sample_generator=sample_generator,
model_dir=model_dir,
model_filename=model_filename,
params_filename=params_filename,
batch_size=batch_size,
batch_nums=batch_nums,
algo=algo,
quantizable_op_type=quantizable_op_type)
ptq.quantize()
ptq.save_quantized_model(save_model_path)
quant_post(executor=exe,
model_dir=model_dir,
model_filename=model_filename,
params_filename=params_filename,
quantize_model_path=save_model_path,
sample_generator=sample_generator,
batch_size=batch_size,
batch_nums=batch_nums,
algo=algo,
quantizable_op_type=quantizable_op_type)
```
对于调用有校准数据训练后量化,以下对接口进行详细介绍。
``` python
class PostTrainingQuantization(
executor=None,
scope=None,
model_dir=None,
model_filename=None,
params_filename=None,
sample_generator=None,
batch_size=10,
batch_nums=None,
algo="KL",
quantizable_op_type=["conv2d", "depthwise_conv2d", "mul"],
is_full_quantize=False,
weight_bits=8,
activation_bits=8,
is_use_cache_file=False,
cache_dir="./temp_post_training"):
```
调用上述api,传入必要的参数。参数说明如下:
* executor(fluid.Executor):执行模型的executor,可以指定在cpu或者gpu上执行。
* scope(fluid.Scope, optional):模型运行时使用的scope,默认为None,则会使用global_scope()。行首有optional,说明用户可以不设置该输入参数,直接使用默认值,下同。
* model_dir(str):待量化模型的路径,其中保存模型文件和权重文件。
* model_filename(str, optional):待量化模型的模型文件名,如果模型文件名不是`__model__`,则需要使用model_filename设置模型文件名。
* params_filename(str, optional):待量化模型的权重文件名,如果所有权重保存成一个文件,则需要使用params_filename设置权重文件名。
* sample_generator(Python Generator):配置的校准数据生成器。
* batch_size(int, optional):一次读取校准数据的数量。
* batch_nums(int, optional):读取校准数据的次数。如果设置为None,则从sample_generator中读取所有校准数据进行训练后量化;如果设置为非None,则从sample_generator中读取`batch_size*batch_nums`个校准数据。
* algo(str, optional):计算待量化激活Tensor的量化因子的方法。设置为`KL`,则使用饱和量化方法,设置为`direct`,则使用非饱和量化方法。默认为`KL`
* quantizable_op_type(list[str], optional): 需要量化的op类型,默认是`["conv2d", "depthwise_conv2d", "mul"]`,列表中的值可以是任意支持量化的op类型。
* is_full_quantize(bool, optional):是否进行全量化。设置为True,则对模型中所有支持量化的op进行量化;设置为False,则只对`quantizable_op_type` 中op类型进行量化。目前支持的量化类型如下:'conv2d', 'depthwise_conv2d', 'mul', "pool2d", "elementwise_add", "concat", "softmax", "argmax", "transpose", "equal", "gather", "greater_equal", "greater_than", "less_equal", "less_than", "mean", "not_equal", "reshape", "reshape2", "bilinear_interp", "nearest_interp", "trilinear_interp", "slice", "squeeze", "elementwise_sub"。
* weight_bits(int, optional):权重量化的比特数,可以设置为1~16。PaddleLite目前仅支持加载权重量化为8bit的量化模型。
* activation_bits(int, optional): 激活量化的比特数,可以设置为1~16。PaddleLite目前仅支持加载激活量化为8bit的量化模型。
* is_use_cache_file(bool, optional):是否使用缓存文件。如果设置为True,训练后量化过程中的采样数据会保存到磁盘文件中;如果设置为False,所有采样数据会保存到内存中。当待量化的模型很大或者校准数据数量很大,建议设置is_use_cache_file为True。默认为False。
* cache_dir(str, optional):当is_use_cache_file等于True,会将采样数据保存到该文件中。量化完成后,该文件中的临时文件会自动删除。
快速开始请参考[文档](https://paddlepaddle.github.io/PaddleSlim/quick_start/quant_post_tutorial.html#)
```python
PostTrainingQuantization.quantize()
```
调用上述接口开始训练后量化。根据校准数据数量、模型的大小和量化op类型不同,训练后量化需要的时间也不一样。比如使用ImageNet2012数据集中100图片对`MobileNetV1`进行训练后量化,花费大概1分钟。
```python
PostTrainingQuantization.save_quantized_model(save_model_path)
```
调用上述接口保存训练后量化模型,其中save_model_path为保存的路径。
API接口请参考[文档](https://paddlepaddle.github.io/PaddleSlim/api_cn/quantization_api.html#quant-post)
训练后量化支持部分量化功能:
* 方法1:设置quantizable_op_type,则只会对quantizable_op_type中的Op类型进行量化,模型中其他Op类型保持不量化。
* 方法2:构建网络的时候,将不需要量化的特定Op定义在 `skip_quant` 的name_scope中,则可以跳过特定Op的量化,示例如下。
```python
with fluid.name_scope('skip_quant'):
pool = fluid.layers.pool2d(input=hidden, pool_size=2, pool_type='avg', pool_stride=2)
# 不对pool2d进行量化
```
Demo请参考[文档](https://github.com/PaddlePaddle/PaddleSlim/tree/release/1.0.1/demo/quant/quant_post)
## 3 量化模型预测
......@@ -158,45 +103,3 @@ with fluid.name_scope('skip_quant'):
### 3.2 量化模型预测
和FP32模型一样,转换后的量化模型可以在Android/IOS APP中加载预测,建议参考[C++ Demo](../demo_guides/cpp_demo)[Java Demo](../demo_guides/java_demo)[Android/IOS Demo](../demo_guides/android_app_demo)
## 4 使用示例
### 4.1 产出量化模型
参考本文 “2.1 安装PaddlePaddle” 安装PaddlePaddle。
下载[打包文件](https://paddle-inference-dist.cdn.bcebos.com/PaddleLite/quantization_demo/post_training_quantization_withdata.tgz),解压到本地。
```bash
wget https://paddle-inference-dist.cdn.bcebos.com/PaddleLite/quantization_demo/post_training_quantization_withdata.tgz
tar zxvf post_training_quantization_withdata.tgz
cd post_training_quantization_withdata
```
执行下面的命令,自动下载预测模型(mobilenetv1_fp32_model)和校准数据集,然后调用有校准数据训练后方法产出量化模型。
```bash
sh run_post_training_quanzation.sh
```
量化模型保存在mobilenetv1_int8_model文件夹中。
### 4.2 量化模型预测
下载测试文件([benchmark_bin](https://paddle-inference-dist.cdn.bcebos.com/PaddleLite/quantization_demo/benchmark_bin))或者参考[Benchmark测试方法](../benchmark/benchmark_tools)编译测试文件。
将mobilenetv1_fp32_model、mobilenetv1_int8_model和benchmark_bin文件都保存到手机上。
```bash
adb push mobilenetv1_fp32_model /data/local/tmp
adb push mobilenetv1_int8_model /data/local/tmp
chmod 777 benchmark_bin
adb push benchmark_bin /data/local/tmp
```
测试量化模型和原始模型的性能,依次执行下面命令:
```bash
./benchmark_bin --is_quantized_model=true --run_model_optimize=true --result_filename=res.txt --warmup=10 --repeats=30 --model_dir=mobilenetv1_int8_model/
./benchmark_bin --is_quantized_model=true --run_model_optimize=true --result_filename=res.txt --warmup=10 --repeats=30 --model_dir=mobilenetv1_fp32_model/
cat res.txt
```
在res.txt文件中可以看到INT8量化模型和FP32原始模型的速度。
举例来说,在骁龙855手机、单线程的情况下测试mobilenetv1,INT8量化模型的计算时间是14.52ms,FP32原始模型的计算时间是31.7ms。
......@@ -236,175 +236,38 @@ brew cask install java
## 二、编译PaddleLite
**注:编译OpenCL、华为NPU、FPGA、CUDA、X86预测库、CV模块,见进阶使用指南的对应章节。**
`develop分支``release/v2.6.0`之后版本的源码编译请参考以下说明,release/v2.3之前版本(包括v2.3)源码编译请参考[release/v2.3源码编译方法](./Compile/v2.3_compile)
### 下载代码
### Android 预测库编译方法
```shell
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
cd Paddle-Lite
git checkout <release-version-tag>
```
### 编译模式与参数
Paddle-Lite支持在 “Docker 环境、Linux 环境、Mac 环境” 源码编译Android 预测库
编译脚本`./lite/tools/build.sh`,支持三种编译模式:
**编译方法参见**[Android预测库编译方法](./Compile/Android)
| 编译模式 | 介绍 | 适用对象 |
|:-------:|-----|:-------:|
| tiny_publish | 编译移动端部署库,无第三方库依赖 | 用户 |
| full_publish | 编译移动端部署库,有第三方依赖如protobuf、glags等,含有可将模型转换为无需protobuf依赖的naive buffer格式的工具,供tiny_publish库使用 | 用户 |
| test | 编译指定`arm_os``arm_abi`下的移动端单元测试 | 框架开发者 |
编译脚本`./lite/tools/build.sh`,追加参数说明:
### iOS 预测库编译方法
| 参数 | 介绍 | 值 |
|-----------|-------------|-------------|
| --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预测库(默认为ON) | `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` |
Paddle-Lite只支持在 “Mac 环境” 源码编译iOS 预测库
### 编译代码
**编译方法参见**[iOS预测库编译方法](./Compile/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 \
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 \
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 \
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`
### Linux 预测库编译方法
![](https://user-images.githubusercontent.com/45189361/65375706-204e8780-dccb-11e9-9816-ab4563ce0963.png)
**编译方法参见**[Linux预测库编译方法](./Compile/Linux)
**目录内容**(可能)如下:
**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`链上相关的第三方依赖的仓库。
移动端相关编译所需的第三方库均位于 `<PaddleLite>/third-party` 目录下,默认编译过程中,会利用`git submodule update --init --recursive`链上相关的第三方依赖的仓库
- 为加速`full_publish``test`编译模式中对`protobuf`等第三方依赖的下载,`build.sh``ci_build.sh`支持了从国内 CDN 下载第三方依赖的压缩包
为加速`full_publish``test`编译模式中对`protobuf`等第三方依赖的下载,`build.sh``ci_build.sh`支持了从国内 CDN 下载第三方依赖的压缩包。
可使用本节方法加速第三方库下载过程,以加速编译:
使用方法`git clone``Paddle-Lite`仓库代码后,手动删除本地仓库根目录下的`third-party`目录:
- **加速方法**`git clone``Paddle-Lite`仓库代码后,手动删除本地仓库根目录下的`third-party`目录:
```shell
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
......@@ -413,4 +276,4 @@ cd Paddle-Lite
rm -rf third-party
```
之后再根据本文档,进行后续编译时,便会忽略第三方依赖对应的`submodule`,改为下载第三方压缩包。
之后再根据本文档,进行后续编译时,便会忽略第三方依赖对应的`submodule`,改为直接下载第三方压缩包。
......@@ -16,7 +16,6 @@ message(STATUS "LITE_WITH_MLU:\t${LITE_WITH_MLU}")
message(STATUS "LITE_WITH_BM:\t${LITE_WITH_BM}")
message(STATUS "LITE_WITH_PROFILE:\t${LITE_WITH_PROFILE}")
message(STATUS "LITE_WITH_CV:\t${LITE_WITH_CV}")
message(STATUS "LITE_WITH_ARM_LANG:\t${LITE_WITH_ARM_LANG}")
set(LITE_MODEL_DIR "${THIRD_PARTY_PATH}/install")
set(LITE_ON_MOBILE ${LITE_WITH_LIGHT_WEIGHT_FRAMEWORK})
......@@ -188,15 +187,17 @@ if (LITE_WITH_CUDA OR LITE_WITH_X86)
COMMAND cp "${CMAKE_BINARY_DIR}/libpaddle_api_light_bundled.a" "${INFER_LITE_PUBLISH_ROOT}/cxx/lib"
COMMAND cp "${CMAKE_BINARY_DIR}/lite/api/*.so" "${INFER_LITE_PUBLISH_ROOT}/cxx/lib"
)
add_custom_target(publish_inference_third_party ${TARGET}
COMMAND mkdir -p "${INFER_LITE_PUBLISH_ROOT}/third_party"
COMMAND cp -r "${CMAKE_BINARY_DIR}/third_party/install/*" "${INFER_LITE_PUBLISH_ROOT}/third_party")
if (LITE_WITH_CUDA)
add_custom_target(publish_inference_third_party ${TARGET}
COMMAND mkdir -p "${INFER_LITE_PUBLISH_ROOT}/third_party"
COMMAND cp -r "${CMAKE_BINARY_DIR}/third_party/install/*" "${INFER_LITE_PUBLISH_ROOT}/third_party")
add_dependencies(publish_inference publish_inference_third_party)
endif()
add_dependencies(publish_inference_cxx_lib bundle_full_api)
add_dependencies(publish_inference_cxx_lib bundle_light_api)
add_dependencies(publish_inference_cxx_lib paddle_full_api_shared)
add_dependencies(publish_inference_cxx_lib paddle_light_api_shared)
add_dependencies(publish_inference publish_inference_cxx_lib)
add_dependencies(publish_inference publish_inference_third_party)
endif()
endif()
......@@ -205,6 +206,7 @@ if (LITE_WITH_X86)
add_custom_target(publish_inference_x86_cxx_lib ${TARGET}
COMMAND ${CMAKE_COMMAND} -E make_directory "${INFER_LITE_PUBLISH_ROOT}/cxx/lib"
COMMAND ${CMAKE_COMMAND} -E make_directory "${INFER_LITE_PUBLISH_ROOT}/bin"
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/lite/api//${CMAKE_BUILD_TYPE}/test_model_bin.exe" "${INFER_LITE_PUBLISH_ROOT}/bin"
COMMAND ${CMAKE_COMMAND} -E make_directory "${INFER_LITE_PUBLISH_ROOT}/cxx/include"
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/lite/api/paddle_api.h" "${INFER_LITE_PUBLISH_ROOT}/cxx/include"
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/lite/api/paddle_place.h" "${INFER_LITE_PUBLISH_ROOT}/cxx/include"
......@@ -215,7 +217,8 @@ if (LITE_WITH_X86)
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/lite/api/${CMAKE_BUILD_TYPE}/libpaddle_api_full_bundled.lib" "${INFER_LITE_PUBLISH_ROOT}/cxx/lib"
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/lite/api/${CMAKE_BUILD_TYPE}/libpaddle_api_light_bundled.lib" "${INFER_LITE_PUBLISH_ROOT}/cxx/lib"
)
add_dependencies(publish_inference_x86_cxx_lib test_model_bin)
add_dependencies(publish_inference_x86_cxx_lib bundle_full_api)
add_dependencies(publish_inference_x86_cxx_lib bundle_light_api)
add_dependencies(publish_inference publish_inference_x86_cxx_lib)
......@@ -225,6 +228,7 @@ if (LITE_WITH_X86)
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_BINARY_DIR}/third_party/install" "${INFER_LITE_PUBLISH_ROOT}/third_party"
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_BINARY_DIR}/third_party/eigen3" "${INFER_LITE_PUBLISH_ROOT}/third_party"
COMMAND ${CMAKE_COMMAND} -E make_directory "${INFER_LITE_PUBLISH_ROOT}/demo/cxx"
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/lite/demo/cxx" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx"
)
add_dependencies(publish_inference_x86_cxx_lib publish_inference_x86_cxx_demos)
add_dependencies(publish_inference_x86_cxx_demos paddle_api_full_bundled eigen3)
......@@ -238,9 +242,13 @@ if (LITE_WITH_X86)
add_dependencies(publish_inference_x86_cxx_lib test_model_bin)
add_custom_target(publish_inference_x86_cxx_demos ${TARGET}
COMMAND rm -rf "${INFER_LITE_PUBLISH_ROOT}/demo/cxx"
COMMAND mkdir -p "${INFER_LITE_PUBLISH_ROOT}/demo/cxx"
COMMAND cp -r "${CMAKE_SOURCE_DIR}/lite/demo/cxx/x86_mobilenetv1_light_demo" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx/mobilenetv1_light"
COMMAND cp -r "${CMAKE_SOURCE_DIR}/lite/demo/cxx/x86_mobilenetv1_full_demo" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx/mobilenetv1_full"
COMMAND mkdir -p "${INFER_LITE_PUBLISH_ROOT}/third_party"
COMMAND cp -r "${CMAKE_BINARY_DIR}/third_party/eigen3" "${INFER_LITE_PUBLISH_ROOT}/third_party"
)
COMMAND cp -r "${CMAKE_BINARY_DIR}/third_party/install/mklml" "${INFER_LITE_PUBLISH_ROOT}/third_party/"
)
add_dependencies(publish_inference_x86_cxx_lib publish_inference_x86_cxx_demos)
add_dependencies(publish_inference_x86_cxx_demos paddle_full_api_shared eigen3)
add_dependencies(publish_inference publish_inference_x86_cxx_lib)
......@@ -369,6 +377,8 @@ if (LITE_WITH_LIGHT_WEIGHT_FRAMEWORK AND LITE_WITH_ARM)
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"
COMMAND cp -r "${CMAKE_SOURCE_DIR}/lite/demo/cxx/test_libs" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx"
COMMAND cp "${CMAKE_SOURCE_DIR}/lite/demo/cxx/makefiles/test_libs/Makefile.${ARM_TARGET_OS}.${ARM_TARGET_ARCH_ABI}" "${INFER_LITE_PUBLISH_ROOT}/demo/cxx/test_libs/Makefile"
)
add_dependencies(publish_inference_android_cxx_demos logging gflags)
add_dependencies(publish_inference_cxx_lib publish_inference_android_cxx_demos)
......
if(LITE_WITH_LIGHT_WEIGHT_FRAMEWORK)
if(LITE_WITH_LIGHT_WEIGHT_FRAMEWORK OR (NOT LITE_WITH_LOG))
lite_cc_library(place SRCS paddle_place.cc DEPS logging)
else()
lite_cc_library(place SRCS paddle_place.cc DEPS glog)
......@@ -42,6 +42,9 @@ if ((NOT LITE_ON_TINY_PUBLISH) AND (LITE_WITH_CUDA OR LITE_WITH_X86 OR LITE_WITH
)
add_dependencies(paddle_light_api_shared op_list_h kernel_list_h)
if(WIN32)
target_link_libraries(paddle_light_api_shared shlwapi.lib)
endif()
target_link_libraries(paddle_light_api_shared ${light_lib_DEPS} ${arm_kernels} ${npu_kernels} ${rknpu_kernels} ${apu_kernels})
if(NOT APPLE AND NOT WIN32)
set(LINK_MAP_FILE "${PADDLE_SOURCE_DIR}/lite/core/lite.map")
......@@ -246,8 +249,10 @@ if(LITE_WITH_LIGHT_WEIGHT_FRAMEWORK AND WITH_TESTING)
ARGS --cl_path=${CMAKE_SOURCE_DIR}/lite/backends/opencl
--model_dir=${LITE_MODEL_DIR}/mobilenet_v1 SERIAL)
add_dependencies(test_mobilenetv1 extern_lite_download_mobilenet_v1_tar_gz)
set(LINK_FLAGS "-Wl,--version-script ${PADDLE_SOURCE_DIR}/lite/core/lite.map")
set_target_properties(test_mobilenetv1 PROPERTIES LINK_FLAGS "${LINK_FLAGS}")
if(NOT WIN32)
set(LINK_FLAGS "-Wl,--version-script ${PADDLE_SOURCE_DIR}/lite/core/lite.map")
set_target_properties(test_mobilenetv1 PROPERTIES LINK_FLAGS "${LINK_FLAGS}")
endif()
lite_cc_test(test_mobilenetv2 SRCS mobilenetv2_test.cc
DEPS ${lite_model_test_DEPS}
......@@ -255,7 +260,9 @@ if(LITE_WITH_LIGHT_WEIGHT_FRAMEWORK AND WITH_TESTING)
ARGS --cl_path=${CMAKE_SOURCE_DIR}/lite/backends/opencl
--model_dir=${LITE_MODEL_DIR}/mobilenet_v2_relu SERIAL)
add_dependencies(test_mobilenetv2 extern_lite_download_mobilenet_v2_relu_tar_gz)
set_target_properties(test_mobilenetv2 PROPERTIES LINK_FLAGS "${LINK_FLAGS}")
if(NOT WIN32)
set_target_properties(test_mobilenetv2 PROPERTIES LINK_FLAGS "${LINK_FLAGS}")
endif()
lite_cc_test(test_resnet50 SRCS resnet50_test.cc
DEPS ${lite_model_test_DEPS} paddle_api_light
......
......@@ -48,6 +48,7 @@ USE_LITE_OP(concat)
USE_LITE_OP(conv2d)
USE_LITE_OP(depthwise_conv2d)
USE_LITE_OP(pool2d)
USE_LITE_OP(max_pool2d_with_index)
USE_LITE_OP(batch_norm)
USE_LITE_OP(fusion_elementwise_sub_activation)
USE_LITE_OP(transpose)
......
......@@ -16,6 +16,7 @@
#if !defined(_WIN32)
#include <sys/time.h>
#else
#define NOMINMAX // msvc max/min macro conflict with std::min/max
#include <windows.h>
#include "lite/backends/x86/port.h"
#endif
......
......@@ -151,6 +151,11 @@ std::vector<std::string> Predictor::GetInputNames() { return input_names_; }
// get outputnames
std::vector<std::string> Predictor::GetOutputNames() { return output_names_; }
// get param names
std::vector<std::string> Predictor::GetParamNames() {
return exec_scope_->AttributeVarNames();
}
// append the names of inputs and outputs into input_names_ and output_names_
void Predictor::PrepareFeedFetch() {
if (!program_) {
......@@ -293,6 +298,7 @@ void Predictor::Build(const cpp::ProgramDesc &desc,
// `inner_places` is used to optimize passes
std::vector<Place> inner_places = valid_places;
for (auto &valid_place : valid_places) {
if (valid_place.target == TARGET(kOpenCL)) continue;
inner_places.emplace_back(
Place(TARGET(kHost), valid_place.precision, valid_place.layout));
}
......@@ -345,9 +351,16 @@ void Predictor::GenRuntimeProgram() {
const lite::Tensor *Predictor::GetTensor(const std::string &name) const {
auto *var = exec_scope_->FindVar(name);
CHECK(var) << "no variable named with " << name << " in exec_scope";
return &var->Get<lite::Tensor>();
}
lite::Tensor *Predictor::GetMutableTensor(const std::string &name) {
auto *var = exec_scope_->FindVar(name);
CHECK(var) << "no variable named with " << name << " in exec_scope";
return var->GetMutable<lite::Tensor>();
}
// get input by name
lite::Tensor *Predictor::GetInputByName(const std::string &name) {
auto element = std::find(input_names_.begin(), input_names_.end(), name);
......
......@@ -85,6 +85,9 @@ class LITE_API Predictor {
// get inputnames and get outputnames.
std::vector<std::string> GetInputNames();
std::vector<std::string> GetOutputNames();
// get param names
std::vector<std::string> GetParamNames();
void PrepareFeedFetch();
// Get offset-th col of fetch results.
......@@ -92,6 +95,9 @@ class LITE_API Predictor {
std::vector<const lite::Tensor*> GetOutputs() const;
const cpp::ProgramDesc& program_desc() const;
// get a mutable tensor according to its name
lite::Tensor* GetMutableTensor(const std::string& name);
// get a const tensor according to its name
const lite::Tensor* GetTensor(const std::string& name) const;
const RuntimeProgram& runtime_program() const;
......@@ -142,9 +148,15 @@ class CxxPaddleApiImpl : public lite_api::PaddlePredictor {
// get inputs names and get outputs names
std::vector<std::string> GetInputNames() override;
std::vector<std::string> GetOutputNames() override;
// get param names
std::vector<std::string> GetParamNames() override;
// get tensor according to tensor's name
std::unique_ptr<const lite_api::Tensor> GetTensor(
const std::string& name) const override;
// get a mutable tensor according to tensor's name
std::unique_ptr<lite_api::Tensor> GetMutableTensor(
const std::string& name) override;
// Get InputTebsor by name
std::unique_ptr<lite_api::Tensor> GetInputByName(
......
......@@ -97,6 +97,10 @@ std::vector<std::string> CxxPaddleApiImpl::GetInputNames() {
return raw_predictor_.GetInputNames();
}
std::vector<std::string> CxxPaddleApiImpl::GetParamNames() {
return raw_predictor_.GetParamNames();
}
std::vector<std::string> CxxPaddleApiImpl::GetOutputNames() {
return raw_predictor_.GetOutputNames();
}
......@@ -123,6 +127,12 @@ std::unique_ptr<const lite_api::Tensor> CxxPaddleApiImpl::GetTensor(
return std::unique_ptr<const lite_api::Tensor>(new lite_api::Tensor(x));
}
std::unique_ptr<lite_api::Tensor> CxxPaddleApiImpl::GetMutableTensor(
const std::string &name) {
return std::unique_ptr<lite_api::Tensor>(
new lite_api::Tensor(raw_predictor_.GetMutableTensor(name)));
}
std::unique_ptr<lite_api::Tensor> CxxPaddleApiImpl::GetInputByName(
const std::string &name) {
return std::unique_ptr<lite_api::Tensor>(
......
......@@ -36,7 +36,7 @@ DEFINE_string(model_dir_0, "", "model_dir_0");
DEFINE_string(input_shape_0,
"1,3,224,224",
"input shapes another, separated by colon and comma");
DEFINE_string(target, "arm", "main target for Predictor: arm, opencl");
DEFINE_bool(use_optimize_nb,
false,
"optimized & naive buffer model for mobile devices");
......@@ -51,9 +51,19 @@ void OutputOptModel(const std::string& load_model_dir,
const std::vector<std::vector<int64_t>>& input_shapes) {
lite_api::CxxConfig config;
config.set_model_dir(load_model_dir);
config.set_valid_places({
Place{TARGET(kARM), PRECISION(kFloat)},
});
if (FLAGS_target == "arm") {
config.set_valid_places({
Place{TARGET(kARM), PRECISION(kFloat)},
});
} else if (FLAGS_target == "opencl") {
config.set_valid_places({
Place{TARGET(kOpenCL), PRECISION(kFP16), DATALAYOUT(kImageDefault)},
Place{TARGET(kOpenCL), PRECISION(kFloat), DATALAYOUT(kNCHW)},
Place{TARGET(kOpenCL), PRECISION(kAny), DATALAYOUT(kImageDefault)},
Place{TARGET(kOpenCL), PRECISION(kAny), DATALAYOUT(kNCHW)},
Place{TARGET(kARM)}, // enable kARM CPU kernel when no opencl kernel
});
}
auto predictor = lite_api::CreatePaddlePredictor(config);
// delete old optimized model
......@@ -78,7 +88,7 @@ void Run(const std::vector<std::vector<int64_t>>& input_shapes,
int tid,
const int warmup_times = 5) {
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_threads(thread_num);
......@@ -197,7 +207,7 @@ void RunTestType_10(const std::vector<std::vector<int64_t>>& input_shapes,
const int repeat,
int warmup = 5) {
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_threads(thread_num);
......@@ -218,13 +228,13 @@ void RunTestType_11(const std::vector<std::vector<int64_t>>& input_shapes,
const int repeat,
int warmup = 5) {
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_threads(thread_num);
auto predictor = lite_api::CreatePaddlePredictor(config);
config.set_model_dir(model_dir_0);
config.set_model_from_file(model_dir_0 + ".nb");
auto predictor_0 = lite_api::CreatePaddlePredictor(config);
for (int i = 0; i < 2 * repeat; i += 2) {
......@@ -246,7 +256,8 @@ int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
if (FLAGS_model_dir == "") {
LOG(INFO) << "usage: "
<< "--model_dir /path/to/your/model";
<< "--model_dir /path/to/your/model --model_dir_0 "
"/path/to/your/model0 --target `arm` or `opencl`";
exit(0);
}
std::string save_optimized_model_dir = "";
......
......@@ -55,7 +55,7 @@ DEFINE_string(model_file, "", "model file path of the combined-param model");
DEFINE_string(param_file, "", "param file path of the combined-param model");
DEFINE_string(
optimize_out_type,
"protobuf",
"naive_buffer",
"store type of the output optimized model. protobuf/naive_buffer");
DEFINE_bool(display_kernels, false, "Display kernel information");
DEFINE_bool(record_tailoring_info,
......@@ -207,7 +207,7 @@ void PrintOpsInfo(std::set<std::string> valid_ops = {}) {
}
std::cout << std::setiosflags(std::ios::internal);
std::cout << std::setw(maximum_optype_length) << "OP_name";
for (int i = 0; i < targets.size(); i++) {
for (size_t i = 0; i < targets.size(); i++) {
std::cout << std::setw(10) << targets[i].substr(1);
}
std::cout << std::endl;
......@@ -215,7 +215,7 @@ void PrintOpsInfo(std::set<std::string> valid_ops = {}) {
for (auto it = supported_ops.begin(); it != supported_ops.end(); it++) {
std::cout << std::setw(maximum_optype_length) << it->first;
auto ops_valid_places = it->second;
for (int i = 0; i < targets.size(); i++) {
for (size_t i = 0; i < targets.size(); i++) {
if (std::find(ops_valid_places.begin(),
ops_valid_places.end(),
targets[i]) != ops_valid_places.end()) {
......@@ -235,7 +235,7 @@ void PrintOpsInfo(std::set<std::string> valid_ops = {}) {
}
// Print OP info.
auto ops_valid_places = supported_ops.at(*op);
for (int i = 0; i < targets.size(); i++) {
for (size_t i = 0; i < targets.size(); i++) {
if (std::find(ops_valid_places.begin(),
ops_valid_places.end(),
targets[i]) != ops_valid_places.end()) {
......@@ -288,11 +288,11 @@ void ParseInputCommand() {
auto valid_places = paddle::lite_api::ParserValidPlaces();
// get valid_targets string
std::vector<TargetType> target_types = {};
for (int i = 0; i < valid_places.size(); i++) {
for (size_t i = 0; i < valid_places.size(); i++) {
target_types.push_back(valid_places[i].target);
}
std::string targets_str = TargetToStr(target_types[0]);
for (int i = 1; i < target_types.size(); i++) {
for (size_t i = 1; i < target_types.size(); i++) {
targets_str = targets_str + TargetToStr(target_types[i]);
}
......@@ -301,7 +301,7 @@ void ParseInputCommand() {
target_types.push_back(TARGET(kUnk));
std::set<std::string> valid_ops;
for (int i = 0; i < target_types.size(); i++) {
for (size_t i = 0; i < target_types.size(); i++) {
auto ops = supported_ops_target[static_cast<int>(target_types[i])];
valid_ops.insert(ops.begin(), ops.end());
}
......@@ -318,7 +318,7 @@ void CheckIfModelSupported() {
auto valid_unktype_ops = supported_ops_target[static_cast<int>(TARGET(kUnk))];
valid_ops.insert(
valid_ops.end(), valid_unktype_ops.begin(), valid_unktype_ops.end());
for (int i = 0; i < valid_places.size(); i++) {
for (size_t i = 0; i < valid_places.size(); i++) {
auto target = valid_places[i].target;
auto ops = supported_ops_target[static_cast<int>(target)];
valid_ops.insert(valid_ops.end(), ops.begin(), ops.end());
......@@ -340,7 +340,7 @@ void CheckIfModelSupported() {
std::set<std::string> unsupported_ops;
std::set<std::string> input_model_ops;
for (int index = 0; index < cpp_prog.BlocksSize(); index++) {
for (size_t index = 0; index < cpp_prog.BlocksSize(); index++) {
auto current_block = cpp_prog.GetBlock<lite::cpp::BlockDesc>(index);
for (size_t i = 0; i < current_block->OpsSize(); ++i) {
auto& op_desc = *current_block->GetOp<lite::cpp::OpDesc>(i);
......@@ -364,13 +364,13 @@ void CheckIfModelSupported() {
unsupported_ops_str = unsupported_ops_str + ", " + *op_str;
}
std::vector<TargetType> targets = {};
for (int i = 0; i < valid_places.size(); i++) {
for (size_t i = 0; i < valid_places.size(); i++) {
targets.push_back(valid_places[i].target);
}
std::sort(targets.begin(), targets.end());
targets.erase(unique(targets.begin(), targets.end()), targets.end());
std::string targets_str = TargetToStr(targets[0]);
for (int i = 1; i < targets.size(); i++) {
for (size_t i = 1; i < targets.size(); i++) {
targets_str = targets_str + "," + TargetToStr(targets[i]);
}
......
......@@ -82,27 +82,56 @@ void OptBase::SetValidPlaces(const std::string& valid_places) {
"command argument 'valid_targets'";
}
void OptBase::SetOptimizeOut(const std::string& optimized_out_path) {
optimize_out_path_ = optimized_out_path;
void OptBase::SetLiteOut(const std::string& lite_out_name) {
lite_out_name_ = lite_out_name;
}
void OptBase::RunOptimize(bool record_strip_info) {
void OptBase::RecordModelInfo(bool record_strip_info) {
record_strip_info_ = record_strip_info;
}
void OptBase::Run() {
CheckIfModelSupported(false);
OpKernelInfoCollector::Global().SetKernel2path(kernel2path_map);
opt_config_.set_valid_places(valid_places_);
if (model_set_dir_ != "") {
RunOptimizeFromModelSet(record_strip_info);
RunOptimizeFromModelSet(record_strip_info_);
} else {
auto opt_predictor = lite_api::CreatePaddlePredictor(opt_config_);
opt_predictor->SaveOptimizedModel(
optimize_out_path_, model_type_, record_strip_info);
lite_out_name_, model_type_, record_strip_info_);
auto resulted_model_name =
record_strip_info ? "information of striped model" : "optimized model";
record_strip_info_ ? "information of striped model" : "optimized model";
std::cout << "Save the " << resulted_model_name
<< " into :" << optimize_out_path_ << "successfully";
<< " into :" << lite_out_name_ << "successfully";
}
}
void OptBase::RunOptimize(const std::string& model_dir_path,
const std::string& model_path,
const std::string& param_path,
const std::string& valid_places,
const std::string& optimized_out_path) {
SetModelDir(model_dir_path);
SetModelFile(model_path);
SetParamFile(param_path);
SetValidPlaces(valid_places);
SetLiteOut(optimized_out_path);
CheckIfModelSupported(false);
OpKernelInfoCollector::Global().SetKernel2path(kernel2path_map);
opt_config_.set_valid_places(valid_places_);
if (model_set_dir_ != "") {
RunOptimizeFromModelSet(record_strip_info_);
} else {
auto opt_predictor = lite_api::CreatePaddlePredictor(opt_config_);
opt_predictor->SaveOptimizedModel(
lite_out_name_, model_type_, record_strip_info_);
auto resulted_model_name =
record_strip_info_ ? "information of striped model" : "optimized model";
std::cout << "Save the " << resulted_model_name
<< " into :" << lite_out_name_ << "successfully";
}
}
// collect ops info of modelset
void CollectModelMetaInfo(const std::string& output_dir,
const std::vector<std::string>& models,
......@@ -125,7 +154,7 @@ void OptBase::SetModelSetDir(const std::string& model_set_path) {
}
void OptBase::RunOptimizeFromModelSet(bool record_strip_info) {
// 1. mkdir of outputed optimized model set.
lite::MkDirRecur(optimize_out_path_);
lite::MkDirRecur(lite_out_name_);
auto model_dirs = lite::ListDir(model_set_dir_, true);
if (model_dirs.size() == 0) {
LOG(FATAL) << "[" << model_set_dir_ << "] does not contain any model";
......@@ -138,7 +167,7 @@ void OptBase::RunOptimizeFromModelSet(bool record_strip_info) {
std::string input_model_dir =
lite::Join<std::string>({model_set_dir_, name}, "/");
std::string output_model_dir =
lite::Join<std::string>({optimize_out_path_, name}, "/");
lite::Join<std::string>({lite_out_name_, name}, "/");
if (opt_config_.model_file() != "" && opt_config_.param_file() != "") {
auto model_file_path =
......@@ -155,7 +184,7 @@ void OptBase::RunOptimizeFromModelSet(bool record_strip_info) {
auto opt_predictor = lite_api::CreatePaddlePredictor(opt_config_);
opt_predictor->SaveOptimizedModel(
optimize_out_path_, model_type_, record_strip_info);
lite_out_name_, model_type_, record_strip_info);
std::cout << "Optimize done. ";
}
......@@ -164,46 +193,60 @@ void OptBase::RunOptimizeFromModelSet(bool record_strip_info) {
if (record_strip_info) {
// Collect all models information
CollectModelMetaInfo(
optimize_out_path_, model_dirs, lite::TAILORD_OPS_SOURCE_LIST_FILENAME);
lite_out_name_, model_dirs, lite::TAILORD_OPS_SOURCE_LIST_FILENAME);
CollectModelMetaInfo(
lite_out_name_, model_dirs, lite::TAILORD_OPS_LIST_NAME);
CollectModelMetaInfo(
optimize_out_path_, model_dirs, lite::TAILORD_OPS_LIST_NAME);
CollectModelMetaInfo(optimize_out_path_,
model_dirs,
lite::TAILORD_KERNELS_SOURCE_LIST_FILENAME);
lite_out_name_, model_dirs, lite::TAILORD_KERNELS_SOURCE_LIST_FILENAME);
CollectModelMetaInfo(
optimize_out_path_, model_dirs, lite::TAILORD_KERNELS_LIST_NAME);
lite_out_name_, model_dirs, lite::TAILORD_KERNELS_LIST_NAME);
std::cout << "Record the information of stripped models into :"
<< optimize_out_path_ << "successfully";
<< lite_out_name_ << "successfully";
}
}
void OptBase::PrintHelpInfo() {
const std::string opt_version = lite::version();
const char help_info[] =
"At least one argument should be inputed. Valid arguments are listed "
"below:\n"
"------------------------------------------------------------------------"
"-----------------------------------------------------------\n"
" Valid arguments of Paddle-Lite opt are listed below:\n"
"------------------------------------------------------------------------"
"-----------------------------------------------------------\n"
" Arguments of help information:\n"
" `help()` Print help infomation\n"
" Arguments of model optimization:\n"
"\n"
" Arguments of model transformation:\n"
" `set_model_dir(model_dir)`\n"
" `set_model_file(model_file_path)`\n"
" `set_param_file(param_file_path)`\n"
" `set_model_type(protobuf|naive_buffer)`\n"
" `set_optimize_out(output_optimize_model_dir)`\n"
" `set_model_type(protobuf|naive_buffer)`: naive_buffer by "
"default\n"
" `set_lite_out(output_optimize_model_dir)`\n"
" `set_valid_places(arm|opencl|x86|npu|xpu|rknpu|apu)`\n"
" `run_optimize(false|true)`\n"
" ` ----fasle&true refer to whether to record ops info for "
"tailoring lib, false by default`\n"
" Arguments of model checking and ops information:\n"
" `record_model_info(false|true)`: refer to whether to record ops "
"info for striping lib, false by default`\n"
" `run() : start model transformation`\n"
" eg. `opt.set_model_dir(\"./mobilenetv1\"); "
"opt.set_lite_out(\"mobilenetv1_opt\"); opt.set_valid_places(\"arm\"); "
"opt.run();`\n"
"\n"
" You can also transform model through a single input argument:\n"
" `run_optimize(model_dir, model_file_path, param_file_path, "
"model_type, valid_places, lite_out_name) `\n"
" eg. `opt.run_optimize(\"./mobilenetv1\", \"\", \"\", "
"\"naive_buffer\", \"arm\", \"mobilenetv1_opt\");`"
"\n"
" Arguments of checking model and printing ops information:\n"
" `print_all_ops()` Display all the valid operators of "
"Paddle-Lite\n"
" `print_supported_ops` Display supported operators of valid "
"places\n"
" `check_if_model_supported()` Check if the input model is "
"supported\n";
std::cout << "opt version:" << opt_version << std::endl
<< help_info << std::endl;
"supported\n"
"------------------------------------------------------------------------"
"-----------------------------------------------------------\n";
std::cout << "opt version:" << opt_version << std::endl << help_info;
}
// 2. Print supported info of inputed ops
void OptBase::PrintOpsInfo(const std::set<std::string>& valid_ops) {
......
......@@ -44,16 +44,21 @@ class LITE_API OptBase {
public:
OptBase() = default;
void SetModelSetDir(const std::string &model_set_path);
void SetModelDir(const std::string &model_path);
void SetModelDir(const std::string &model_dir_path);
void SetModelFile(const std::string &model_path);
void SetParamFile(const std::string &param_path);
void SetValidPlaces(const std::string &valid_places);
void SetOptimizeOut(const std::string &optimized_out_path);
void SetLiteOut(const std::string &lite_out_name);
void RecordModelInfo(bool record_strip_info = true);
// set optimized_model type
void SetModelType(std::string model_type);
// transform and save the optimized model
void RunOptimize(bool record_strip_info = false);
void Run();
void RunOptimize(const std::string &model_dir_path = "",
const std::string &model_path = "",
const std::string &param_path = "",
const std::string &valid_places = "",
const std::string &optimized_out_path = "");
// fuctions of printing info
// 1. help info
void PrintHelpInfo();
......@@ -71,12 +76,12 @@ class LITE_API OptBase {
// valid places for the optimized_model
std::vector<Place> valid_places_;
// filename of the optimized_model
std::string optimize_out_path_;
std::string lite_out_name_;
// type of the optimized_model, kNaiveBuffer default.
LiteModelType model_type_{LiteModelType::kNaiveBuffer};
// Dir path of a set of models, this should be combined with model
std::string model_set_dir_;
bool record_strip_info_{false};
void RunOptimizeFromModelSet(bool record_strip_info = false);
};
......
......@@ -167,6 +167,20 @@ lod_t Tensor::lod() const { return ctensor(raw_tensor_)->lod(); }
void Tensor::SetLoD(const lod_t &lod) { tensor(raw_tensor_)->set_lod(lod); }
std::unique_ptr<Tensor> PaddlePredictor::GetMutableTensor(
const std::string &name) {
LOG(FATAL)
<< "The GetMutableTensor API is only supported by CxxConfig predictor.";
return nullptr;
}
std::vector<std::string> PaddlePredictor::GetParamNames() {
std::vector<std::string> null_result = {};
LOG(FATAL)
<< "The GetParamNames API is only supported by CxxConfig predictor.";
return null_result;
}
void PaddlePredictor::SaveOptimizedModel(const std::string &model_dir,
LiteModelType model_type,
bool record_info) {
......
......@@ -86,6 +86,8 @@ class LITE_API PaddlePredictor {
virtual std::vector<std::string> GetInputNames() = 0;
// Get output names
virtual std::vector<std::string> GetOutputNames() = 0;
// Get output names
virtual std::vector<std::string> GetParamNames();
// Get Input by name
virtual std::unique_ptr<Tensor> GetInputByName(const std::string& name) = 0;
......@@ -93,6 +95,9 @@ class LITE_API PaddlePredictor {
/// Get a readonly tensor, return null if no one called `name` exists.
virtual std::unique_ptr<const Tensor> GetTensor(
const std::string& name) const = 0;
/// Get a mutable tensor, return null if on one called `name` exists
/// internal infereces API, not recommanded.
virtual std::unique_ptr<Tensor> GetMutableTensor(const std::string& name);
/// Persist the optimized model to disk. This API is only supported by
/// CxxConfig, and the persisted model can be reused for MobileConfig.
......@@ -176,7 +181,7 @@ class LITE_API CxxConfig : public ConfigBase {
#endif
#ifdef LITE_WITH_CUDA
void set_multi_stream(bool multi_stream) { multi_stream_ = multi_stream; }
int multi_stream() const { return multi_stream_; }
bool multi_stream() const { return multi_stream_; }
#endif
#ifdef LITE_WITH_MLU
......@@ -208,6 +213,8 @@ class LITE_API CxxConfig : public ConfigBase {
// current thread.
void set_xpu_workspace_l3_size_per_thread(int l3_size = 0xfffc00);
// XPU only, specify the target device ID for the current thread.
// **DEPRECATED**, use xpu_set_device() at the very beginning of each worker
// thread
void set_xpu_dev_per_thread(int dev_no = 0);
};
......
......@@ -19,7 +19,13 @@
#pragma once
// some platform-independent defintion
#include "lite/utils/macros.h"
#if defined(_WIN32)
#define UNUSED
#define __builtin_expect(EXP, C) (EXP)
#else
#define UNUSED __attribute__((unused))
#endif
#define USE_LITE_OP(op_type__) \
extern int touch_op_##op_type__(); \
......
......@@ -24,9 +24,9 @@ namespace lite_api {
size_t Place::hash() const {
std::hash<int> h;
size_t hash = h(static_cast<int>(target));
hash = lite::hash_combine(hash, static_cast<int>(precision));
hash = lite::hash_combine(hash, static_cast<int>(layout));
hash = lite::hash_combine(hash, static_cast<int>(device));
lite::CombineHash(static_cast<int64_t>(precision), &hash);
lite::CombineHash(static_cast<int64_t>(layout), &hash);
lite::CombineHash(static_cast<int64_t>(device), &hash);
return hash;
}
......@@ -161,6 +161,7 @@ std::set<TargetType> ExpandValidTargets(TargetType target) {
TARGET(kBM),
TARGET(kMLU),
TARGET(kAPU),
TARGET(kRKNPU),
TARGET(kFPGA)});
if (target == TARGET(kAny)) {
return valid_set;
......
......@@ -33,10 +33,11 @@ USE_MIR_PASS(lite_transpose_softmax_transpose_fuse_pass);
USE_MIR_PASS(lite_interpolate_fuse_pass);
USE_MIR_PASS(lite_sequence_pool_concat_fuse_pass);
USE_MIR_PASS(identity_scale_eliminate_pass);
USE_MIR_PASS(identity_dropout_eliminate_pass);
USE_MIR_PASS(lite_conv_elementwise_fuse_pass);
USE_MIR_PASS(lite_conv_activation_fuse_pass);
USE_MIR_PASS(lite_var_conv_2d_activation_fuse_pass);
USE_MIR_PASS(lite_elementwise_add_activation_fuse_pass);
USE_MIR_PASS(lite_elementwise_activation_fuse_pass);
USE_MIR_PASS(lite_quant_dequant_fuse_pass);
USE_MIR_PASS(type_precision_cast_pass);
USE_MIR_PASS(type_layout_cast_pass);
......@@ -51,5 +52,8 @@ USE_MIR_PASS(mlu_postprocess_pass);
USE_MIR_PASS(weight_quantization_preprocess_pass);
USE_MIR_PASS(apu_subgraph_pass);
USE_MIR_PASS(quantized_op_attributes_inference_pass);
USE_MIR_PASS(lite_scale_activation_fuse_pass);
USE_MIR_PASS(__xpu__resnet_fuse_pass);
USE_MIR_PASS(__xpu__multi_encoder_fuse_pass);
USE_MIR_PASS(__xpu__embedding_with_eltwise_add_fuse_pass);
USE_MIR_PASS(__xpu__fc_fuse_pass);
......@@ -62,8 +62,10 @@ void BindLiteOpt(py::module *m) {
.def("set_model_file", &OptBase::SetModelFile)
.def("set_param_file", &OptBase::SetParamFile)
.def("set_valid_places", &OptBase::SetValidPlaces)
.def("set_optimize_out", &OptBase::SetOptimizeOut)
.def("set_lite_out", &OptBase::SetLiteOut)
.def("set_model_type", &OptBase::SetModelType)
.def("record_model_info", &OptBase::RecordModelInfo)
.def("run", &OptBase::Run)
.def("run_optimize", &OptBase::RunOptimize)
.def("help", &OptBase::PrintHelpInfo)
.def("print_supported_ops", &OptBase::PrintSupportedOps)
......
......@@ -33,11 +33,17 @@ else:
PADDLELITE_VERSION = PADDLELITE_TAG
# core lib of paddlelite is stored as lite.so
LITE_PATH = '${PADDLE_BINARY_DIR}/inference_lite_lib/python/install/lite'
files = os.listdir('${PADDLE_BINARY_DIR}')
INFERENCE_LITE_LIB_PATH = ''
for file in files:
if file.find('inference_lite_lib') == 0:
INFERENCE_LITE_LIB_PATH = '${PADDLE_BINARY_DIR}/' + file
break
LITE_PATH = INFERENCE_LITE_LIB_PATH + '/python/install/lite'
PACKAGE_DATA = {'paddlelite': ['lite.so' if os.name!='nt' else 'lite.pyd']}
# put all thirdparty libraries in paddlelite.libs
PACKAGE_DATA['paddlelite.libs'] = []
LIB_PATH = '${PADDLE_BINARY_DIR}/inference_lite_lib/python/install/libs'
LIB_PATH = INFERENCE_LITE_LIB_PATH + '/python/install/libs/'
if '${WITH_MKL}' == 'ON':
shutil.copy('${MKLML_SHARED_IOMP_LIB}', LIB_PATH)
shutil.copy('${MKLML_SHARED_LIB}', LIB_PATH)
......@@ -49,8 +55,7 @@ if '${WITH_MKL}' == 'ON':
PACKAGE_DATA['paddlelite.libs'] += ['msvcr120.dll']
# link lite.so to paddlelite.libs
if os.name != 'nt':
COMMAND = "patchelf --set-rpath '$ORIGIN/../libs/' ${PADDLE_BINARY_DIR}\
/inference_lite_lib/python/install/lite/lite.so"
COMMAND = "patchelf --set-rpath '$ORIGIN/../libs/' " + LITE_PATH + "/lite.so"
if os.system(COMMAND) != 0:
raise Exception("patch third_party libs failed, command: %s" % COMMAND)
......
......@@ -15,6 +15,7 @@
#include <gflags/gflags.h>
#include <gtest/gtest.h>
#include <fstream>
#include <thread> //NOLINT
#include <vector>
#include "lite/api/cxx_api.h"
#include "lite/api/paddle_use_kernels.h"
......@@ -30,14 +31,18 @@ DEFINE_string(input_img_txt_path,
namespace paddle {
namespace lite {
void TestModel(const std::vector<Place>& valid_places) {
const int g_batch_size = 1;
const int g_thread_num = 1;
void instance_run() {
lite::Predictor predictor;
std::vector<std::string> passes;
std::vector<Place> valid_places({Place{TARGET(kBM), PRECISION(kFloat)},
Place{TARGET(kX86), PRECISION(kFloat)}});
predictor.Build(FLAGS_model_dir, "", "", valid_places, passes);
auto* input_tensor = predictor.GetInput(0);
input_tensor->Resize(DDim(
std::vector<DDim::value_type>({1, 3, FLAGS_im_height, FLAGS_im_width})));
input_tensor->Resize(DDim(std::vector<DDim::value_type>(
{g_batch_size, 3, FLAGS_im_height, FLAGS_im_width})));
auto* data = input_tensor->mutable_data<float>();
auto item_size = input_tensor->dims().production();
if (FLAGS_input_img_txt_path.empty()) {
......@@ -45,12 +50,15 @@ void TestModel(const std::vector<Place>& valid_places) {
data[i] = 1;
}
} else {
std::fstream fs(FLAGS_input_img_txt_path, std::ios::in);
if (!fs.is_open()) {
LOG(FATAL) << "open input_img_txt error.";
}
for (int i = 0; i < item_size; i++) {
fs >> data[i];
for (int j = 0; j < g_batch_size; j++) {
std::fstream fs(FLAGS_input_img_txt_path, std::ios::in);
if (!fs.is_open()) {
LOG(FATAL) << "open input_img_txt error.";
}
for (int i = 0; i < item_size / g_batch_size; i++) {
fs >> data[i];
}
data += j * item_size / g_batch_size;
}
}
for (int i = 0; i < FLAGS_warmup; ++i) {
......@@ -72,6 +80,7 @@ void TestModel(const std::vector<Place>& valid_places) {
FILE* fp = fopen("result.txt", "wb");
for (int i = 0; i < out.size(); i++) {
auto* out_data = out[i]->data<float>();
LOG(INFO) << out[i]->numel();
for (int j = 0; j < out[i]->numel(); j++) {
fprintf(fp, "%f\n", out_data[j]);
}
......@@ -79,6 +88,16 @@ void TestModel(const std::vector<Place>& valid_places) {
fclose(fp);
}
void TestModel(const std::vector<Place>& valid_places) {
std::vector<std::unique_ptr<std::thread>> instances_vec;
for (int i = 0; i < g_thread_num; ++i) {
instances_vec.emplace_back(new std::thread(&instance_run));
}
for (int i = 0; i < g_thread_num; ++i) {
instances_vec[i]->join();
}
}
TEST(Classify, test_bm) {
std::vector<Place> valid_places({Place{TARGET(kBM), PRECISION(kFloat)},
Place{TARGET(kX86), PRECISION(kFloat)}});
......
......@@ -61,11 +61,11 @@ TEST(CXXApi, test_lite_googlenet) {
<< " ms in average.";
auto out = predictor->GetOutput(0);
std::vector<float> results(
{0.00034298553, 0.0008200012, 0.0005046297, 0.000839279,
0.00052616704, 0.0003447803, 0.0010877076, 0.00081762316,
0.0003941339, 0.0011430943, 0.0008892841, 0.00080191303,
0.0004442384, 0.000658702, 0.0026721435, 0.0013686896,
0.0005618166, 0.0006556497, 0.0006984528, 0.0014619455});
{0.00034298553f, 0.0008200012f, 0.0005046297f, 0.000839279f,
0.00052616704f, 0.0003447803f, 0.0010877076f, 0.00081762316f,
0.0003941339f, 0.0011430943f, 0.0008892841f, 0.00080191303f,
0.0004442384f, 0.000658702f, 0.0026721435f, 0.0013686896f,
0.0005618166f, 0.0006556497f, 0.0006984528f, 0.0014619455f});
for (size_t i = 0; i < results.size(); ++i) {
EXPECT_NEAR(out->data<float>()[i * 51], results[i], 1e-5);
}
......
......@@ -18,6 +18,7 @@
#if !defined(_WIN32)
#include <sys/time.h>
#else
#define NOMINMAX // msvc max/min macro conflict with std::min/max
#include <windows.h>
#include "lite/backends/x86/port.h"
#endif
......
......@@ -62,11 +62,11 @@ TEST(InceptionV4, test_inceptionv4_lite_x86) {
std::vector<std::vector<float>> results;
// i = 1
results.emplace_back(std::vector<float>(
{0.0011684548, 0.0010390386, 0.0011301535, 0.0010133048,
0.0010259597, 0.0010982729, 0.00093195855, 0.0009141837,
0.00096620916, 0.00089982944, 0.0010064574, 0.0010474789,
0.0009782845, 0.0009230255, 0.0010548076, 0.0010974824,
0.0010612885, 0.00089107914, 0.0010112736, 0.00097655767}));
{0.0011684548f, 0.0010390386f, 0.0011301535f, 0.0010133048f,
0.0010259597f, 0.0010982729f, 0.00093195855f, 0.0009141837f,
0.00096620916f, 0.00089982944f, 0.0010064574f, 0.0010474789f,
0.0009782845f, 0.0009230255f, 0.0010548076f, 0.0010974824f,
0.0010612885f, 0.00089107914f, 0.0010112736f, 0.00097655767f}));
auto out = predictor->GetOutput(0);
ASSERT_EQ(out->shape().size(), 2u);
......
......@@ -62,11 +62,11 @@ TEST(Mobilenet_v1, test_mobilenetv1_lite_x86) {
std::vector<std::vector<float>> results;
// i = 1
results.emplace_back(std::vector<float>(
{0.00019130898, 9.467885e-05, 0.00015971427, 0.0003650665,
0.00026431272, 0.00060884043, 0.0002107942, 0.0015819625,
0.0010323516, 0.00010079765, 0.00011006987, 0.0017364529,
0.0048292773, 0.0013995157, 0.0018453331, 0.0002428986,
0.00020211363, 0.00013668182, 0.0005855956, 0.00025901722}));
{0.00019130898f, 9.467885e-05f, 0.00015971427f, 0.0003650665f,
0.00026431272f, 0.00060884043f, 0.0002107942f, 0.0015819625f,
0.0010323516f, 0.00010079765f, 0.00011006987f, 0.0017364529f,
0.0048292773f, 0.0013995157f, 0.0018453331f, 0.0002428986f,
0.00020211363f, 0.00013668182f, 0.0005855956f, 0.00025901722f}));
auto out = predictor->GetOutput(0);
ASSERT_EQ(out->shape().size(), 2u);
ASSERT_EQ(out->shape()[0], 1);
......
......@@ -63,11 +63,11 @@ TEST(Mobilenet_v2, test_mobilenetv2_lite_x86) {
std::vector<std::vector<float>> results;
// i = 1
results.emplace_back(std::vector<float>(
{0.00017082224, 5.699624e-05, 0.000260885, 0.00016412718,
0.00034818667, 0.00015230637, 0.00032959113, 0.0014772735,
0.0009059976, 9.5378724e-05, 5.386537e-05, 0.0006427285,
0.0070957416, 0.0016094646, 0.0018807327, 0.00010506048,
6.823785e-05, 0.00012269315, 0.0007806194, 0.00022354358}));
{0.00017082224f, 5.699624e-05f, 0.000260885f, 0.00016412718f,
0.00034818667f, 0.00015230637f, 0.00032959113f, 0.0014772735f,
0.0009059976f, 9.5378724e-05f, 5.386537e-05f, 0.0006427285f,
0.0070957416f, 0.0016094646f, 0.0018807327f, 0.00010506048f,
6.823785e-05f, 0.00012269315f, 0.0007806194f, 0.00022354358f}));
auto out = predictor->GetOutput(0);
ASSERT_EQ(out->shape().size(), 2u);
ASSERT_EQ(out->shape()[0], 1);
......
......@@ -63,11 +63,11 @@ TEST(Resnet50, test_resnet50_lite_x86) {
std::vector<std::vector<float>> results;
// i = 1
results.emplace_back(std::vector<float>(
{0.00024139918, 0.00020566184, 0.00022418296, 0.00041731037,
0.0005366107, 0.00016948722, 0.00028638865, 0.0009257241,
0.00072681636, 8.531815e-05, 0.0002129998, 0.0021168243,
0.006387163, 0.0037145028, 0.0012812682, 0.00045948103,
0.00013535398, 0.0002483765, 0.00076759676, 0.0002773295}));
{0.00024139918f, 0.00020566184f, 0.00022418296f, 0.00041731037f,
0.0005366107f, 0.00016948722f, 0.00028638865f, 0.0009257241f,
0.00072681636f, 8.531815e-05f, 0.0002129998f, 0.0021168243f,
0.006387163f, 0.0037145028f, 0.0012812682f, 0.00045948103f,
0.00013535398f, 0.0002483765f, 0.00076759676f, 0.0002773295f}));
auto out = predictor->GetOutput(0);
ASSERT_EQ(out->shape().size(), 2u);
ASSERT_EQ(out->shape()[0], 1);
......
......@@ -82,7 +82,7 @@ TEST(Step_rnn, test_step_rnn_lite_x86) {
std::vector<std::vector<float>> results;
// i = 1
results.emplace_back(std::vector<float>({0.5030127, 0.496987}));
results.emplace_back(std::vector<float>({0.5030127f, 0.496987f}));
auto out = predictor->GetOutput(0);
std::vector<int64_t> out_shape = out->shape();
......
......@@ -2,4 +2,5 @@ if(NOT LITE_WITH_APU)
return()
endif()
lite_cc_library(device_apu SRCS device.cc)
lite_cc_library(neuron_adapter SRCS neuron_adapter.cc)
lite_cc_library(device_apu SRCS device.cc DEPS neuron_adapter)
......@@ -20,48 +20,19 @@ namespace paddle {
namespace lite {
namespace apu {
inline void* LoadFunc(void* libHandle, const char* name) {
CHECK(libHandle != nullptr);
CHECK(name != nullptr);
void* fn = dlsym(libHandle, name);
if (fn == nullptr) {
LOG(WARNING) << "Unable to open Neuron Runtime function [" << name
<< "] Because " << dlerror();
}
return fn;
}
NeuronCompilation* Device::Build(void* libHandle, NeuronModel* model) {
typedef int (*NeuronCompilation_create)(NeuronModel * model,
NeuronCompilation * *compilation);
typedef void (*NeuronCompilation_free)(NeuronCompilation * compilation);
typedef int (*NeuronCompilation_finish)(NeuronCompilation * compilation);
#define LOAD_FUNCTIONS(libHandle, FUNC_NAME, VARIABLE_NAME) \
FUNC_NAME VARIABLE_NAME = \
reinterpret_cast<FUNC_NAME>(LoadFunc(libHandle, #FUNC_NAME));
LOAD_FUNCTIONS(libHandle, NeuronCompilation_create, neuron_compilation_create)
LOAD_FUNCTIONS(libHandle, NeuronCompilation_free, neuron_compilation_free)
LOAD_FUNCTIONS(libHandle, NeuronCompilation_finish, neuron_compilation_finish)
#undef LOAD_FUNCTIONS
int neuron_errCode = 0;
NeuronCompilation* compilation = NULL;
NeuronCompilation* Device::Build(NeuronModel* model) {
VLOG(3) << "[APU] Compile model";
neuron_errCode = (*neuron_compilation_create)(model, &compilation);
NeuronCompilation* compilation = NULL;
int neuron_errCode = NeuronCompilation_create(model, &compilation);
if (NEURON_NO_ERROR != neuron_errCode) {
LOG(WARNING) << "[APU] create compile failed! " << neuron_errCode;
return nullptr;
}
neuron_errCode = (*neuron_compilation_finish)(compilation);
neuron_errCode = NeuronCompilation_finish(compilation);
if (NEURON_NO_ERROR != neuron_errCode) {
LOG(WARNING) << "[APU] compile failed! " << neuron_errCode;
return nullptr;
}
VLOG(3) << "[APU] Build done";
return compilation;
}
......
......@@ -18,7 +18,7 @@
#include <string>
#include <unordered_map>
#include <vector>
#include "NeuronAdapter.h" // NOLINT
#include "lite/backends/apu/neuron_adapter.h"
namespace paddle {
namespace lite {
......@@ -32,7 +32,7 @@ class Device {
}
Device() {}
NeuronCompilation* Build(void* libHandle, NeuronModel* model);
NeuronCompilation* Build(NeuronModel* model);
};
} // namespace apu
......
/* 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 "lite/backends/apu/neuron_adapter.h"
#include <dlfcn.h>
#include <string>
#include <vector>
namespace paddle {
namespace lite {
NeuronAdapter* NeuronAdapter::Global() {
static NeuronAdapter adapter;
return &adapter;
}
NeuronAdapter::NeuronAdapter() {
CHECK(InitHandle()) << "Fail to initialize the Neuron Adapter library!";
InitFunctions();
}
bool NeuronAdapter::InitHandle() {
const std::vector<std::string> paths = {
"libneuron_adapter.so",
#if defined(__aarch64__)
"/vendor/lib64/libneuron_adapter.so",
"/system/lib64/libneuron_adapter.so",
"/system/vendor/lib64/libneuron_adapter.so",
#else
"/vendor/lib/libneuron_adapter.so",
"/system/lib/libneuron_adapter.so",
"/system/vendor/lib/libneuron_adapter.so",
#endif
};
std::string target_lib = "Unknown";
for (auto path : paths) {
handle_ = dlopen(path.c_str(), RTLD_LAZY);
if (handle_ != nullptr) {
target_lib = path;
break;
}
}
VLOG(4) << "Load the Neuron Adapter library from " << target_lib;
if (handle_ != nullptr) {
return true;
} else {
return false;
}
}
void NeuronAdapter::InitFunctions() {
CHECK(handle_ != nullptr) << "The library handle can't be null!";
#define PADDLE_DLSYM(neuron_adapter_func) \
do { \
neuron_adapter_func##_ = \
(neuron_adapter_func##_Type)dlsym(handle_, #neuron_adapter_func); \
if (neuron_adapter_func##_ == nullptr) { \
LOG(FATAL) << "Cannot find the " << #neuron_adapter_func \
<< " symbol in libneuron_adapter.so!"; \
break; \
} \
VLOG(4) << "Loaded the " << #neuron_adapter_func \
<< " symbol successfully."; \
} while (false)
PADDLE_DLSYM(Neuron_getVersion);
PADDLE_DLSYM(NeuronModel_create);
PADDLE_DLSYM(NeuronModel_free);
PADDLE_DLSYM(NeuronModel_finish);
PADDLE_DLSYM(NeuronModel_addOperand);
PADDLE_DLSYM(NeuronModel_setOperandValue);
PADDLE_DLSYM(NeuronModel_setOperandSymmPerChannelQuantParams);
PADDLE_DLSYM(NeuronModel_addOperation);
PADDLE_DLSYM(NeuronModel_identifyInputsAndOutputs);
PADDLE_DLSYM(NeuronCompilation_create);
PADDLE_DLSYM(NeuronCompilation_free);
PADDLE_DLSYM(NeuronCompilation_finish);
PADDLE_DLSYM(NeuronExecution_create);
PADDLE_DLSYM(NeuronExecution_free);
PADDLE_DLSYM(NeuronExecution_setInput);
PADDLE_DLSYM(NeuronExecution_setOutput);
PADDLE_DLSYM(NeuronExecution_compute);
#undef PADDLE_DLSYM
}
} // namespace lite
} // namespace paddle
int Neuron_getVersion(uint32_t* version) {
return paddle::lite::NeuronAdapter::Global()->Neuron_getVersion()(version);
}
int NeuronModel_create(NeuronModel** model) {
return paddle::lite::NeuronAdapter::Global()->NeuronModel_create()(model);
}
void NeuronModel_free(NeuronModel* model) {
return paddle::lite::NeuronAdapter::Global()->NeuronModel_free()(model);
}
int NeuronModel_finish(NeuronModel* model) {
return paddle::lite::NeuronAdapter::Global()->NeuronModel_finish()(model);
}
int NeuronModel_addOperand(NeuronModel* model, const NeuronOperandType* type) {
return paddle::lite::NeuronAdapter::Global()->NeuronModel_addOperand()(model,
type);
}
int NeuronModel_setOperandValue(NeuronModel* model,
int32_t index,
const void* buffer,
size_t length) {
return paddle::lite::NeuronAdapter::Global()->NeuronModel_setOperandValue()(
model, index, buffer, length);
}
int NeuronModel_setOperandSymmPerChannelQuantParams(
NeuronModel* model,
int32_t index,
const NeuronSymmPerChannelQuantParams* channelQuant) {
return paddle::lite::NeuronAdapter::Global()
->NeuronModel_setOperandSymmPerChannelQuantParams()(
model, index, channelQuant);
}
int NeuronModel_addOperation(NeuronModel* model,
NeuronOperationType type,
uint32_t inputCount,
const uint32_t* inputs,
uint32_t outputCount,
const uint32_t* outputs) {
return paddle::lite::NeuronAdapter::Global()->NeuronModel_addOperation()(
model, type, inputCount, inputs, outputCount, outputs);
}
int NeuronModel_identifyInputsAndOutputs(NeuronModel* model,
uint32_t inputCount,
const uint32_t* inputs,
uint32_t outputCount,
const uint32_t* outputs) {
return paddle::lite::NeuronAdapter::Global()
->NeuronModel_identifyInputsAndOutputs()(
model, inputCount, inputs, outputCount, outputs);
}
int NeuronCompilation_create(NeuronModel* model,
NeuronCompilation** compilation) {
return paddle::lite::NeuronAdapter::Global()->NeuronCompilation_create()(
model, compilation);
}
void NeuronCompilation_free(NeuronCompilation* compilation) {
return paddle::lite::NeuronAdapter::Global()->NeuronCompilation_free()(
compilation);
}
int NeuronCompilation_finish(NeuronCompilation* compilation) {
return paddle::lite::NeuronAdapter::Global()->NeuronCompilation_finish()(
compilation);
}
int NeuronExecution_create(NeuronCompilation* compilation,
NeuronExecution** execution) {
return paddle::lite::NeuronAdapter::Global()->NeuronExecution_create()(
compilation, execution);
}
void NeuronExecution_free(NeuronExecution* execution) {
return paddle::lite::NeuronAdapter::Global()->NeuronExecution_free()(
execution);
}
int NeuronExecution_setInput(NeuronExecution* execution,
int32_t index,
const NeuronOperandType* type,
const void* buffer,
size_t length) {
return paddle::lite::NeuronAdapter::Global()->NeuronExecution_setInput()(
execution, index, type, buffer, length);
}
int NeuronExecution_setOutput(NeuronExecution* execution,
int32_t index,
const NeuronOperandType* type,
void* buffer,
size_t length) {
return paddle::lite::NeuronAdapter::Global()->NeuronExecution_setOutput()(
execution, index, type, buffer, length);
}
int NeuronExecution_compute(NeuronExecution* execution) {
return paddle::lite::NeuronAdapter::Global()->NeuronExecution_compute()(
execution);
}
/* 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. */
#pragma once
#include "NeuronAdapter.h" // NOLINT
#include "lite/utils/cp_logging.h"
namespace paddle {
namespace lite {
class NeuronAdapter final {
public:
static NeuronAdapter *Global();
// Platform APIs
using Neuron_getVersion_Type = int (*)(uint32_t *);
using NeuronModel_create_Type = int (*)(NeuronModel **);
using NeuronModel_free_Type = void (*)(NeuronModel *);
using NeuronModel_finish_Type = int (*)(NeuronModel *);
using NeuronModel_addOperand_Type = int (*)(NeuronModel *,
const NeuronOperandType *);
using NeuronModel_setOperandValue_Type = int (*)(NeuronModel *,
int32_t,
const void *,
size_t);
using NeuronModel_setOperandSymmPerChannelQuantParams_Type =
int (*)(NeuronModel *, int32_t, const NeuronSymmPerChannelQuantParams *);
using NeuronModel_addOperation_Type = int (*)(NeuronModel *,
NeuronOperationType,
uint32_t,
const uint32_t *,
uint32_t,
const uint32_t *);
using NeuronModel_identifyInputsAndOutputs_Type = int (*)(
NeuronModel *, uint32_t, const uint32_t *, uint32_t, const uint32_t *);
using NeuronCompilation_create_Type = int (*)(NeuronModel *,
NeuronCompilation **);
using NeuronCompilation_free_Type = void (*)(NeuronCompilation *);
using NeuronCompilation_finish_Type = int (*)(NeuronCompilation *);
using NeuronExecution_create_Type = int (*)(NeuronCompilation *,
NeuronExecution **);
using NeuronExecution_free_Type = void (*)(NeuronExecution *);
using NeuronExecution_setInput_Type = int (*)(NeuronExecution *,
int32_t,
const NeuronOperandType *,
const void *,
size_t);
using NeuronExecution_setOutput_Type = int (*)(
NeuronExecution *, int32_t, const NeuronOperandType *, void *, size_t);
using NeuronExecution_compute_Type = int (*)(NeuronExecution *);
Neuron_getVersion_Type Neuron_getVersion() {
CHECK(Neuron_getVersion_ != nullptr) << "Cannot load Neuron_getVersion!";
return Neuron_getVersion_;
}
NeuronModel_create_Type NeuronModel_create() {
CHECK(NeuronModel_create_ != nullptr) << "Cannot load NeuronModel_create!";
return NeuronModel_create_;
}
NeuronModel_free_Type NeuronModel_free() {
CHECK(NeuronModel_free_ != nullptr) << "Cannot load NeuronModel_free!";
return NeuronModel_free_;
}
NeuronModel_finish_Type NeuronModel_finish() {
CHECK(NeuronModel_finish_ != nullptr) << "Cannot load NeuronModel_finish!";
return NeuronModel_finish_;
}
NeuronModel_addOperand_Type NeuronModel_addOperand() {
CHECK(NeuronModel_addOperand_ != nullptr)
<< "Cannot load NeuronModel_addOperand!";
return NeuronModel_addOperand_;
}
NeuronModel_setOperandValue_Type NeuronModel_setOperandValue() {
CHECK(NeuronModel_setOperandValue_ != nullptr)
<< "Cannot load NeuronModel_setOperandValue!";
return NeuronModel_setOperandValue_;
}
NeuronModel_setOperandSymmPerChannelQuantParams_Type
NeuronModel_setOperandSymmPerChannelQuantParams() {
CHECK(NeuronModel_setOperandSymmPerChannelQuantParams_ != nullptr)
<< "Cannot load NeuronModel_setOperandSymmPerChannelQuantParams!";
return NeuronModel_setOperandSymmPerChannelQuantParams_;
}
NeuronModel_addOperation_Type NeuronModel_addOperation() {
CHECK(NeuronModel_addOperation_ != nullptr)
<< "Cannot load NeuronModel_addOperation!";
return NeuronModel_addOperation_;
}
NeuronModel_identifyInputsAndOutputs_Type
NeuronModel_identifyInputsAndOutputs() {
CHECK(NeuronModel_identifyInputsAndOutputs_ != nullptr)
<< "Cannot load NeuronModel_identifyInputsAndOutputs!";
return NeuronModel_identifyInputsAndOutputs_;
}
NeuronCompilation_create_Type NeuronCompilation_create() {
CHECK(NeuronCompilation_create_ != nullptr)
<< "Cannot load NeuronCompilation_create!";
return NeuronCompilation_create_;
}
NeuronCompilation_free_Type NeuronCompilation_free() {
CHECK(NeuronCompilation_free_ != nullptr)
<< "Cannot load NeuronCompilation_free!";
return NeuronCompilation_free_;
}
NeuronCompilation_finish_Type NeuronCompilation_finish() {
CHECK(NeuronCompilation_finish_ != nullptr)
<< "Cannot load NeuronCompilation_finish!";
return NeuronCompilation_finish_;
}
NeuronExecution_create_Type NeuronExecution_create() {
CHECK(NeuronExecution_create_ != nullptr)
<< "Cannot load NeuronExecution_create!";
return NeuronExecution_create_;
}
NeuronExecution_free_Type NeuronExecution_free() {
CHECK(NeuronExecution_free_ != nullptr)
<< "Cannot load NeuronExecution_free!";
return NeuronExecution_free_;
}
NeuronExecution_setInput_Type NeuronExecution_setInput() {
CHECK(NeuronExecution_setInput_ != nullptr)
<< "Cannot loadcl NeuronExecution_setInput!";
return NeuronExecution_setInput_;
}
NeuronExecution_setOutput_Type NeuronExecution_setOutput() {
CHECK(NeuronExecution_setOutput_ != nullptr)
<< "Cannot load NeuronExecution_setOutput!";
return NeuronExecution_setOutput_;
}
NeuronExecution_compute_Type NeuronExecution_compute() {
CHECK(NeuronExecution_compute_ != nullptr)
<< "Cannot load NeuronExecution_compute!";
return NeuronExecution_compute_;
}
private:
NeuronAdapter();
NeuronAdapter(const NeuronAdapter &) = delete;
NeuronAdapter &operator=(const NeuronAdapter &) = delete;
bool InitHandle();
void InitFunctions();
void *handle_{nullptr};
Neuron_getVersion_Type Neuron_getVersion_{nullptr};
NeuronModel_create_Type NeuronModel_create_{nullptr};
NeuronModel_free_Type NeuronModel_free_{nullptr};
NeuronModel_finish_Type NeuronModel_finish_{nullptr};
NeuronModel_addOperand_Type NeuronModel_addOperand_{nullptr};
NeuronModel_setOperandValue_Type NeuronModel_setOperandValue_{nullptr};
NeuronModel_setOperandSymmPerChannelQuantParams_Type
NeuronModel_setOperandSymmPerChannelQuantParams_{nullptr};
NeuronModel_addOperation_Type NeuronModel_addOperation_{nullptr};
NeuronModel_identifyInputsAndOutputs_Type
NeuronModel_identifyInputsAndOutputs_{nullptr};
NeuronCompilation_create_Type NeuronCompilation_create_{nullptr};
NeuronCompilation_free_Type NeuronCompilation_free_{nullptr};
NeuronCompilation_finish_Type NeuronCompilation_finish_{nullptr};
NeuronExecution_create_Type NeuronExecution_create_{nullptr};
NeuronExecution_free_Type NeuronExecution_free_{nullptr};
NeuronExecution_setInput_Type NeuronExecution_setInput_{nullptr};
NeuronExecution_setOutput_Type NeuronExecution_setOutput_{nullptr};
NeuronExecution_compute_Type NeuronExecution_compute_{nullptr};
};
} // namespace lite
} // namespace paddle
......@@ -92,19 +92,20 @@ void conv_compute_2x2_3x3_int8(const int8_t* input,
int threads = ctx->threads();
int16_t* g_tmp_data =
static_cast<int16_t*>(tmp_work_space + ic_8 * ic_8_stride +
oc_8 * oc_8_stride * sizeof(int32_t));
(int16_t*)(tmp_work_space + ic_8 * ic_8_stride + // NOLINT
oc_8 * oc_8_stride * sizeof(int32_t));
int tmp_input_thread_stride = tile_block * ic_8 * 128;
int tmp_output_thread_stride = tile_block * oc_8 * 128;
int tmp_data_thread_stride_size = tmp_input_thread_stride * sizeof(int16_t) +
tmp_output_thread_stride * sizeof(int32_t);
memset(g_tmp_data, 0, tmp_data_thread_stride_size);
int8_t* g_trans_remain_tmp_data = static_cast<int8_t*>(
g_tmp_data +
threads * (tmp_input_thread_stride +
tmp_output_thread_stride * sizeof(int32_t) / sizeof(int16_t)));
int8_t* g_trans_remain_tmp_data =
(int8_t*)(g_tmp_data + // NOLINT
threads * (tmp_input_thread_stride +
tmp_output_thread_stride * sizeof(int32_t) /
sizeof(int16_t)));
int32_t* g_trans_tmp_data =
static_cast<int32_t*>(g_trans_remain_tmp_data + threads * 128);
(int32_t*)(g_trans_remain_tmp_data + threads * 128); // NOLINT
// begin compute
for (int ni = 0; ni < num; ++ni) {
......@@ -121,7 +122,7 @@ void conv_compute_2x2_3x3_int8(const int8_t* input,
win,
hin);
}
int32_t* output_c8 = static_cast<int32_t*>(input_c8 + ic_8 * ic_8_stride);
int32_t* output_c8 = (int32_t*)(input_c8 + ic_8 * ic_8_stride); // NOLINT
Dtype* output_ptr = output + ni * out_n_stride;
const int16_t* weight_ptr = weight;
......@@ -202,7 +203,7 @@ void conv_compute_2x2_3x3_int8(const int8_t* input,
// *
//*
int32_t* dst_temp_data =
static_cast<int32_t*>(tmp_data + tmp_input_thread_stride);
(int32_t*)(tmp_data + tmp_input_thread_stride); // NOLINT
int16_t* b_ptr = tmp_data;
int w_gi_stride = ic_8 * oc_8 * 64;
for (int gi = 0; gi < 16; ++gi) {
......@@ -229,7 +230,7 @@ void conv_compute_2x2_3x3_int8(const int8_t* input,
int32_t* src_ptr = dst_temp_data + ti * 8;
int32_t* trans_remain_tmp_i32_data =
static_cast<int32_t*>(trans_remain_tmp_data);
(int32_t*)(trans_remain_tmp_data); // NOLINT
int32_t* dst_ptr = output_c8 + (dst_y * wout + dst_x) * 8;
if (ex == 2 && ey == 2) {
......
......@@ -33,6 +33,7 @@ void add_bias_rowwise(Tensor* input,
for (int w = start_w; w < w_adds; ++w) {
i_data[w] += b_data[w];
}
i_data += width;
}
}
void vector_dot(
......@@ -67,15 +68,8 @@ void vector_dot(
for (int i = 0; i < remain; ++i) {
if (!v2) {
out_ptr[i] = in_ptr[i] * v1_ptr[i];
++out_ptr;
++in_ptr;
++v1_ptr;
} else {
out_ptr[i] = in_ptr[i] + v1_ptr[i] * v2_ptr[i];
++out_ptr;
++in_ptr;
++v1_ptr;
++v2_ptr;
}
}
}
......
......@@ -21,6 +21,17 @@ namespace paddle {
namespace lite {
namespace arm {
namespace math {
int AdaptStartIndex(int ph, int input_size, int output_size) {
return static_cast<int>(
floor(static_cast<double>(ph * input_size) / output_size));
}
int AdaptEndIndex(int ph, int input_size, int output_size) {
return static_cast<int>(
ceil(static_cast<double>((ph + 1) * input_size) / output_size));
}
void pooling_basic(const float* din,
float* dout,
int num,
......@@ -88,15 +99,27 @@ void pooling_basic(const float* din,
#pragma omp parallel for
for (int ind_c = 0; ind_c < chin; ++ind_c) {
for (int ind_h = 0; ind_h < hout; ++ind_h) {
int sh = ind_h * stride_h;
int eh = sh + kernel_h;
sh = (sh - pad_h) < 0 ? 0 : sh - pad_h;
eh = (eh - pad_h) > hin ? hin : eh - pad_h;
int sh, eh;
if (adaptive) {
sh = AdaptStartIndex(ind_h, hin, hout);
eh = AdaptEndIndex(ind_h, hin, hout);
} else {
sh = ind_h * stride_h;
eh = sh + kernel_h;
sh = (sh - pad_h) < 0 ? 0 : sh - pad_h;
eh = (eh - pad_h) > hin ? hin : eh - pad_h;
}
for (int ind_w = 0; ind_w < wout; ++ind_w) {
int sw = ind_w * stride_w;
int ew = sw + kernel_w;
sw = (sw - pad_w) < 0 ? 0 : sw - pad_w;
ew = (ew - pad_w) > win ? win : ew - pad_w;
int sw, ew;
if (adaptive) {
sw = AdaptStartIndex(ind_w, win, wout);
ew = AdaptEndIndex(ind_w, win, wout);
} else {
sw = ind_w * stride_w;
ew = sw + kernel_w;
sw = (sw - pad_w) < 0 ? 0 : sw - pad_w;
ew = (ew - pad_w) > win ? win : ew - pad_w;
}
float result = static_cast<float>(0);
int dst_ind = (ind_n * chout + ind_c) * size_channel_out +
ind_h * wout + ind_w;
......
此差异已折叠。
......@@ -40,6 +40,15 @@ void scale_compute_basic(const operators::ScaleParam& param) {
template <typename T>
void scale(const T* din, T* dout, int num, T scale, T bias);
template <typename T>
void scale_relu(const T* din, T* dout, int num, T scale, T bias);
template <typename T>
void scale_relu6(const T* din, T* dout, int num, T scale, T bias, T alpha);
template <typename T>
void scale_leaky_relu(const T* din, T* dout, int num, T scale, T bias, T alpha);
template <typename T>
void scale(const T* din,
T* dout,
......
......@@ -24,16 +24,17 @@ std::map<int, void*> TargetWrapperBM::bm_hds_;
size_t TargetWrapperBM::num_devices() {
int count = 0;
bm_dev_getcount(&count);
bm_status_t ret = bm_dev_getcount(&count);
CHECK_EQ(ret, BM_SUCCESS) << "Failed with error code: "
<< static_cast<int>(ret);
return count;
}
int TargetWrapperBM::GetDevice() { return device_id_; }
void TargetWrapperBM::SetDevice(int id) {
/*
if (id < 0 || (size_t)id >= num_devices()) {
LOG(FATAL) << "Failed with invalid device id " << id;
}
*/
if (id < 0 || (size_t)id >= num_devices()) {
LOG(FATAL) << "Failed with invalid device id " << id;
}
device_id_ = id;
if (bm_hds_.find(id) == bm_hds_.end()) {
bm_handle_t bm_handle;
......
......@@ -31,6 +31,7 @@ class TargetWrapper<TARGET(kBM)> {
static size_t maximum_stream() { return 0; }
static void SetDevice(int id);
static int GetDevice();
static void CreateStream(stream_t* stream) {}
static void DestroyStream(const stream_t& stream) {}
......
......@@ -33,6 +33,9 @@ bool BatchedGemm<float, float>::init(const bool trans_a,
}
cu_trans_a_ = trans_a ? CUBLAS_OP_T : CUBLAS_OP_N;
cu_trans_b_ = trans_b ? CUBLAS_OP_T : CUBLAS_OP_N;
if (A_ != nullptr) {
cudaFree(A_);
}
cudaMalloc(reinterpret_cast<void **>(&A_),
3 * max_batch_size * sizeof(float *));
return true;
......
......@@ -13,6 +13,7 @@
// limitations under the License.
#include "lite/backends/cuda/math/elementwise.h"
#include "lite/utils/cp_logging.h"
namespace paddle {
namespace lite {
......@@ -62,6 +63,52 @@ __global__ void elementwise_relu_kernel(const size_t total,
}
}
template <typename Dtype>
__global__ void elementwise_abs_kernel(const size_t total,
const Dtype* x_data,
const Dtype* y_data,
Dtype* out_data,
int pre,
int n,
int post,
BinaryOperation type) {
int tid = blockIdx.x * blockDim.x + threadIdx.x;
if (tid < total) {
int idx = tid / post % n;
Dtype temp;
#if __CUDA_ARCH__ >= 350
temp = binary_calc(__ldg(x_data + tid), __ldg(y_data + idx), type);
#else
temp = binary_calc(x_data[tid], y_data[idx], type);
#endif
out_data[tid] = temp > 0 ? temp : -temp;
}
}
template <typename Dtype>
__global__ void elementwise_tanh_kernel(const size_t total,
const Dtype* x_data,
const Dtype* y_data,
Dtype* out_data,
int pre,
int n,
int post,
BinaryOperation type) {
int tid = blockIdx.x * blockDim.x + threadIdx.x;
if (tid < total) {
int idx = tid / post % n;
Dtype temp;
#if __CUDA_ARCH__ >= 350
temp = binary_calc(__ldg(x_data + tid), __ldg(y_data + idx), type);
#else
temp = binary_calc(x_data[tid], y_data[idx], type);
#endif
out_data[tid] = tanh(temp);
}
}
template <typename Dtype>
__global__ void elementwise_add_kernel(const size_t total,
const Dtype* x_data,
......@@ -135,19 +182,30 @@ void elementwise(const Dtype* x_data,
}
template <typename Dtype>
void elementwise_relu(const Dtype* x_data,
const Dtype* y_data,
Dtype* out_data,
int pre,
int n,
int post,
BinaryOperation type,
cudaStream_t stream) {
void elementwise_act(const Dtype* x_data,
const Dtype* y_data,
Dtype* out_data,
int pre,
int n,
int post,
std::string act,
BinaryOperation type,
cudaStream_t stream) {
int num = pre * n * post;
int thread = 256;
int block = (num + thread - 1) / thread;
elementwise_relu_kernel<<<block, thread, 0, stream>>>(
num, x_data, y_data, out_data, pre, n, post, type);
if (act == "relu") {
elementwise_relu_kernel<<<block, thread, 0, stream>>>(
num, x_data, y_data, out_data, pre, n, post, type);
} else if (act == "tanh") {
elementwise_tanh_kernel<<<block, thread, 0, stream>>>(
num, x_data, y_data, out_data, pre, n, post, type);
} else if (act == "abs") {
elementwise_abs_kernel<<<block, thread, 0, stream>>>(
num, x_data, y_data, out_data, pre, n, post, type);
} else {
LOG(FATAL) << "not supported activate type: " << act;
}
}
template void elementwise(const float*,
......@@ -159,14 +217,15 @@ template void elementwise(const float*,
BinaryOperation,
cudaStream_t);
template void elementwise_relu(const float*,
const float*,
float*,
int,
int,
int,
BinaryOperation,
cudaStream_t);
template void elementwise_act(const float* x_data,
const float* y_data,
float* out_data,
int pre,
int n,
int post,
std::string act,
BinaryOperation type,
cudaStream_t stream);
template <typename Dtype>
void elementwise_add(int num,
......
......@@ -15,6 +15,7 @@
#pragma once
#include <cuda.h>
#include <cuda_runtime.h>
#include <string>
#include "lite/backends/cuda/math/utils.h"
namespace paddle {
......@@ -33,14 +34,15 @@ void elementwise(const Dtype* x_data,
cudaStream_t stream);
template <typename Dtype>
void elementwise_relu(const Dtype* x_data,
const Dtype* y_data,
Dtype* out_data,
int pre,
int n,
int post,
BinaryOperation type,
cudaStream_t stream);
void elementwise_act(const Dtype* x_data,
const Dtype* y_data,
Dtype* out_data,
int pre,
int n,
int post,
std::string act,
BinaryOperation type,
cudaStream_t stream);
template <typename Dtype>
void elementwise_add(int num,
......
......@@ -157,6 +157,48 @@ cl::NDRange CLContext::LocalWorkSizeTurn(cl::NDRange global_work_size,
static_cast<size_t>(gws0)};
#endif
}
cl::NDRange CLContext::LocalWorkSizeTurnReverse(cl::NDRange global_work_size,
size_t max_work_size,
int divisor) {
int preferred_lws = 0;
#if 0
auto gws0 = global_work_size[0];
auto gws1 = global_work_size[1];
auto gws2 = global_work_size[2];
#else
auto gws2 = global_work_size[0];
auto gws1 = global_work_size[1];
auto gws0 = global_work_size[2];
#endif
if (divisor > 1) {
max_work_size /= divisor;
}
if (preferred_lws > 0 && preferred_lws <= max_work_size) {
max_work_size = preferred_lws;
}
while (gws1 > max_work_size && max_work_size > 0) {
gws1 = gws1 % 2 == 0 ? gws1 / 2 : 1;
}
while (gws2 * gws1 > max_work_size && max_work_size > 0) {
gws2 = gws2 % 2 == 0 ? gws2 / 2 : 1;
}
while (gws0 * gws1 * gws2 > max_work_size && max_work_size > 0) {
gws0 = gws0 % 2 == 0 ? gws0 / 2 : 1;
}
#if 0
return cl::NDRange{static_cast<size_t>(gws0),
static_cast<size_t>(gws1),
static_cast<size_t>(gws2)};
#else
return cl::NDRange{static_cast<size_t>(gws2),
static_cast<size_t>(gws1),
static_cast<size_t>(gws0)};
#endif
}
bool CLContext::IsArmMali() {
return CLRuntime::Global()->GetGpuType() == GpuType::ARM_MALI;
}
cl::NDRange CLContext::LocalWorkSize(cl::NDRange global_work_size,
size_t max_work_size) {
......
......@@ -28,6 +28,7 @@ namespace lite {
class CLContext {
public:
~CLContext() {
GetCommandQueue().finish();
for (size_t kidx = 0; kidx < kernels_.size(); ++kidx) {
// Note(ysh329): Don't need `clReleaseKernel`
kernels_[kidx].reset();
......@@ -65,6 +66,10 @@ class CLContext {
cl::NDRange LocalWorkSizeTurn(cl::NDRange global_work_size,
size_t max_work_size,
int divitor = 2);
cl::NDRange LocalWorkSizeTurnReverse(cl::NDRange global_work_size,
size_t max_work_size,
int divitor = 2);
bool IsArmMali();
// cl::NDRange LocalWorkSizeConv1x1(cl::NDRange global_work_size,
// size_t max_work_size);
......
#include <cl_common.h>
__kernel void conv2d_1x1_opt(
__private const int global_size_dim0,
__private const int global_size_dim1,
......@@ -27,10 +28,7 @@ __kernel void conv2d_1x1_opt(
const int out_c = get_global_id(0);
const int out_w = get_global_id(1);
const int out_nh = get_global_id(2);
if (out_c >= global_size_dim0 || out_w >= global_size_dim1 ||
out_nh >= global_size_dim2) {
return;
}
int out_w0 = out_w;
int out_w1 = out_w + global_size_dim1;
int out_w2 = out_w + global_size_dim1 * 2;
......@@ -76,10 +74,10 @@ __kernel void conv2d_1x1_opt(
CL_DTYPE4 output3 = output0;
#else
CL_DTYPE4 output0 = (CL_DTYPE4)(0.0f, 0.0f, 0.0f, 0.0f);
CL_DTYPE4 output1 = (CL_DTYPE4)(0.0f, 0.0f, 0.0f, 0.0f);
CL_DTYPE4 output2 = (CL_DTYPE4)(0.0f, 0.0f, 0.0f, 0.0f);
CL_DTYPE4 output3 = (CL_DTYPE4)(0.0f, 0.0f, 0.0f, 0.0f);
CL_DTYPE4 output0 = 0.0f;
CL_DTYPE4 output1 = 0.0f;
CL_DTYPE4 output2 = 0.0f;
CL_DTYPE4 output3 = 0.0f;
#endif
int max_w_bound = input_c_block * input_width;
......@@ -88,14 +86,6 @@ __kernel void conv2d_1x1_opt(
// ------------0---------------
int2 pos_in = (int2)(i * input_width + in_pos_in_one_block0.x,
in_pos_in_one_block0.y);
pos_in.x = select(
pos_in.x,
-1,
(pos_in.x < i * input_width + in_pos_in_one_block0.x ||
pos_in.x >= i * input_width + in_pos_in_one_block0.x + input_width));
pos_in.y =
select(pos_in.y, -1, (pos_in.y < 0 || pos_in.y >= global_size_dim2));
CL_DTYPE4 input0 =
READ_IMG_TYPE(CL_DTYPE_CHAR, input_image, sampler, pos_in);
......@@ -142,14 +132,6 @@ __kernel void conv2d_1x1_opt(
// -------------1--------------
pos_in = (int2)(i * input_width + in_pos_in_one_block1.x,
in_pos_in_one_block1.y);
pos_in.x = select(
pos_in.x,
-1,
(pos_in.x < i * input_width + in_pos_in_one_block0.x ||
pos_in.x >= i * input_width + in_pos_in_one_block0.x + input_width));
pos_in.y =
select(pos_in.y, -1, (pos_in.y < 0 || pos_in.y >= global_size_dim2));
CL_DTYPE4 input1 =
READ_IMG_TYPE(CL_DTYPE_CHAR, input_image, sampler, pos_in);
......@@ -186,14 +168,6 @@ __kernel void conv2d_1x1_opt(
// -------------2--------------
pos_in = (int2)(i * input_width + in_pos_in_one_block2.x,
in_pos_in_one_block2.y);
pos_in.x = select(
pos_in.x,
-1,
(pos_in.x < i * input_width + in_pos_in_one_block0.x ||
pos_in.x >= i * input_width + in_pos_in_one_block0.x + input_width));
pos_in.y =
select(pos_in.y, -1, (pos_in.y < 0 || pos_in.y >= global_size_dim2));
CL_DTYPE4 input2 =
READ_IMG_TYPE(CL_DTYPE_CHAR, input_image, sampler, pos_in);
......@@ -230,14 +204,6 @@ __kernel void conv2d_1x1_opt(
// -------------3--------------
pos_in = (int2)(i * input_width + in_pos_in_one_block3.x,
in_pos_in_one_block3.y);
pos_in.x = select(
pos_in.x,
-1,
(pos_in.x < i * input_width + in_pos_in_one_block0.x ||
pos_in.x >= i * input_width + in_pos_in_one_block0.x + input_width));
pos_in.y =
select(pos_in.y, -1, (pos_in.y < 0 || pos_in.y >= global_size_dim2));
CL_DTYPE4 input3 =
READ_IMG_TYPE(CL_DTYPE_CHAR, input_image, sampler, pos_in);
......@@ -339,10 +305,7 @@ __kernel void conv2d_1x1_simple(
const int out_c = get_global_id(0);
const int out_w = get_global_id(1);
const int out_nh = get_global_id(2);
if (out_c >= global_size_dim0 || out_w >= global_size_dim1 ||
out_nh >= global_size_dim2) {
return;
}
int out_w0 = out_w;
int out_w1 = out_w + global_size_dim1;
int out_w2 = out_w + global_size_dim1 * 2;
......@@ -388,25 +351,16 @@ __kernel void conv2d_1x1_simple(
CL_DTYPE4 output3 = output0;
#else
CL_DTYPE4 output0 = (CL_DTYPE4)(0.0f, 0.0f, 0.0f, 0.0f);
CL_DTYPE4 output1 = (CL_DTYPE4)(0.0f, 0.0f, 0.0f, 0.0f);
CL_DTYPE4 output2 = (CL_DTYPE4)(0.0f, 0.0f, 0.0f, 0.0f);
CL_DTYPE4 output3 = (CL_DTYPE4)(0.0f, 0.0f, 0.0f, 0.0f);
CL_DTYPE4 output0 = 0.0f;
CL_DTYPE4 output1 = 0.0f;
CL_DTYPE4 output2 = 0.0f;
CL_DTYPE4 output3 = 0.0f;
#endif
for (int i = 0; i < input_c; ++i) {
// ------------0---------------
int2 pos_in = (int2)(i * input_width + in_pos_in_one_block0.x,
in_pos_in_one_block0.y);
pos_in.x = select(
pos_in.x,
-1,
(pos_in.x < i * input_width + in_pos_in_one_block0.x ||
pos_in.x >= i * input_width + in_pos_in_one_block0.x + input_width));
pos_in.y =
select(pos_in.y, -1, (pos_in.y < 0 || pos_in.y >= global_size_dim2));
CL_DTYPE4 input0 =
READ_IMG_TYPE(CL_DTYPE_CHAR, input_image, sampler, pos_in);
......@@ -426,15 +380,6 @@ __kernel void conv2d_1x1_simple(
pos_in = (int2)(i * input_width + in_pos_in_one_block1.x,
in_pos_in_one_block1.y);
pos_in.x = select(
pos_in.x,
-1,
(pos_in.x < i * input_width + in_pos_in_one_block0.x ||
pos_in.x >= i * input_width + in_pos_in_one_block0.x + input_width));
pos_in.y =
select(pos_in.y, -1, (pos_in.y < 0 || pos_in.y >= global_size_dim2));
CL_DTYPE4 input1 =
READ_IMG_TYPE(CL_DTYPE_CHAR, input_image, sampler, pos_in);
output1 = mad(input1.x, weight0, output1);
......@@ -444,14 +389,6 @@ __kernel void conv2d_1x1_simple(
pos_in = (int2)(i * input_width + in_pos_in_one_block2.x,
in_pos_in_one_block2.y);
pos_in.x = select(
pos_in.x,
-1,
(pos_in.x < i * input_width + in_pos_in_one_block0.x ||
pos_in.x >= i * input_width + in_pos_in_one_block0.x + input_width));
pos_in.y =
select(pos_in.y, -1, (pos_in.y < 0 || pos_in.y >= global_size_dim2));
CL_DTYPE4 input2 =
READ_IMG_TYPE(CL_DTYPE_CHAR, input_image, sampler, pos_in);
output2 = mad(input2.x, weight0, output2);
......@@ -461,16 +398,6 @@ __kernel void conv2d_1x1_simple(
pos_in = (int2)(i * input_width + in_pos_in_one_block3.x,
in_pos_in_one_block3.y);
pos_in.x = select(
pos_in.x,
-1,
(pos_in.x < i * input_width + in_pos_in_one_block0.x ||
pos_in.x >= i * input_width + in_pos_in_one_block0.x + input_width));
pos_in.y =
select(pos_in.y, -1, (pos_in.y < 0 || pos_in.y >= global_size_dim2));
CL_DTYPE4 input3 =
READ_IMG_TYPE(CL_DTYPE_CHAR, input_image, sampler, pos_in);
output3 = mad(input3.x, weight0, output3);
......@@ -502,16 +429,6 @@ __kernel void conv2d_1x1_simple(
output2 = activation_type4(output2);
output3 = activation_type4(output3);
// const int debug_pos = 0;
// int2 pos_test = (int2)(debug_pos, debug_pos);
// if (input_height == 112 && input_width == 112 && output_width == 112 &&
// output_height == 112) {
// output0 = READ_IMG_TYPE(CL_DTYPE_CHAR, input_image, sampler, pos_test);
// output1 = output0;
// output2 = output1;
// output3 = output2;
// }
if (out_w0 < old_w) {
WRITE_IMG_TYPE(CL_DTYPE_CHAR, output_image, output_pos0, output0);
}
......
......@@ -13,6 +13,7 @@ See the License for the specific language governing permissions and
limitations under the License. */
#include <cl_common.h>
__kernel void conv2d_3x3_opt(__private const int item_ch,
__private const int item_w,
__private const int item_h,
......
......@@ -30,10 +30,6 @@ __kernel void buffer_to_image2d(__global CL_DTYPE* in,
const int out_w = get_global_id(1);
const int out_nh = get_global_id(2);
if (out_c >= out_C || out_w >= out_W || out_nh >= out_H) {
return;
}
const int out_n = out_nh / out_H;
const int out_h = out_nh % out_H;
......@@ -59,18 +55,12 @@ __kernel void buffer_to_image2d(__global CL_DTYPE* in,
if (out_C - 4 * out_c >= 2) {
output.y = CONVERT_TYPE_TO(in[input_pos1], CL_COMPUTE_DTYPE);
} else {
output.y = CONVERT_TYPE_TO(0.f, CL_COMPUTE_DTYPE);
}
if (out_C - 4 * out_c >= 3) {
output.z = CONVERT_TYPE_TO(in[input_pos2], CL_COMPUTE_DTYPE);
} else {
output.z = CONVERT_TYPE_TO(0.f, CL_COMPUTE_DTYPE);
}
if (out_C - 4 * out_c >= 4) {
output.w = CONVERT_TYPE_TO(in[input_pos3], CL_COMPUTE_DTYPE);
} else {
output.w = CONVERT_TYPE_TO(0.f, CL_COMPUTE_DTYPE);
}
#ifdef DEBUG
......@@ -146,11 +136,9 @@ __kernel void image2d_to_buffer(__read_only image2d_t input,
if (C - 4 * in_c >= 2) {
out[index + size_ch] = CONVERT_TYPE_TO(in.y, CL_DTYPE);
}
if (C - 4 * in_c >= 3) {
out[index + size_ch * 2] = CONVERT_TYPE_TO(in.z, CL_DTYPE);
}
if (C - 4 * in_c >= 4) {
out[index + size_ch * 3] = CONVERT_TYPE_TO(in.w, CL_DTYPE);
}
......
此差异已折叠。
此差异已折叠。
......@@ -32,7 +32,7 @@ const char* opencl_error_to_str(cl_int error);
__FILE__, \
__LINE__); \
}
#ifndef LITE_SHUTDOWN_LOG
#ifdef LITE_WITH_LOG
#define CL_CHECK_FATAL(err_code__) \
if (err_code__ != CL_SUCCESS) { \
LOG(FATAL) << string_format( \
......
......@@ -90,7 +90,7 @@ void *TargetWrapperCL::MallocImage<uint16_t>(const size_t cl_image2d_width,
cl_int status;
cl::Image2D *cl_image =
new cl::Image2D(CLRuntime::Global()->context(),
CL_MEM_READ_WRITE | (host_ptr ? CL_MEM_USE_HOST_PTR
CL_MEM_READ_WRITE | (host_ptr ? CL_MEM_COPY_HOST_PTR
: CL_MEM_ALLOC_HOST_PTR),
img_format,
cl_image2d_width,
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册