From 788257151b74b904d2037f5cdbced4dff7c7d9ac Mon Sep 17 00:00:00 2001 From: andyjpaddle <87074272+andyjpaddle@users.noreply.github.com> Date: Tue, 19 Apr 2022 16:20:35 +0800 Subject: [PATCH] add tipc for cpp infer (#5499) --- .../Step6/test_tipc/README.md | 2 +- .../mobilenet_v3_small/inference_cpp.txt | 14 + .../test_tipc/docs/test_inference_cpp.md | 245 +++++++++++ .../Step6/test_tipc/test_inference_cpp.sh | 74 ++++ tutorials/tipc/README.md | 2 +- tutorials/tipc/infer_cpp/README.md | 9 + tutorials/tipc/infer_cpp/test_infer_cpp.md | 379 +++++++++++++++++- 7 files changed, 719 insertions(+), 6 deletions(-) create mode 100755 tutorials/mobilenetv3_prod/Step6/test_tipc/configs/mobilenet_v3_small/inference_cpp.txt create mode 100644 tutorials/mobilenetv3_prod/Step6/test_tipc/docs/test_inference_cpp.md create mode 100644 tutorials/mobilenetv3_prod/Step6/test_tipc/test_inference_cpp.sh diff --git a/tutorials/mobilenetv3_prod/Step6/test_tipc/README.md b/tutorials/mobilenetv3_prod/Step6/test_tipc/README.md index e0f0b38e..238c3802 100644 --- a/tutorials/mobilenetv3_prod/Step6/test_tipc/README.md +++ b/tutorials/mobilenetv3_prod/Step6/test_tipc/README.md @@ -60,7 +60,7 @@ test_tipc - 更多部署方式测试(coming soon): - [Linux GPU/CPU PYTHON 服务化部署测试](docs/test_serving_infer_python.md) - [Linux GPU/CPU C++ 服务化部署测试] - - [Linux GPU/CPU C++ 推理测试] + - [Linux GPU/CPU C++ 推理测试](docs/test_inference_cpp.md) - [Paddle.js 部署测试] - [Paddle2ONNX 测试](docs/test_paddle2onnx.md) - [Lite ARM CPU 部署测试](docs/test_lite_infer_cpp_arm_cpu.md) diff --git a/tutorials/mobilenetv3_prod/Step6/test_tipc/configs/mobilenet_v3_small/inference_cpp.txt b/tutorials/mobilenetv3_prod/Step6/test_tipc/configs/mobilenet_v3_small/inference_cpp.txt new file mode 100755 index 00000000..d7eafc9c --- /dev/null +++ b/tutorials/mobilenetv3_prod/Step6/test_tipc/configs/mobilenet_v3_small/inference_cpp.txt @@ -0,0 +1,14 @@ +# model load config +use_gpu 0 +gpu_id 0 +gpu_mem 4000 +cpu_math_library_num_threads 10 +use_mkldnn 1 +use_tensorrt 0 +use_fp16 0 + +# cls config +cls_model_path ./deploy/inference_cpp/mobilenet_v3_small_infer/inference.pdmodel +cls_params_path ./deploy/inference_cpp/mobilenet_v3_small_infer/inference.pdiparams +resize_short_size 256 +crop_size 224 diff --git a/tutorials/mobilenetv3_prod/Step6/test_tipc/docs/test_inference_cpp.md b/tutorials/mobilenetv3_prod/Step6/test_tipc/docs/test_inference_cpp.md new file mode 100644 index 00000000..8830f4f3 --- /dev/null +++ b/tutorials/mobilenetv3_prod/Step6/test_tipc/docs/test_inference_cpp.md @@ -0,0 +1,245 @@ +# Linux GPU/CPU C++ 推理功能测试 + +Linux GPU/CPU C++ 推理功能测试的主程序为`test_inference_cpp.sh`,可以测试基于C++预测引擎的推理功能。 + +## 1. 测试结论汇总 + +- 推理相关: + +| 算法名称 | 模型名称 | device_CPU | device_GPU | tensorrt | mkldnn | +| :----: | :----: | :----: | :----: | :----: | :----: | +| MobileNetV3 | mobilenet_v3_small | 支持 | 支持 | 支持 | 支持 | + +## 2. 测试流程 + +### 2.1 准备数据和推理模型 + +#### 2.1.1 准备数据 + +从验证集或者测试集中抽出至少一张图像,用于后续的推理过程验证。 + +#### 2.1.2 准备推理模型 + +* 如果已经训练好了模型,可以参考[模型导出](../../tools/export_model.py),导出`inference model`,用于模型预测。得到预测模型后,假设模型文件放在`inference`目录下,则目录结构如下。 + +``` +mobilenet_v3_small_infer/ +|--inference.pdmodel +|--inference.pdiparams +|--inference.pdiparams.info +``` +**注意**:上述文件中,`inference.pdmodel`文件存储了模型结构信息,`inference.pdiparams`文件存储了模型参数信息。注意两个文件的路径需要与[配置文件](../configs/mobilenet_v3_small/inference_cpp.txt)中的`cls_model_path`和`cls_params_path`参数对应一致。 + +### 2.2 准备环境 + +#### 2.2.1 运行准备 + +配置合适的编译和执行环境,其中包括编译器,cuda等一些基础库,建议安装docker环境,[参考链接](https://www.paddlepaddle.org.cn/install/quick?docurl=/documentation/docs/zh/install/docker/linux-docker.html)。 + +#### 2.2.2 编译opencv库 + +* 首先需要从opencv官网上下载Linux环境下的源码,以3.4.7版本为例,下载及解压缩命令如下: + +``` +cd deploy/inference_cpp +wget https://github.com/opencv/opencv/archive/3.4.7.tar.gz +tar -xvf 3.4.7.tar.gz +``` + +* 编译opencv,首先设置opencv源码路径(`root_path`)以及安装路径(`install_path`),`root_path`为下载的opencv源码路径,`install_path`为opencv的安装路径。在本例中,源码路径即为当前目录下的`opencv-3.4.7/`。 + +```shell +cd ./opencv-3.4.7 +export root_path=$PWD +export install_path=${root_path}/opencv3 +``` + +* 然后在opencv源码路径下,按照下面的命令进行编译。 + +```shell +rm -rf build +mkdir build +cd build + +cmake .. \ + -DCMAKE_INSTALL_PREFIX=${install_path} \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF \ + -DWITH_IPP=OFF \ + -DBUILD_IPP_IW=OFF \ + -DWITH_LAPACK=OFF \ + -DWITH_EIGEN=OFF \ + -DCMAKE_INSTALL_LIBDIR=lib64 \ + -DWITH_ZLIB=ON \ + -DBUILD_ZLIB=ON \ + -DWITH_JPEG=ON \ + -DBUILD_JPEG=ON \ + -DWITH_PNG=ON \ + -DBUILD_PNG=ON \ + -DWITH_TIFF=ON \ + -DBUILD_TIFF=ON + +make -j +make install +``` + +* `make install`完成之后,会在该文件夹下生成opencv头文件和库文件,用于后面的代码编译。 + +以opencv3.4.7版本为例,最终在安装路径下的文件结构如下所示。**注意**:不同的opencv版本,下述的文件结构可能不同。 + +``` +opencv3/ +|-- bin :可执行文件 +|-- include :头文件 +|-- lib64 :库文件 +|-- share :部分第三方库 +``` + +#### 2.2.3 下载或者编译Paddle预测库 + +* 有2种方式获取Paddle预测库,下面进行详细介绍。 + +##### 预测库源码编译 +* 如果希望获取最新预测库特性,可以从Paddle github上克隆最新代码,源码编译预测库。 +* 可以参考[Paddle预测库官网](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/05_inference_deployment/inference/build_and_install_lib_cn.html#id16)的说明,从github上获取Paddle代码,然后进行编译,生成最新的预测库。使用git获取代码方法如下。 + +```shell +git clone https://github.com/PaddlePaddle/Paddle.git +``` + +* 进入Paddle目录后,使用如下命令编译。 + +```shell +rm -rf build +mkdir build +cd build + +cmake .. \ + -DWITH_CONTRIB=OFF \ + -DWITH_MKL=ON \ + -DWITH_MKLDNN=ON \ + -DWITH_TESTING=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + -DWITH_INFERENCE_API_TEST=OFF \ + -DON_INFER=ON \ + -DWITH_PYTHON=ON +make -j +make inference_lib_dist +``` + +更多编译参数选项可以参考Paddle C++预测库官网:[https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/05_inference_deployment/inference/build_and_install_lib_cn.html#id16](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/05_inference_deployment/inference/build_and_install_lib_cn.html#id16)。 + + +* 编译完成之后,可以在`build/paddle_inference_install_dir/`文件下看到生成了以下文件及文件夹。 + +``` +build/paddle_inference_install_dir/ +|-- CMakeCache.txt +|-- paddle +|-- third_party +|-- version.txt +``` + +其中`paddle`就是之后进行C++预测时所需的Paddle库,`version.txt`中包含当前预测库的版本信息。 + +##### 直接下载安装 + +* [Paddle预测库官网](https://paddleinference.paddlepaddle.org.cn/user_guides/download_lib.html)上提供了不同cuda版本的Linux预测库,可以在官网查看并选择合适的预测库版本。 + + 以`manylinux_cuda11.1_cudnn8.1_avx_mkl_trt7_gcc8.2`版本为例,使用下述命令下载并解压: + + +```shell +wget https://paddle-inference-lib.bj.bcebos.com/2.2.2/cxx_c/Linux/GPU/x86-64_gcc8.2_avx_mkl_cuda11.1_cudnn8.1.1_trt7.2.3.4/paddle_inference.tgz + +tar -xvf paddle_inference.tgz +``` + +最终会在当前的文件夹中生成`paddle_inference/`的子文件夹,文件内容和上述的paddle_inference_install_dir一样。 + + +#### 2.2.4 编译C++预测Demo + +* 编译命令如下,其中Paddle C++预测库、opencv等其他依赖库的地址需要换成自己机器上的实际地址。 + + +```shell +sh tools/build.sh +``` + +具体地,`tools/build.sh`中内容如下。 + +```shell +OPENCV_DIR=your_opencv_dir +LIB_DIR=your_paddle_inference_dir +CUDA_LIB_DIR=your_cuda_lib_dir +CUDNN_LIB_DIR=your_cudnn_lib_dir +TENSORRT_DIR=your_tensorrt_lib_dir + +BUILD_DIR=build +rm -rf ${BUILD_DIR} +mkdir ${BUILD_DIR} +cd ${BUILD_DIR} +cmake .. \ + -DPADDLE_LIB=${LIB_DIR} \ + -DWITH_MKL=ON \ + -DDEMO_NAME=clas_system \ + -DWITH_GPU=OFF \ + -DWITH_STATIC_LIB=OFF \ + -DWITH_TENSORRT=OFF \ + -DTENSORRT_DIR=${TENSORRT_DIR} \ + -DOPENCV_DIR=${OPENCV_DIR} \ + -DCUDNN_LIB=${CUDNN_LIB_DIR} \ + -DCUDA_LIB=${CUDA_LIB_DIR} \ + +make -j +``` + +上述命令中, + +* `OPENCV_DIR`为opencv编译安装的地址(本例中为`opencv-3.4.7/opencv3`文件夹的路径); + +* `LIB_DIR`为下载的Paddle预测库(`paddle_inference`文件夹),或编译生成的Paddle预测库(`build/paddle_inference_install_dir`文件夹)的路径; + +* `CUDA_LIB_DIR`为cuda库文件地址,在docker中一般为`/usr/local/cuda/lib64`; + +* `CUDNN_LIB_DIR`为cudnn库文件地址,在docker中一般为`/usr/lib64`。 + +* `TENSORRT_DIR`是tensorrt库文件地址,在dokcer中一般为`/usr/local/TensorRT-7.2.3.4/`,TensorRT需要结合GPU使用。 + +在执行上述命令,编译完成之后,会在当前路径下生成`build`文件夹,其中生成一个名为`clas_system`的可执行文件。 + + +### 2.3 功能测试 + + +测试方法如下所示,希望测试不同的模型文件,只需更换为自己的参数配置文件,即可完成对应模型的测试。 + +```bash +bash test_tipc/test_inference_cpp.sh ${your_params_file} +``` + +以`mobilenet_v3_small`的`Linux GPU/CPU C++推理测试`为例,命令如下所示。 + +```bash +bash test_tipc/test_inference_cpp.sh test_tipc/configs/mobilenet_v3_small/inference_cpp.txt +``` + +输出结果如下,表示命令运行成功。 + +```bash +Run successfully with command - ./deploy/inference_cpp/build/clas_system test_tipc/configs/mobilenet_v3_small/inference_cpp.txt ./images/demo.jpg > ./log/infer_cpp/infer_cpp_use_cpu_use_mkldnn.log 2>&1 ! +``` + +最终log中会打印出结果,如下所示 +``` +img_file_list length: 1 +result: + class id: 8 + score: 0.9014719725 +Current image path: ./images/demo.jpg +Current time cost: 0.1409450000 s, average time cost in all: 0.1409450000 s. +``` +详细log位于`./log/infer_cpp/infer_cpp_use_cpu_use_mkldnn.log`中。 + +如果运行失败,也会在终端中输出运行失败的日志信息以及对应的运行命令。可以基于该命令,分析运行失败的原因。 diff --git a/tutorials/mobilenetv3_prod/Step6/test_tipc/test_inference_cpp.sh b/tutorials/mobilenetv3_prod/Step6/test_tipc/test_inference_cpp.sh new file mode 100644 index 00000000..ad609057 --- /dev/null +++ b/tutorials/mobilenetv3_prod/Step6/test_tipc/test_inference_cpp.sh @@ -0,0 +1,74 @@ +#!/bin/bash +source test_tipc/common_func.sh + +function func_parser_key_cpp(){ + strs=$1 + IFS=" " + array=(${strs}) + tmp=${array[0]} + echo ${tmp} +} + +function func_parser_value_cpp(){ + strs=$1 + IFS=" " + array=(${strs}) + tmp=${array[1]} + echo ${tmp} +} + +FILENAME=$1 + +dataline=$(cat ${FILENAME}) +lines=(${dataline}) + +# parser params +dataline=$(awk 'NR==1, NR==14{print}' $FILENAME) +IFS=$'\n' +lines=(${dataline}) + +# parser load config +use_gpu_key=$(func_parser_key_cpp "${lines[1]}") +use_gpu_value=$(func_parser_value_cpp "${lines[1]}") +use_mkldnn_key=$(func_parser_key_cpp "${lines[5]}") +use_mkldnn_value=$(func_parser_value_cpp "${lines[5]}") +use_tensorrt_key=$(func_parser_key_cpp "${lines[6]}") +use_tensorrt_value=$(func_parser_value_cpp "${lines[6]}") +use_fp16_key=$(func_parser_key_cpp "${lines[7]}") +use_fp16_value=$(func_parser_value_cpp "${lines[7]}") + +LOG_PATH="./log/infer_cpp" +mkdir -p ${LOG_PATH} +status_log="${LOG_PATH}/results_infer_cpp.log" + +function func_infer_cpp(){ + # inference cpp + if test $use_gpu_value -gt 0; then + if test $use_tensorrt_value -gt 0; then + if test $use_fp16_value -gt 0; then + _save_log_path="${LOG_PATH}/infer_cpp_${use_gpu_key}_${use_tensorrt_key}_${use_fp16_key}.log" + else + _save_log_path="${LOG_PATH}/infer_cpp_${use_gpu_key}_${use_tensorrt_key}.log" + fi + else + _save_log_path="${LOG_PATH}/infer_cpp_${use_gpu_key}.log" + fi + else + if test $use_mkldnn_value -gt 0; then + _save_log_path="${LOG_PATH}/infer_cpp_use_cpu_${use_mkldnn_key}.log" + else + _save_log_path="${LOG_PATH}/infer_cpp_use_cpu.log" + fi + fi + # run infer cpp + inference_cpp_cmd="./deploy/inference_cpp/build/clas_system" + inference_cpp_img="./images/demo.jpg" + infer_cpp_full_cmd="${inference_cpp_cmd} ${FILENAME} ${inference_cpp_img} > ${_save_log_path} 2>&1 " + eval $infer_cpp_full_cmd + last_status=${PIPESTATUS[0]} + status_check $last_status "${infer_cpp_full_cmd}" "${status_log}" +} + +echo "################### run test cpp inference ###################" + +func_infer_cpp diff --git a/tutorials/tipc/README.md b/tutorials/tipc/README.md index 962f5c1b..d9fb54dc 100644 --- a/tutorials/tipc/README.md +++ b/tutorials/tipc/README.md @@ -19,7 +19,7 @@ - 更多部署方式开发文档 - [Linux GPU/CPU PYTHON 服务化部署开发文档](./serving_python/README.md) - Linux GPU/CPU C++ 服务化部署开发文档 (coming soon) - - Linux GPU/CPU C++ 推理开发文档 (coming soon) + - [Linux GPU/CPU C++ 推理开发文档](./infer_cpp/README.md) - Paddle.js 部署开发文档 (coming soon) - [Paddle2ONNX 开发文档](./paddle2onnx/README.md) - [Lite ARM CPU 部署开发文档](./lite_infer_cpp_arm_cpu/README.md) diff --git a/tutorials/tipc/infer_cpp/README.md b/tutorials/tipc/infer_cpp/README.md index 576dc4ec..aab19f5c 100644 --- a/tutorials/tipc/infer_cpp/README.md +++ b/tutorials/tipc/infer_cpp/README.md @@ -38,3 +38,12 @@ Paddle Inference 是飞桨的原生推理库, 作用于服务器端和云端 参考[C++推理文档开发文档](./infer_cpp.md)得到可执行的代码。 +## 3. C++推理功能测试开发与规范 + +### 3.1 开发流程 + +具体参考[C++推理功能测试开发文档](./test_infer_cpp.md) + +### 3.2 核验点 + +具体参考[C++推理功能测试开发文档](./test_infer_cpp.md) diff --git a/tutorials/tipc/infer_cpp/test_infer_cpp.md b/tutorials/tipc/infer_cpp/test_infer_cpp.md index d8dc7821..e66544b7 100644 --- a/tutorials/tipc/infer_cpp/test_infer_cpp.md +++ b/tutorials/tipc/infer_cpp/test_infer_cpp.md @@ -2,7 +2,378 @@ # 目录 -- [1. 简介](#1---) -- [2. 基本C++推理功能测试开发](#2---) -- [3. 高级C++推理功能测试开发](#3---) -- [4. FAQ](#4---) +- [1. 简介](#1) +- [2. 命令与配置文件解析](#2) + - [2.1 命令解析](#2.1) + - [2.2 配置文件和运行命令映射解析](#2.2) +- [3. 基本C++推理功能测试开发](#3) + - [3.1 准备系统环境](#3.1) + - [3.2 准备输入数据和推理模型](#3.2) + - [3.3 准备推理所需代码](#3.3) + - [3.4 编译得到可执行代码](#3.4) + - [3.5 运行得到结果](#3.5) + - [3.6 填写配置文件](#3.6) + - [3.7 验证配置正确性](#3.7) + - [3.8 撰写说明文档](#3.8) +- [4. FAQ](#4) + + + +## 1. 简介 + +Paddle Inference 是飞桨的原生推理库, 作用于服务器端和云端,提供高性能的推理能力。相比于直接基于预训练模型进行预测,Paddle Inference可使用MKLDNN、CUDNN、TensorRT进行预测加速,从而实现更优的推理性能。 +更多关于Paddle Inference推理引擎的介绍,可以参考[Paddle Inference官网教程](https://www.paddlepaddle.org.cn/documentation/docs/zh/guides/05_inference_deployment/inference/inference_cn.html)。 +本文档主要介绍飞桨模型在 Linux GPU/CPU 下基于C++预测引擎的推理过程开发。 + + + +## 2. 命令与配置文件解析 + + + +### 2.1 命令解析 + +基于paddle inference的C++预测命令如下: +``` +run_scripts configs_path img_path +``` + +* `run_scripts`:最终编译好的可执行命令。 +* `configs_path`:设置模型路径、是否使用GPU、是否开启mkldnn、是否开启TensorRT等。 +* `img_path`:待预测的图像路径。 + + + +### 2.2 配置文件解析 +完整的`inference_cpp.txt`配置文件共有14行,包含两个方面的内容。 +* 运行环境参数配置:第1~8行 +* 模型参数配置:第10~14行 + +具体内容见[inference_cpp.txt](../../mobilenetv3_prod/Step6/test_tipc/configs/mobilenet_v3_small/inference_cpp.txt) + +配置文件中主要有以下2种类型的字段。 + +* 一行内容以空格为分隔符:该行可以被解析为`key value`的格式,需要根据实际的含义修改该行内容,下面进行详细说明。 +* 一行内容为`# xxxxx`:该行内容为注释信息,无需修改。 + +
+配置参数(点击以展开详细内容或者折叠) + + +| 行号 | 参考内容 | 含义 | key是否需要修改 | value是否需要修改 | 修改内容 | +|----|-------------------------------------|---------------|-----------|-------------|----------------------------------| +| 2 | use_gpu | 是否使用GPU | 否 | 是 | value根据是否使用GPU进行修改 | +| 3 | gpu_id | 使用的GPU卡号 | 否 | 是 | value修改为自己的GPU ID | +| 4 | gpu_mem | 显存 | 否 | 是 | value修改为自己的GPU 显存 | +| 5 | cpu_math_library_num_thread | 底层科学计算库所用线程的数量 | 否 | 是 | value修改为合适的线程数 | +| 6 | use_mkldnn | 是否使用MKLDNN加速 | 否 | 是 | value根据是否使用MKLDNN进行修改 | +| 7 | use_tensorrt | 是否使用tensorRT进行加速 | 否 | 是 | value根据是否使用tensorRT进行修改 | +| 8 | use_fp16 | 是否使用半精度浮点数进行计算,该选项仅在use_tensorrt为true时有效 | 否 | 是 | value根据在开启tensorRT时是否使用半精度进行修改| +| 11 | cls_model_path | 预测模型结构文件路径 | 否 | 是 | value修改为预测模型结构文件路径 | +| 12 | cls_params_path | 预测模型参数文件路径 | 否 | 是 | vvalue修改为预测模型参数文件路径 | +| 13 | resize_short_size | 预处理时图像缩放大小 | 否 | 是 | value修改为预处理时图像缩放大小 +| 14 | crop_size | 预处理时图像裁剪后的大小 | 否 | 是 | value修改为预处理时图像裁剪后的大小 + + +
+ + + +## 3. 基本C++推理功能测试开发 + +基于Paddle Inference的推理过程可以分为5个步骤,如下图所示。 +
+ +
+其中设置了2个核验点,分别为 + +* 准备输入数据和推理模型 +* 编译得到可执行代码 + + + +### 3.1 准备系统环境 + +该部分可参考 [文档](../../mobilenetv3_prod/Step6/test_tipc/docs/test_inference_cpp.md) 中的2.2.1,2.2.1,2.2.3章节准备环境。 + + + +### 3.2 准备输入数据和推理模型 +该部分可参考 [文档](../../mobilenetv3_prod/Step6/test_tipc/docs/test_inference_cpp.md) 中的2.1章节准备数据和推理模型。 + + + +### 3.3 准备推理所需代码 +基于预测引擎的推理过程包含4个步骤:初始化预测引擎、预处理、推理、后处理。 + +#### 3.3.1 初始化预测引擎 + +**【基本内容】** +该部分主要根据配置文件对预测引擎进行初始化,包括设置模型结构和参数文件路径、是否使用GPU、是否开启MKLDNN、是否开启TensorRT等。 +**【实战】** +以mobilenet_v3_small模型为例,推理引擎初始化函数实现如下,其中模型结构和参数文件路径、是否使用GPU、是否开启MKLDNN等内容都是可以配置的。 +主要实现在[cls.cpp](../../mobilenetv3_prod/Step6/deploy/inference_cpp/src/cls.cpp) +```c++ +void Classifier::LoadModel(const std::string &model_path, + const std::string ¶ms_path) { + paddle_infer::Config config; + config.SetModel(model_path, params_path); + + if (this->use_gpu_) { + config.EnableUseGpu(this->gpu_mem_, this->gpu_id_); + if (this->use_tensorrt_) { + config.EnableTensorRtEngine( + 1 << 20, 1, 3, + this->use_fp16_ ? paddle_infer::Config::Precision::kHalf + : paddle_infer::Config::Precision::kFloat32, + false, false); + } + } else { + config.DisableGpu(); + if (this->use_mkldnn_) { + config.EnableMKLDNN(); + // cache 10 different shapes for mkldnn to avoid memory leak + config.SetMkldnnCacheCapacity(10); + } + config.SetCpuMathLibraryNumThreads(this->cpu_math_library_num_threads_); + } + + config.SwitchUseFeedFetchOps(false); + // true for multiple input + config.SwitchSpecifyInputNames(true); + + config.SwitchIrOptim(true); + + config.EnableMemoryOptim(); + config.DisableGlogInfo(); + + this->predictor_ = CreatePredictor(config); +} + +``` +#### 3.3.2 预处理 +**【基本内容】** +该部分主要用来读取指定图像,对其进行数据变换,转化为符合模型推理所需要的输入格式, +**【实战】** +以mobilenet_v3_small模型为例,使用的数据预处理如下: + +* resize +* crop +* normalize +* RGB -> CHW + +主要实现在[preprocess_op.cpp](../../mobilenetv3_prod/Step6/deploy/inference_cpp/src/preprocess_op.cpp)中。 +```c++ +//Resize +class ResizeImg { +public: + virtual void Run(const cv::Mat &img, cv::Mat &resize_img, int max_size_len); +}; +//Crop +class CenterCropImg { +public: + virtual void Run(cv::Mat &im, const int crop_size = 224); +}; +//Norm +class Normalize { +public: + virtual void Run(cv::Mat *im, const std::vector &mean, + const std::vector &scale, const bool is_scale = true); +}; +// RGB -> CHW +class Permute { +public: + virtual void Run(const cv::Mat *im, float *data); +}; +``` +#### 3.3.3 推理 +**【基本内容】** +前向推理是主要步骤,会将预处理好的输入图像输出到预测引擎中,得到输出结果。 +**【实战】** +以mobilenet_v3_small模型为例,前向推理主要实现在[cls.cpp](../../mobilenetv3_prod/Step6/deploy/inference_cpp/src/cls.cpp)。 +```C++ + auto input_names = this->predictor_->GetInputNames(); + auto input_t = this->predictor_->GetInputHandle(input_names[0]); + input_t->Reshape({1, 3, resize_img.rows, resize_img.cols}); + auto start = std::chrono::system_clock::now(); + input_t->CopyFromCpu(input.data()); + this->predictor_->Run(); + + std::vector out_data; + auto output_names = this->predictor_->GetOutputNames(); + auto output_t = this->predictor_->GetOutputHandle(output_names[0]); + std::vector output_shape = output_t->shape(); + int out_num = std::accumulate(output_shape.begin(), output_shape.end(), 1, + std::multiplies()); + out_data.resize(out_num); + output_t->CopyToCpu(out_data.data()); +``` + +#### 3.3.4 后处理 +**【基本内容】** +模型最后的输出可能是数组,一般并不是我们最后想要获取的结果,因此需要对模型的输出做后处理。 +**【实战】** +以mobilenet_v3_small模型为例,模型输出的是一个一维的数组,代表输入图片分类到每个类目的概率,为了得到有实际含义的输出,需要获取该数组中最大值的位置和大小,mobilenet_v3_small的后处理代码如下所示。 + +```c++ +int maxPosition = max_element(out_data.begin(), out_data.end()) - out_data.begin(); +int score = out_data[maxPosition]; +``` + + +### 3.4 编译得到可执行代码 +**【基本内容】** +在准备好相应的代码后需要开始准备编译,这里可以利用cmake来实现。 +**【实战】** +以mobilenet_v3_small模型为例,代码示例如:[CMakeLists.txt](../../mobilenetv3_prod/Step6/deploy/inference_cpp/CMakeLists.txt) +```bash +set(DEPS ${DEPS} ${OpenCV_LIBS}) +AUX_SOURCE_DIRECTORY(./src SRCS) +add_executable(${DEMO_NAME} ${SRCS}) +target_link_libraries(${DEMO_NAME} ${DEPS}) +``` +执行脚本: +```bash +OPENCV_DIR=../opencv-3.4.7/opencv3/ +LIB_DIR=../paddle_inference/ +CUDA_LIB_DIR=/usr/local/cuda/lib64 +CUDNN_LIB_DIR=/usr/lib64 +TENSORRT_DIR=/usr/local/TensorRT-7.2.3.4 + +BUILD_DIR=build +rm -rf ${BUILD_DIR} +mkdir ${BUILD_DIR} +cd ${BUILD_DIR} +cmake .. \ + -DPADDLE_LIB=${LIB_DIR} \ + -DWITH_MKL=ON \ + -DWITH_GPU=OFF \ + -DWITH_STATIC_LIB=OFF \ + -DUSE_TENSORRT=OFF \ + -DOPENCV_DIR=${OPENCV_DIR} \ + -DCUDNN_LIB=${CUDNN_LIB_DIR} \ + -DCUDA_LIB=${CUDA_LIB_DIR} \ + +make -j +``` + +上述命令中,Paddle C++预测库、opencv等其他依赖库的地址需要换成自己机器上的实际地址。 + +* `OPENCV_DIR`为opencv编译安装的地址(本例中为`opencv-3.4.7/opencv3`文件夹的路径); + +* `LIB_DIR`为下载的Paddle预测库(`paddle_inference`文件夹),或编译生成的Paddle预测库(`build/paddle_inference_install_dir`文件夹)的路径; + +* `CUDA_LIB_DIR`为cuda库文件地址,在docker中一般为`/usr/local/cuda/lib64`; + +* `CUDNN_LIB_DIR`为cudnn库文件地址,在docker中一般为`/usr/lib64`。 + +* `TENSORRT_DIR`是tensorrt库文件地址,在dokcer中一般为`/usr/local/TensorRT-7.2.3.4/`,TensorRT需要结合GPU使用。 + +在执行上述命令,编译完成之后,会在当前路径下生成`build`文件夹,其中生成一个名为`clas_system`的可执行文件。 + + + +### 3.5 运行得到结果 + +相关脚本位置[run.sh](../../mobilenetv3_prod/Step6/deploy/inference_cpp/tools/run.sh) +```bash +./build/clas_system ./tools/config.txt ../../images/demo.jpg +``` + + + +### 3.6 填写配置文件 +**【基本内容】** + +在repo的`test_tipc/`目录中新建`configs/model_name`,将文件 [inference_cpp.txt](../../mobilenetv3_prod/Step6/test_tipc/configs/mobilenet_v3_small/inference_cpp.txt) 拷贝到该目录中,其中`model_name`需要修改为您自己的模型名称。 + +**【实战】** + +配置文件的含义解析可以参考 [2.2节配置文件解析](#2.2) 部分。 + +mobilenet_v3_small的测试开发配置文件可以参考:[inference_cpp.txt](../../mobilenetv3_prod/Step6/test_tipc/configs/mobilenet_v3_small/inference_cpp.txt)。 + + + +### 3.7 验证配置正确性 + +**【基本内容】** + +基于修改完的配置,运行 + +```bash +bash test_tipc/test_inference_cpp.sh ${your_params_file} +``` + +**【注意事项】** + +如果运行失败,会输出具体的报错命令,可以根据输出的报错命令排查下配置文件的问题并修改,示例报错如下所示。 + +``` +Run failed with command - ./deploy/inference_cpp/build/clas_system test_tipc/configs/mobilenet_v3_small/inference_cpp.txt ./images/demo.jpg > ./log/infer_cpp/infer_cpp_use_cpu_use_mkldnn.log 2>&1 ! +``` + +**【实战】** + +以mobilenet_v3_small的`Linux GPU/CPU C++推理功能测试` 为例,命令如下所示。 + +```bash +bash test_tipc/test_inference_cpp.sh test_tipc/configs/mobilenet_v3_small/inference_cpp.txt +``` + +输出结果如下,表示命令运行成功。 + +```bash +Run successfully with command - ./deploy/inference_cpp/build/clas_system test_tipc/configs/mobilenet_v3_small/inference_cpp.txt ./images/demo.jpg > ./log/infer_cpp/infer_cpp_use_cpu_use_mkldnn.log 2>&1 ! +``` + +也可以在`./log/infer_cpp/infer_cpp_use_cpu_use_mkldnn.log`中查看详细的输出结果。 + +**【核验】** + +基于修改后的配置文件,测试通过,全部命令成功 + + + +### 3.8 撰写说明文档 + +**【基本内容】** + +撰写TIPC功能总览和测试流程说明文档,分别为 + +1. TIPC功能总览文档:test_tipc/README.md +2. Linux GPU/CPU C++推理功能测试说明文档:test_tipc/docs/test_inference_cpp.md + +2个文档模板分别位于下述位置,可以直接拷贝到自己的repo中,根据自己的模型进行修改。 + +1. [README.md](../../mobilenetv3_prod/Step6/test_tipc/README.md) +2. [test_inference_cpp](../../mobilenetv3_prod/Step6/test_tipc/docs/test_inference_cpp.md) + +**【实战】** + +mobilenet_v3_small中`test_tipc`文档如下所示。 + +1. TIPC功能总览文档:[README.md](../../mobilenetv3_prod/Step6/test_tipc/README.md) +2. Paddle2ONNX 测试说明文档:[test_inference_cpp.md](../../mobilenetv3_prod/Step6/test_tipc/docs/test_inference_cpp.md) + +**【核验】** + +repo中最终目录结构如下所示。 + +``` +test_tipc + |--configs # 配置目录 + | |--model_name # 您的模型名称 + | |--inference_cpp.txt # inference_cpp测试配置文件 + |--docs # 文档目录 + | |--test_inference_cpp.md # inference_cpp测试说明文档 + |----README.md # TIPC说明文档 + |----test_inference_cpp.sh # TIPC inference_cpp解析脚本,无需改动 + |----common_func.sh # TIPC基础训练推理测试常用函数,无需改动 +``` + +基于`test_inference_cpp.md`文档,跑通`inference_cpp功能测试`流程。 + + + +## 4. FAQ -- GitLab