提交 b31ef95d 编写于 作者: J jameswu2014

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

要显示的变更太多。

To preserve performance only 1000 of 1000+ files are displayed.
......@@ -34,6 +34,8 @@
.DS_Store
build/
build_fpga/
docs/_build/
.idea/
......@@ -71,6 +73,9 @@ build
cmake-build-debug
cmake-build-release
# vscode
.vscode
# ios
tools/libomp.a
......
[submodule "third-party/gflags"]
path = third-party/gflags
url = https://github.com/gflags/gflags.git
[submodule "third-party/googletest"]
path = third-party/googletest
url = https://github.com/google/googletest.git
[submodule "third-party/protobuf-mobile"]
path = third-party/protobuf-mobile
url = https://github.com/tensor-tang/protobuf.git
[submodule "third-party/protobuf-host"]
path = third-party/protobuf-host
url = https://github.com/protocolbuffers/protobuf.git
......@@ -3,7 +3,8 @@ repos:
sha: v1.0.1
hooks:
- id: remove-crlf
files: (?!.*third_party)^.*$ | (?!.*book)^.*$ ^mobile/ ^metal/ ^web/
files: (?!.*third_party)^.*$|(?!.*book)^.*$
exclude: ^(mobile/|metal/|web/)
#- repo: https://github.com/PaddlePaddle/mirrors-yapf.git
#sha: 0d79c0c469bab64f7229c9aca2b1186ef47f0e37
#hooks:
......@@ -16,7 +17,7 @@ repos:
- id: check-merge-conflict
- id: check-symlinks
- id: detect-private-key
files: (?!.*third_party)^.*$ | (?!.*book)^.*$
files: (?!.*third_party)^.*$|(?!.*book)^.*$
- id: end-of-file-fixer
- repo: local
hooks:
......@@ -25,7 +26,8 @@ repos:
description: Format files with ClangFormat.
entry: bash ./tools/codestyle/clang_format.hook -i
language: system
files: \.(c|cc|cxx|cpp|cu|h|hpp|hxx|proto)$ ^mobile/ ^metal/ ^web/
files: \.(c|cc|cxx|cpp|cu|h|hpp|hxx|proto)$
exclude: ^(mobile/|metal/|web/)
- repo: local
hooks:
- id: cpplint-cpp-source
......@@ -33,7 +35,8 @@ repos:
description: Check C++ code style using cpplint.py.
entry: bash ./tools/codestyle/cpplint_pre_commit.hook
language: system
files: \.(c|cc|cxx|cpp|cu|h|hpp|hxx)$ ^mobile/ ^metal/ ^web/
files: \.(c|cc|cxx|cpp|cu|h|hpp|hxx)$
exclude: ^(mobile/) | ^(metal/) | ^(web/)
#- repo: local
#hooks:
#- id: pylint-doc-string
......@@ -48,5 +51,6 @@ repos:
name: copyright_checker
entry: python ./tools/codestyle/copyright.hook
language: system
files: \.(c|cc|cxx|cpp|cu|h|hpp|hxx|proto|py)$ ^mobile/ ^metal/ ^web/
exclude: (?!.*third_party)^.*$ | (?!.*book)^.*$
files: \.(c|cc|cxx|cpp|cu|h|hpp|hxx|proto|py)$
exclude: (?!.*third_party)^.*$|(?!.*book)^.*$
exclude: ^(mobile/|metal/|web/)
language: cpp
cache: ccache
sudo: required
dist: trusty
dist: xenial
os:
- linux
......@@ -9,18 +9,17 @@ os:
addons:
apt:
packages:
- git
- python
- python-pip
- python2.7-dev
- libc6-i386
- curl
compiler:
- clang
# - git
# - python
# - python-pip
# - python2.7-dev
# - libc6-i386
# - curl
- clang-format-3.8
before_install:
- sudo pip install -U virtualenv pre-commit pip
- sudo pip install cpplint pre-commit==1.10.3
- sudo ln -s /usr/bin/clang-format-3.8 /usr/bin/clang-format
# Download and install recent cmake
script:
......
......@@ -11,6 +11,8 @@ cd `dirname $0`
cd ..
export PATH=/usr/bin:$PATH
pre-commit install
which clang-format
clang-format --version
if ! pre-commit run -a ; then
ls -lh
......
......@@ -14,8 +14,9 @@
cmake_minimum_required(VERSION 3.0)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(lite_utils)
option(WITH_PADDLE_MOBILE "Use the paddle-mobile legacy build" OFF)
lite_option(WITH_PADDLE_MOBILE "Use the paddle-mobile legacy build" OFF)
if (WITH_PADDLE_MOBILE)
add_subdirectory(mobile)
return()
......@@ -44,13 +45,38 @@ find_package(Threads REQUIRED)
include(simd)
################################ Exposed Configurations #######################################
option(WITH_DSO "Compile PaddlePaddle with dynamic linked CUDA" ON)
option(WITH_AVX "Compile PaddlePaddle with AVX intrinsics" ${AVX_FOUND})
option(WITH_PYTHON "Compile PaddlePaddle with python interpreter" ON)
option(WITH_TESTING "Compile PaddlePaddle with unit testing" OFF)
option(WITH_MKL "Compile PaddlePaddle with MKL support." ${AVX_FOUND})
option(WITH_ARM_DOTPROD "Compile PaddlePaddle with ARM dot production" ON)
option(WITH_SYSTEM_BLAS "Use system blas library" OFF)
lite_option(WITH_DSO "Compile PaddlePaddle with dynamic linked CUDA" ON)
lite_option(WITH_AVX "Compile PaddlePaddle with AVX intrinsics" ON IF ${AVX_FOUND})
lite_option(WITH_TESTING "Compile PaddlePaddle with unit testing" OFF)
lite_option(WITH_MKL "Compile PaddlePaddle with MKL support." ON IF ${AVX_FOUND})
lite_option(WITH_ARM_DOTPROD "Compile PaddlePaddle with ARM dot production" ON)
lite_option(WITH_SYSTEM_BLAS "Use system blas library" OFF)
# for lite, both server and mobile framework.
lite_option(LITE_WITH_JAVA "Enable Java JNI lib in lite mode" OFF)
lite_option(LITE_WITH_PYTHON "Enable Python api lib in lite mode" OFF)
lite_option(LITE_WITH_CUDA "Enable CUDA in lite mode" OFF)
lite_option(LITE_WITH_X86 "Enable X86 in lite mode" ON)
lite_option(LITE_WITH_ARM "Enable ARM in lite mode" OFF)
lite_option(LITE_WITH_NPU "Enable NPU in lite mode" OFF)
lite_option(LITE_WITH_XPU "Enable XPU in lite mode" OFF)
lite_option(LITE_WITH_BM "Enable BM in lite mode" OFF)
lite_option(LITE_WITH_OPENMP "Enable OpenMP in lite framework" ON)
lite_option(LITE_WITH_OPENCL "Enable OpenCL support in lite" OFF)
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 IF LITE_WITH_PROFILE)
lite_option(LITE_SHUTDOWN_LOG "Shutdown log system or not." OFF)
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
lite_option(LITE_BUILD_EXTRA "Enable extra algorithm support in Lite, both kernels and operators" OFF)
lite_option(LITE_BUILD_TAILOR "Enable tailoring library according to model" OFF)
# cv build options
lite_option(LITE_WITH_CV "Enable build cv image in lite" OFF)
lite_option(LITE_WITH_STATIC_CUDA "Statically link cuda libraries." ON)
# TODO(Superjomn) Remove WITH_ANAKIN option if not needed latter.
if(ANDROID OR IOS OR ARMLINUX)
set(WITH_GPU OFF CACHE STRING
......@@ -59,27 +85,16 @@ if(ANDROID OR IOS OR ARMLINUX)
"Disable DSO when cross-compiling for Android and iOS" FORCE)
set(WITH_AVX OFF CACHE STRING
"Disable AVX when cross-compiling for Android and iOS" FORCE)
set(WITH_PYTHON OFF CACHE STRING
"Disable PYTHON when cross-compiling for Android and iOS" FORCE)
set(WITH_RDMA OFF CACHE STRING
"Disable RDMA when cross-compiling for Android and iOS" FORCE)
set(WITH_MKL OFF CACHE STRING
"Disable MKL when cross-compiling for Android and iOS" FORCE)
endif()
# for lite, both server and mobile framework.
option(LITE_WITH_JAVA "Enable Java JNI lib in lite mode" OFF)
option(LITE_WITH_CUDA "Enable CUDA in lite mode" OFF)
option(LITE_WITH_X86 "Enable X86 in lite mode" ON)
option(LITE_WITH_ARM "Enable ARM in lite mode" OFF)
option(LITE_WITH_NPU "Enable NPU in lite mode" OFF)
option(LITE_WITH_OPENMP "Enable OpenMP in lite framework" ON)
option(LITE_WITH_OPENCL "Enable OpenCL support in lite" OFF)
option(LITE_WITH_FPGA "Enable FPGA support in lite" OFF)
option(LITE_WITH_LIGHT_WEIGHT_FRAMEWORK "Enable light-weight framework" OFF)
option(LITE_WITH_PROFILE "Enable profile mode in lite framework" OFF)
option(LITE_SHUTDOWN_LOG "Shutdown log system or not." OFF)
option(LITE_ON_TINY_PUBLISH "Publish tiny predictor lib." OFF)
if(ANDROID OR IOS)
set(LITE_WITH_PYTHON OFF CACHE STRING
"Disable PYTHON when cross-compiling for Android and iOS" FORCE)
endif()
set(THIRD_PARTY_PATH "${CMAKE_BINARY_DIR}/third_party" CACHE STRING
"A path setting third party libraries download & build directories.")
......@@ -90,16 +105,26 @@ if(NOT CMAKE_BUILD_TYPE)
"Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel"
FORCE)
endif()
message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
# check options
if (LITE_ON_TINY_PUBLISH)
if (NOT (WITH_LITE AND LITE_WITH_LIGHT_WEIGHT_FRAMEWORK AND LITE_WITH_JAVA AND NOT WITH_TESTING))
if (NOT (WITH_LITE AND LITE_WITH_LIGHT_WEIGHT_FRAMEWORK AND NOT WITH_TESTING))#LITE_WITH_JAVA AND
message(FATAL_ERROR "LITE_ON_TINY_PUBLISH=ON must be used with WITH_LITE=ON LITE_WITH_LIGHT_WEIGHT_FRAMEWORK=ON LITE_WITH_JAVA=ON WITH_TESTING=OFF")
return()
endif()
endif()
include_directories("${PADDLE_SOURCE_DIR}")
# the generated header files.
set(LITE_GENERATED_INCLUDE_DIR "${CMAKE_BINARY_DIR}")
include_directories("${LITE_GENERATED_INCLUDE_DIR}")
if (LITE_WITH_PYTHON)
include(external/python) # download, build, install python
include(external/pybind11) # download, build, install pybind11
endif()
# for mobile
if (WITH_LITE AND LITE_WITH_LIGHT_WEIGHT_FRAMEWORK)
......@@ -107,16 +132,15 @@ if (WITH_LITE AND LITE_WITH_LIGHT_WEIGHT_FRAMEWORK)
include(cross_compiling/postproject)
include(cross_compiling/npu) # check and prepare NPU DDK
# include the necessary thirdparty dependencies
include(external/gflags) # download, build, install gflags
# LITE_WITH_LIGHT_WEIGHT_FRAMEWORK=ON will disable glog
# TODO(sangoly): refine WITH_LITE and LITE_WITH_LIGHT_WEIGHT_FRAMEWORK
#include(external/glog) # download, build, install glog
include(external/gtest) # download, build, install gtest
include(ccache) # set ccache for compilation
# We compile the mobile deployment library when LITE_ON_TINY_PUBLISH=ON
# So the following third party dependencies are not needed.
if (NOT LITE_ON_TINY_PUBLISH)
# include the necessary thirdparty dependencies
include(external/gflags) # download, build, install gflags
# LITE_WITH_LIGHT_WEIGHT_FRAMEWORK=ON will disable glog
# TODO(sangoly): refine WITH_LITE and LITE_WITH_LIGHT_WEIGHT_FRAMEWORK
include(external/gtest) # download, build, install gtest
include(ccache) # set ccache for compilation
include(external/protobuf) # download, build, install protobuf
endif()
......@@ -132,6 +156,7 @@ if (WITH_LITE AND LITE_WITH_LIGHT_WEIGHT_FRAMEWORK)
add_subdirectory(lite)
return()
endif()
################################# End of mobile compile ##############################
set(WITH_MKLML ${WITH_MKL})
if (NOT DEFINED WITH_MKLDNN)
......@@ -145,6 +170,10 @@ endif()
########################################################################################
if(LITE_WITH_XPU)
include(xpu)
endif()
include(external/mklml) # download mklml package
include(external/xbyak) # download xbyak package
include(external/libxsmm) # download, build, install libxsmm
......@@ -157,12 +186,21 @@ include(external/mkldnn) # download, build, install mkldnn
include(external/eigen) # download eigen3
include(external/xxhash) # download install xxhash needed for x86 jit
include(cudnn)
include(configure) # add paddle env configuration
if(LITE_WITH_CUDA)
include(cuda)
endif()
if(LITE_WITH_BM)
include(bm)
endif()
include(generic) # simplify cmake module
include(ccache) # set ccache for compilation
include(util) # set unittest and link libs
include(version) # set PADDLE_VERSION
include(flags)
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG")
......
......@@ -3,14 +3,14 @@
# Paddle Lite
<!--[![Build Status](https://travis-ci.org/PaddlePaddle/Paddle-Lite.svg?branch=develop&longCache=true&style=flat-square)](https://travis-ci.org/PaddlePaddle/Paddle-Lite)-->
[![Documentation Status](https://img.shields.io/badge/中文文档-最新-brightgreen.svg)](https://github.com/PaddlePaddle/Paddle-Lite/wiki)
[![Documentation Status](https://img.shields.io/badge/中文文档-最新-brightgreen.svg)](https://paddle-lite.readthedocs.io/zh/latest/)
[![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE)
<!-- [![Release](https://img.shields.io/github/release/PaddlePaddle/Paddle-Mobile.svg)](https://github.com/PaddlePaddle/Paddle-Mobile/releases) -->
Paddle Lite is an updated version of Paddle-Mobile, an open-open source deep learning framework designed to make it easy to perform inference on mobile, embeded, and IoT devices. It is compatible with PaddlePaddle and pre-trained models from other sources.
For tutorials, please see [PaddleLite Wiki](https://github.com/PaddlePaddle/Paddle-Lite/wiki).
For tutorials, please see [PaddleLite Document](https://paddle-lite.readthedocs.io/zh/latest/).
## Key Features
......@@ -30,7 +30,7 @@ It also supports INT8 quantizations with [PaddleSlim model compression tools](ht
On Huawei NPU and FPGA, the performance is also boosted.
The latest benchmark is located at [benchmark](https://github.com/PaddlePaddle/Paddle-Lite/wiki/benchmark)
The latest benchmark is located at [benchmark](https://paddlepaddle.github.io/Paddle-Lite/develop/benchmark/)
### High Compatibility
......@@ -44,7 +44,7 @@ Framework compatibility: In addition to models trained on PaddlePaddle, those tr
Paddle Lite is designed to support a wide range of hardwares and devices, and it enables mixed execution of a single model on multiple devices, optimization on various phases, and leight-weighted applications on devices.
![img](https://github.com/Superjomn/_tmp_images/raw/master/images/paddle-lite-architecture.png)
![img](https://user-images.githubusercontent.com/45189361/70908123-6ce4fd00-2045-11ea-97e1-ad08446c5c86.png)
As is shown in the figure above, analysis phase includes Machine IR module, and it enables optimizations like Op fusion and redundant computation pruning. Besides, excecution phase only involves Kernal exevution, so it can be deployed on its own to ensure maximized light-weighted deployment.
......@@ -63,8 +63,12 @@ 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.
## Feedback and Community Support
- Questions, reports, and suggestions are welcome through Github Issues!
- Forum: Opinions and questions are welcome at our [PaddlePaddle Forum](https://ai.baidu.com/forum/topic/list/168)
- QQ group chat: 696965088
- WeChat Official Account: PaddlePaddle
- QQ Group Chat: 696965088
<p align="center"><img width="200" height="200" src="https://user-images.githubusercontent.com/45189361/64117959-1969de80-cdc9-11e9-84f7-e1c2849a004c.jpeg"/>&#8194;&#8194;&#8194;&#8194;&#8194;<img width="200" height="200" margin="500" src="https://user-images.githubusercontent.com/45189361/64117844-cb54db00-cdc8-11e9-8c08-24bbe594608e.jpeg"/></p>
<p align="center">&#8194; WeChat Official Account&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;QQ Group Chat&#8194;&#8194;&#8194;&#8194;&#8194;</p>
# Paddle Lite
<!--[![Build Status](https://travis-ci.org/PaddlePaddle/Paddle-Lite.svg?branch=develop&longCache=true&style=flat-square)](https://travis-ci.org/PaddlePaddle/Paddle-Lite)-->
[![Documentation Status](https://img.shields.io/badge/中文文档-最新-brightgreen.svg)](https://github.com/PaddlePaddle/Paddle-Lite/wiki)
[![Documentation Status](https://img.shields.io/badge/中文文档-最新-brightgreen.svg)](https://paddle-lite.readthedocs.io/zh/latest/)
[![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE)
<!-- [![Release](https://img.shields.io/github/release/PaddlePaddle/Paddle-Mobile.svg)](https://github.com/PaddlePaddle/Paddle-Mobile/releases) -->
Paddle Lite为Paddle-Mobile的升级版,定位支持包括手机移动端在内更多场景的轻量化高效预测,支持更广泛的硬件和平台,是一个高性能、轻量级的深度学习预测引擎。在保持和PaddlePaddle无缝对接外,也兼容支持其他训练框架产出的模型。
完整使用文档位于 [PaddleLite Wiki](https://github.com/PaddlePaddle/Paddle-Lite/wiki)
完整使用文档位于 [PaddleLite 文档](https://paddle-lite.readthedocs.io/zh/latest/)
## 特性
......@@ -21,7 +21,7 @@ Paddle Lite为Paddle-Mobile的升级版,定位支持包括手机移动端在
支持INT8量化计算,结合 [PaddleSlim 模型压缩工具](https://github.com/PaddlePaddle/models/tree/v1.5/PaddleSlim) 中 INT8量化训练功能,可以提供高精度高性能的预测能力。
在Huawei NPU, FPGA上也具有有很好的性能表现。
最新 Benchmark 位于 [benchmark](https://github.com/PaddlePaddle/Paddle-Lite/wiki/benchmark)
最新 Benchmark 位于 [benchmark](https://paddlepaddle.github.io/Paddle-Lite/develop/benchmark/)
### 通用性
硬件方面,Paddle Lite 的架构设计为多硬件兼容支持做了良好设计。除了支持ARM CPU、Mali GPU、Adreno GPU,还特别支持了华为 NPU,以及 FPGA 等边缘设备广泛使用的硬件。即将支持支持包括寒武纪、比特大陆等AI芯片,未来会增加对更多硬件的支持。
......@@ -34,7 +34,7 @@ Paddle Lite为Paddle-Mobile的升级版,定位支持包括手机移动端在
PaddleLite 的架构设计着重考虑了对多硬件和平台的支持,并且强化了多个硬件在一个模型中混合执行的能力,多个层面的性能优化处理,以及对端侧应用的轻量化设计。
![](https://github.com/Superjomn/_tmp_images/raw/master/images/paddle-lite-architecture.png)
![](https://user-images.githubusercontent.com/45189361/70908123-6ce4fd00-2045-11ea-97e1-ad08446c5c86.png)
其中,Analysis Phase 包括了 MIR(Machine IR) 相关模块,能够对原有的模型的计算图针对具体的硬件列表进行算子融合、计算裁剪 在内的多种优化。Execution Phase 只涉及到Kernel 的执行,且可以单独部署,以支持极致的轻量级部署。
......@@ -51,8 +51,12 @@ Paddle Lite 借鉴了以下开源项目:
- [ARM compute library]((https://github.com/ARM-software/ComputeLibrary))
- [Anakin](https://github.com/PaddlePaddle/Anakin) ,Anakin对应底层的一些优化实现已被集成到Paddle Lite。Anakin作为PaddlePaddle组织下的一个高性能预测项目,极具前瞻性,对Paddle Lite有重要贡献。Anakin已和本项目实现整合。之后,Anakin不再升级。
## 交流与反馈
* 欢迎您通过Github Issues来提交问题、报告与建议
* 微信公众号:飞桨PaddlePaddle
* QQ群: 696965088
<p align="center"><img width="200" height="200" src="https://user-images.githubusercontent.com/45189361/64117959-1969de80-cdc9-11e9-84f7-e1c2849a004c.jpeg"/>&#8194;&#8194;&#8194;&#8194;&#8194;<img width="200" height="200" margin="500" src="https://user-images.githubusercontent.com/45189361/64117844-cb54db00-cdc8-11e9-8c08-24bbe594608e.jpeg"/></p>
<p align="center"> &#8194;&#8194;&#8194;微信公众号&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;官方技术交流QQ群</p>
* 论坛: 欢迎大家在[PaddlePaddle论坛](https://ai.baidu.com/forum/topic/list/168)分享在使用PaddlePaddle中遇到的问题和经验, 营造良好的论坛氛围
# Copyright (c) 2019 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.
if(NOT LITE_WITH_BM)
return()
endif()
if(NOT DEFINED BM_SDK_ROOT)
set(BM_SDK_ROOT $ENV{BM_SDK_ROOT})
if(NOT BM_SDK_ROOT)
message(FATAL_ERROR "Must set BM_SDK_ROOT or env BM_SDK_ROOT when LITE_WITH_BM=ON")
endif()
endif()
message(STATUS "BM_SDK_ROOT: ${BM_SDK_ROOT}")
find_path(BM_SDK_INC NAMES bmruntime_interface.h
PATHS ${BM_SDK_ROOT}/include/bmruntime NO_DEFAULT_PATH)
if(NOT BM_SDK_INC)
message(FATAL_ERROR "Can not find bmruntime_interface.h in ${BM_SDK_ROOT}/include")
endif()
include_directories("${BM_SDK_ROOT}/include/bmruntime")
include_directories("${BM_SDK_ROOT}/include/bmlib")
include_directories("${BM_SDK_ROOT}/include/bmcompiler")
include_directories("${BM_SDK_ROOT}/include/bmcpu")
include_directories("${BM_SDK_ROOT}/include/bmlog")
find_library(BM_SDK_RT_LIB NAMES bmrt
PATHS ${BM_SDK_ROOT}/lib/bmnn/pcie)
if(NOT BM_SDK_RT_LIB)
message(FATAL_ERROR "Can not find bmrt Library in ${BM_SDK_ROOT}")
else()
message(STATUS "Found bmrt Library: ${BM_SDK_RT_LIB}")
add_library(bmrt SHARED IMPORTED GLOBAL)
set_property(TARGET bmrt PROPERTY IMPORTED_LOCATION ${BM_SDK_RT_LIB})
endif()
find_library(BM_SDK_BM_LIB NAMES bmlib
PATHS ${BM_SDK_ROOT}/lib/bmnn/pcie)
if(NOT BM_SDK_BM_LIB)
message(FATAL_ERROR "Can not find bmlib Library in ${BM_SDK_ROOT}")
else()
message(STATUS "Found bmlib Library: ${BM_SDK_BM_LIB}")
add_library(bmlib SHARED IMPORTED GLOBAL)
set_property(TARGET bmlib PROPERTY IMPORTED_LOCATION ${BM_SDK_BM_LIB})
endif()
find_library(BM_SDK_COMPILER_LIB NAMES bmcompiler
PATHS ${BM_SDK_ROOT}/lib/bmcompiler)
if(NOT BM_SDK_COMPILER_LIB)
message(FATAL_ERROR "Can not find bmcompiler Library in ${BM_SDK_ROOT}")
else()
message(STATUS "Found bmcompiler Library: ${BM_SDK_COMPILER_LIB}")
add_library(bmcompiler SHARED IMPORTED GLOBAL)
set_property(TARGET bmcompiler PROPERTY IMPORTED_LOCATION ${BM_SDK_COMPILER_LIB})
endif()
find_library(BM_SDK_CPU_LIB NAMES bmcpu
PATHS ${BM_SDK_ROOT}/lib/bmnn/pcie)
if(NOT BM_SDK_CPU_LIB)
message(FATAL_ERROR "Can not find bmcpu Library in ${BM_SDK_ROOT}")
else()
message(STATUS "Found bmcpu Library: ${BM_SDK_CPU_LIB}")
add_library(bmcpu SHARED IMPORTED GLOBAL)
set_property(TARGET bmcpu PROPERTY IMPORTED_LOCATION ${BM_SDK_CPU_LIB})
endif()
set(bm_runtime_libs bmrt bmlib bmcompiler bmcpu CACHE INTERNAL "bm runtime libs")
set(bm_builder_libs bmrt bmlib bmcompiler bmcpu CACHE INTERNAL "bm builder libs")
......@@ -34,35 +34,8 @@ elseif(SSE3_FOUND)
set(SIMD_FLAG ${SSE3_FLAG})
endif()
if(WIN32)
# windows header option for all targets.
add_definitions(-D_XKEYCHECK_H)
# Use symbols instead of absolute path, reduce the cmake link command length.
SET(CMAKE_C_USE_RESPONSE_FILE_FOR_LIBRARIES 1)
SET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_LIBRARIES 1)
SET(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1)
SET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS 1)
SET(CMAKE_C_USE_RESPONSE_FILE_FOR_INCLUDES 1)
SET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 1)
SET(CMAKE_C_RESPONSE_FILE_LINK_FLAG "@")
SET(CMAKE_CXX_RESPONSE_FILE_LINK_FLAG "@")
# Specify the program to use when building static libraries
SET(CMAKE_C_CREATE_STATIC_LIBRARY "<CMAKE_AR> lib <TARGET> <LINK_FLAGS> <OBJECTS>")
SET(CMAKE_CXX_CREATE_STATIC_LIBRARY "<CMAKE_AR> lib <TARGET> <LINK_FLAGS> <OBJECTS>")
# set defination for the dll export
if (NOT MSVC)
message(FATAL "Windows build only support msvc. Which was binded by the nvcc compiler of NVIDIA.")
endif(NOT MSVC)
endif(WIN32)
if(WITH_PSLIB)
add_definitions(-DPADDLE_WITH_PSLIB)
endif()
if(LITE_WITH_GPU)
add_definitions(-DPADDLE_WITH_CUDA)
if(LITE_WITH_CUDA)
add_definitions(-DLITE_WITH_CUDA)
add_definitions(-DEIGEN_USE_GPU)
FIND_PACKAGE(CUDA REQUIRED)
......@@ -86,36 +59,6 @@ if(LITE_WITH_GPU)
include_directories(${CUDNN_INCLUDE_DIR})
include_directories(${CUDA_TOOLKIT_INCLUDE})
if(TENSORRT_FOUND)
if(${CUDA_VERSION_MAJOR} VERSION_LESS 8)
message(FATAL_ERROR "TensorRT needs CUDA >= 8.0 to compile")
endif()
if(${CUDNN_MAJOR_VERSION} VERSION_LESS 7)
message(FATAL_ERROR "TensorRT needs CUDNN >= 7.0 to compile")
endif()
if(${TENSORRT_MAJOR_VERSION} VERSION_LESS 4)
message(FATAL_ERROR "Paddle needs TensorRT >= 4.0 to compile")
endif()
include_directories(${TENSORRT_INCLUDE_DIR})
endif()
if(WITH_ANAKIN)
if(${CUDA_VERSION_MAJOR} VERSION_LESS 8)
message(WARNING "Anakin needs CUDA >= 8.0 to compile. Force WITH_ANAKIN=OFF")
set(WITH_ANAKIN OFF CACHE STRING "Anakin is valid only when CUDA >= 8.0." FORCE)
endif()
if(${CUDNN_MAJOR_VERSION} VERSION_LESS 7)
message(WARNING "Anakin needs CUDNN >= 7.0 to compile. Force WITH_ANAKIN=OFF")
set(WITH_ANAKIN OFF CACHE STRING "Anakin is valid only when CUDNN >= 7.0." FORCE)
endif()
add_definitions(-DWITH_ANAKIN)
endif()
if(WITH_ANAKIN)
# NOTICE(minqiyang): the end slash is important because $CUDNN_INCLUDE_DIR
# is a softlink to real cudnn.h directory
set(ENV{CUDNN_INCLUDE_DIR} "${CUDNN_INCLUDE_DIR}/")
get_filename_component(CUDNN_LIBRARY_DIR ${CUDNN_LIBRARY} DIRECTORY)
set(ENV{CUDNN_LIBRARY} ${CUDNN_LIBRARY_DIR})
endif()
elseif(WITH_AMD_GPU)
add_definitions(-DPADDLE_WITH_HIP)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__HIP_PLATFORM_HCC__")
......@@ -168,18 +111,18 @@ endif()
# for lite
# TODO(Superjomn) not work fine with the option
if (LITE_WITH_CUDA)
add_definitions("-DLITE_WITH_CUDA")
endif()
if (LITE_WITH_X86)
add_definitions("-DLITE_WITH_X86")
endif()
if (LITE_WITH_ARM)
add_definitions("-DLITE_WITH_ARM")
if (LITE_WITH_CV)
add_definitions("-DLITE_WITH_CV")
endif()
endif()
if (WITH_ARM_DOTPROD)
add_definitions("-DWITH_ARM_DOTPROD")
endif()
......@@ -188,6 +131,10 @@ if (LITE_WITH_NPU)
add_definitions("-DLITE_WITH_NPU")
endif()
if (LITE_WITH_XPU)
add_definitions("-DLITE_WITH_XPU")
endif()
if (LITE_WITH_OPENCL)
add_definitions("-DLITE_WITH_OPENCL")
endif()
......@@ -196,8 +143,15 @@ if (LITE_WITH_FPGA)
add_definitions("-DLITE_WITH_FPGA")
endif()
if (LITE_WITH_BM)
add_definitions("-DLITE_WITH_BM")
endif()
if (LITE_WITH_PROFILE)
add_definitions("-DLITE_WITH_PROFILE")
if (LITE_WITH_PRECISION_PROFILE)
add_definitions("-DLITE_WITH_PRECISION_PROFILE")
endif()
endif()
if (LITE_WITH_LIGHT_WEIGHT_FRAMEWORK)
......@@ -211,3 +165,8 @@ endif()
if (LITE_ON_TINY_PUBLISH)
add_definitions("-DLITE_ON_TINY_PUBLISH")
endif()
if (LITE_ON_MODEL_OPTIMIZE_TOOL)
add_definitions("-DLITE_ON_MODEL_OPTIMIZE_TOOL")
endif(LITE_ON_MODEL_OPTIMIZE_TOOL)
......@@ -18,6 +18,7 @@ endif()
set(ANDROID TRUE)
add_definitions(-DLITE_WITH_LINUX)
add_definitions(-DLITE_WITH_ANDROID)
if(NOT DEFINED ANDROID_NDK)
set(ANDROID_NDK $ENV{NDK_ROOT})
......@@ -32,7 +33,10 @@ if(ARM_TARGET_LANG STREQUAL "gcc")
endif()
if(NOT DEFINED ANDROID_API_LEVEL)
set(ANDROID_API_LEVEL "22")
set(ANDROID_API_LEVEL "23")
if(ARM_TARGET_ARCH_ABI STREQUAL "armv7")
set(ANDROID_API_LEVEL "22")
endif()
endif()
# then check input arm abi
......
......@@ -120,6 +120,7 @@
#
## Lite settings
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto")
if (ARM_TARGET_OS STREQUAL "ios")
set(PLATFORM "OS")
elseif(ARM_TARGET_OS STREQUAL "ios64")
......@@ -127,6 +128,7 @@ elseif(ARM_TARGET_OS STREQUAL "ios64")
else()
return()
endif()
add_definitions(-DTARGET_IOS)
# if do not specify the ARM_TARGET_ARCH_ABI then use default all supported
if(ARM_TARGET_ARCH_ABI STREQUAL "armv7"
......
......@@ -30,16 +30,25 @@ if(NOT NPU_DDK_INC)
message(FATAL_ERROR "Can not find HiAiModelManagerService.h in ${NPU_DDK_ROOT}/include")
endif()
include_directories("${NPU_DDK_ROOT}")
include_directories("${NPU_DDK_ROOT}/include")
set(NPU_SUB_LIB_PATH "lib64")
if(ARM_TARGET_ARCH_ABI STREQUAL "armv8")
set(NPU_SUB_LIB_PATH "lib64")
endif()
if(ARM_TARGET_ARCH_ABI STREQUAL "armv7")
set(NPU_SUB_LIB_PATH "lib")
endif()
find_library(NPU_DDK_HIAI_FILE NAMES hiai
PATHS ${NPU_DDK_ROOT}/lib64)
PATHS ${NPU_DDK_ROOT}/${NPU_SUB_LIB_PATH})
find_library(NPU_DDK_IR_FILE NAMES hiai_ir
PATHS ${NPU_DDK_ROOT}/lib64)
PATHS ${NPU_DDK_ROOT}/${NPU_SUB_LIB_PATH})
find_library(NPU_DDK_IR_BUILD_FILE NAMES hiai_ir_build
PATHS ${NPU_DDK_ROOT}/lib64)
PATHS ${NPU_DDK_ROOT}/${NPU_SUB_LIB_PATH})
if(NOT NPU_DDK_HIAI_FILE)
message(FATAL_ERROR "Can not find NPU_DDK_HIAI_FILE in ${NPU_DDK_ROOT}")
......@@ -65,6 +74,8 @@ else()
set_property(TARGET npu_ddk_ir_build PROPERTY IMPORTED_LOCATION ${NPU_DDK_IR_BUILD_FILE})
endif()
set(npu_ddk_libs npu_ddk_hiai npu_ddk_ir npu_ddk_ir_build CACHE INTERNAL "npu ddk libs")
set(npu_runtime_libs npu_ddk_hiai CACHE INTERNAL "npu ddk runtime libs")
set(npu_builder_libs npu_ddk_ir npu_ddk_ir_build CACHE INTERNAL "npu ddk builder libs")
......@@ -26,6 +26,8 @@ if(ANDROID)
endif()
if(ARMLINUX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
if(ARMLINUX_ARCH_ABI STREQUAL "armv8")
set(CMAKE_CXX_FLAGS "-march=armv8-a ${CMAKE_CXX_FLAGS}")
set(CMAKE_C_FLAGS "-march=armv8-a ${CMAKE_C_FLAGS}")
......@@ -57,9 +59,17 @@ function(check_linker_flag)
endfunction()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
if (LITE_ON_TINY_PUBLISH)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffast-math -Ofast -Os -fno-exceptions -fomit-frame-pointer -fno-asynchronous-unwind-tables -fno-unwind-tables")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto -fvisibility=hidden -fvisibility-inlines-hidden -fdata-sections -ffunction-sections")
check_linker_flag(-Wl,--gc-sections)
if(NOT LITE_WITH_PYTHON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
endif()
if(ARM_TARGET_LANG STREQUAL "clang")
# TODO(ysh329): add options for clang like gcc branch below
else() # ARM_TARGET_LANG STREQUAL "gcc"
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffast-math -Ofast -Os -fomit-frame-pointer -fno-asynchronous-unwind-tables -fno-unwind-tables")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden -ffunction-sections")
check_linker_flag(-Wl,--gc-sections)
endif()
endif()
if(LITE_WITH_OPENMP)
......
if(NOT WITH_GPU)
if(NOT LITE_WITH_CUDA)
return()
endif()
set(paddle_known_gpu_archs "30 35 50 52 60 61 70")
set(paddle_known_gpu_archs7 "30 35 50 52")
set(paddle_known_gpu_archs8 "30 35 50 52 60 61")
set(paddle_known_gpu_archs9 "30 35 50 52 60 61 70")
set(paddle_known_gpu_archs10 "30 35 50 52 60 61 70 75")
set(paddle_known_gpu_archs8 "30 35 50 52 53 60 61 62")
set(paddle_known_gpu_archs9 "30 35 50 52 53 60 61 62 70")
set(paddle_known_gpu_archs10 "30 35 50 52 53 60 61 62 70 72 75")
######################################################################################
# A function for automatic detection of GPUs installed (if autodetection is enabled)
......@@ -174,6 +174,45 @@ if(NOT WITH_DSO)
endif(WIN32)
endif(NOT WITH_DSO)
function(add_cuda_lib TARGET_NAME)
set(options STATIC SHARED)
set(oneValueArgs "NAME")
set(multiValueArgs "PATHS")
cmake_parse_arguments(add_cuda_lib "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
unset(ABS_PATH CACHE)
if (NOT add_cuda_lib_PATHS)
set(add_cuda_lib_PATHS CUDNN_CHECK_LIBRARY_DIRS)
endif()
find_library(ABS_PATH NAMES ${add_cuda_lib_NAME} PATHS ${${add_cuda_lib_PATHS}} NO_DEFAULT_PATH)
add_library(${TARGET_NAME} SHARED IMPORTED GLOBAL)
set_property(TARGET ${TARGET_NAME} PROPERTY IMPORTED_LOCATION ${ABS_PATH})
set(CUDA_MODULES ${CUDA_MODULES} ${TARGET_NAME} PARENT_SCOPE)
if (NOT ABS_PATH)
message(FATAL_ERROR "Can not find CUDA library: ${add_cuda_lib_NAME}")
endif()
endfunction()
if(LITE_WITH_STATIC_CUDA)
message(STATUS "Static link CUDA toolkit.")
add_cuda_lib(cudart_static STATIC NAME libcudart_static.a)
add_cuda_lib(cublas_static STATIC NAME libcublas_static.a)
add_cuda_lib(curand_static STATIC NAME libcurand_static.a)
add_cuda_lib(culibos_static STATIC NAME libculibos.a)
if(NOT ${CUDA_VERSION} LESS 10.1)
add_cuda_lib(cublasLt_static STATIC NAME libcublasLt_static.a)
endif()
set_property(GLOBAL PROPERTY CUDA_MODULES cudnn_static ${CUDA_MODULES})
else()
message(STATUS "Dynamic Link CUDA toolkit.")
add_cuda_lib(cudart SHARED NAME libcudart.so)
add_cuda_lib(cublas SHARED NAME libcublas.so)
add_cuda_lib(curand SHARED NAME libcurand.so)
if(NOT ${CUDA_VERSION} LESS 10.1)
add_cuda_lib(cublasLt SHARED NAME libcublasLt.so)
endif()
set_property(GLOBAL PROPERTY CUDA_MODULES cudnn ${CUDA_MODULES})
endif()
# setting nvcc arch flags
select_nvcc_arch_flags(NVCC_FLAGS_EXTRA)
list(APPEND CUDA_NVCC_FLAGS ${NVCC_FLAGS_EXTRA})
......
if(NOT WITH_GPU)
if(NOT LITE_WITH_CUDA)
return()
endif()
......@@ -26,18 +26,24 @@ list(APPEND CUDNN_CHECK_LIBRARY_DIRS
${CUDNN_ROOT}/lib64
${CUDNN_ROOT}/lib
${CUDNN_ROOT}/lib/${TARGET_ARCH}-linux-gnu
${CUDNN_ROOT}/local/cuda-${CUDA_VERSION}/targets/${TARGET_ARCH}-linux/lib/
/usr/local/cuda-${CUDA_VERSION}/targets/${TARGET_ARCH}-linux/lib/
/usr/lib/${TARGET_ARCH}-linux-gnu/
$ENV{CUDNN_ROOT}
$ENV{CUDNN_ROOT}/lib64
$ENV{CUDNN_ROOT}/lib
/usr/lib
${CUDA_TOOLKIT_ROOT_DIR}
${CUDA_TOOLKIT_ROOT_DIR}/lib/x64
)
set(CUDNN_LIB_NAME "")
if (LINUX)
${CUDA_TOOLKIT_ROOT_DIR}
${CUDA_TOOLKIT_ROOT_DIR}/lib/x64
${CUDA_TOOLKIT_ROOT_DIR}/lib64)
if((${CUDA_VERSION} GREATER 10.0) OR (${CUDA_VERSION} EQUAL 10.0))
find_library(CUBLAS_LIBRARY NAMES libcublas.so PATHS ${CUDNN_CHECK_LIBRARY_DIRS} NO_DEFAULT_PATH)
set(CUBLAS_LIBRARIES ${CUBLAS_LIBRARY})
else()
set(CUBLAS_LIBRARIES ${CUDA_CUBLAS_LIBRARIES})
endif()
set(CUDNN_LIB_NAME "libcudnn.so")
endif(LINUX)
if(WIN32)
# only support cudnn7
......@@ -48,11 +54,10 @@ if(APPLE)
set(CUDNN_LIB_NAME "libcudnn.dylib" "libcudnn.so")
endif(APPLE)
find_library(CUDNN_LIBRARY NAMES ${CUDNN_LIB_NAME} # libcudnn_static.a
find_library(CUDNN_LIBRARY NAMES ${CUDNN_LIB_NAME}
PATHS ${CUDNN_CHECK_LIBRARY_DIRS} ${CUDNN_INCLUDE_DIR} ${__libpath_hist}
NO_DEFAULT_PATH
DOC "Path to cuDNN library.")
DOC "Path to cuDNN dynamic library.")
if(CUDNN_INCLUDE_DIR AND CUDNN_LIBRARY)
set(CUDNN_FOUND ON)
......@@ -64,6 +69,15 @@ if(CUDNN_FOUND)
file(READ ${CUDNN_INCLUDE_DIR}/cudnn.h CUDNN_VERSION_FILE_CONTENTS)
get_filename_component(CUDNN_LIB_PATH ${CUDNN_LIBRARY} DIRECTORY)
if(LITE_WITH_STATIC_CUDA)
add_library(cudnn_static STATIC IMPORTED GLOBAL)
set_property(TARGET cudnn_static PROPERTY IMPORTED_LOCATION
"${CUDNN_LIB_PATH}/libcudnn_static.a")
else()
add_library(cudnn SHARED IMPORTED GLOBAL)
set_property(TARGET cudnn PROPERTY IMPORTED_LOCATION
"${CUDNN_LIB_PATH}/libcudnn.so")
endif(LITE_WITH_STATIC_CUDA)
string(REGEX MATCH "define CUDNN_VERSION +([0-9]+)"
CUDNN_VERSION "${CUDNN_VERSION_FILE_CONTENTS}")
......
INCLUDE(ExternalProject)
SET(EIGEN_SOURCECODE_DIR ${CMAKE_SOURCE_DIR}/third-party/eigen3)
SET(EIGEN_SOURCE_DIR ${THIRD_PARTY_PATH}/eigen3)
SET(EIGEN_INCLUDE_DIR ${EIGEN_SOURCE_DIR}/src/extern_eigen3)
INCLUDE_DIRECTORIES(${EIGEN_INCLUDE_DIR})
if(NOT WITH_FAST_MATH)
# EIGEN_FAST_MATH: https://eigen.tuxfamily.org/dox/TopicPreprocessorDirectives.html
# enables some optimizations which might affect the accuracy of the result.
# This currently enables the SSE vectorization of sin() and cos(),
# enables some optimizations which might affect the accuracy of the result.
# This currently enables the SSE vectorization of sin() and cos(),
# and speedups sqrt() for single precision.
# Defined to 1 by default. Define it to 0 to disable.
add_definitions(-DEIGEN_FAST_MATH=0)
......@@ -16,9 +17,12 @@ if(WITH_AMD_GPU)
ExternalProject_Add(
extern_eigen3
${EXTERNAL_PROJECT_LOG_ARGS}
GIT_REPOSITORY "https://github.com/sabreshao/hipeigen.git"
GIT_TAG 7cb2b6e5a4b4a1efe658abb215cd866c6fb2275e
GIT_TAG
URL http://paddle-inference-dist.bj.bcebos.com/PaddleLite_ThirdParty%2Fhipeigen-upstream-702834151eaebcf955fd09ed0ad83c06.zip
DOWNLOAD_DIR ${EIGEN_SOURCECODE_DIR}
DOWNLOAD_NO_PROGRESS 1
PREFIX ${EIGEN_SOURCE_DIR}
DOWNLOAD_NAME "hipeigen-upstream-702834151eaebcf955fd09ed0ad83c06.zip"
UPDATE_COMMAND ""
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
......@@ -29,12 +33,14 @@ else()
ExternalProject_Add(
extern_eigen3
${EXTERNAL_PROJECT_LOG_ARGS}
GIT_REPOSITORY "https://github.com/eigenteam/eigen-git-mirror"
# eigen on cuda9.1 missing header of math_funtions.hpp
# https://stackoverflow.com/questions/43113508/math-functions-hpp-not-found-when-using-cuda-with-eigen
GIT_TAG 917060c364181f33a735dc023818d5a54f60e54c
GIT_TAG
URL http://paddle-inference-dist.bj.bcebos.com/PaddleLite_ThirdParty%2Feigen-git-mirror-master-9ab917e9db99f5907d086aa73d5f9103.zip
DOWNLOAD_DIR ${EIGEN_SOURCECODE_DIR}
DOWNLOAD_NO_PROGRESS 1
PREFIX ${EIGEN_SOURCE_DIR}
DOWNLOAD_NAME "eigen"
DOWNLOAD_NAME "eigen-git-mirror-master-9ab917e9db99f5907d086aa73d5f9103.zip"
UPDATE_COMMAND ""
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
......
......@@ -14,7 +14,7 @@
INCLUDE(ExternalProject)
SET(GFLAGS_SOURCES_DIR ${THIRD_PARTY_PATH}/gflags)
SET(GFLAGS_SOURCES_DIR ${CMAKE_SOURCE_DIR}/third-party/gflags)
SET(GFLAGS_INSTALL_DIR ${THIRD_PARTY_PATH}/install/gflags)
SET(GFLAGS_INCLUDE_DIR "${GFLAGS_INSTALL_DIR}/include" CACHE PATH "gflags include directory." FORCE)
IF(WIN32)
......@@ -37,9 +37,10 @@ SET(OPTIONAL_ARGS "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
ExternalProject_Add(
extern_gflags
${EXTERNAL_PROJECT_LOG_ARGS}
GIT_REPOSITORY "https://github.com/gflags/gflags.git"
GIT_REPOSITORY ""
GIT_TAG 77592648e3f3be87d6c7123eb81cbad75f9aef5a
PREFIX ${GFLAGS_SOURCES_DIR}
SOURCE_DIR ${GFLAGS_SOURCES_DIR}
PREFIX ${GFLAGS_INCLUDE_DIR}
UPDATE_COMMAND ""
CMAKE_ARGS -DBUILD_STATIC_LIBS=ON
-DCMAKE_INSTALL_PREFIX=${GFLAGS_INSTALL_DIR}
......
......@@ -12,15 +12,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#FIXME:(gongwb) Move brpc's gtest dependency.
IF(WITH_TESTING OR (WITH_DISTRIBUTE AND NOT WITH_GRPC))
# the gtest is only used when WITH_TESTING=ON
IF(WITH_TESTING)
IF(WITH_TESTING)
ENABLE_TESTING()
ENDIF(WITH_TESTING)
INCLUDE(ExternalProject)
SET(GTEST_SOURCES_DIR ${THIRD_PARTY_PATH}/gtest)
SET(GTEST_SOURCES_DIR ${CMAKE_SOURCE_DIR}/third-party/googletest)
SET(GTEST_INSTALL_DIR ${THIRD_PARTY_PATH}/install/gtest)
SET(GTEST_INCLUDE_DIR "${GTEST_INSTALL_DIR}/include" CACHE PATH "gtest include directory." FORCE)
......@@ -56,9 +56,10 @@ IF(WITH_TESTING OR (WITH_DISTRIBUTE AND NOT WITH_GRPC))
extern_gtest
${EXTERNAL_PROJECT_LOG_ARGS}
DEPENDS ${GTEST_DEPENDS}
GIT_REPOSITORY "https://github.com/google/googletest.git"
GIT_REPOSITORY ""
SOURCE_DIR ${GTEST_SOURCES_DIR}
GIT_TAG "release-1.8.0"
PREFIX ${GTEST_SOURCES_DIR}
PREFIX ${GTEST_INSTALL_DIR}
UPDATE_COMMAND ""
CMAKE_ARGS ${CROSS_COMPILE_CMAKE_ARGS}
${OPTIONAL_ARGS}
......@@ -82,4 +83,4 @@ IF(WITH_TESTING OR (WITH_DISTRIBUTE AND NOT WITH_GRPC))
SET_PROPERTY(TARGET gtest_main PROPERTY IMPORTED_LOCATION ${GTEST_MAIN_LIBRARIES})
ADD_DEPENDENCIES(gtest_main extern_gtest)
ENDIF(WITH_TESTING OR (WITH_DISTRIBUTE AND NOT WITH_GRPC))
ENDIF()
......@@ -109,8 +109,7 @@ macro(PROMPT_PROTOBUF_LIB)
ADD_LIBRARY(protobuf ${protobuf_LIBTYPE} IMPORTED GLOBAL)
SET_PROPERTY(TARGET protobuf PROPERTY IMPORTED_LOCATION ${PROTOBUF_LIBRARY})
ADD_LIBRARY(protobuf_lite ${protobuf_LIBTYPE} IMPORTED GLOBAL)
ADD_LIBRARY(protobuf_lite ${protobuf_LIBTYPE} IMPORTED GLOBAL)
SET_PROPERTY(TARGET protobuf_lite PROPERTY IMPORTED_LOCATION ${PROTOBUF_LITE_LIBRARY})
ADD_LIBRARY(libprotoc ${protobuf_LIBTYPE} IMPORTED GLOBAL)
......@@ -177,12 +176,20 @@ FUNCTION(build_protobuf TARGET_NAME BUILD_FOR_HOST)
"${PROTOBUF_INSTALL_DIR}/bin/protoc${CMAKE_EXECUTABLE_SUFFIX}"
PARENT_SCOPE)
SET(PROTOBUF_REPO "https://github.com/protocolbuffers/protobuf.git")
# https://github.com/protocolbuffers/protobuf.git
SET(PROTOBUF_REPO "")
SET(PROTOBUF_TAG "9f75c5aa851cd877fb0d93ccc31b8567a6706546")
SET(OPTIONAL_CACHE_ARGS "")
SET(OPTIONAL_ARGS "")
SET(SOURCE_DIR "${CMAKE_SOURCE_DIR}/third-party/protobuf-host")
IF(BUILD_FOR_HOST)
# set for server compile.
if (NOT LITE_WITH_LIGHT_WEIGHT_FRAMEWORK)
set(HOST_C_COMPILER "${CMAKE_C_COMPILER}")
set(HOST_CXX_COMPILER "${CMAKE_CXX_COMPILER}")
endif()
SET(OPTIONAL_ARGS
"-DCMAKE_C_COMPILER=${HOST_C_COMPILER}"
"-DCMAKE_CXX_COMPILER=${HOST_CXX_COMPILER}"
......@@ -191,8 +198,10 @@ FUNCTION(build_protobuf TARGET_NAME BUILD_FOR_HOST)
SET(OPTIONAL_CACHE_ARGS "-DZLIB_ROOT:STRING=${ZLIB_ROOT}")
ELSE()
# protobuf have compile issue when use android stl c++_static
SET(PROTOBUF_REPO "https://github.com/tensor-tang/protobuf.git")
# https://github.com/tensor-tang/protobuf.git
SET(PROTOBUF_REPO "")
SET(PROTOBUF_TAG "mobile")
SET(SOURCE_DIR "${CMAKE_SOURCE_DIR}/third-party/protobuf-mobile")
SET(OPTIONAL_ARGS "-Dprotobuf_WITH_ZLIB=OFF"
${CROSS_COMPILE_CMAKE_ARGS}
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
......@@ -215,8 +224,9 @@ FUNCTION(build_protobuf TARGET_NAME BUILD_FOR_HOST)
PREFIX ${PROTOBUF_SOURCES_DIR}
SOURCE_SUBDIR cmake
UPDATE_COMMAND ""
GIT_REPOSITORY ${PROTOBUF_REPO}
GIT_REPOSITORY ""
GIT_TAG ${PROTOBUF_TAG}
SOURCE_DIR ${SOURCE_DIR}
CMAKE_ARGS
${OPTIONAL_ARGS}
-Dprotobuf_BUILD_TESTS=OFF
......@@ -237,12 +247,13 @@ FUNCTION(build_protobuf TARGET_NAME BUILD_FOR_HOST)
ExternalProject_Add(
${TARGET_NAME}
${EXTERNAL_PROJECT_LOG_ARGS}
PREFIX ${PROTOBUF_SOURCES_DIR}
PREFIX ${SOURCE_DIR}
UPDATE_COMMAND ""
GIT_REPOSITORY ${PROTOBUF_REPO}
GIT_REPOSITORY ""
GIT_TAG ${PROTOBUF_TAG}
CONFIGURE_COMMAND
${CMAKE_COMMAND} ${PROTOBUF_SOURCES_DIR}/src/${TARGET_NAME}/cmake
SOURCE_DIR ${SOURCE_DIR}
BUILD_ALWAYS 1
CONFIGURE_COMMAND ${CMAKE_COMMAND} ${SOURCE_DIR}/cmake
${OPTIONAL_ARGS}
-Dprotobuf_BUILD_TESTS=OFF
-DCMAKE_SKIP_RPATH=ON
......@@ -271,7 +282,11 @@ IF(LITE_WITH_LIGHT_WEIGHT_FRAMEWORK)
ENDIF()
IF(NOT PROTOBUF_FOUND)
build_protobuf(extern_protobuf FALSE)
if (LITE_WITH_LIGHT_WEIGHT_FRAMEWORK)
build_protobuf(extern_protobuf FALSE)
else()
build_protobuf(extern_protobuf TRUE)
endif()
SET(PROTOBUF_INCLUDE_DIR ${extern_protobuf_INCLUDE_DIR}
CACHE PATH "protobuf include directory." FORCE)
......
# Copyright (c) 2016 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.
if(NOT LITE_WITH_PYTHON)
return()
endif()
include(ExternalProject)
set(PYBIND_SOURCE_DIR ${THIRD_PARTY_PATH}/pybind)
include_directories(${PYBIND_SOURCE_DIR}/src/extern_pybind/include)
ExternalProject_Add(
extern_pybind
${EXTERNAL_PROJECT_LOG_ARGS}
GIT_REPOSITORY "https://github.com/pybind/pybind11.git"
GIT_TAG "v2.2.4"
PREFIX ${PYBIND_SOURCE_DIR}
UPDATE_COMMAND ""
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
if(${CMAKE_VERSION} VERSION_LESS "3.3.0")
set(dummyfile ${CMAKE_CURRENT_BINARY_DIR}/pybind_dummy.c)
file(WRITE ${dummyfile} "const char * dummy_pybind = \"${dummyfile}\";")
add_library(pybind STATIC ${dummyfile})
else()
add_library(pybind INTERFACE)
endif()
add_dependencies(pybind extern_pybind)
# Copyright (c) 2016 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.
IF(NOT LITE_WITH_PYTHON)
return()
ENDIF()
INCLUDE(python_module)
FIND_PACKAGE(PythonInterp ${PY_VERSION} REQUIRED)
FIND_PACKAGE(PythonLibs ${PY_VERSION} REQUIRED)
if(WIN32)
execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
"from distutils import sysconfig as s;import sys;import struct;
print(sys.prefix);
print(s.get_config_var('LDVERSION') or s.get_config_var('VERSION'));
"
RESULT_VARIABLE _PYTHON_SUCCESS
OUTPUT_VARIABLE _PYTHON_VALUES
ERROR_VARIABLE _PYTHON_ERROR_VALUE)
if(NOT _PYTHON_SUCCESS MATCHES 0)
set(PYTHONLIBS_FOUND FALSE)
return()
endif()
# Convert the process output into a list
string(REGEX REPLACE ";" "\\\\;" _PYTHON_VALUES ${_PYTHON_VALUES})
string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES})
list(GET _PYTHON_VALUES 0 PYTHON_PREFIX)
list(GET _PYTHON_VALUES 1 PYTHON_LIBRARY_SUFFIX)
# Make sure all directory separators are '/'
string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX ${PYTHON_PREFIX})
set(PYTHON_LIBRARY
"${PYTHON_PREFIX}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib")
# when run in a venv, PYTHON_PREFIX points to it. But the libraries remain in the
# original python installation. They may be found relative to PYTHON_INCLUDE_DIR.
if(NOT EXISTS "${PYTHON_LIBRARY}")
get_filename_component(_PYTHON_ROOT ${PYTHON_INCLUDE_DIR} DIRECTORY)
set(PYTHON_LIBRARY
"${_PYTHON_ROOT}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib")
endif()
# raise an error if the python libs are still not found.
if(NOT EXISTS "${PYTHON_LIBRARY}")
message(FATAL_ERROR "Python libraries not found")
endif()
SET(PYTHON_LIBRARIES "${PYTHON_LIBRARY}")
endif(WIN32)
# Fixme: Maybe find a static library. Get SHARED/STATIC by FIND_PACKAGE.
ADD_LIBRARY(python SHARED IMPORTED GLOBAL)
SET_PROPERTY(TARGET python PROPERTY IMPORTED_LOCATION ${PYTHON_LIBRARIES})
SET(py_env "")
IF(PYTHONINTERP_FOUND)
find_python_module(pip REQUIRED)
find_python_module(numpy REQUIRED)
#find_python_module(wheel REQUIRED)
#find_python_module(google.protobuf REQUIRED)
FIND_PACKAGE(NumPy REQUIRED)
#IF(${PY_GOOGLE.PROTOBUF_VERSION} AND ${PY_GOOGLE.PROTOBUF_VERSION} VERSION_LESS "3.0.0")
# MESSAGE(FATAL_ERROR "Found Python Protobuf ${PY_GOOGLE.PROTOBUF_VERSION} < 3.0.0, "
# "please use pip to upgrade protobuf. pip install -U protobuf")
#ENDIF()
ENDIF(PYTHONINTERP_FOUND)
INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${PYTHON_NUMPY_INCLUDE_DIR})
......@@ -20,6 +20,7 @@ endif()
include(ExternalProject)
SET(XBYAK_SOURCECODE_DIR ${CMAKE_SOURCE_DIR}/third-party/xbyak)
set(XBYAK_PROJECT extern_xbyak)
set(XBYAK_PREFIX_DIR ${THIRD_PARTY_PATH}/xbyak)
set(XBYAK_INSTALL_ROOT ${THIRD_PARTY_PATH}/install/xbyak)
......@@ -38,8 +39,11 @@ ExternalProject_Add(
${XBYAK_PROJECT}
${EXTERNAL_PROJECT_LOG_ARGS}
DEPENDS ""
GIT_REPOSITORY "https://github.com/herumi/xbyak.git"
GIT_TAG "v5.661" # Jul 26th
URL http://paddle-inference-dist.bj.bcebos.com/PaddleLite_ThirdParty%2Fxbyak-5.66.zip
DOWNLOAD_DIR ${XBYAK_SOURCECODE_DIR}
DOWNLOAD_NAME "xbyak-5.66.zip"
DOWNLOAD_NO_PROGRESS 1
PREFIX ${XBYAK_PREFIX_DIR}
UPDATE_COMMAND ""
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${XBYAK_INSTALL_ROOT}
......
INCLUDE(ExternalProject)
SET(XXHASH_SOURCECODE_DIR ${CMAKE_SOURCE_DIR}/third-party/xxhash)
set(XXHASH_SOURCE_DIR ${THIRD_PARTY_PATH}/xxhash)
set(XXHASH_INSTALL_DIR ${THIRD_PARTY_PATH}/install/xxhash)
set(XXHASH_INCLUDE_DIR "${XXHASH_INSTALL_DIR}/include")
......@@ -18,10 +19,12 @@ if(WIN32)
ExternalProject_Add(
extern_xxhash
${EXTERNAL_PROJECT_LOG_ARGS}
GIT_REPOSITORY "https://github.com/Cyan4973/xxHash"
GIT_TAG "v0.6.5"
URL http://paddle-inference-dist.bj.bcebos.com/PaddleLite_ThirdParty%2FxxHash-0.6.5.zip
DOWNLOAD_DIR ${XXHASH_SOURCECODE_DIR}
DOWNLOAD_NAME "xxHash-0.6.5.zip"
DOWNLOAD_NO_PROGRESS 1
PREFIX ${XXHASH_SOURCE_DIR}
DOWNLOAD_NAME "xxhash"
UPDATE_COMMAND ""
BUILD_IN_SOURCE 1
PATCH_COMMAND
......@@ -41,10 +44,12 @@ else()
ExternalProject_Add(
extern_xxhash
${EXTERNAL_PROJECT_LOG_ARGS}
GIT_REPOSITORY "https://github.com/Cyan4973/xxHash"
GIT_TAG "v0.6.5"
URL http://paddle-inference-dist.bj.bcebos.com/PaddleLite_ThirdParty%2FxxHash-0.6.5.zip
DOWNLOAD_DIR ${XXHASH_SOURCECODE_DIR}
DOWNLOAD_NO_PROGRESS 1
PREFIX ${XXHASH_SOURCE_DIR}
DOWNLOAD_NAME "xxhash"
DOWNLOAD_NAME "xxHash-0.6.5.zip"
UPDATE_COMMAND ""
CONFIGURE_COMMAND ""
BUILD_IN_SOURCE 1
......
......@@ -146,8 +146,11 @@ set(GPU_COMMON_FLAGS
-Wno-error=unused-local-typedefs
-Wno-error=unused-function # Warnings in Numpy Header.
-Wno-error=array-bounds # Warnings in Eigen::array
-gencode arch=compute_62,code=sm_62
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64")
if(NOT LITE_WITH_CUDA)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64")
endif()
endif(NOT WIN32)
if (APPLE)
......
......@@ -105,8 +105,8 @@ set_property(GLOBAL PROPERTY FLUID_MODULES "")
function(find_fluid_modules TARGET_NAME)
get_filename_component(__target_path ${TARGET_NAME} ABSOLUTE)
string(REGEX REPLACE "^${PADDLE_SOURCE_DIR}/" "" __target_path ${__target_path})
string(FIND "${__target_path}" "fluid" pos)
if(pos GREATER 1)
string(FIND "${__target_path}" "lite" pos)
if((pos GREATER 0) OR (pos EQUAL 0))
get_property(fluid_modules GLOBAL PROPERTY FLUID_MODULES)
set(fluid_modules ${fluid_modules} ${TARGET_NAME})
set_property(GLOBAL PROPERTY FLUID_MODULES "${fluid_modules}")
......@@ -303,10 +303,12 @@ function(cc_library TARGET_NAME)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${source}.h)
list(APPEND cc_library_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/${source}.h)
endif()
if(${source_file} MATCHES "framework.pb.cc")
if(${source_file} MATCHES "__generated_code__.cc")
list(APPEND full_path_src ${source_file})
else()
list(APPEND full_path_src ${CMAKE_CURRENT_SOURCE_DIR}/${source_file})
if(NOT ${source_file} MATCHES "framework.pb.cc" AND NOT ${source_file} MATCHES "__generated_code__.cc")
list(APPEND full_path_src ${CMAKE_CURRENT_SOURCE_DIR}/${source_file})
endif()
endif()
endforeach()
set(__lite_cc_files ${__lite_cc_files} ${full_path_src} CACHE INTERNAL "")
......@@ -371,6 +373,7 @@ function(cc_binary TARGET_NAME)
endif()
get_property(os_dependency_modules GLOBAL PROPERTY OS_DEPENDENCY_MODULES)
target_link_libraries(${TARGET_NAME} ${os_dependency_modules})
find_fluid_modules(${TARGET_NAME})
endfunction(cc_binary)
function(cc_test TARGET_NAME)
......@@ -387,8 +390,8 @@ function(cc_test TARGET_NAME)
endif()
endif(WIN32)
get_property(os_dependency_modules GLOBAL PROPERTY OS_DEPENDENCY_MODULES)
target_link_libraries(${TARGET_NAME} ${cc_test_DEPS} ${os_dependency_modules} paddle_gtest_main lod_tensor memory gtest gflags glog)
add_dependencies(${TARGET_NAME} ${cc_test_DEPS} paddle_gtest_main lod_tensor memory gtest gflags glog)
target_link_libraries(${TARGET_NAME} ${cc_test_DEPS} ${os_dependency_modules} paddle_gtest_main memory gtest gflags glog)
add_dependencies(${TARGET_NAME} ${cc_test_DEPS} paddle_gtest_main memory gtest gflags glog)
common_link(${TARGET_NAME})
add_test(NAME ${TARGET_NAME}
COMMAND ${TARGET_NAME} ${cc_test_ARGS}
......@@ -447,7 +450,7 @@ function(_lite_cc_test args)
endfunction()
function(nv_library TARGET_NAME)
if (WITH_GPU)
if (LITE_WITH_CUDA)
set(options STATIC static SHARED shared)
set(oneValueArgs "")
set(multiValueArgs SRCS DEPS)
......@@ -481,12 +484,15 @@ function(nv_library TARGET_NAME)
endfunction(nv_library)
function(nv_binary TARGET_NAME)
if (WITH_GPU)
if (LITE_WITH_CUDA)
set(options "")
set(oneValueArgs "")
set(multiValueArgs SRCS DEPS)
cmake_parse_arguments(nv_binary "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
cuda_add_executable(${TARGET_NAME} ${nv_binary_SRCS})
target_link_libraries(${TARGET_NAME} ${CUDNN_LIBRARY} ${CUBLAS_LIBRARIES})
get_property(os_dependency_modules GLOBAL PROPERTY OS_DEPENDENCY_MODULES)
target_link_libraries(${TARGET_NAME} ${os_dependency_modules})
if(nv_binary_DEPS)
target_link_libraries(${TARGET_NAME} ${nv_binary_DEPS})
add_dependencies(${TARGET_NAME} ${nv_binary_DEPS})
......@@ -496,24 +502,21 @@ function(nv_binary TARGET_NAME)
endfunction(nv_binary)
function(nv_test TARGET_NAME)
if (WITH_GPU AND WITH_TESTING)
if (LITE_WITH_CUDA AND WITH_TESTING)
set(options SERIAL)
set(oneValueArgs "")
set(multiValueArgs SRCS DEPS)
cmake_parse_arguments(nv_test "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
cuda_add_executable(${TARGET_NAME} ${nv_test_SRCS})
get_property(os_dependency_modules GLOBAL PROPERTY OS_DEPENDENCY_MODULES)
target_link_libraries(${TARGET_NAME} ${nv_test_DEPS} paddle_gtest_main lod_tensor memory gtest gflags glog ${os_dependency_modules})
add_dependencies(${TARGET_NAME} ${nv_test_DEPS} paddle_gtest_main lod_tensor memory gtest gflags glog)
target_link_libraries(${TARGET_NAME} ${nv_test_DEPS} lite_gtest_main gtest
gflags glog ${os_dependency_modules} ${CUDNN_LIBRARY} ${CUBLAS_LIBRARIES} )
add_dependencies(${TARGET_NAME} ${nv_test_DEPS} lite_gtest_main gtest gflags glog)
common_link(${TARGET_NAME})
add_test(${TARGET_NAME} ${TARGET_NAME})
if (nv_test_SERIAL)
set_property(TEST ${TARGET_NAME} PROPERTY RUN_SERIAL 1)
endif()
set_property(TEST ${TARGET_NAME} PROPERTY ENVIRONMENT FLAGS_cpu_deterministic=true)
set_property(TEST ${TARGET_NAME} PROPERTY ENVIRONMENT FLAGS_init_allocated_mem=true)
set_property(TEST ${TARGET_NAME} PROPERTY ENVIRONMENT FLAGS_limit_of_tmp_allocation=4294967296) # 4G
set_property(TEST ${TARGET_NAME} PROPERTY ENVIRONMENT FLAGS_cudnn_deterministic=true)
endif()
endfunction(nv_test)
......
......@@ -22,7 +22,7 @@ endfunction()
function (lite_deps TARGET)
set(options "")
set(oneValueArgs "")
set(multiValueArgs DEPS X86_DEPS CUDA_DEPS ARM_DEPS PROFILE_DEPS LIGHT_DEPS HVY_DEPS CL_DEPS FPGA_DEPS NPU_DEPS ARGS)
set(multiValueArgs DEPS X86_DEPS CUDA_DEPS ARM_DEPS PROFILE_DEPS LIGHT_DEPS HVY_DEPS CL_DEPS FPGA_DEPS BM_DEPS NPU_DEPS XPU_DEPS CV_DEPS ARGS)
cmake_parse_arguments(lite_deps "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
set(deps ${lite_deps_DEPS})
......@@ -43,6 +43,11 @@ function (lite_deps TARGET)
foreach(var ${lite_deps_ARM_DEPS})
set(deps ${deps} ${var})
endforeach(var)
if(LITE_WITH_CV)
foreach(var ${lite_deps_CV_DEPS})
set(deps ${deps} ${var})
endforeach(var)
endif()
endif()
if(LITE_WITH_PROFILE)
......@@ -57,6 +62,8 @@ function (lite_deps TARGET)
endforeach(var)
endif()
if (NOT LITE_WITH_LIGHT_WEIGHT_FRAMEWORK)
foreach(var ${lite_deps_HVY_DEPS})
set(deps ${deps} ${var})
......@@ -81,6 +88,18 @@ function (lite_deps TARGET)
endforeach(var)
endif()
if (LITE_WITH_XPU)
foreach(var ${lite_deps_XPU_DEPS})
set(deps ${deps} ${var})
endforeach(var)
endif()
if (LITE_WITH_BM)
foreach(var ${lite_deps_BM_DEPS})
set(deps ${deps} ${var})
endforeach(var)
endif()
set(${TARGET} ${deps} PARENT_SCOPE)
endfunction()
......@@ -102,10 +121,11 @@ file(WRITE ${offline_lib_registry_file} "") # clean
# LIGHT_DEPS: LITE_WITH_LIGHT_WEIGHT_FRAMEWORK
# HVY_DEPS: NOT LITE_WITH_LIGHT_WEIGHT_FRAMEWORK
# EXCLUDE_COMPILE_DEPS: TARGET will not be included in lite_compile_deps if this is not None
# CV_DEPS: LITE_WITH_CV
function(lite_cc_library TARGET)
set(options SHARED shared STATIC static MODULE module)
set(oneValueArgs "")
set(multiValueArgs SRCS DEPS X86_DEPS CUDA_DEPS CL_DEPS NPU_DEPS ARM_DEPS FPGA_DEPS PROFILE_DEPS LIGHT_DEPS
set(multiValueArgs SRCS DEPS X86_DEPS CUDA_DEPS CL_DEPS ARM_DEPS FPGA_DEPS BM_DEPS NPU_DEPS XPU_DEPS CV_DEPS PROFILE_DEPS LIGHT_DEPS
HVY_DEPS EXCLUDE_COMPILE_DEPS ARGS)
cmake_parse_arguments(args "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
......@@ -115,21 +135,24 @@ function(lite_cc_library TARGET)
X86_DEPS ${args_X86_DEPS}
CUDA_DEPS ${args_CUDA_DEPS}
CL_DEPS ${args_CL_DEPS}
NPU_DEPS ${args_NPU_DEPS}
BM_DEPS ${args_BM_DEPS}
ARM_DEPS ${args_ARM_DEPS}
CV_DEPS ${args_CV_DEPS}
FPGA_DEPS ${args_FPGA_DEPS}
NPU_DEPS ${args_NPU_DEPS}
XPU_DEPS ${args_XPU_DEPS}
PROFILE_DEPS ${args_PROFILE_DEPS}
LIGHT_DEPS ${args_LIGHT_DEPS}
HVY_DEPS ${args_HVY_DEPS}
)
if (args_SHARED OR ARGS_shared)
cc_library(${TARGET} SRCS ${args_SRCS} DEPS ${deps} ${args_DEPS} SHARED)
cc_library(${TARGET} SRCS ${args_SRCS} DEPS ${deps} SHARED)
elseif (args_MODULE OR ARGS_module)
add_library(${TARGET} MODULE ${args_SRCS})
add_dependencies(${TARGET} ${deps} ${args_DEPS})
else()
cc_library(${TARGET} SRCS ${args_SRCS} DEPS ${deps} ${args_DEPS})
cc_library(${TARGET} SRCS ${args_SRCS} DEPS ${deps})
endif()
target_compile_options(${TARGET} BEFORE PRIVATE -Wno-ignored-qualifiers)
......@@ -143,10 +166,12 @@ function(lite_cc_library TARGET)
endfunction()
function(lite_cc_binary TARGET)
set(options "")
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
set(options " -g ")
endif()
set(oneValueArgs "")
set(multiValueArgs SRCS DEPS X86_DEPS CUDA_DEPS CL_DEPS ARM_DEPS FPGA_DEPS PROFILE_DEPS
LIGHT_DEPS HVY_DEPS EXCLUDE_COMPILE_DEPS ARGS)
set(multiValueArgs SRCS DEPS X86_DEPS CUDA_DEPS CL_DEPS ARM_DEPS FPGA_DEPS BM_DEPS NPU_DEPS XPU_DEPS PROFILE_DEPS
LIGHT_DEPS HVY_DEPS EXCLUDE_COMPILE_DEPS CV_DEPS ARGS)
cmake_parse_arguments(args "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
set(deps "")
......@@ -157,12 +182,25 @@ function(lite_cc_binary TARGET)
CL_DEPS ${args_CL_DEPS}
ARM_DEPS ${args_ARM_DEPS}
FPGA_DEPS ${args_FPGA_DEPS}
NPU_DEPS ${args_NPU_DEPS}
XPU_DEPS ${args_XPU_DEPS}
BM_DEPS ${args_BM_DEPS}
PROFILE_DEPS ${args_PROFILE_DEPS}
LIGHT_DEPS ${args_LIGHT_DEPS}
HVY_DEPS ${args_HVY_DEPS}
CV_DEPS ${CV_DEPS}
)
cc_binary(${TARGET} SRCS ${args_SRCS} DEPS ${deps} ${args_DEPS})
cc_binary(${TARGET} SRCS ${args_SRCS} DEPS ${deps})
target_compile_options(${TARGET} BEFORE PRIVATE -Wno-ignored-qualifiers)
if (NOT APPLE)
# strip binary target to reduce size
if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
add_custom_command(TARGET ${TARGET} POST_BUILD
COMMAND "${CMAKE_STRIP}" -s
"${TARGET}"
COMMENT "Strip debug symbols done on final executable file.")
endif()
endif()
# collect targets need to compile for lite
if (NOT args_EXCLUDE_COMPILE_DEPS)
add_dependencies(lite_compile_deps ${TARGET})
......@@ -180,11 +218,18 @@ function(lite_cc_test TARGET)
endif()
set(options "")
set(oneValueArgs "")
set(multiValueArgs SRCS DEPS X86_DEPS CUDA_DEPS CL_DEPS ARM_DEPS FPGA_DEPS PROFILE_DEPS
LIGHT_DEPS HVY_DEPS EXCLUDE_COMPILE_DEPS
ARGS)
set(multiValueArgs SRCS DEPS X86_DEPS CUDA_DEPS CL_DEPS ARM_DEPS FPGA_DEPS BM_DEPS NPU_DEPS XPU_DEPS PROFILE_DEPS
LIGHT_DEPS HVY_DEPS EXCLUDE_COMPILE_DEPS CV_DEPS
ARGS
COMPILE_LEVEL # (basic|extra)
)
cmake_parse_arguments(args "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if (args_COMPILE_LEVEL STREQUAL "extra" AND (NOT LITE_BUILD_EXTRA))
MESSAGE(STATUS "Ignore test ${TARGET} due to compile level ${args_COMPILE_LEVEL}")
return()
endif()
set(deps "")
lite_deps(deps
DEPS ${args_DEPS}
......@@ -193,11 +238,22 @@ function(lite_cc_test TARGET)
CL_DEPS ${args_CL_DEPS}
ARM_DEPS ${args_ARM_DEPS}
FPGA_DEPS ${args_FPGA_DEPS}
NPU_DEPS ${args_NPU_DEPS}
XPU_DEPS ${args_XPU_DEPS}
BM_DEPS ${args_BM_DEPS}
PROFILE_DEPS ${args_PROFILE_DEPS}
LIGHT_DEPS ${args_LIGHT_DEPS}
HVY_DEPS ${args_HVY_DEPS}
CV_DEPS ${args_CV_DEPS}
)
_lite_cc_test(${TARGET} SRCS ${args_SRCS} DEPS ${deps} ARGS ${args_ARGS})
# strip binary target to reduce size
if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
add_custom_command(TARGET ${TARGET} POST_BUILD
COMMAND "${CMAKE_STRIP}" -s
"${TARGET}"
COMMENT "Strip debug symbols done on final executable file.")
endif()
target_compile_options(${TARGET} BEFORE PRIVATE -Wno-ignored-qualifiers)
file(APPEND ${offline_test_registry_file} "${TARGET}\n")
......@@ -207,10 +263,197 @@ function(lite_cc_test TARGET)
endif()
endfunction()
set(arm_kernels CACHE INTERNAL "arm kernels")
set(x86_kernels CACHE INTERNAL "x86 kernels")
set(cuda_kernels CACHE INTERNAL "cuda kernels")
set(fpga_kernels CACHE INTERNAL "fpga kernels")
set(npu_kernels CACHE INTERNAL "npu kernels")
set(xpu_kernels CACHE INTERNAL "xpu kernels")
set(bm_kernels CACHE INTERNAL "bm kernels")
set(opencl_kernels CACHE INTERNAL "opencl kernels")
set(host_kernels CACHE INTERNAL "host kernels")
set(kernels_src_list "${CMAKE_BINARY_DIR}/kernels_src_list.txt")
file(WRITE ${kernels_src_list} "") # clean
if(LITE_BUILD_TAILOR)
set(tailored_kernels_list_path "${LITE_OPTMODEL_DIR}/.tailored_kernels_source_list")
file(STRINGS ${tailored_kernels_list_path} tailored_kernels_list)
endif()
# add a kernel for some specific device
# device: one of (Host, ARM, X86, NPU, FPGA, OPENCL, CUDA, BM)
# level: one of (basic, extra)
function(add_kernel TARGET device level)
set(options "")
set(oneValueArgs "")
set(multiValueArgs SRCS DEPS X86_DEPS CUDA_DEPS CL_DEPS ARM_DEPS FPGA_DEPS BM_DEPS NPU_DEPS XPU_DEPS PROFILE_DEPS
LIGHT_DEPS HVY_DEPS EXCLUDE_COMPILE_DEPS
ARGS)
cmake_parse_arguments(args "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(LITE_BUILD_TAILOR)
foreach(src ${args_SRCS})
list (FIND tailored_kernels_list ${src} _index)
if (${_index} EQUAL -1)
return()
endif()
endforeach()
endif()
if ("${level}" STREQUAL "extra" AND (NOT LITE_BUILD_EXTRA))
return()
endif()
if (LITE_ON_MODEL_OPTIMIZE_TOOL)
# the source list will collect for model_optimize_tool to fake kernel generation.
foreach(src ${args_SRCS})
file(APPEND ${kernels_src_list} "${CMAKE_CURRENT_SOURCE_DIR}/${src}\n")
endforeach()
return()
endif()
# when compiling the model_optimize_tool, a source file with all the fake kernel definitions will be generated,
# no need to continue the compilation of the true kernel source.
if (LITE_ON_MODEL_OPTIMIZE_TOOL)
return()
endif(LITE_ON_MODEL_OPTIMIZE_TOOL)
if ("${device}" STREQUAL "Host")
set(host_kernels "${host_kernels};${TARGET}" CACHE INTERNAL "")
endif()
if ("${device}" STREQUAL "ARM")
if (NOT LITE_WITH_ARM)
return()
endif()
set(arm_kernels "${arm_kernels};${TARGET}" CACHE INTERNAL "")
endif()
if ("${device}" STREQUAL "X86")
if (NOT LITE_WITH_X86)
return()
endif()
set(x86_kernels "${x86_kernels};${TARGET}" CACHE INTERNAL "")
endif()
if ("${device}" STREQUAL "NPU")
if (NOT LITE_WITH_NPU)
return()
endif()
set(npu_kernels "${npu_kernels};${TARGET}" CACHE INTERNAL "")
endif()
if ("${device}" STREQUAL "XPU")
if (NOT LITE_WITH_XPU)
return()
endif()
set(xpu_kernels "${xpu_kernels};${TARGET}" CACHE INTERNAL "")
endif()
if ("${device}" STREQUAL "FPGA")
if (NOT LITE_WITH_FPGA)
return()
endif()
set(fpga_kernels "${fpga_kernels};${TARGET}" CACHE INTERNAL "")
endif()
if ("${device}" STREQUAL "BM")
if (NOT LITE_WITH_BM)
return()
endif()
set(bm_kernels "${bm_kernels};${TARGET}" CACHE INTERNAL "")
endif()
if ("${device}" STREQUAL "OPENCL")
if (NOT LITE_WITH_OPENCL)
return()
endif()
set(opencl_kernels "${opencl_kernels};${TARGET}" CACHE INTERNAL "")
endif()
if ("${device}" STREQUAL "CUDA")
if (NOT LITE_WITH_CUDA)
return()
endif()
set(cuda_kernels "${cuda_kernels};${TARGET}" CACHE INTERNAL "")
foreach(src ${args_SRCS})
file(APPEND ${kernels_src_list} "${CMAKE_CURRENT_SOURCE_DIR}/${src}\n")
endforeach()
nv_library(${TARGET} SRCS ${args_SRCS} DEPS ${args_DEPS})
return()
endif()
# the source list will collect for paddle_use_kernel.h code generation.
foreach(src ${args_SRCS})
file(APPEND ${kernels_src_list} "${CMAKE_CURRENT_SOURCE_DIR}/${src}\n")
endforeach()
lite_cc_library(${TARGET} SRCS ${args_SRCS}
DEPS ${args_DEPS}
X86_DEPS ${args_X86_DEPS}
CUDA_DEPS ${args_CUDA_DEPS}
CL_DEPS ${args_CL_DEPS}
ARM_DEPS ${args_ARM_DEPS}
FPGA_DEPS ${args_FPGA_DEPS}
NPU_DEPS ${args_NPU_DEPS}
XPU_DEPS ${args_XPU_DEPS}
BM_DEPS ${args_BM_DEPS}
PROFILE_DEPS ${args_PROFILE_DEPS}
LIGHT_DEPS ${args_LIGHT_DEPS}
HVY_DEPS ${args_HVY_DEPS}
)
endfunction()
set(ops CACHE INTERNAL "ops")
set(ops_src_list "${CMAKE_BINARY_DIR}/ops_src_list.txt")
file(WRITE ${ops_src_list} "") # clean
if(LITE_BUILD_TAILOR)
set(tailored_ops_list_path "${LITE_OPTMODEL_DIR}/.tailored_ops_source_list")
file(STRINGS ${tailored_ops_list_path} tailored_ops_list)
endif()
# add an operator
# level: one of (basic, extra)
function(add_operator TARGET level)
set(options "")
set(oneValueArgs "")
set(multiValueArgs SRCS DEPS X86_DEPS CUDA_DEPS CL_DEPS ARM_DEPS FPGA_DEPS BM_DEPS NPU_DEPS XPU_DEPS PROFILE_DEPS
LIGHT_DEPS HVY_DEPS EXCLUDE_COMPILE_DEPS
ARGS)
cmake_parse_arguments(args "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if ("${level}" STREQUAL "extra" AND (NOT LITE_BUILD_EXTRA))
return()
endif()
foreach(src ${args_SRCS})
if(LITE_BUILD_TAILOR)
list(FIND tailored_ops_list ${src} _index)
if (${_index} EQUAL -1)
return()
endif()
endif()
file(APPEND ${ops_src_list} "${CMAKE_CURRENT_SOURCE_DIR}/${src}\n")
endforeach()
set(ops "${ops};${TARGET}" CACHE INTERNAL "source")
lite_cc_library(${TARGET} SRCS ${args_SRCS}
DEPS ${args_DEPS}
X86_DEPS ${args_X86_DEPS}
CUDA_DEPS ${args_CUDA_DEPS}
CL_DEPS ${args_CL_DEPS}
ARM_DEPS ${args_ARM_DEPS}
FPGA_DEPS ${args_FPGA_DEPS}
NPU_DEPS ${args_NPU_DEPS}
XPU_DEPS ${args_XPU_DEPS}
BM_DEPS ${args_BM_DEPS}
PROFILE_DEPS ${args_PROFILE_DEPS}
LIGHT_DEPS ${args_LIGHT_DEPS}
HVY_DEPS ${args_HVY_DEPS}
)
endfunction()
# Bundle several static libraries into one.
function(bundle_static_library tgt_name bundled_tgt_name fake_target)
list(APPEND static_libs ${tgt_name})
# for x86
add_dependencies(lite_compile_deps ${fake_target})
function(_recursively_collect_dependencies input_target)
set(_input_link_libraries LINK_LIBRARIES)
......@@ -248,7 +491,7 @@ function(bundle_static_library tgt_name bundled_tgt_name fake_target)
set(bundled_tgt_full_name
${CMAKE_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${bundled_tgt_name}${CMAKE_STATIC_LIBRARY_SUFFIX})
message(STATUS "+++++ bundled_tgt_full_name: ${bundled_tgt_full_name}")
#message(STATUS "bundled_tgt_full_name: ${bundled_tgt_full_name}")
if(NOT IOS)
file(WRITE ${CMAKE_BINARY_DIR}/${bundled_tgt_name}.ar.in
......
# Copyright (c) 2019 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.
# ----------------------------------------------------------------------------
# section: Provides an paddle lite config option macro
# usage: lite_option(var "help string to describe the var" [if or IF (condition)])
# ----------------------------------------------------------------------------
macro(lite_option variable description value)
set(__value ${value})
set(__condition "")
set(__varname "__value")
foreach(arg ${ARGN})
if(arg STREQUAL "IF" OR arg STREQUAL "if")
set(__varname "__condition")
else()
list(APPEND ${__varname} ${arg})
endif()
endforeach()
unset(__varname)
if(__condition STREQUAL "")
set(__condition 2 GREATER 1)
endif()
if(${__condition})
if(__value MATCHES ";")
if(${__value})
option(${variable} "${description}" ON)
else()
option(${variable} "${description}" OFF)
endif()
elseif(DEFINED ${__value})
if(${__value})
option(${variable} "${description}" ON)
else()
option(${variable} "${description}" OFF)
endif()
else()
option(${variable} "${description}" ${__value})
endif()
else()
unset(${variable} CACHE)
endif()
unset(__condition)
unset(__value)
endmacro()
# Copyright (c) 2019 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.
if(NOT LITE_WITH_MLU)
return()
endif()
if(NOT DEFINED NEUWARE_HOME)
set(NEUWARE_HOME $ENV{NEUWARE_HOME})
if(NOT NEUWARE_HOME)
message(FATAL_ERROR "Must set NEUWARE_HOME or env NEUWARE_HOME when LITE_WITH_MLU=ON")
endif()
endif()
message(STATUS "LITE_WITH_MLU: ${LITE_WITH_MLU}")
find_path(CNML_INC NAMES cnml.h
PATHS ${NEUWARE_HOME}/include NO_DEFAULT_PATH)
if(NOT CNML_INC)
message(FATAL_ERROR "Can not find cnml.h in ${NEUWARE_HOME}/include")
endif()
find_path(CNRT_INC NAMES cnrt.h
PATHS ${NEUWARE_HOME}/include NO_DEFAULT_PATH)
if(NOT CNRT_INC)
message(FATAL_ERROR "Can not find cnrt.h in ${NEUWARE_HOME}/include")
endif()
include_directories("${NEUWARE_HOME}/include")
find_library(CNML_LIB_FILE NAMES cnml
PATHS ${NEUWARE_HOME}/lib64)
if(NOT CNML_LIB_FILE)
message(FATAL_ERROR "Can not find CNML Library in ${NEUWARE_HOME}/lib64")
else()
message(STATUS "Found CNML Library: ${CNML_LIB_FILE}")
add_library(cnml_lib SHARED IMPORTED GLOBAL)
set_property(TARGET cnml_lib PROPERTY IMPORTED_LOCATION ${CNML_LIB_FILE})
endif()
find_library(CNRT_LIB_FILE NAMES cnrt
PATHS ${NEUWARE_HOME}/lib64)
if(NOT CNRT_LIB_FILE)
message(FATAL_ERROR "Can not find CNRT Library in ${NEUWARE_HOME}/lib64")
else()
message(STATUS "Found CNRT Library: ${CNRT_LIB_FILE}")
add_library(cnrt_lib SHARED IMPORTED GLOBAL)
set_property(TARGET cnrt_lib PROPERTY IMPORTED_LOCATION ${CNRT_LIB_FILE})
endif()
# Find if a Python module is installed
# Found at http://www.cmake.org/pipermail/cmake/2011-January/041666.html
# To use do: find_python_module(PyQt4 REQUIRED)
function(find_python_module module)
string(TOUPPER ${module} module_upper)
if(NOT PY_${module_upper})
if(ARGC GREATER 1 AND ARGV1 STREQUAL "REQUIRED")
set(${module}_FIND_REQUIRED TRUE)
else()
set(${module}_FIND_REQUIRED FALSE)
endif()
# A module's location is usually a directory, but for binary modules
# it's a .so file.
execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
"import re, ${module}; print(re.compile('/__init__.py.*').sub('',${module}.__file__))"
RESULT_VARIABLE _${module}_status
OUTPUT_VARIABLE _${module}_location
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT _${module}_status)
set(PY_${module_upper} ${_${module}_location} CACHE STRING
"Location of Python module ${module}")
endif(NOT _${module}_status)
endif(NOT PY_${module_upper})
find_package_handle_standard_args(PY_${module} DEFAULT_MSG PY_${module_upper})
if(NOT PY_${module_upper}_FOUND AND ${module}_FIND_REQUIRED)
message(FATAL_ERROR "python module ${module} is not found")
endif()
execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
"import sys, ${module}; sys.stdout.write(${module}.__version__)"
OUTPUT_VARIABLE _${module}_version
RESULT_VARIABLE _${module}_status
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT _${module}_status)
set(PY_${module_upper}_VERSION ${_${module}_version} CACHE STRING
"Version of Python module ${module}")
endif(NOT _${module}_status)
set(PY_${module_upper}_FOUND ${PY_${module_upper}_FOUND} PARENT_SCOPE)
set(PY_${module_upper}_VERSION ${PY_${module_upper}_VERSION} PARENT_SCOPE)
endfunction(find_python_module)
......@@ -32,7 +32,11 @@ ELSE(WIN32)
SET(CMAKE_OSX_DEPLOYMENT_TARGET ${MACOS_VERSION} CACHE STRING
"Minimum OS X version to target for deployment (at runtime); newer APIs weak linked. Set to empty string for default value.")
ENDIF()
set(CMAKE_EXE_LINKER_FLAGS "-framework CoreFoundation -framework Security")
IF(ARM_TARGET_OS STREQUAL "android" OR ARM_TARGET_OS STREQUAL "armlinux"
OR ARM_TARGET_OS STREQUAL "ios" OR ARM_TARGET_OS STREQUAL "ios64")
ELSE()
set(CMAKE_EXE_LINKER_FLAGS "-framework CoreFoundation -framework Security")
ENDIF()
ELSE(APPLE)
IF(EXISTS "/etc/issue")
......
# Copyright (c) 2019 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.
if(NOT LITE_WITH_XPU)
return()
endif()
if(NOT DEFINED XPU_SDK_ROOT)
set(XPU_SDK_ROOT $ENV{XPU_SDK_ROOT})
if(NOT XPU_SDK_ROOT)
message(FATAL_ERROR "Must set XPU_SDK_ROOT or env XPU_SDK_ROOT when LITE_WITH_XPU=ON")
endif()
endif()
message(STATUS "XPU_SDK_ROOT: ${XPU_SDK_ROOT}")
find_path(XPU_SDK_INC NAMES xtcl.h
PATHS ${XPU_SDK_ROOT}/XTCL/include/xtcl NO_DEFAULT_PATH)
if(NOT XPU_SDK_INC)
message(FATAL_ERROR "Can not find xtcl.h in ${XPU_SDK_ROOT}/include")
endif()
include_directories("${XPU_SDK_ROOT}/XTCL/include")
include_directories("${XPU_SDK_ROOT}/XTDK/include")
find_library(XPU_SDK_XTCL_FILE NAMES xtcl
PATHS ${XPU_SDK_ROOT}/XTCL/so)
if(NOT XPU_SDK_XTCL_FILE)
message(FATAL_ERROR "Can not find XPU XTCL Library in ${XPU_SDK_ROOT}")
else()
message(STATUS "Found XPU XTCL Library: ${XPU_SDK_XTCL_FILE}")
add_library(xpu_sdk_xtcl SHARED IMPORTED GLOBAL)
set_property(TARGET xpu_sdk_xtcl PROPERTY IMPORTED_LOCATION ${XPU_SDK_XTCL_FILE})
endif()
find_library(XPU_SDK_TVM_FILE NAMES tvm
PATHS ${XPU_SDK_ROOT}/XTCL/so)
if(NOT XPU_SDK_TVM_FILE)
message(FATAL_ERROR "Can not find XPU TVM Library in ${XPU_SDK_ROOT}")
else()
message(STATUS "Found XPU TVM Library: ${XPU_SDK_TVM_FILE}")
add_library(xpu_sdk_tvm SHARED IMPORTED GLOBAL)
set_property(TARGET xpu_sdk_tvm PROPERTY IMPORTED_LOCATION ${XPU_SDK_TVM_FILE})
endif()
find_library(XPU_SDK_XPU_API_FILE NAMES xpuapi
PATHS ${XPU_SDK_ROOT}/XTDK/shlib)
if(NOT XPU_SDK_XPU_API_FILE)
message(FATAL_ERROR "Can not find XPU API Library in ${XPU_SDK_ROOT}")
else()
message(STATUS "Found XPU API Library: ${XPU_SDK_XPU_API_FILE}")
add_library(xpu_sdk_xpu_api SHARED IMPORTED GLOBAL)
set_property(TARGET xpu_sdk_xpu_api PROPERTY IMPORTED_LOCATION ${XPU_SDK_XPU_API_FILE})
endif()
find_library(XPU_SDK_XPU_RT_FILE NAMES xpurt
PATHS ${XPU_SDK_ROOT}/XTDK/shlib)
if(NOT XPU_SDK_XPU_RT_FILE)
message(FATAL_ERROR "Can not find XPU RT Library in ${XPU_SDK_ROOT}")
else()
message(STATUS "Found XPU RT Library: ${XPU_SDK_XPU_RT_FILE}")
add_library(xpu_sdk_xpu_rt SHARED IMPORTED GLOBAL)
set_property(TARGET xpu_sdk_xpu_rt PROPERTY IMPORTED_LOCATION ${XPU_SDK_XPU_RT_FILE})
endif()
find_library(XPU_SDK_XPU_JITC_FILE NAMES xpujitc
PATHS ${XPU_SDK_ROOT}/XTDK/shlib)
if(NOT XPU_SDK_XPU_JITC_FILE)
message(FATAL_ERROR "Can not find XPU JITC Library in ${XPU_SDK_ROOT}")
else()
message(STATUS "Found XPU JITC Library: ${XPU_SDK_XPU_JITC_FILE}")
add_library(xpu_sdk_xpu_jitc SHARED IMPORTED GLOBAL)
set_property(TARGET xpu_sdk_xpu_jitc PROPERTY IMPORTED_LOCATION ${XPU_SDK_XPU_JITC_FILE})
endif()
find_library(XPU_SDK_LLVM_FILE NAMES LLVM-8
PATHS ${XPU_SDK_ROOT}/XTDK/shlib)
if(NOT XPU_SDK_LLVM_FILE)
message(FATAL_ERROR "Can not find LLVM Library in ${XPU_SDK_ROOT}")
else()
message(STATUS "Found XPU LLVM Library: ${XPU_SDK_LLVM_FILE}")
add_library(xpu_sdk_llvm SHARED IMPORTED GLOBAL)
set_property(TARGET xpu_sdk_llvm PROPERTY IMPORTED_LOCATION ${XPU_SDK_LLVM_FILE})
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDMLC_USE_GLOG=1 -D_GLIBCXX_USE_CXX11_ABI=0")
set(xpu_runtime_libs xpu_sdk_xtcl xpu_sdk_tvm xpu_sdk_xpu_api xpu_sdk_xpu_rt xpu_sdk_xpu_jitc xpu_sdk_llvm CACHE INTERNAL "xpu runtime libs")
set(xpu_builder_libs xpu_sdk_xtcl xpu_sdk_tvm xpu_sdk_xpu_api xpu_sdk_xpu_rt xpu_sdk_xpu_jitc xpu_sdk_llvm CACHE INTERNAL "xpu builder libs")
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
\ No newline at end of file
请参考[PaddleLite文档开发规范](http://agroup.baidu.com/paddle-infer/md/article/2561104)
# 如何增加Layout
Paddle-Lite中Place包含了Target、Layout、Precision信息,用来注册和选择模型中的具体Kernel。下面以增加Place中的layout:`ImageDefault``ImageFolder``ImageNW`为例,讲解如何增加新Layout。
根据在`lite/core/``lite/api`目录下以`NHWC`为关键词检索代码,发现需要分别在以下的文件中加入Layout内容:
1. lite/api/paddle_place.h
2. lite/api/paddle_place.cc
3. lite/api/python/pybind/pybind.cc
4. lite/core/op_registry.h
5. lite/core/op_registry.cc
## 1. lite/api/paddle_place.h
`enum class DataLayoutType`中加入对应的Layout,注意已有的Layout不能改变值,增加新Layout递增即可:
```cpp
enum class DataLayoutType : int {
kUnk = 0,
kNCHW = 1,
kNHWC = 3,
kImageDefault = 4, // for opencl image2d
kImageFolder = 5, // for opencl image2d
kImageNW = 6, // for opencl image2d
kAny = 2, // any data layout
NUM = 7, // number of fields.
};
```
## 2. lite/api/paddle_place.cc
本文件有3处修改,注意在` DataLayoutToStr`函数中加入对应Layout的字符串名,顺序为`lite/api/paddle_place.h`中枚举值的顺序:
```cpp
// 该文件第1处
const std::string& DataLayoutToStr(DataLayoutType layout) {
static const std::string datalayout2string[] = {
"unk", "NCHW", "any", "NHWC", "ImageDefault", "ImageFolder", "ImageNW"};
auto x = static_cast<int>(layout);
CHECK_LT(x, static_cast<int>(DATALAYOUT(NUM)));
return datalayout2string[x];
}
// 该文件第2处
const std::string& DataLayoutRepr(DataLayoutType layout) {
static const std::string datalayout2string[] = {"kUnk",
"kNCHW",
"kAny",
"kNHWC",
"kImageDefault",
"kImageFolder",
"kImageNW"};
auto x = static_cast<int>(layout);
CHECK_LT(x, static_cast<int>(DATALAYOUT(NUM)));
return datalayout2string[x];
}
// 该文件第3处
std::set<DataLayoutType> ExpandValidLayouts(DataLayoutType layout) {
static const std::set<DataLayoutType> valid_set({DATALAYOUT(kNCHW),
DATALAYOUT(kAny),
DATALAYOUT(kNHWC),
DATALAYOUT(kImageDefault),
DATALAYOUT(kImageFolder),
DATALAYOUT(kImageNW)});
if (layout == DATALAYOUT(kAny)) {
return valid_set;
}
return std::set<DataLayoutType>({layout});
}
```
## 3. lite/api/python/pybind/pybind.cc
```cpp
// DataLayoutType
py::enum_<DataLayoutType>(*m, "DataLayoutType")
.value("NCHW", DataLayoutType::kNCHW)
.value("NHWC", DataLayoutType::kNHWC)
.value("ImageDefault", DataLayoutType::kImageDefault)
.value("ImageFolder", DataLayoutType::kImageFolder)
.value("ImageNW", DataLayoutType::kImageNW)
.value("Any", DataLayoutType::kAny);
```
## 4. lite/core/op_registry.h
找到KernelRegister final中的`using any_kernel_registor_t =`,加入下面修改信息:
```cpp
// 找到KernelRegister final中的`using any_kernel_registor_t =`
// 加入如下内容:
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kFP16),
DATALAYOUT(kNCHW)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kFP16),
DATALAYOUT(kNHWC)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kFP16),
DATALAYOUT(kImageDefault)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kFP16),
DATALAYOUT(kImageFolder)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kFP16),
DATALAYOUT(kImageNW)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kFloat),
DATALAYOUT(kImageDefault)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kFloat),
DATALAYOUT(kImageFolder)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kFloat),
DATALAYOUT(kImageNW)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kAny),
DATALAYOUT(kImageDefault)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kAny),
DATALAYOUT(kImageFolder)> *, //
KernelRegistryForTarget<TARGET(kOpenCL),
PRECISION(kAny),
DATALAYOUT(kImageNW)> *, //
```
## 5. lite/core/op_registry.cc
该文件有2处修改:
```cpp
// 该文件第1处
#define CREATE_KERNEL1(target__, precision__) \
switch (layout) { \
case DATALAYOUT(kNCHW): \
return Create<TARGET(target__), \
PRECISION(precision__), \
DATALAYOUT(kNCHW)>(op_type); \
case DATALAYOUT(kAny): \
return Create<TARGET(target__), \
PRECISION(precision__), \
DATALAYOUT(kAny)>(op_type); \
case DATALAYOUT(kNHWC): \
return Create<TARGET(target__), \
PRECISION(precision__), \
DATALAYOUT(kNHWC)>(op_type); \
case DATALAYOUT(kImageDefault): \
return Create<TARGET(target__), \
PRECISION(precision__), \
DATALAYOUT(kImageDefault)>(op_type); \
case DATALAYOUT(kImageFolder): \
return Create<TARGET(target__), \
PRECISION(precision__), \
DATALAYOUT(kImageFolder)>(op_type); \
case DATALAYOUT(kImageNW): \
return Create<TARGET(target__), \
PRECISION(precision__), \
DATALAYOUT(kImageNW)>(op_type); \
default: \
LOG(FATAL) << "unsupported kernel layout " << DataLayoutToStr(layout); \
}
// 该文件第2处
// 找到文件中的下面的函数
KernelRegistry::KernelRegistry()
: registries_(static_cast<int>(TARGET(NUM)) *
static_cast<int>(PRECISION(NUM)) *
static_cast<int>(DATALAYOUT(NUM)))
// 在该函数中加入新增Layout的下面内容
INIT_FOR(kOpenCL, kFP16, kNCHW);
INIT_FOR(kOpenCL, kFP16, kNHWC);
INIT_FOR(kOpenCL, kFP16, kImageDefault);
INIT_FOR(kOpenCL, kFP16, kImageFolder);
INIT_FOR(kOpenCL, kFP16, kImageNW);
INIT_FOR(kOpenCL, kFloat, kImageDefault);
INIT_FOR(kOpenCL, kFloat, kImageFolder);
INIT_FOR(kOpenCL, kFloat, kImageNW);
INIT_FOR(kOpenCL, kAny, kImageDefault);
INIT_FOR(kOpenCL, kAny, kImageFolder);
INIT_FOR(kOpenCL, kAny, kImageNW);
```
# 如何增加Pass
本文从三个方面介绍了`Lite`中的`Pass`结构:**Pass是什么****Pass的实现与接口****Pass的一般注册流程**。最后以`Fc_fuse_pass`为例介绍了`fusion_pass`的作用与注册方法。
## 前述:Pass是什么?
**CxxPredictor加载模型后,在执行预测前会先优化模型。模型优化过程是通过Pass实现的。**
具体调用关系如下:
![图片](https://user-images.githubusercontent.com/45189361/69638690-20d21880-1096-11ea-8169-1d2c7e1a1609.png)
- `CreatePredictor(CxxConfig)`函数调用了Predictor->Build(CxxConfig)
- CxxPredictor的构建过程(Build)分为两步:
- Predictor->LoadModel() 加载模型文件到program中
- Predicotr->optimizer_.Run() 对Program中的原始图形结构进行优化
- 对图结构的优化是通过调用 `Pass->Apply(const std::unique_ptr<SSAGraph>& graph)`方法实现的。
**每一类Pass定义了一种优化过程**,包括:原模型中的kernel选取、OP融合、冗余OP去除、子图创建、内存优化、类型推导、类型转换等。
## Pass的实现与接口 :Pass基类、PassManager和Pass注册
### 1、Pass基类:`paddle::lite::mir::Pass`
```c++
class Pass {
public:
// Pass的类型,Pass按照作用的不同可以分为三种
enum class Kind { //种类的作用不太清楚
// 1. 修改模型中的图拓扑结构的Pass
kProgramWise = 0,
// 2. 不修改图结构,修改状态的Pass
kStmtWise,
// 3. 不修改 IR,用于搜集信息和可视化信息的Pass.
kDebug,
};
// 主要实现函数:Apply 函数定义了 Pass 运行时执行的操作
virtual void Apply(const std::unique_ptr<SSAGraph>& graph) = 0;
bool is_program_pass() const { return kind_ == Kind::kProgramWise; }
bool is_stmt_pass() const { return kind_ == Kind::kStmtWise; }
virtual ~Pass() = default;
private:
const Kind kind_; // pass 的种类
std::string name_; // pass 的名称
std::set<TargetType> bound_targets_; // 指定了Pass运行的硬件平台,模型优化过程会根据当前硬件平台是否匹配筛选Pass。
std::unordered_map<std::string, std::set<lite_api::Place>> bound_kernels_; // 绑定的kernel
};
// Different kinds.
class ProgramPass : public Pass {
public:
ProgramPass() : Pass(Kind::kProgramWise) {}
};
class StmtPass : public Pass {
public:
StmtPass() : Pass(Kind::kStmtWise) {}
};
class DebugPass : public Pass {
public:
DebugPass() : Pass(Kind::kDebug) {}
};
```
**代码位置**`lite/core/mir/pass.h`
**主要类成员**
`const Kind kind_` : Pass类型。pass 有三种基本基本类型 :修改图结构的`ProgramPass`、修改状态量的`StmtPass`和Debug过程采集信息与控制可视化的`DebugPass`
`std::string name_` :pass 的名称
`std::set<TargetType> bound_targets_` : Pass运行的硬件平台,optimizer.Run()优化过程会根据硬件平台选择匹配的Pass。------根据硬件平台自动选择需要的pass
`std::unordered_map<std::string, std::set<lite_api::Place>> bound_kernels_` : Pass 绑定的kernel (what's this used for)
**主要接口**
`Pass::Apply(const std::unique_ptr& graph)` : Pass优化过程的具体操作,是新注册Pass需要实现的接口。输入为`SSAGraph`型指针,是对模型结构的拓扑表示。
### 2、Pass管理 `paddle::lite::mir::PassManager`
```c++
class PassManager {
public:
// 内部静态变量PassManager,用来存储使用的Pass和图优化操作
static PassManager& Global() {
static PassManager x;
return x;
}
// 执行所有的 Pass
void Run(const std::unique_ptr<SSAGraph>& graph) {
for (auto& pass : passes_) {
LOG(INFO) << "Running MIR pass " << pass->name();
pass->Apply(graph);
}
private:
std::list<std::unique_ptr> passes_; //存储所有的 Pass
std::map<std::string, mir::Pass*> pass_map_; //使用map变量存储 PassName::Pass
}
```
**代码位置**`lite/core/mir/pass_manager.h`
**主要类成员**
`std::list:unique_ptr> passes_;` : List类型,存储了所有已注册Pass。
`std::map<std::string, mir::Pass*> pass_map_; ` : Map类型,存储了所有"Pass名称-Pass类"键对,用于根据名称查找Pass。
**主要接口**
`static PassManager& Global()` 返回PassManager全局静态变量,该变量存储了所有已注册的Pass
` bool AddNewPass(const std::string& name, Pass* pass)` 添加新的Pass到PassManager中
### 3、 Pass 注册 `paddle::lite::mir::PassRegistry`
**代码位置**`lite/core/mir/pass_registry.h`
**主要接口**
`REGISTER_MIR_PASS(name__, class__)` :宏定义函数,用于注册Pass。注册Pass过程实现的是 `PassManager::Global().AddNewPass(name__, class__)`,将新注册Pass添加到全局变量`PassManager`中。
## Pass的一般注册流程与使用方法
### 1. Pass 注册流程
`lite/core/mir`或其子目录下继承`Pass基类`,实现`Pass::Apply`接口,并使用宏`REGISTER_MIR_PASS(name__, class__)`将Pass注册到`PassManager`即完成了新Pass注册。
**以新建 **`new_demo_pass`**为例**,具体流程如下:
(1)在`lite/core/mir`路径下新建`example_pass.cc``new_demo_pass.h` 文件
(2)在`example_pass.h` 文件中继承Pass基类(ProgramPass、StmtPass或DebugPass)定义自己的Pass类。
```c++
#include "lite/core/mir/pass.h"
namespace paddle {
namespace lite {
namespace mir {
class ExamplePass : public ProgramPass {
void Apply(const std::unique_ptr<SSAGraph> &graph) override {}
...
};
} // namespace mir
} // namespace lite
} // namespace paddle
```
(3)在`example_pass.cc` 文件中实现`ExamplePass::Apply()`接口,并注册`ExamplePass`
```c++
#include "lite/core/mir/pass_registry.h"
#include "lite/core/mir/example_pass.h"
namespace paddle {
namespace lite {
namespace mir {
void ExamplePass::Apply(const std::unique_ptr<SSAGraph>& graph) {
...
}
} // namespace mir
} // namespace lite
} // namespace paddle
REGISTER_MIR_PASS(example_pass, paddle::lite::mir::ExamplePass)
.BindTargets({TARGET(kARM)}); // Pass执行的目标硬件平台
// .BindKernel("conv2d"); //Pass绑定的 kernel
```
(4)修改`lite/core/mir/CMakeLists.txt`文件,将`example_pass.cc` 编译到`mir_passes`库中
```cmake
lite_cc_library(mir_passes
SRCS
demo_pass.cc // 新建的Pass文件
...
memory_optimize_pass.cc
DEPS mir_pass types context ${mir_fusers} ${subgraph_passes})
```
### 2. Pass使用流程
将Pass注册到PassManager后不会自动生效。需要在`optimizer->run()` 函数中添加该Pass才会在模型优化过程中调用。
(1)在`paddle_use_passes.h`文件中调用该Pass
```cmake
#include "paddle_lite_factory_helper.h" // NOLINT
...
USE_MIR_PASS(new_demo_pass); //调用 new_demo_pass
```
(2)要想在优化模型时调用该Pass,需要在`optimizer->run()`函数中手动添加调用。
修改`lite/core/optimizer.h`文件,添加`new_demo_pass``Optimizer::Run()`函数;
```c++
class Optimizer {
public:
void Run(...) {
...
if (passes.empty()) {
RunPasses(std::vector<std::string>{
{"new_demo_pass" //将新注册的Pass添加在这里
...
}
...
}
```
(3)只有CxxPredictor才会在模型加载后根据Pass优化模型。
```c++
...
#include "paddle_use_passes.h" // 引用Pass优化模型
void RunModel() {
// 1. 创建 CxxConfig
CxxConfig config;
config.set_model_dir(FLAGS_model_dir);
config.set_valid_places(Place{TARGET(kARM), PRECISION(kFloat)});
// 2. 创建CxxPredictor,该过程包括加载模型和用Pass优化模型
std::shared_ptr> predictor =
Creat<CxxConfig>(config);
}
```
## Fusion Pass的定义与注册
`Fusion Pass`是一种常见图结构优化Pass,可将多个连续OP融合成单个等效OP,减少数据交换并简化图结构。Pass运行时调用`Fuser`自动查找并替换指定图结构,所以注册`FuserPass`时还需要实现对应的Fuser类。
下面以`fc_fuse_pass`为例,详细说明`FusionPass`的效果和注册方法。
### `fc_fuse_pass`的作用
将相邻的`mul`算子和 `element_wise add `算子 融合成一个 `FC` 算子
```c++
mul(X) = X * W
elementwise_add( mul(x) ) = X * W + Bias
//----------> after fusion
FC(X) = X * W +Bias
```
Pass 运行效果如下:
![图片](https://user-images.githubusercontent.com/45189361/69639193-12383100-1097-11ea-9063-21f030414080.png)
mul和elementwise_add的原有参数映射到FC的参数上:
![图片](https://user-images.githubusercontent.com/45189361/69638836-74446680-1096-11ea-9cdc-a961fa995dfe.png)
### `fc_fuse_pass`的注册方法
#### 1、创建FcFuser
(1)在`lite/core/mir/fusion`路径下新建`fc_fuser.cc``fc_fuser.h` 文件
(2)在`fc_fuser.h` 文件中继承`FuseBase`定义自己的Fuser类。
```c++
#include "lite/core/mir/pattern_matcher_high_api.h"
namespace paddle {
namespace lite {
namespace mir {
namespace fusion {
class FcFuser : public FuseBase {
public:
void BuildPattern() override;
void InsertNewNode(SSAGraph* graph, const key2nodes_t& matched) override;
private:
cpp::OpDesc GenOpDesc(const key2nodes_t& matched) override;
};
} // namespace fusion
} // namespace mir
} // namespace lite
} // namespace paddle
```
**主要接口**
`FuseBase::BuildPattern` : 描述需要替换位置的图结构(pattern),Fuser运行时会自动查找并替换该pattern。
`FuseBase::GenOpDesc` : 创建融合后的等效Fused_op。
`FuseBase::InsertNewNode` :用Fused_op替换原始图结构(pattern)。
对于 `FcFuser`:BuildPattern描述的Pattern是`mul+elementwise add`,GenOpDesc创建的FC_op,InsertNewNode函数的效果是用新建的`FC_op`替换模型中的`mul+elementwise add` pattern。
(3) 在`fc_fuser.cc`文件中实现 `BuildPattern()``GenOpDesc()``InsertNewNode() `接口
下面以FcFuser为例介绍三种接口的实现:
```c++
// 1. BuildPattern函数,描述需要替换的图结构
// FcFuser::BuildPattern() 描述了 mul + element_wise add 图结构
void FcFuser::BuildPattern() {
// (1) 用OpNode描述和VarNode
// mul OP
auto* mul = OpNode("mul", "mul");
// mul OP 的输入和输出
auto* x = VarNode("x")->assert_is_op_input("mul", "X");
auto* W = VarNode("W")->assert_is_op_input("mul", "Y");
auto* mul_out = VarNode("mul_out");
// elementwise_add OP
auto* add = OpNode("add", "elementwise_add");
//elementwise_add 的输入
auto* b = VarNode("b")->assert_is_persistable_var();
// elementwise_add OP的输出(最终输出)
auto* Out = VarNode("Out");
//(2) 描述拓扑连接 (Fuse之前mul 和elementwise_add的连接)
std::vector<PMNode*> mul_inputs{W, x};
std::vector<PMNode*> add_inputs{mul_out, b};
mul_inputs >> *mul >> *mul_out;
add_inputs >> *add >> *Out;
//(3) 声明新的拓扑结构中将会被移除的节点,包括被fuse的OP和OP之间的中间变量
mul_out->AsIntermediate();
mul->AsIntermediate();
add->AsIntermediate();
}
// 2. GenOpDesc函数新建等效 Fused_op
// FcFuser::GenOpDesc() 新建了Fc_op
cpp::OpDesc FcFuser::GenOpDesc(const key2nodes_t& matched) {
// (1) 得到第一个OP节点的 OpDesc ,并清空输入输出信息
cpp::OpDesc op_desc = *matched.at("mul")->stmt()->op_info();
op_desc.mutable_inputs()->clear();
op_desc.mutable_outputs()->clear();
// (2) 修改OpDesc , 将OpType设置为 "fc" (FC OP 的OP_type),
op_desc.SetType("fc");
// (3) 设置OpDesc中的Input、Output、Attrbute。分别连接到BuildPattern()函数中创建的VarNode
op_desc.SetInput("Input", {matched.at("x")->arg()->name});
op_desc.SetInput("W", {matched.at("W")->arg()->name});
op_desc.SetInput("Bias", {matched.at("b")->arg()->name});
op_desc.SetOutput("Out", {matched.at("Out")->arg()->name});
op_desc.SetAttr(
"in_num_col_dims",
matched.at("mul")->stmt()->op_info()->GetAttr<int>("x_num_col_dims"));
return op_desc;
}
// 3. InsertNewNode函数用Fused OP 替换模型图中的原始 Pattern
// FcFuser::InsertNewNode() 用Fc_OP替换原始模型图中的 " mul + element_wise add "
void FcFuser::InsertNewNode(SSAGraph* graph, const key2nodes_t& matched) {
// (1) 创建FC OP的参数(OpDesc)
auto op_desc = GenOpDesc(matched);
// 创建一个 FC OP
auto fc_op = LiteOpRegistry::Global().Create("fc");
// 找到原拓扑结构中的scope (作用域)和 valid_places (可支持设备类型)
auto mul = matched.at("mul")->stmt()->op();
auto* scope = mul->scope();
auto& valid_places = mul->valid_places();
// (2) 将 FC OP的 scope和 valid_places设置与fuse前相同,并在图中创建该节点(node)
fc_op->Attach(op_desc, scope);
auto* new_op_node = graph->GraphCreateInstructNode(fc_op, valid_places);
// (3) 将FC节点连接到输入输出(var_node)
IR_NODE_LINK_TO(matched.at("W"), new_op_node);
IR_NODE_LINK_TO(matched.at("x"), new_op_node);
IR_NODE_LINK_TO(matched.at("b"), new_op_node);
IR_NODE_LINK_TO(new_op_node, matched.at("Out"));
}
```
#### 2、注册fc_fuse_pass
(1)在`lite/core/mir/fusion`路径下新建`fc_fuse_pass.cc``fc_fuse_pass.h` 文件
(2)在`fc_fuse_pass.h` 文件中,继承`ProgramPass`定义`FcFusePass`
```c++
#include "lite/core/mir/pass.h"
namespace paddle {
namespace lite {
namespace mir {
class FcFusePass : public ProgramPass {
public:
void Apply(const std::unique_ptr<SSAGraph>& graph) override; namespace mir namespace lite namespace paddle
```
(3)在`fc_fuse_pass.cc` 文件中实现`FcFusePass::Apply()`接口,并注册`FcFusePass`
```c++
#include "lite/core/mir/pass_registry.h"
#include "lite/core/mir/example_pass.h"
namespace paddle {
namespace lite {
namespace mir {
void FcFusePass::Apply(const std::unique_ptr<SSAGraph>& graph) {
fusion::FcFuser fuser;
fuser(graph.get());namespace mir
} // namespace lite
} // namespace paddle
REGISTER_MIR_PASS(lite_fc_fuse_pass, paddle::lite::mir::FcFusePass)
.BindTargets({TARGET(kAny)}) // FcFusePass 可以在任何硬件平台执行
.BindKernel("fc"); // FcFusePass 绑定 fc_kernel
```
(4)修改`lite/core/mir/fusion/CMakeLists.txt`文件,将`fc_fuser.cc` 编译到`mir_fusers`
```cmake
lite_cc_library(fuse_fc
SRCS fc_fuser.cc
DEPS pattern_matcher_high_api)
set(mir_fusers
fuse_fc
...
CACHE INTERNAL "fusers")
```
(5)修改`lite/core/mir/CMakeLists.txt`文件,将`fc_fuse_pass.cc` 编译到`mir_pass`
```cmake
lite_cc_library(mir_passes
SRCS
fusion/fc_fuse_pass.cc
...
DEPS mir_pass types context ${mir_fusers} ${subgraph_passes})
```
#### 3、使用 fc_fuse_pass
(1) `lite/api/paddle_use_passes.h`使用`USE_LITE_PASS`宏来引入新加入的pass
```c++
USE_MIR_PASS(lite_fc_fuse_pass);
```
(2) 在`lite/core/optimizer.h`文件的`Optimizer::Run()`函数中添加新注册的pass
```C++
class Optimizer {
public:
void Run(Program&& program,
const std::vector<Place>& valid_places,
core::KernelPickFactor kernel_pick_factor,
const std::vector<std::string>& passes = {}) {
...
if (passes.empty()) {
RunPasses(std::vector<std::string>{
{"lite_fc_fuse_pass", // the newly registered pass
...
"argument_type_display_pass"}});
} else {
RunPasses(passes);
}
exec_scope_ = program.exec_scope();
}
```
(3) 以上修改完成后,在CreatePredictor(CxxConfig)创建CxxPredictor时,模型优化过程会调用`lite_fc_fuse_pass `,扫描`mul + element_wise add`结构并替换为等效的Fc_OP。
# 如何增加OP
以下以添加argmax为例,详细说明新增op的方法。
## 1. 添加OpParam 结构体以传导 Op 的输入和输出
- 这里命名为 `ArgmaxParam`
-`paddlelite/lite/operators/op_params.h` 中添加 `ArgmaxParam` 结构体,代码如下:
```c++
struct ArgmaxParam {
lite::Tensor* X{};
lite::Tensor* Out{};
int Axis{0};
};
```
## 2. 添加 Argmax Op 并注册
- 在paddlelite/lite/operators/目录下新建argmax_op.h文件,主要代码如下:
```c++
class ArgmaxOpLite : public OpLite {
public:
ArgmaxOpLite() {}
explicit ArgmaxOpLite(const std::string &op_type) : OpLite(op_type) {}
bool CheckShape() const override;
bool InferShape() const override;
bool AttachImpl(const cpp::OpDesc &opdesc, lite::Scope *scope) override;
void AttachKernel(KernelBase *kernel) override { kernel->SetParam(param_); }
std::string DebugString() const override { return "argmax"; }
private:
mutable ArgmaxParam param_;
};
```
`ArgmaxOpLite` 继承 `OpLite` ,成员变量包括 `ArgmaxParam` 结构体,需要实现的接口包括 `CheckShape()``InferShape()``AttachImp()``AttachKernel()``DebugString()` 函数。`AttachKernel()``DebugString() `函数较为简单,此处直接实现;
-`paddlelite/lite/operators/` 目录下新建argmax_op.cc文件,需要具体实现`CheckShape()``InferShape()``AttachImp()`函数。`CheckShape()`函数检查输入是否符合要求,`InferShape()`函数基于输入推断得到输出的维度,`AttachImp()`函数绑定Op的输入输出。然后在argmax_op.cc文件中注册argmax,核心代码如下:
```c++
bool ArgmaxOpLite::CheckShape() const {
CHECK_OR_FALSE(param_.X);
CHECK_OR_FALSE(param_.Out);
CHECK_OR_FALSE(param_.Axis < (param_.X)->dims().size());
return true;
}
bool ArgmaxOpLite::InferShape() const {
auto x_dims = param_.X->dims();
int x_rank = x_dims.size();
int axis = param_.Axis;
if (axis < 0) axis += x_rank;
std::vector<int64_t> out_dims;
for (int64_t i = 0; i < axis; i++) {
out_dims.push_back(x_dims[i]);
}
for (int64_t i = axis + 1; i < x_rank; i++) {
out_dims.push_back(x_dims[i]);
}
// Set output dims
param_.Out->Resize(lite::DDim(out_dims));
return true;
}
bool ArgmaxOpLite::AttachImpl(const cpp::OpDesc &op_desc, lite::Scope *scope) {
auto x = op_desc.Input("X").front();
auto out = op_desc.Output("Out").front();
param_.X = scope->FindVar(x)->GetMutable<lite::Tensor>();
param_.Out = scope->FindVar(out)->GetMutable<lite::Tensor>();
param_.Axis = op_desc.GetAttr<int>("Axis");
return true;
}
REGISTER_LITE_OP(argmax, paddle::lite::operators::ArgmaxOpLite);
```
- 在paddlelite/lite/operators/CMakeLists.txt中添加```add_operator(argmax_op basic SRCS argmax_op.cc DEPS ${op_DEPS})```
## 3. 添加Argmax Kernel并绑定
以下以arm端argmax实现为例说明
- 在paddlelite/lite/kernels/arm/目录下新建argmax_compute.h文件,声明ArgmaxCompute类,并继承KernelLite,主要代码如下:
```c++
class ArgmaxCompute : public KernelLite<TARGET(kARM), PRECISION(kFloat)> {
public:
using param_t = operators::ArgmaxParam;
void Run() override;
virtual ~ArgmaxCompute() = default;
};
```
- 在paddlelite/lite/kernels/arm/目录下新建argmax_compute.cc文件,主要实现Run函数。`Run()`函数调用paddlelite/lite/bachends/arm/math/argmax.h中的`argmax_func()`函数,根据输入计算输出。最后在argmax_compute.cc文件中,我们绑定argmax的输入输出(为tensor的输入参数都需要绑定),代码如下:
```c++
void ArgmaxCompute::Run() {
auto& param = Param<operators::ArgmaxParam>();
lite::Tensor* input = param.X;
lite::Tensor* output = param.Out;
int axis = param.Axis;
lite::arm::math::argmax_func(input, axis, output);
return;
}
REGISTER_LITE_KERNEL(
argmax, kARM, kFloat, kNCHW, paddle::lite::kernels::arm::ArgmaxCompute, def)
.BindInput("X", {LiteType::GetTensorTy(TARGET(kARM))})
.BindOutput("Out", {LiteType::GetTensorTy(TARGET(kARM))})
.Finalize();
```
- 在paddlelite/lite/kernels/arm/CMakeLists.txt中添加
```cmake
add_kernel(argmax_compute_arm ARM basic SRCS argmax_compute.cc DEPS ${lite_kernel_deps} math_arm)
```
## 4. 添加Argmax实现
- 在paddlelite/lite/backends/arm/math/目录下新建argmax.h文件,声明`argmax_func()`函数,代码如下:
```c++
void argmax_func(const lite::Tensor* input, const int axis, lite::Tensor* output);
```
- 在paddlelite/lite/backends/arm/math/目录下新建argmax.cc文件,具体实现`argmax_func()`函数,代码如下:
```c++
void argmax_func(const lite::Tensor *input,
const int axis,
lite::Tensor *output) {
auto input_ddim = input->dims();
auto output_ddim = output->dims();
const int size = input_ddim[axis];
const int in_channel = input_ddim.count(axis, input_ddim.size());
const int out_channel = output_ddim.count(axis, output_ddim.size());
const int in_stride = input_ddim.count(axis + 1, input_ddim.size());
const int out_stride = input_ddim.count(0, axis);
for (int n = 0; n < out_stride; n++) {
for (int k = 0; k < in_stride; k++) {
const float *in_ptr = input->data<float>() + n * in_channel + k;
std::vector<std::pair<float, int>> vec;
vec.resize(size);
for (int i = 0; i < size; i++) {
vec[i] = std::make_pair(in_ptr[i * in_stride], i);
}
// sort
std::partial_sort(vec.begin(),
vec.begin() + 1,
vec.end(),
std::greater<std::pair<float, int>>());
// out
float *out_ptr = output->mutable_data<float>() + n * out_channel + k;
*out_ptr = vec[0].second;
}
}
}
```
- 在paddlelite/lite/backends/arm/math/CMakeFile.txt中的```math_arm library```中添加argmax.cc,在paddlelite/lite/backends/arm/math/funcs.h中添加```#include "lite/arm/math/argmax.h"```
## 5. 添加Argmax单测
- 在paddlelite/lite/tests/kernels目录下新建argmax_compute_test.cc文件,声明并实现ArgmaxComputeTester类;
- ArgmaxComputeTester类中主要包括PrepareOpDesc、PrepareData和RunBaseline函数。PrepareOpDesc函数设定单测op的类型和输入输出参数,PrepareData函数对输入tensor进行初始化,RunBaseline是基于输入计算得到输出,用于和框架计算的输出进行对比;
- 使用gtest添加单测,代码如下:
```c++
TEST(Argmax, precision) {
#ifdef LITE_WITH_ARM
LOG(INFO) << "test argmax arm";
Place place(TARGET(kARM));
for (int axis : {0, 1, 2, 3}) {
for (int n : {1, 3}) {
for (int c : {3, 6}) {
for (int h : {9, 18}) {
for (int w : {9, 18}) {
std::unique_ptr<arena::TestCase> tester(
new ArgmaxComputeTester(place, "def", axis, n, c, h, w));
arena::Arena arena(std::move(tester), place, 2e-5);
arena.TestPrecision();
}
}
}
}
}
#endif
}
```
- 在paddlelite/lite/tests/kernels/CMakeLists.txt中添加
```cmake
lite_cc_test(test_kernel_argmax_compute SRCS argmax_compute_test.cc DEPS arena_framework ${x86_kernels} ${arm_kernels} ${lite_ops} ${host_kernels})
```
## 6. 编译运行
- 在paddlelite目录中,执行```./lite/tools/ci_build.sh build_test_arm```,该脚本会创建手机模拟器,并编译运行所有单测(花费时间较久)。如果运行无误,则表明添加argmax成功。
# Lite基于CUDA的模型预测
Lite支持在x86_64,arm64架构上(如:TX2)进行CUDA的编译运行。
## 编译
**NOTE:** 如果是在TX2等NVIDIA嵌入式硬件上编译,请使用最新的[Jetpack](https://developer.nvidia.com/embedded/jetpack) 安装依赖库。
一: 下载代码
```
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
```
二:编译
```
# 进入代码目录
cd Paddle-Lite
# 运行编译脚本
# 编译结束会在本目录下生成 build_cuda 目录
# 编译过程中如果提示找不到CUDA,CUDNN,请在环境变量设置CUDA_TOOLKIT_ROOT_DIR, CUDNN_ROOT
# CUDA_TOOLKIT_ROOT_DIR,CUDNN_ROOT分别表示CUDA,CUDNN的根目录
./lite/tools/build.sh cuda
# 如果使用python接口,需要打开build_python选项
./lite/tools/build.sh --build_python=ON cuda
```
编译结束会在 `build_cuda/inference_lite_lib/python/lib/` 目录下生成 `lite_core.so`
## 运行
以下以Yolov3模型为例,介绍如何在Nvidia GPU硬件上运行模型。
一: 下载darknet_yolov3模型,模型信息请参考[这里](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/yolov3)
```
# 下载模型
wget https://paddle-inference-dist.cdn.bcebos.com/PaddleLite/yolov3_infer.tar.gz
tar -zxf yolov3_infer.tar.gz
# 下载图片样例
wget https://paddle-inference-dist.cdn.bcebos.com/PaddleLite/kite.jpg
```
二: 运行
**NOTE:**此处示例使用的是python接口,后续会开放C++接口以及示例。
``` python
#-*- coding: utf-8 -*-
from __future__ import print_function
import sys
import numpy as np
import cv2
sys.path.append('build_cuda/inference_lite_lib/python/lib')
from lite_core import *
def read_img(im_path, resize_h, resize_w):
im = cv2.imread(im_path).astype('float32')
im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
h, w, _ = im.shape
im_scale_x = resize_h / float(w)
im_scale_y = resize_w / float(h)
out_img = cv2.resize(im, None, None, fx=im_scale_x, fy=im_scale_y, interpolation=cv2.INTER_CUBIC)
mean = np.array([0.485, 0.456, 0.406]).reshape((1, 1, -1))
std = np.array([0.229, 0.224, 0.225]).reshape((1, 1, -1))
out_img = (out_img / 255.0 - mean) / std
out_img = out_img.transpose((2, 0, 1))
return out_img
# 配置config
a = CxxConfig()
a.set_model_file('./yolov3_infer/__model__') # 指定模型文件路径
a.set_param_file('./yolov3_infer/__params__') # 指定参数文件路径
place_cuda = Place(TargetType.CUDA)
a.set_valid_places([place_cuda])
# 创建predictor
predictor = create_paddle_predictor(a)
# 设置输入
input_tensor = predictor.get_input(0);
height, width = 608, 608
input_tensor.resize([1, 3, height, width])
data = read_img('./kite.jpg', height, width).flatten()
input_tensor.set_float_data(data, TargetType.CUDA)
in2 = predictor.get_input(1);
in2.resize([1, 2])
in2.set_int32_data([height, width], TargetType.CUDA)
# 运行
predictor.run()
# 获取输出
output_tensor = predictor.get_output(0);
print (output_tensor.shape())
# [100L, 6L]
print (output_tensor.target())
# TargetType.Host
print (output_tensor.float_data()[:6])
# [0.0, 0.9862784743309021, 98.51927185058594, 471.2381286621094, 120.73092651367188, 578.33251953125]
```
**NOTE:** 对CUDA的支持还在持续开发中。
# CV图像预处理库
请把编译脚本`Paddle-Lite/lite/too/build.sh``BUILD_CV`变量设置为`ON`, 其他编译参数设置请参考[源码编译](../source_compile), 以确保 Lite 可以正确编译。这样`CV`图像的加速库就会编译进去,且会生成`paddle_image_preprocess.h`的API文件
- 硬件平台: `ARM`
- 操作系统:`MAC``LINUX`
## CV 图像预处理功能
Lite 支持不同颜色空间的图像相互转换 `Convert` 、缩放 `Resize` 、翻转 `Flip`、旋转 `Rotate` 和图像数据转换为 `Tensor` 存储`ImageToTensor` 功能,下文将详细介绍每个功能的API接口。
### CV 枚举变量和结构体变量
- 颜色空间
```cpp
enum ImageFormat {
RGBA = 0,
BGRA,
RGB,
BGR,
GRAY,
NV21 = 11,
NV12,
};
```
- 翻转参数
```cpp
enum FlipParam {
X = 0, // flip along the X axis
Y, // flip along the Y axis
XY // flip along the XY axis
};
```
- 转换参数
```cpp
typedef struct {
int ih; // input height
int iw; // input width
int oh; // outpu theight
int ow; // output width
FlipParam flip_param; // flip, support x, y, xy
float rotate_param; // rotate, support 90, 180, 270
} TransParam;
```
### ImagePreprocess 类的成员变量
`ImagePreprocess` 类含有以下三个私有成员变量,通过构造函数进行初始化。
```cpp
private:
ImageFormat srcFormat_; // input image color format
ImageFormat dstFormat_; // output image color format
TransParam transParam_; // image transform parameter
// init
ImagePreprocess::ImagePreprocess(ImageFormat srcFormat, ImageFormat dstFormat, TransParam param) {
this->srcFormat_ = srcFormat;
this->dstFormat_ = dstFormat;
this->transParam_ = param;
}
```
### 颜色空间转换 Convert
`Convert` 函数支持颜色空间:GRAY、NV12(NV21)、RGB(BGR)和RGBA(BGRA)
+ 目前支持以下颜色空间的相互转换:
- GRAY2BGR
- GRAY2RGB
- BGR2RGB
- BGRA2BGR
- BGRA2RGB
- RGBA2RGB
- RGBA2BGR
- BGRA2RGBA
+ 目前支持以下颜色空间的单向转换:
- NV12—BGR
- NV21—BGR
- NV12—RGB
- NV21—RGB
- NV12—BGRA
- NV21—BGRA
- NV12—RGBA
- NV21—RGBA
+ `Convert` 功能的API接口
```cpp
// 方法一
void ImagePreprocess::imageCovert(const uint8_t* src, uint8_t* dst);
// 方法二
void ImagePreprocess::imageCovert(const uint8_t* src,
uint8_t* dst, ImageFormat srcFormat, ImageFormat dstFormat);
```
+ 第一个 `imageCovert` 接口,缺省参数来源于 `ImagePreprocess` 类的成员变量。故在初始化 `ImagePreprocess` 类的对象时,必须要给以下成员变量赋值:
- param srcFormat:`ImagePreprocess` 类的成员变量`srcFormat_`
- param dstFormat:`ImagePreprocess` 类的成员变量`dstFormat_`
- 第二个`imageCovert` 接口,可以直接使用
### 缩放 Resize
`Resize` 功能支持颜色空间:GRAY、NV12(NV21)、RGB(BGR)和RGBA(BGRA)
`Resize` 功能目前支持的方法:`bilinear`
+ `Resize` 功能的API接口
```cpp
// 方法一
void ImagePreprocess::imageResize(const uint8_t* src, uint8_t* dst);
// 方法二
void ImagePreprocess::imageResize(const uint8_t* src, uint8_t* dst, ImageFormat srcFormat, ImageFormat srcFormat, int srcw, int srch, int dstw, int dsth);
```
+ 第一个`imageResize` 接口,缺省参数来源于`ImagePreprocess` 类的成员变量。故在初始化`ImagePreprocess` 类的对象时,必须要给以下成员变量赋值:
- param srcFormat:`ImagePreprocess` 类的成员变量`dstFormat_`
- param srcw:`ImagePreprocess` 类的成员变量`transParam_.iw`
- param srch:`ImagePreprocess` 类的成员变量`transParam_.ih`
- param dstw:`ImagePreprocess` 类的成员变量`transParam_.ow`
- param dsth:`ImagePreprocess` 类的成员变量`transParam_.ow`
- 第二个`imageResize` 接口,可以直接使用
### 旋转 Rotate
`Rotate` 功能支持颜色空间:GRAY、RGB(BGR)和RGBA(BGRA)
`Rotate` 功能目前支持的角度:90、180 和 270
+ `Rotate` 功能的API接口
```cpp
// 方法一
void ImagePreprocess::imageRotate(const uint8_t* src, uint8_t* dst);
// 方法二
void ImagePreprocess::imageRotate(const uint8_t* src, uint8_t* dst, ImageFormat srcFormat, ImageFormat srcFormat, int srcw, int srch, float degree);
```
+ 第一个`imageRotate` 接口,缺省参数来源于`ImagePreprocess` 类的成员变量。故在初始化`ImagePreprocess` 类的对象时,必须要给以下成员变量赋值:
- param srcFormat:`ImagePreprocess` 类的成员变量`dstFormat_`
- param srcw:`ImagePreprocess` 类的成员变量`transParam_.ow`
- param srch:`ImagePreprocess` 类的成员变量`transParam_.oh`
- param degree:`ImagePreprocess` 类的成员变量`transParam_.rotate_param`
- 第二个`imageRotate` 接口,可以直接使用
### 翻转 Flip
`Flip` 功能支持颜色空间:GRAY、RGB(BGR)和RGBA(BGRA)
`Flip` 功能目前支持的功能:沿X轴翻转、沿Y轴翻转和沿XY轴翻转
+ `Flip` 功能的API接口
```cpp
// 方法一
void ImagePreprocess::imageFlip(const uint8_t* src, uint8_t* dst);
// 方法二
void ImagePreprocess::imageFlip(const uint8_t* src, uint8_t* dst, ImageFormat srcFormat, ImageFormat srcFormat, int srcw, int srch, FlipParam flip_param);
```
+ 第一个`imageFlip` 接口,缺省参数来源于`ImagePreprocess` 类的成员变量。故在初始化`ImagePreprocess` 类的对象时,必须要给以下成员变量赋值:
- param srcFormat:`ImagePreprocess` 类的成员变量`dstFormat_`
- param srcw:`ImagePreprocess` 类的成员变量`transParam_.ow`
- param srch:`ImagePreprocess` 类的成员变量`transParam_.oh`
- param flip_param:`ImagePreprocess` 类的成员变量`transParam_.flip_param`
- 第二个`imageFlip` 接口,可以直接使用
### Image2Tensor
`Image2Tensor` 功能支持颜色空间:RGB(BGR)和RGBA(BGRA)
`Image2Tensor` 功能目前支持的Layout:`NCHW``NHWC`
`Image2Tensor` 不仅完成图像转换为`Tensor`数据处理,而且还完成了图像数据的归一化处理
+ `Image2Tensor` 功能的API接口
```cpp
// 方法一
void ImagePreprocess::image2Tensor(const uint8_t* src, Tensor* dstTensor, LayoutType layout, float* means, float* scales);
// 方法二
void ImagePreprocess::image2Tensor(const uint8_t* src, Tensor* dstTensor, ImageFormat srcFormat, srcw, int srch, LayoutType layout, float* means, float* scales;
```
+ 第一个`image2Tensor` 接口,缺省参数来源于`ImagePreprocess` 类的成员变量。故在初始化`ImagePreprocess` 类的对象时,必须要给以下成员变量赋值:
- param srcFormat:`ImagePreprocess` 类的成员变量`dstFormat_`
- param srcw:`ImagePreprocess` 类的成员变量`transParam_.ow`
- param srch:`ImagePreprocess` 类的成员变量`transParam_.oh`
- 第二个`image2Tensor` 接口,可以直接使用
## CV 图像预处理 Demo 示例
例子:输入 `1920x1080` 大小的 `NV12` 图像src,输出 `960x540` 大小 `RGB` 格式的图像dst;然后,完成 `90` 度旋转和沿 `X` 轴翻转功能;最后,用 `NHWC` 格式存储在Tensor里。
定义 `ImagePreprocess` 类的对象,初始化成员变量
```cpp
// init
srcFormat = ImageFormat::NV12;
dstFormat = ImageFormat::RGB;
srch = 1920;
srcw = 1080;
dsth = 960;
dstw = 540;
flip_param = FlipParam::X;
degree = 90;
layout = LayoutType::NHWC
// 方法一:
TransParam tparam;
tparam.ih = srch;
tparam.iw = srcw;
tparam.oh = dsth;
tparam.ow = dstw;
tparam.flip_param = flip_param;
tparam.rotate_param = degree;
ImagePreprocess image_preprocess(srcFormat, dstFormat, tparam);
// 方法二:
ImagePreprocess image_preprocess();
```
### imageConvert Demo
```cpp
// 方法一:
image_preprocess.imageCovert(src, lite_dst);
// 方法二:
image_preprocess.imageCovert(src, lite_dst, (ImageFormat)srcFormat, (ImageFormat)dstFormat);
```
### imageResize Demo
```cpp
// 方法一:
image_preprocess.imageResize(lite_dst, resize_tmp);
// 方法二:
image_preprocess.imageResize(lite_dst,resize_tmp, (ImageFormat)dstFormat, srcw,
srch, dstw, dsth);
```
### imageRotate Demo
```cpp
// 方法一:
image_preprocess.imageRotate(resize_tmp, tv_out_ratote);
// 方法二:
image_preprocess.imageRotate(resize_tmp,tv_out_ratote, (ImageFormat)dstFormat, dstw, dsth, degree);
```
### imageFlip Demo
```cpp
// 方法一:
image_preprocess.imageFlip(tv_out_ratote, tv_out_flip);
// 方法二:
image_preprocess.imageFlip(tv_out_ratote, tv_out_flip, (ImageFormat)dstFormat dstw, dsth, flip_param);
```
### image2Tensor Demo
```cpp
// 方法一:
image_preprocess.image2Tensor(tv_out_flip, &dst_tensor, layout, means, scales);
// 方法二:
image_preprocess.image2Tensor(tv_out_flip, &dst_tensor,(ImageFormat)dstFormat, dstw, dsth, layout, means, scales);
```
# 调试方法
**Lite Model Debug Tool** 是用来检查Paddle-Lite框架与Paddle-Fluid框架运行时tensor(包括variable与weight)之间diff信息的基础工具。
## 编译方法:
1. 参照 [编译安装](../installation/source_compile) 中的**full_publish**部分进行环境配置和编译。
2. 在生成的`build`目录下,执行`make lite_model_debug_tool``lite_model_debug_tool`产出在编译目录的`lite/tools/debug`目录下。
## 工作流程:
1. 运行 `/bin/bash check_model.sh --model_dir=<your_model_path> --build_root_dir=<your_cmake_root_dir> debug_cpp_stage` 获得模型在Paddle-Lite框架下的运行拓扑信息、varibles信息和weights信息。运行后拓扑信息将会存储在默认名为 `topo_file.txt` 的文件中,variables和weights信息将会存储在默认名为 `tensor_cpp.txt` 的文件中。
2. 运行 `/bin/bash check_model.sh --model_dir=<your_model_path> --build_root_dir=<your_cmake_root_dir> debug_py_stage`执行fluid框架预测以获取相同模型在fluid框架下的variable与weight信息(注意:我们使用fluid的python api运行fluid模型,因此您在运行此步之前应确保已正确安装fluid的python api)。然后debug tool将会自动比较Paddle-Lite框架输出的信息和Paddle-Fluid框架输出的信息来检查是否存在运行时diff。 执行Paddle-Fluid框架,输出的信息将会存储在默认名为 `tensor_py.txt` 的文件中,相应的diff信息将会存储在默认名为 `diff.txt`的文件中(默认情况下,只会输出执行拓扑序中第一个有diff的variable相关的信息)。
## 注意事项:
1. 输出的结果是在**执行完一次预测后**输出的相应变量/权重的最终值,因此如果您在预测过程进行过诸如变量复用/子图融合等优化方法,则相应的输出可能会出现偏差。
2. 默认情况下debug tools将以全1作为输入进行比对。
3. 默认情况下,为了保证与Paddle-Fluid框架的结果可比对,debug tool将会禁用掉所有的Paddle-Lite的优化策略。
4. Paddle-Lite框架的执行环境由与您的编译选项有关,比如您开启了LITE_WITH_ARM编译选项,那debug tool的`debug_cpp_stage`也需要在ARM平台下运行。
## Diff信息输出:
如果debug tool检测到diff信息,那么在`diff.txt`中将会输出类似以下结构信息
```c++
>>>>>>>>>>>>>>>>>>DIFF VARIABLE: dropout_0.tmp_0<<<<<<<<<<<<<<<<<<<
dropout (X:pool2d_7.tmp_0) (Mask:dropout_0.tmp_1 Out:dropout_0.tmp_0)
--------------- Tensor File info ---------------
pool2d_7.tmp_0 {1,1536,1,1} 0.749892 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0150336 0.621641 0.147099 0.636727 0.0 0.0 0.00410917 0.784708 0.0 0.0704846 0.233599 0.840123 0.239201 0.112878 0.0 0.155352 0.306906 0.0 0.0 0.860938 0.221037 0.787316 0.256585 ...
dropout_0.tmp_0 {1,1536,1,1} 0.749892 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0150336 0.621641 0.147099 0.636727 0.0 0.0 0.00410917 0.784708 0.0 0.0704846 0.233599 0.840123 0.239201 0.112878 0.0 0.155352 0.306906 0.0 0.0 0.860938 0.221037 0.787316 0.256585 ...
--------------- Fluid Tensor info ---------------
pool2d_7.tmp_0 {1,1536,1,1} 0.7498912 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.015033395 0.6216395 0.14709876 0.63672537 0.0 0.0 0.0041093696 0.7847073 0.0 0.07048465 0.23359808 0.8401219 0.23919891 0.1128789 0.0 0.1553514 0.3069055 0.0 0.0 0.8609365 0.22103554 ...
dropout_0.tmp_0 {1,1536,1,1} 0.599913 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.012026716 0.4973116 0.117679015 0.5093803 0.0 0.0 0.0032874958 0.62776583 0.0 0.056387722 0.18687847 0.67209756 0.19135913 0.090303116 0.0 0.12428112 0.2455244 0.0 0.0 0.68874925 ...
```
其中第二行为op相关信息,标明了执行哪个op出现了diff及其对应的输入输出变量名。Tensor File info为Paddle-Lite框架的输出信息,而Fluid Tensor info为Paddle-Fluid框架的相应输出信息。
示例中的`dropout_0.tmp_1`没有相应的tensor信息是因为工具检测到其在预测的后序流程中未被使用,因此不会对预测结果造成影响,从而将其自动屏蔽掉以保证输出尽量简洁。
## 其他选项:
| Option | Description |
| --------------------------- | ------------------------------------------------------------ |
| --input_file | 输入文件名,不同field以逗号分隔,相同field内以空格分隔, 只有文件中的第一行输入信息会被使用. 如果您不指定input_file,那么所有输入将会被置为1。注意:`debug_py_stage`目前不支持多field输入。 |
| --cpp_topo_file | 存储运行时拓扑信息,由`debug_cpp_stage`写入并且由`debug_py_stage`读取使用。 默认为`topo_file.txt` 。 |
| --cpp_tensor_file | 存储`debug_cpp_stage` 在运行拓扑序下的输出信息,默认为 `tensor_cpp.txt` 。 |
| --tensor_names | 如果此选项不为空,那么只输出由此选项中指定名字的variable/weight信息,名字间用逗号分隔。 |
| --tensor_output_length | 输出数据的长度,默认为全部输出。 |
| --py_threshold | 判断diff发生的阈值,默认为 `1e-5` 。 |
| --py_tensor_file | 存储`debug_py_stage` 在运行拓扑序下的输出信息,默认为`tensor_py.txt`. |
| --py_output_file | diff信息的存储文件,默认为`diff.txt`。 |
| --py_only_output_first_diff | 是否只输出运行时拓扑序中第一个有diff的var/op信息,默认为true |
您可以参考 `check_model.sh` 脚本中的代码以获得更多细节.
# Lite基于FPGA的模型预测
Paddle Lite支持基于arm的FPGA zu3/zu5/zu9的模型预测,提供armv8的交叉编译
Lite基于FPGA运行模型需要相应的FPGA驱动,目前只支持百度[Edgeboard开发板](https://ai.baidu.com/tech/hardware/deepkit)
## Lite实现FPGA简介
Lite支持FPGA作为后端硬件进行模型推理,其主要特性如下:
- Lite中FPGA的kernel(feed、fetch除外)均以FP16、NHWC的格式作为输入输出格式,所有的weights和bias仍为FP32、NCHW的格式,feed的输入和fetch的输出均为FP32、NCHW格式的数据,在提升计算速度的同时能做到用户对数据格式无感知
- 对于FPGA暂不支持的kernel,均会切回arm端运行,实现arm+FPGA混合布署运行
- 目前FPGA成本功耗都较低,Lite基于FPGA的模型性能远远好于arm端,可作为边缘设备首选硬件
## 编译
需要提前准备带有FPGAdrv.ko的FPGA开发板(如edgeboard开发板)和Lite代码
CMAKE编译选项:
- 设置`LITE_WITH_FPGA=ON``LITE_WITH_ARM=ON`
其他编译选项与ARM编译相同,可以参考[“Paddle Lite在Docker下的ARM编译”](../source_compile)
示例如下:
```shell
cmake .. \
-DWITH_GPU=OFF \
-DWITH_MKL=OFF \
-DWITH_LITE=ON \
-DLITE_WITH_CUDA=OFF \
-DLITE_WITH_X86=OFF \
-DLITE_WITH_ARM=ON \
-DLITE_WITH_OPENMP=ON \
-DLITE_WITH_LIGHT_WEIGHT_FRAMEWORK=ON \
-DWITH_TESTING=OFF \
-DLITE_WITH_FPGA=ON \
-DARM_TARGET_OS=armlinux
make publish_inference -j2
```
Lite提供FPGA编译脚本,位于lite/tools/build_FPGA.sh,在Lite根目录执行该脚本即可编译
## 运行示例
- **运行文件准备**
下面以Resnet50模型为例,介绍如何使用edgeboard开发板实现模型运行
```bash
#连接开发板,并利用screen命令启动 [本机执行]
screen /dev/cu.SLAB_USBtoUART 115200
#查看开发板ip并ssh登录到开发板,假设开发板ip为192.0.1.1 [本机执行]
ssh root@192.0.1.1
#在开发板上建立目录workspace,拷贝FPGA驱动FPGAdrv.ko到workspace目录 [开发板执行]
mkdir workspace && scp $DRIVER_PATH/FPGAdrv.ko workspace
#将Lite中编译好的测试程序拷贝到开发板workspace目录 [本机执行]
scp $LITE_ROOT/build_FPGA/lite/api/test_resnet50_FPGA root@$EDGEBOARD_IP:workspace/
#把Resnet50的模型和参数scp到开发板workspace目录 [本机执行]
scp -r $LITE_ROOT/build_FPGA/lite/third_party/install/resnet50/ root@$EDGEBOARD_IP:workspace/
#在运行模型前需要加载FPGA驱动 [开发板执行]
insmod FPGAdrv.ko
#给测试程序添加可运行权限 [开发板执行]
chmod +x test_resnet50_FPGA
```
- **使用FPGA进行模型预测**
```bash
#以下命令均在开发板上运行
#直接运行单测程序
./test_resnet50_FPGA --model_dir=resnet50
#如果需要测试性能,可以用repeats参数设置模型运行次数(如1000),同时可以设置预热次数(如10)来让硬件事先运行到稳定水平
./test_resnet50_FPGA --model_dir=resnet50 --repeats=1000 --warmup=10
```
## 如何在Code中使用
在Lite中使用FPGA与ARM相似,具体的区别如下:
- 由于fpga运行模式为fp16精度、nhwc布局,所以需要修改相应的`valid_place`
- fpga不需要device的初始化和运行模式设置
代码示例:
```cpp
lite::Predictor predictor;
std::vector<Place> valid_places(
{Place{TARGET(kFPGA), PRECISION(kFP16), DATALAYOUT(kNHWC)},Place{TARGET(kARM)});
predictor.Build(model_dir, "", "", valid_places);
auto* input_tensor = predictor.GetInput(0);
input_tensor->Resize(DDim(std::vector<DDim::value_type>({1, 3, 224, 224})));
auto* data = input_tensor->mutable_data<float>();
auto item_size = input_tensor->dims().production();
//假设设置输入数据全为1
for (int i = 0; i < item_size; i++) {
data[i] = 1;
}
predictor.Run();
auto* out = predictor.GetOutput(0);
```
# 模型量化-量化训练
本文主要介绍使用Paddle-Lite加载PaddlePaddle产出的量化模型,并进行推理执行。我们以MobileNetV1模型为示例,首先说明产出量化模型,然后说明预测部署。
## 1 简介
量化训练是基于大量训练数据,对训练好的预测模型进行量化。该方法使用模拟量化的思想,在训练阶段更新权重,实现减小量化误差。
使用条件:
* 有预训练模型
* 有较多训练数据
使用步骤:
* 产出量化模型:使用PaddlePaddle调用量化训练接口,产出量化模型
* 量化模型预测:使用PaddleLite加载量化模型进行预测推理
优点:
* 减小计算量、降低计算内存、减小模型大小
* 模型精度受量化影响小
缺点:
* 使用条件较苛刻,使用门槛稍高
建议首先使用“有校准数据训练后量化”对模型进行量化,然后使用使用量化模型进行预测。如果该量化模型的精度达不到要求,再使用“量化训练”。
## 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框架量化训练产出的模型,可以进一步加快模型在移动端的执行速度。
温馨提示:如果您是初次接触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)。
## 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 \
--prefer_int8_kernel=true
```
如前所述,量化训练后,float目录下的模型参数范围为int8,但参数数据类型仍为float32类型,这样确实没有起到模型参数压缩的效果。但是,经过model\_optimize\_tool工具优化后对应的量化参数均会以int8类型重新存储达到参数压缩的效果,且模型结构也被优化(如进行了各种operator fuse操作)。
### 在手机端准备量化模型文件
使用如下命令将mobilenet_v1_quant_opt目录下的量化模型文件导入到手机端:
```bash
adb push mobilenet_v1_quant_opt /data/local/tmp
```
### 使用mobilenetv1\_light\_api运行优化后的量化模型
参考[源码编译](../source_compile)配置编译环境后,在Paddle-Lite执行如下命令获取轻量级API的demo:
```bash
cd /Paddle-Lite/build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/demo/cxx/mobile_light
make clean && make -j
```
执行完上述命令后,可在`Paddle-Lite/build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/demo/cxx/mobile_light/`路径下看到`mobilenetv1_light_api`可执行文件。将`mobilenetv1_light_api`导入到手机端并运行量化模型推理。执行命令如下:
```bash
adb push Paddle-Lite/build.lite.android.armv8.gcc/inference_lite_lib.android.armv8/demo/cxx/mobile_light/mobilenetv1_light_api /data/local/tmp
adb shell chmod +x /data/local/tmp/mobilenetv1_light_api
adb shell /data/local/tmp/mobilenetv1_light_api \
--model_dir=/data/local/tmp/mobilenet_v1_quant_opt
```
**程序运行结果如下:**
```bash
Output dim: 1000
Output[0]: 0.000228
Output[100]: 0.000260
Output[200]: 0.000250
Output[300]: 0.000560
Output[400]: 0.000950
Output[500]: 0.000275
Output[600]: 0.005143
Output[700]: 0.002509
Output[800]: 0.000538
Output[900]: 0.000969
```
在C++中使用Paddle-Lite API的方法请猛戳[此处](../cpp_demo),用户也可参考[mobilenetv1_light_api.cc](https://github.com/PaddlePaddle/Paddle-Lite/blob/develop/lite/demo/cxx/mobile_light/mobilenetv1_light_api.cc)的代码示例。
## FAQ
**问题**:Compiled with WITH_GPU, but no GPU found in runtime
**解答**:检查本机是否支持GPU训练,如果不支持请使用CPU训练。如果在docker进行GPU训练,请使用nvidia_docker启动容器。
**问题**:Inufficient GPU memory to allocation. at [/paddle/paddle/fluid/platform/gpu_info.cc:262]
**解答**:正确设置run.sh脚本中`CUDA_VISIBLE_DEVICES`,确保显卡剩余内存大于需要内存。
# Lite基于华为NPU的模型预测
Paddle Lite是首款支持华为自研达芬奇架构NPU(Kirin 810/990 SoC搭载的NPU)的预测框架。
原理是在线分析Paddle模型,将Paddle算子转成HiAI IR后,调用HiAI IR/Builder/Runtime APIs生成并执行HiAI模型。
## 已支持的设备
- 华为nova5、nova5i pro、mate30、mate30 pro、mate30 5G、荣耀v30,以及即将推出的mate40、p40。据华为透露,今后上市的大部分手机都会搭载其自研达芬奇架构NPU。
## 已支持的模型
- MobileNetV1
- MobileNetV2
- ResNet-18/50
- ShuffleNetV2
- CycleGAN (暂时需要华为内部rom的支持)
- 百度内部业务模型(由于涉密,不方便透露具体细节)
## 已支持(或部分支持)的Paddle算子
- sigmoid
- relu
- tanh
- relu_clipped
- leaky_relu
- softsign
- hard_sigmoid
- batch_norm
- concat
- conv2d
- depthwise_conv2d
- conv2d_transpose
- dropout
- elementwise_add
- elementwise_sub
- elementwise_mul
- elementwise_div
- fusion_elementwise_add_activation
- fusion_elementwise_sub_activation
- fusion_elementwise_mul_activation
- fusion_elementwise_div_activation
- fc
- bilinear_interp
- nearest_interp
- matmul
- mul
- pad2d
- pool2d
- reduce_mean
- reshape
- reshape2
- scale
- shuffle_channel
- softmax
- split
- sqrt
- square
- transpose
- transpose2
- unsqueeze
- unsqueeze2
- instance_norm (暂时需要华为内部rom的支持)
- layer_norm (暂时需要华为内部rom的支持)
## 编译支持NPU的Paddle Lite库
- 从https://developer.huawei.com/consumer/cn/hiai/下载华为HiAI DDK后解压到任意路径(注意:华为提供了多个版本的DDK,我们需要下载针对麒麟810/990芯片HiAI Foundation开发套件,例如最新的[DDK V310版本](https://obs.cn-north-2.myhwclouds.com/hms-ds-wf/sdk/hwhiai-ddk-100.310.011.010.zip))。
- 将HiAI DDK中的ai_ddk_lib目录拷贝至Paddle Lite源码根目录后,使用[NPU编译脚本](https://github.com/PaddlePaddle/Paddle-Lite/blob/develop/lite/tools/build_npu.sh)编译full_publish和tiny_publish。
注意:以下是HiAI DDK V310版解压后的目录结构,需要将ai_ddk_lib目录拷贝至Paddle Lite源码根目录。
```shell
- app_sample
- ddk
- ai_ddk_lib
- include
- lib # for armv7
- lib64 # for armv8
- document
- tools
```
- full_publish and tiny_publish for armv8,由于HiAI DDK的armv7和armv8的so库均基于c++_shared构建,因此,建议使用c++_shared编译Paddle Lite。
```shell
$ ./lite/tools/build_npu.sh --arm_os=android --arm_abi=armv8 --arm_lang=gcc --android_stl=c++_shared full_publish
$ ./lite/tools/build_npu.sh --arm_os=android --arm_abi=armv8 --arm_lang=gcc --android_stl=c++_shared tiny_publish
```
- full_publish and tiny_publish for armv7
```shell
$ ./lite/tools/build_npu.sh --arm_os=android --arm_abi=armv7 --arm_lang=gcc --android_stl=c++_shared full_publish
$ ./lite/tools/build_npu.sh --arm_os=android --arm_abi=armv7 --arm_lang=gcc --android_stl=c++_shared tiny_publish
```
注意:为了保证编译环境一致,建议参考[源码编译](../installation/source_compile)中的Docker开发环境进行配置,然后再执行上述命令。
## 优化生成NPU模型
- model_optimize_tool工具已经支持生成NPU模型,仅需要将valid_targets设置为npu,arm即可,具体参考[模型转化方法](../user_guides/model_optimize_tool)
```shell
./model_optimize_tool --model_dir=<model_param_dir> \
--model_file=<model_path> \
--param_file=<param_path> \
--optimize_out_type=(protobuf|naive_buffer) \
--optimize_out=<output_optimize_model_dir> \
--valid_targets=npu,arm \
--prefer_int8_kernel=(true|false) \
--record_tailoring_info =(true|false)
```
- model_optimize_tool生成的模型只是标记了NPU支持的Paddle算子,并没有真正生成NPU HiAI模型,只有在执行时才会将标记的Paddle算子转成HiAI IR,最终生成并执行HiAI模型,具体实现参考PR[2576](https://github.com/PaddlePaddle/Paddle-Lite/pull/2576)
- 不同模型,不同型号(ROM版本)的华为手机,在执行阶段,由于某些Paddle算子无法完全转成HiAI IR,或目标手机的HiAI版本过低等原因,可能导致HiAI模型无法成功生成,在这种情况下,Paddle Lite会调用CPU版算子进行运算完成整个预测任务。
## 通过JAVA接口加载并执行NPU模型
- 使用方法和[Java实例](../user_guides/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++实例](../user_guides/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性能进行优化。
# Lite基于OpenCL的模型预测
Lite支持在Android系统上运行基于OpenCL的程序,目前支持Ubuntu环境下armv8、armv7的交叉编译。
## 编译
### 编译环境
1. Docker 容器环境;
2. Linux(推荐 Ubuntu 16.04)环境。
详见 **源码编译指南-环境准备** 章节。
### 编译Paddle-Lite OpenCL库范例
注:以android-armv8-opencl的目标、Docker容器的编译开发环境为例,CMake3.10,android-ndk-r17c位于`/opt/`目录下。
#### 针对 Lite 用户的编译命令(无单元测试,有编译产物)
- `arm_os`: `[android]`,目前不支持linux;
- `arm_abi`: `[armv7 | armv8]`
- `arm_lang`: `[gcc]`,目前不支持clang;
- `build_extra`: `[OFF | ON]`,编译全量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`执行编译。
```bash
# 假设当前位于处于Lite源码根目录下
# 导入NDK_ROOT变量,注意检查您的安装目录若与本示例不同
export NDK_ROOT=/opt/android-ndk-r17c
# 删除上一次CMake自动生成的.h文件
rm ./lite/api/paddle_use_kernels.h
rm ./lite/api/paddle_use_ops.h
# 根据指定编译参数编译
./lite/tools/build.sh \
--arm_os=android \
--arm_abi=armv8 \
--arm_lang=gcc \
--build_extra=OFF \
--build_cv=OFF \
--android_stl=c++_shared \
opencl
```
#### 针对 Lite 开发者的编译命令(有单元测试,编译产物)
注:调用`./lite/tools/ci_build.sh`执行编译,该命令会编译armv7和armv8的opencl库。虽然有编译产物,但因编译单元测试,编译产物包体积可能较大,不推荐使用。
```bash
# 假设当前位于处于Lite源码根目录下
# 导入NDK_ROOT变量,注意检查您的安装目录若与本示例不同
export NDK_ROOT=/opt/android-ndk-r17c
# 删除上一次CMake自动生成的.h文件
rm ./lite/api/paddle_use_kernels.h
rm ./lite/api/paddle_use_ops.h
# 根据指定编译参数编译
./lite/tools/ci_build.sh \
--arm_os=android \
--arm_abi=armv8 \
--arm_lang=gcc \
build_test_arm_opencl
```
### 编译产物说明
编译产物位于`build.lite.android.armv8.gcc.opencl`下的`inference_lite_lib.android.armv8.opencl`文件夹内,这里仅罗列关键产物:
- `cxx`:该目录是编译目标的C++的头文件和库文件;
- `demo`:该目录包含了两个demo,用来调用使用`libpaddle_api_full_bundled.a``libpaddle_api_light_bundled.a`,分别对应`mobile_full``mobile_light`文件夹。编译对应的demo仅需在`mobile_full``mobile_light`
- `mobile_full`:使用cxx config,可直接加载fluid模型,若使用OpenCL需要在`mobilenetv1_full_api.cc`代码里开启`DEMO_USE_OPENCL`的宏,详细见代码注释;
- `mobile_light`:使用mobile config,只能加载`model_optimize_tool`优化过的模型。
注:`opencl`实现的相关kernel已经打包到动态库中。
```bash
.
|-- cxx
| |-- include
| | |-- paddle_api.h
| | |-- paddle_image_preprocess.h
| | |-- paddle_lite_factory_helper.h
| | |-- paddle_place.h
| | |-- paddle_use_kernels.h
| | |-- paddle_use_ops.h
| | `-- paddle_use_passes.h
| `-- lib
| |-- libpaddle_api_full_bundled.a
| |-- libpaddle_api_light_bundled.a
| |-- libpaddle_full_api_shared.so
| `-- libpaddle_light_api_shared.so
`-- demo
`-- cxx
|-- Makefile.def
|-- README.md
|-- include
| |-- paddle_api.h
| |-- paddle_lite_factory_helper.h
| |-- paddle_place.h
| |-- paddle_use_kernels.h
| |-- paddle_use_ops.h
| `-- paddle_use_passes.h
|-- mobile_full
| |-- Makefile
| `-- mobilenetv1_full_api.cc
`-- mobile_light
|-- Makefile
`-- mobilenetv1_light_api.cc
```
调用`libpaddle_api_full_bundled.a``libpaddle_api_light_bundled.a`见下一部分运行示例。
## 运行示例
下面以android、ARMv8、gcc的环境为例,介绍3个示例,分别如何在手机上执行基于OpenCL的ARM GPU推理过程。
### 运行示例1: 编译产物demo示例
```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
# 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
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"
```
**注:** `GLOG_v`是指定需要显示VLOG的日志级别,默认为0。权重参数会在第一次运行时加载,所以第一次执行时间略长。一般将warmup的值设为10,repeats值设为多次。
### 运行示例2: test_mobilenetv1单元测试
- **运行文件准备**
```bash
# 在/data/local/tmp目录下创建OpenCL文件目录
adb shell mkdir -p /data/local/tmp/opencl
# 将mobilenet_v1的模型文件推送到/data/local/tmp/opencl目录下
adb shell mkdir -p /data/local/tmp/opencl/mobilenet_v1
adb push build.lite.android.armv8.gcc.opencl/third_party/install/mobilenet_v1/* /data/local/tmp/opencl/mobilenet_v1/
# 将OpenCL单元测试程序test_mobilenetv1,推送到/data/local/tmp/opencl目录下
adb push build.lite.android.armv8.gcc.opencl/lite/api/test_mobilenetv1 /data/local/tmp/opencl
```
- **执行OpenCL推理过程**
```bash
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/ \
--warmup=10 \
--repeats=100"
```
### 运行示例3: test_layout_opencl单元测试
```bash
adb shell mkdir -p /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代码:
1. [./lite/demo/cxx/mobile_light/mobilenetv1_light_api.cc](https://github.com/PaddlePaddle/Paddle-Lite/blob/develop/lite/demo/cxx/mobile_light/mobilenetv1_light_api.cc);
2. [./lite/demo/cxx/mobile_full/mobilenetv1_full_api.cc](https://github.com/PaddlePaddle/Paddle-Lite/blob/develop/lite/demo/cxx/mobile_full/mobilenetv1_full_api.cc).
注:这里给出的链接会跳转到线上最新develop分支的代码,很可能与您本地的代码存在差异,建议参考自己本地位于`lite/demo/cxx/`目录的代码,查看如何使用。
**NOTE:** 对OpenCL的支持还在持续开发中。
# 模型量化-无校准数据训练后量化
本文首先简单介绍无校准数据训练后量化,然后说明产出量化模型,最好阐述量化模型预测。
## 1 简介
无校准数据训练后量化,将模型中特定OP的权重从FP32类型量化成INT8/16类型,可以减小预测模型的大小。使用该量化模型预测,首先将INT8/16类型的权重反量化成FP32类型,然后再进行预测。
使用条件:
* 有训练好的预测模型
使用步骤:
* 产出量化模型:使用PaddlePaddle调用无校准数据训练后量化接口,产出量化模型
* 量化模型预测:使用PaddleLite加载量化模型进行预测推理
优点:
* 权重量化成INT16类型,模型精度不受影响,模型大小为原始的1/2
* 权重量化成INT8类型,模型精度会受到影响,模型大小为原始的1/4
缺点:
* 暂无
## 2 产出量化模型
大家可以使用PaddlePaddle调用无校准数据训练后量化接口,得到量化模型。
### 2.1 安装PaddlePaddle
参考PaddlePaddle[官网](https://www.paddlepaddle.org.cn/install/quick),安装PaddlePaddle CPU/GPU 1.7版本。
### 2.2 准备模型
准备已经训练好的FP32预测模型,即 `save_inference_model()` 保存的模型。
### 2.3 调用无校准数据训练后量化
对于调用无校准数据训练后量化,首先给出一个例子。
```python
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=8,
quantizable_op_type=['conv2d', 'depthwise_conv2d', 'mul'])
```
对于调用无校准数据训练后量化,以下对api接口进行详细介绍。
```python
class WeightQuantization(model_dir, model_filename=None, params_filename=None)
```
参数说明如下:
* model_dir(str):待量化模型的路径,其中保存模型文件和权重文件。
* model_filename(str, optional):待量化模型的模型文件名,如果模型文件名不是`__model__`,则需要使用model_filename设置模型文件名。
* 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)
```
参数说明如下:
* 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。
## 3 量化模型预测
首先,使用PaddleLite提供的模型转换工具(model_optimize_tool)将量化模型转换成移动端预测的模型,然后加载转换后的模型进行预测部署。
### 3.1 模型转换
参考[模型转换](../user_guides/model_optimize_tool)准备模型转换工具,建议从Release页面下载。
参考[模型转换](../user_guides/model_optimize_tool)使用模型转换工具。
因为该模型会将量化的权重反量化,然后实际加载并执行FP32预测模型,所以opt命令的输入参数--prefer_int8_kernel不需要设置为true,同时其他参数按照实际情况参考文档设置。
比如在安卓手机ARM端进行预测,模型转换的命令为:
```bash
./opt --model_dir=./mobilenet_v1_quant \
--optimize_out_type=naive_buffer \
--optimize_out=mobilenet_v1_quant_opt \
--valid_targets=arm
```
### 3.2 量化模型预测
和FP32模型一样,转换后的量化模型可以在Android/IOS APP中加载预测,建议参考[C++ Demo](../user_guides/cpp_demo)[Java Demo](../user_guides/java_demo)[Android/IOS Demo](../user_guides/android_ios_app_demo)
# 模型量化-有校准数据训练后量化
本文首先简单介绍有校准数据训练后量化,然后说明产出量化模型、量化模型预测,最后给出一个使用示例。
如果想快速上手,大家可以先参考使用示例,再查看详细使用方法。
## 1 简介
有校准数据训练后量化,使用少量校准数据计算量化因子,可以快速得到量化模型。使用该量化模型进行预测,可以减少计算量、降低计算内存、减小模型大小。
有校准数据训练后量化中,有两种计算量化因子的方法,非饱和量化方法和饱和量化方法。非饱和量化方法计算整个Tensor的绝对值最大值`abs_max`,将其映射为127。饱和量化方法使用KL散度计算一个合适的阈值`T` (`0<T<mab_max`),将其映射为127。一般而言,待量化Op的权重采用非饱和量化方法,待量化Op的激活(输入和输出)采用饱和量化方法 。
使用条件:
* 有训练好的预测模型
* 有少量校准数据,比如100~500张图片
使用步骤:
* 产出量化模型:使用PaddlePaddle或者PaddleSlim调用有校准数据训练后量化接口,产出量化模型
* 量化模型预测:使用PaddleLite加载量化模型进行预测推理
优点:
* 减小计算量、降低计算内存、减小模型大小
* 不需要大量训练数据
* 快速产出量化模型,简单易用
缺点:
* 对少部分的模型,尤其是计算量小、精简的模型,量化后精度可能会受到影响
## 2 产出量化模型
大家可以使用PaddlePaddle或者PaddleSlim调用有校准数据训练后量化接口,得到量化模型。本文主要介绍使用PaddlePaddle产出量化模型,使用PaddleSlim可以参考[文档](https://github.com/PaddlePaddle/models/tree/develop/PaddleSlim)
### 2.1 安装PaddlePaddle
参考PaddlePaddle[官网](https://www.paddlepaddle.org.cn/install/quick),安装PaddlePaddle CPU/GPU 1.7版本。
### 2.2 准备模型和校准数据
准备已经训练好的FP32预测模型,即 `save_inference_model()` 保存的模型。
准备校准数据集,校准数据集应该是测试集/训练集中随机挑选的一部分,量化因子才会更加准确。对常见的视觉模型,建议校准数据的数量为100~500张图片。
### 2.3 配置校准数据生成器
有校准数据训练后量化内部使用异步数据读取的方式读取校准数据,大家只需要根据模型的输入,配置读取数据的sample_generator。sample_generator是Python生成器,**必须每次返回单个样本数据**,会用作`DataLoader.set_sample_generator()`的数据源。
建议参考[异步数据读取文档](https://www.paddlepaddle.org.cn/documentation/docs/zh/user_guides/howto/prepare_data/use_py_reader.html)和本文示例,学习如何配置校准数据生成器。
### 2.4 调用有校准数据训练后量化
对于调用有校准数据训练后量化,首先给出一个例子,让大家有个直观了解。
```python
import paddle.fluid as fluid
from paddle.fluid.contrib.slim.quantization import PostTrainingQuantization
exe = fluid.Executor(fluid.CPUPlace())
model_dir = path/to/fp32_model_params
# set model_filename as None when the filename is __model__,
# otherwise set it as the real filename
model_filename = None
# set params_filename as None when all parameters were saved in
# separate files, otherwise set it as the real filename
params_filename = None
save_model_path = path/to/save_model_path
# prepare the sample generator according to the model, and the
# sample generator must return a sample every time. The reference
# document: https://www.paddlepaddle.org.cn/documentation/docs/zh
# /user_guides/howto/prepare_data/use_py_reader.html
sample_generator = your_sample_generator
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)
```
对于调用有校准数据训练后量化,以下对接口进行详细介绍。
``` 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,会将采样数据保存到该文件中。量化完成后,该文件中的临时文件会自动删除。
```python
PostTrainingQuantization.quantize()
```
调用上述接口开始训练后量化。根据校准数据数量、模型的大小和量化op类型不同,训练后量化需要的时间也不一样。比如使用ImageNet2012数据集中100图片对`MobileNetV1`进行训练后量化,花费大概1分钟。
```python
PostTrainingQuantization.save_quantized_model(save_model_path)
```
调用上述接口保存训练后量化模型,其中save_model_path为保存的路径。
训练后量化支持部分量化功能:
* 方法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进行量化
```
## 3 量化模型预测
首先,使用PaddleLite提供的模型转换工具(model_optimize_tool)将量化模型转换成移动端预测的模型,然后加载转换后的模型进行预测部署。
### 3.1 模型转换
参考[模型转换](../user_guides/model_optimize_tool)准备模型转换工具,建议从Release页面下载。
参考[模型转换](../user_guides/model_optimize_tool)使用模型转换工具。注意opt命令的输入参数--prefer_int8_kernel必须设置为true,其他参数按照实际情况参考文档设置。比如在安卓手机ARM端进行预测,模型转换的命令为:
```bash
./opt --model_dir=./mobilenet_v1_quant \
--optimize_out_type=naive_buffer \
--optimize_out=mobilenet_v1_quant_opt \
--valid_targets=arm \
--prefer_int8_kernel=true
```
### 3.2 量化模型预测
和FP32模型一样,转换后的量化模型可以在Android/IOS APP中加载预测,建议参考[C++ Demo](../user_guides/cpp_demo)[Java Demo](../user_guides/java_demo)[Android/IOS Demo](../user_guides/android_ios_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。
# 支持OP列表
## Ops (共计158个算子)
### Basic Operators (默认编译的算子)
- affine_channel
- arg_max
- batch_norm
- bilinear_interp
- box_coder
- calib
- cast
- concat
- conv2d
- conv2d_transpose
- density_prior_box
- depthwise_conv2d
- dropout
- elementwise_add
- elementwise_div
- elementwise_max
- elementwise_mul
- elementwise_sub
- exp
- expand
- fake_channel_wise_dequantize_max_abs
- fake_dequantize_max_abs
- fake_quantize_abs_max
- fake_quantize_dequantize_moving_average_abs_max
- fake_quantize_moving_average_abs_max
- fake_quantize_range_abs_max
- fc
- feed
- fetch
- fill_constant
- fill_constant_batch_size_like
- flatten
- flatten2
- floor
- fusion_elementwise_add_activation
- fusion_elementwise_div_activation
- fusion_elementwise_max_activation
- fusion_elementwise_mul_activation
- fusion_elementwise_sub_activation
- gelu
- grid_sampler
- hard_sigmoid
- instance_norm
- io_copy
- io_copy_once
- layout
- leaky_relu
- log
- matmul
- mean
- mul
- multiclass_nms
- nearest_interp
- pad2d
- pool2d
- prelu
- prior_box
- range
- reduce_mean
- relu
- relu6
- relu_clipped
- reshape
- reshape2
- rsqrt
- scale
- search_fc
- sequence_topk_avg_pooling
- shuffle_channel
- sigmoid
- slice
- softmax
- softsign
- split
- sqrt
- square
- squeeze
- squeeze2
- stack
- subgraph
- swish
- tanh
- transpose
- transpose2
- unsqueeze
- unsqueeze2
- yolo_box
### Extra Operators (打开 `--build_extra=ON`开关才会编译)
- anchor_generator
- assign
- assign_value
- attention_padding_mask
- axpy
- beam_search
- beam_search_decode
- box_clip
- calib_once
- collect_fpn_proposals
- conditional_block
- crop
- decode_bboxes
- distribute_fpn_proposals
- equal
- gather
- generate_proposals
- graph_op
- greater_equal
- greater_than
- gru
- gru_unit
- im2sequence
- increment
- is_empty
- layer_norm
- layout_once
- less_equal
- less_than
- lod_reset
- logical_and
- logical_not
- logical_or
- logical_xor
- lookup_table
- lookup_table_v2
- lrn
- match_matrix_tensor
- merge_lod_tensor
- negative
- norm
- not_equal
- power
- read_from_array
- reduce_max
- reduce_prod
- reduce_sum
- roi_align
- search_aligned_mat_mul
- search_attention_padding_mask
- search_grnn
- search_group_padding
- search_seq_arithmetic
- search_seq_depadding
- search_seq_fc
- search_seq_softmax
- sequence_arithmetic
- sequence_concat
- sequence_expand
- sequence_expand_as
- sequence_pool
- sequence_reshape
- sequence_reverse
- sequence_softmax
- shape
- split_lod_tensor
- top_k
- uniform_random
- var_conv_2d
- while
- write_to_array
## Kernels
### Host kernels
- feed
- fetch
- flatten
- flatten2
- multiclass_nms
- reshape
- reshape2
### ARM kernels
- affine_channel
- anchor_generator
- arg_max
- assign
- assign_value
- axpy
- batch_norm
- beam_search
- beam_search_decode
- bilinear_interp
- box_clip
- box_coder
- cast
- collect_fpn_proposals
- concat
- conditional_block
- conv2d
- conv2d_transpose
- crop
- decode_bboxes
- density_prior_box
- depthwise_conv2d
- distribute_fpn_proposals
- dropout
- elementwise_add
- elementwise_div
- elementwise_max
- elementwise_mul
- elementwise_sub
- equal
- exp
- expand
- fc
- fill_constant
- fill_constant_batch_size_like
- floor
- fusion_elementwise_add_activation
- fusion_elementwise_div_activation
- fusion_elementwise_max_activation
- fusion_elementwise_mul_activation
- fusion_elementwise_sub_activation
- gather
- generate_proposals
- greater_equal
- greater_than
- gru
- gru_unit
- hard_sigmoid
- im2sequence
- increment
- instance_norm
- is_empty
- layer_norm
- layout
- layout_once
- leaky_relu
- less_equal
- less_than
- lod_reset
- log
- logical_and
- logical_not
- logical_or
- logical_xor
- lookup_table
- lookup_table_v2
- lrn
- matmul
- merge_lod_tensor
- mul
- nearest_interp
- negative
- norm
- not_equal
- pad2d
- pool2d
- power
- prelu
- prior_box
- range
- read_from_array
- reduce_max
- reduce_mean
- reduce_prod
- relu
- relu6
- relu_clipped
- roi_align
- rsqrt
- scale
- sequence_expand
- sequence_pool
- sequence_softmax
- shape
- shuffle_channel
- sigmoid
- slice
- softmax
- split
- split_lod_tensor
- squeeze
- squeeze2
- stack
- swish
- tanh
- top_k
- transpose
- transpose2
- unsqueeze
- unsqueeze2
- while
- write_to_array
- yolo_box
### X86 kernels
- batch_norm
- cast
- concat
- conv2d
- depthwise_conv2d
- dropout
- elementwise_add
- elementwise_sub
- fc
- fill_constant_batch_size_like
- gather
- gelu
- gru
- layer_norm
- match_matrix_tensor
- matmul
- mul
- pool2d
- reduce_sum
- relu
- reshape
- reshape2
- scale
- search_aligned_mat_mul
- search_attention_padding_mask
- search_fc
- search_grnn
- search_group_padding
- search_seq_arithmetic
- search_seq_depadding
- search_seq_fc
- search_seq_softmax
- sequence_arithmetic
- sequence_concat
- sequence_expand_as
- sequence_pool
- sequence_reverse
- sequence_topk_avg_pooling
- shape
- slice
- softmax
- softsign
- square
- squeeze
- squeeze2
- stack
- tanh
- transpose
- transpose2
- var_conv_2d
### CUDA kernels
- attention_padding_mask
- bilinear_interp
- calib
- concat
- conv
- dropout
- elementwise_add
- fusion_elementwise_add_activation
- fusion_elementwise_mul_activation
- elementwise_mul
- feed
- io_copy
- layout
- layout_once
- leaky_relu
- lookup_table
- match_matrix_tensor
- mul
- nearest_interp
- pool2d
- relu
- scale
- search_aligned_mat_mul
- search_fc
- search_grnn
- search_group_padding
- search_seq_depadding
- search_seq_fc
- sequence_arithmetic
- sequence_concat
- sequence_pool
- sequence_reverse
- sequence_topk_avg_pooling
- softmax
- transpose
- var_conv_2d
- yolo_box
### OpenCL kernels
- conv2d
- depthwise_conv2d
- elementwise_add
- fc
- fusion_elementwise_add_activation
- layout
- layout_once
- io_copy
- io_copy_once
- mul
- pool2d
- relu
# 测试工具
Basic profiler 用于 CPU 上kernel 耗时的统计。
## 开启方法:
参照 [编译安装](../installation/source_compile) 中的**full_publish**部分进行环境配置,在 cmake 时添加 `-DLITE_WITH_PROFILE=ON` ,就可以开启相应支持。
## 使用示例:
在模型执行完毕后,会自动打印类似如下 profiler 的日志
```
kernel average min max count
feed/def/1/4/2 0 0 0 1
conv2d/def/4/1/1 1175 1175 1175 1
conv2d/def/4/1/1 1253 1253 1253 1
depthwise_conv2d/def/4/1/1 519 519 519 1
conv2d/def/4/1/1 721 721 721 1
elementwise_add/def/4/1/1 18 18 18 1
conv2d/def/4/1/1 2174 2174 2174 1
depthwise_conv2d/def/4/1/1 380 380 380 1
conv2d/def/4/1/1 773 773 773 1
elementwise_add/def/4/1/1 2 2 2 1
conv2d/def/4/1/1 1248 1248 1248 1
depthwise_conv2d/def/4/1/1 492 492 492 1
conv2d/def/4/1/1 1150 1150 1150 1
elementwise_add/def/4/1/1 33 33 33 1
elementwise_add/def/4/1/1 3 3 3 1
conv2d/def/4/1/1 1254 1254 1254 1
depthwise_conv2d/def/4/1/1 126 126 126 1
```
# 通过 X2Paddle 转换模型
[X2Paddle](https://github.com/PaddlePaddle/X2Paddle)支持将Caffe/TensorFlow模型转换为PaddlePaddle模型。目前X2Paddle支持的模型参考[x2paddle_model_zoo](https://github.com/PaddlePaddle/X2Paddle/blob/develop/x2paddle_model_zoo.md)
## 安装
```
pip install x2paddle
```
安装最新版本,可使用如下安装方式
```
pip install git+https://github.com/PaddlePaddle/X2Paddle.git@develop
```
## 使用
### Caffe
```
x2paddle --framework caffe \
--prototxt model.proto \
--weight model.caffemodel \
--save_dir paddle_model
```
### TensorFlow
```
x2paddle --framework tensorflow \
--model model.pb \
--save_dir paddle_model
```
## 转换结果说明
在指定的`save_dir`下生成两个目录
1. inference_model : 模型结构和参数均序列化保存的模型格式
2. model_with_code : 保存了模型参数文件和模型的python代码
## 问题反馈
X2Paddle使用时存在问题时,欢迎您将问题或Bug报告以[Github Issues](https://github.com/PaddlePaddle/X2Paddle/issues)的形式提交给我们,我们会实时跟进。
# X2Paddle 支持模型列表
## 多框架支持
|模型 | caffe | tensorflow | onnx |
|---|---|---|---|
|mobilenetv1 | Y | Y | |
|mobilenetv2 | Y | Y | Y |
|resnet18 | Y | Y | |
|resnet50 | Y | Y | Y |
|mnasnet | Y | Y | |
|efficientnet | Y | Y | Y |
|squeezenetv1.1 | Y | Y | Y |
|shufflenet | Y | Y | |
|mobilenet_ssd | Y | Y | |
|mobilenet_yolov3 | | Y | |
|inceptionv4 | | | |
|mtcnn | Y | Y | |
|facedetection | Y | | |
|unet | Y | Y | |
|ocr_attention | | | |
|vgg16 | | | |
# 使用X86预测库
Paddle-Lite 支持在Docker或Linux环境编译x86预测库。环境搭建参考[环境准备](../installation/source_compile)
(注意:非docker Linux环境需要是Ubuntu16.04)
## 编译
1、 下载代码
```bash
git clone https://github.com/PaddlePaddle/Paddle-Lite.git
# 切换到release分支
git checkout release/v2.3
```
2、 源码编译
```bash
cd Paddle-Lite
./lite/tools/build.sh x86
```
## 编译结果说明
x86编译结果位于 `build.lite.x86/inference_lite_lib`
**具体内容**说明:
1、 `bin`文件夹:可执行工具文件 `test_model_bin`
2、 `cxx`文件夹:包含c++的库文件与相应的头文件
- `include` : 头文件
- `lib` : 库文件
- 打包的静态库文件:
- `libpaddle_api_full_bundled.a` :包含 full_api 和 light_api 功能的静态库
- `libpaddle_api_light_bundled.a` :只包含 light_api 功能的静态库
- 打包的动态态库文件:
- `libpaddle_full_api_shared.so` :包含 full_api 和 light_api 功能的动态库
- `libpaddle_light_api_shared.so`:只包含 light_api 功能的动态库
3、 `third_party` 文件夹:第三方库文件
## x86预测API使用示例
1、我们提供Linux环境下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.sh`为编译的脚本。
2、demo内容与使用方法
``` bash
# 1、编译
sh build.sh
```
编译结果为当前目录下的 `mobilenet_full_api `
``` bash
# 2、执行预测
mobilenet_full_api mobilenet_v1
```
`mobilenet_v1`为当前目录下的模型路径,`mobilenet_full_api`为第一步编译出的可执行文件。
3、示例源码`mobilenet_full_api.cc`
```c++
#include <iostream>
#include <vector>
#include "paddle_api.h"
using namespace paddle::lite_api; // NOLINT
int64_t ShapeProduction(const shape_t& shape) {
int64_t res = 1;
for (auto i : shape) res *= i;
return res;
}
void RunModel(std::string model_dir) {
// 1. Create CxxConfig
CxxConfig config;
config.set_model_dir(model_dir);
config.set_valid_places({
Place{TARGET(kX86), PRECISION(kFloat)},
Place{TARGET(kHost), PRECISION(kFloat)}
});
// 2. Create PaddlePredictor by CxxConfig
std::shared_ptr<PaddlePredictor> predictor =
CreatePaddlePredictor<CxxConfig>(config);
// 3. Prepare input data
std::unique_ptr<Tensor> input_tensor(std::move(predictor->GetInput(0)));
input_tensor->Resize({1, 3, 224, 224});
auto* data = input_tensor->mutable_data<float>();
for (int i = 0; i < ShapeProduction(input_tensor->shape()); ++i) {
data[i] = 1;
}
// 4. Run predictor
predictor->Run();
// 5. Get output
std::unique_ptr<const Tensor> output_tensor(
std::move(predictor->GetOutput(0)));
std::cout << "Output shape " << output_tensor->shape()[1] << std::endl;
for (int i = 0; i < ShapeProduction(output_tensor->shape()); i += 100) {
std::cout << "Output[" << i << "]: " << output_tensor->data<float>()[i]
<< std::endl;
}
}
int main(int argc, char** argv) {
if (argc < 2) {
std::cerr << "[ERROR] usage: ./" << argv[0] << " naive_buffer_model_dir\n";
exit(1);
}
std::string model_dir = argv[1];
RunModel(model_dir);
return 0;
}
```
此差异已折叠。
此差异已折叠。
此差异已折叠。
# Benchmark 数据
可以参考[benchmark_tools](benchmark_tools),推荐**一键benchmark**
## 测试环境
* 测试模型
* fp32模型
* mobilenet_v1
* mobilenet_v2
* squeezenet_v1.1
* mnasnet
* shufflenet_v2
* int8模型
* mobilenet_v1
* mobilenet_v2
* 测试机器(android ndk ndk-r17c)
* 骁龙855
* xiaomi mi9, snapdragon 855
* 4xA76(1@2.84GHz + 3@2.4GHz) + 4xA55@1.78GHz
* 骁龙845
* xiaomi mi8, 845
* 2.8GHz(大四核),1.7GHz(小四核)
* 骁龙835
* xiaomi mix2, snapdragon 835
* 2.45GHz(大四核),1.9GHz(小四核)
* 麒麟970
* HUAWEI Mate10
* 测试说明
* branch: release/v2.3.0
* warmup=10, repeats=30,统计平均时间,单位是ms
* 当线程数为1时,```DeviceInfo::Global().SetRunMode```设置LITE_POWER_HIGH,否者设置LITE_POWER_NO_BIND
* 模型的输入图像的维度是{1, 3, 224, 224},输入图像的每一位数值是1
## 测试数据
### fp32模型测试数据
#### paddlepaddle model
骁龙855|armv7 | armv7 | armv7 |armv8 | armv8 |armv8
----| ---- | ---- | ---- | ---- |---- |----
threads num|1 |2 |4 |1 |2 |4
mobilenet_v1 |33.27 |19.52 |11.14 |31.72 |18.76 |10.24 |
mobilenet_v2 |29.08 |15.79 |9.25 |25.89 |14.17 |8.38 |
shufflenet_v2 |4.40 |3.09 |2.30 |4.28 |3.02 |2.35 |
squeezenet_v1.1 |19.96 |12.61 |8.76 |18.25 |11.46 |7.97 |
mnasnet |21.00 |12.54 |7.28 |19.65 |11.65 |6.96 |
骁龙845|armv7 | armv7 | armv7 |armv8 | armv8 |armv8
----| ---- | ---- | ---- | ---- |---- |----
threads num|1 |2 |4 |1 |2 |4
mobilenet_v1 |66.36 |35.97 |19.45 |62.66 |33.87 |17.85 |
mobilenet_v2 |45.86 |25.53 |14.6 |41.58 |23.24 |13.39 |
shufflenet_v2 |7.58 |4.89 |3.41 |7.44 |4.91 |3.58 |
squeezenet_v1.1 |37.15 |22.74 |13.51 |34.69 |21.27 |12.74 |
mnasnet |40.09 |21.73 |11.91 |38.19 |21.02 |12.11 |
骁龙835|armv7 | armv7 | armv7 |armv8 | armv8 |armv8
----| ---- | ---- | ---- | ---- |---- |----
threads num|1 |2 |4 |1 |2 |4
mobilenet_v1 |96.98 |53.92 |32.24 |89.31 |48.02 |27.58 |
mobilenet_v2 |67.72 |37.66 |23.82 |60.10 |34.36 |21.05 |
shufflenet_v2 |10.72 |6.62 |4.63 |10.10 |6.44 |4.63 |
squeezenet_v1.1 |53.89 |33.28 |20.73 |50.83 |32.31 |19.51 |
mnasnet |59.55 |33.53 |20.32 |56.21 |31.58 |19.06 |
#### caffe model
骁龙855|armv7 | armv7 | armv7 |armv8 | armv8 |armv8
----| ---- | ---- | ---- | ---- |---- |----
threads num|1 |2 |4 |1 |2 |4 |
mobilenet_v1 |33.36 |19.45 |11.26 |31.63 |18.74 |10.31 |
mobilenet_v2 |31.63 |19.21 |11.61 |28.34 |17.14 |10.16 |
shufflenet_v2 |4.46 |3.08 |2.32 |4.26 |2.98 |2.35 |
骁龙845|armv7 | armv7 | armv7 |armv8 | armv8 |armv8
----| ---- | ---- | ---- | ---- |---- |----
threads num|1 |2 |4 |1 |2 |4 |
mobilenet_v1 |66.32 |35.83 |19.56 |62.52 |33.79 |17.91 |
mobilenet_v2 |58.46 |32.69 |18.56 |53.72 |29.86 |16.80 |
shufflenet_v2 |7.65 |4.82 |3.46 |7.55 |4.97 |3.62 |
骁龙835|armv7 | armv7 | armv7 |armv8 | armv8 |armv8
----| ---- | ---- | ---- | ---- |---- |----
threads num|1 |2 |4 |1 |2 |4 |
mobilenet_v1 |95.38 |54.09 |32.03 |95.05 |48.33 |27.54 |
mobilenet_v2 |88.46 |48.98 |30.23 |79.28 |44.64 |27.10 |
shufflenet_v2 |10.07 |6.51 |4.61 |10.31 |6.50 |4.66 |
#### int8量化模型测试数据
骁龙855|armv7 | armv7 | armv7 |armv8 | armv8 |armv8
----| ---- | ---- | ---- | ---- |---- |----
threads num|1 |2 |4 |1 |2 |4 |
mobilenet_v1 |36.80 |21.58 |11.12 | 14.01 |8.13 |4.32 |
mobilenet_v2 |28.72 |19.08 |12.49 | 17.24 |11.55 |7.82 |
骁龙835|armv7 | armv7 | armv7 |armv8 | armv8 |armv8
----| ---- | ---- | ---- | ---- |---- |----
threads num|1 |2 |4 |1 |2 |4 |
mobilenet_v1 |60.76 |32.25 |16.66 |56.57 |29.84 |15.24 |
mobilenet_v2 |49.38 |31.10 |22.07 |47.52 |28.18 |19.24 |
麒麟970|armv7 | armv7 | armv7 |armv8 | armv8 |armv8
----| ---- | ---- | ---- | ---- |---- |----
threads num|1 |2 |4 |1 |2 |4 |
mobilenet_v1 |65.95 |34.39 |18.68 |60.86 |30.98 |16.31 |
mobilenet_v2 |68.87 |39.39 |24.43 |65.57 |37.31 |20.87 |
此差异已折叠。
此差异已折叠。
此差异已折叠。
# 开发基础须知
可以参考 [Paddle 开发者文档](https://www.paddlepaddle.org.cn/documentation/docs/zh/1.5/advanced_usage/development/contribute_to_paddle/local_dev_guide.html)
## 提交PR
需要在 commit message 里加上 `test=develop` 才能触发 CI
## 版本发布检查清单
1. 所有 feature 梳理,确认状态
2. 所有 QA 测试结果梳理,确认版本可靠
3. Release note 确认 review 通过
4. 确认需要 release 的 binary 编译完毕
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
# FAQ
## 问题或建议可以发Issue,为加快问题解决效率,可先检索是否有类似问题,我们也会及时解答!
## 欢迎加入Paddle-Lite百度官方QQ群:696965088
## 1. 在Host端采用交叉编译方式编译PaddleLite,将编译后的libpaddle_light_api_shared.so和可执行程序放到板卡上运行,出现了如下图所示的错误,怎么解决?
![host_target_compiling_env_miss_matched](https://user-images.githubusercontent.com/9973393/75761527-31b8b700-5d74-11ea-8a9a-0bc0253ee003.png)
- 原因是Host端的交叉编译环境与Target端板卡的运行环境不一致,导致libpaddle_light_api_shared.so链接的GLIBC库高于板卡环境的GLIBC库。目前有四种解决办法(为了保证编译环境与官方一致,推荐第一种方式):1)在Host端,参考[源码编译](../installation/source_compile)中的Docker方式重新编译libpaddle_light_api_shared.so;2)在Host端,使用与Target端版本一致的ARM GCC和GLIBC库重新编译libpaddle_light_api_shared.so;3)在Target端板卡上,参考[源码编译](../installation/source_compile)中的ARM Linux本地编译方式重新编译libpaddle_light_api_shared.so;4)在Target端板卡上,将GLIBC库升级到和Host端一致的版本,即GLIBC2.27。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
sphinx
recommonmark
sphinx_markdown_tables
sphinx_rtd_theme
# Android/IOS APP demo
请参考[Paddle-Lite-Demo](https://github.com/PaddlePaddle/Paddle-Lite-Demo)
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册