# C++预测测试开发规范
## 目录
- [0.概述](#概述)
- [1.总览](#总览)
- [1.1 全链条自动化测试](#全链条自动化测试)
- [1.2 文本检测样板间概览](#文本检测样板间概览)
- [2.全链条规范接入流程](#全链条规范接入流程)
- [2.1 规范化输出预测日志](#规范化输出预测日志)
- [2.2 准备测试模型和数据](#准备测试模型和数据)
- [2.3 编写自动化测试代码](#编写自动化测试代码)
- [3.附录:自动化测试脚本test_inference_cpp.sh 函数介绍](#附录)
## 0. 概述
训推一体认证(TIPC)旨在监控框架代码更新可能导致的**模型训练、预测报错、性能下降**等问题。本文主要介绍TIPC中**C++预测测试**的接入规范和监测点,是在[基础测试]()上针对C++测试的补充说明。
主要监控的内容有:
- 飞桨框架更新后,代码仓库模型的C++预测是否能正常走通;(比如API的不兼容升级)
- 飞桨框架更新后,代码仓库模型的C++预测速度是否合理;
为了能监控上述问题,希望把代码仓库模型的C++预测测试加到飞桨框架的CI和CE中,提升PR合入的质量。因此,需要在代码仓库中加入运行脚本(不影响代码仓库正常运行),完成模型的自动化测试。
可以建立的CI/CE机制包括:
1. **全量数据走通开源模型C++预测,并验证模型预测速度和精度是否符合设定预期;(单模型30分钟内)**
a. 保证预测结果正确,预测速度符合预期(QA添加中)
注:由于CI有时间限制,所以在测试的时候需要限制运行时间,所以需要构建一个很小的数据集完成测试。
## 1. 总览
### 1.1 全链条自动化测试
本规范测试的链条如下(其中相邻两个模块之间是两两组合关系),可以根据代码仓库需要,适当删减链条。
![](images/tipc_cpp_infer.png)
上图各模块具体测试点如下:
- slim模型选型方面:
- **非量化模型(必选)**
- 量化模型(可选)
- Paddle C++ inference 预测部署方面:
- **Linux GPU上不同batchsize,是否开启TensorRT,不同预测精度(FP32,FP16,INT8)的运行状态(必选)**
- **Linux CPU上不同batchsize,是否开启MKLDNN,不同预测精度(FP32,FP16,INT8)的运行状态(必选)**
- Win GPU,macOS CPU和Win CPU(可选)
### 1.2 文本检测样板间概览
在PaddleOCR中,以文本检测为例,提供了本规范的样板间,可以完成概述部分提到的1种CI/CE机制。
C++预测测试工具位于PaddleOCR dygraph分支下的[test_tipc目录](https://github.com/PaddlePaddle/PaddleOCR/tree/dygraph/test_tipc),与C++预测样板间相关的主要文件如下:
```
test_tipc/
├── configs/ # 配置文件目录
├── ch_ppocr_mobile_v2.0_det # ch_ppocr_mobile_v2.0_det模型的测试配置文件目录
├── model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt # 测试Linux上c++预测的配置文件
├── results/ # 预先保存的预测结果,用于和实际预测结果进行精读比对
├── cpp_ppocr_det_mobile_results_fp32.txt # 预存的mobile版ppocr检测模型c++预测的fp32精度的结果
├── cpp_ppocr_det_mobile_results_fp16.txt # 预存的mobile版ppocr检测模型c++预测的fp16精度的结果
├── prepare.sh # 完成test_*.sh运行所需要的数据和模型下载
├── test_inference_cpp.sh # 测试c++预测的主程序
├── compare_results.py # 用于对比log中的预测结果与results中的预存结果精度误差是否在限定范围内
└── readme.md # 使用文档
```
不同代码仓库的`configs`和`results`目录下的内容可根据实际情况进行调整。
配置文件`model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt`需满足[TIPC配置文件命名规范](https://github.com/PaddlePaddle/PaddleOCR/tree/dygraph/test_tipc#%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E5%91%BD%E5%90%8D%E8%A7%84%E8%8C%83)。
## 2. C++预测接入TIPC流程
C++预测接入TIPC包含如下三个步骤,接下来将依次介绍这三个部分。
- 规范化输出预测日志
- 准备测试模型和数据
- 编写自动化测试代码
### 2.1 规范化输出预测日志
类似于python预测等基础测试,C++预测测试也需要规范不同代码仓库中Paddle Inference预测输出的格式,方便QA统一自动化测试。针对C++的预测log规范输出工具也已集成到[AutoLog工具包](https://github.com/LDOUBLEV/AutoLog)。
C++测试要求规范输出预测结果及以下信息:
- 运行的硬件,CPU还是GPU
- 运行配置信息,是否开启了IR 优化、TRT、MKLDNN,以及具体使用的线程数
- 运行的模型名称
- 运行的数据信息,包括batch size,数据量
- 性能信息,inference 各阶段平均预测时间
- 单张图片的预测结果
代码主要修改有三步:编译中引入auto_log类,预测耗时打点和打印输出信息。
1.在`external-cmake`目录中引入日志打印工具[`autolog`](https://github.com/LDOUBLEV/AutoLog/blob/main/auto_log/autolog.h),并在`CMakeLists.txt`中进行配置。
`external-cmake/auto-log.cmake`内容如下,[查看完整源码](https://github.com/PaddlePaddle/PaddleOCR/blob/dygraph/deploy/cpp_infer/external-cmake/auto-log.cmake):
```
find_package(Git REQUIRED)
include(FetchContent)
set(FETCHCONTENT_BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}/third-party")
FetchContent_Declare(
extern_Autolog
PREFIX autolog
GIT_REPOSITORY https://github.com/LDOUBLEV/AutoLog.git
GIT_TAG main
)
FetchContent_MakeAvailable(extern_Autolog)
```
在`CMakeLists.txt`中进行配置,[查看完整源码](https://github.com/PaddlePaddle/PaddleOCR/blob/dygraph/deploy/cpp_infer/CMakeLists.txt#L210):
```
include(FetchContent)
include(external-cmake/auto-log.cmake)
include_directories(${FETCHCONTENT_BASE_DIR}/extern_autolog-src)
```
2.添加预测耗时打点:
(1)在模型预测中,统计前处理,预测,后处理时间,可参考[代码](https://github.com/PaddlePaddle/PaddleOCR/blob/dygraph/deploy/cpp_infer/src/ocr_det.cpp#L171)。
(2)在main函数中使用autolog工具打印日志,参考[代码](https://github.com/PaddlePaddle/PaddleOCR/blob/dygraph/deploy/cpp_infer/src/main.cpp#L122)。主要包括:
- 引入头文件:
```
#include "auto_log/autolog.h"
```
- 调用AutoLogger类打印日志:
```
if (FLAGS_benchmark) {
AutoLogger autolog("ocr_det",
FLAGS_use_gpu,
FLAGS_use_tensorrt,
FLAGS_enable_mkldnn,
FLAGS_cpu_threads,
1,
"dynamic",
FLAGS_precision,
time_info,
cv_all_img_names.size());
autolog.report();
}
```
3.打印输出信息:
按照上述两步添加预测耗时打点和日志打印工具后,运行程序可打印出如下格式日志:
![](images/tipc_cpp_infer_log.png)
4.预测结果格式规范化:
(1)预先保存预测结果,作为校验预测结果是否正确的groundtruth。预测结果可以保存为两个,分别是FP32精度下预测的输入,和FP16预测精度下的输出结果。以OCR 为例,在`PaddleOCR/test_tipc/results`文件夹下存放了两个txt文件,分别是:
```
cpp_ppocr_det_mobile_results_fp32.txt
cpp_ppocr_det_mobile_results_fp16.txt
```
里面按行存储每张测试图片的预测结果,格式如下:
![](images/tipc_cpp_infer_gt.png)
(2)在运行日志中打印预测结果,或者保存在txt文件中,确保可以用python加载到输出并和预先保存的预测的结果进行对比。以PP-OCR检测为例,日志中的预测结果打印格式如下:
![](images/tipc_cpp_infer_res.png)
(3)预测精度比对。分别读取预测日志文件中的预测结果和预先保存在txt文件中的预测结果,验证两个结果是否是一致的。【验证是否一致的步骤可以在QA测试时完成,但是线下需要自测通过】
结果对比脚本可以参考代码:https://github.com/PaddlePaddle/PaddleOCR/blob/dygraph/test_tipc/compare_results.py
### 2.2 准备测试模型和数据
与python训练预测等基础测试方式类似,使用脚本`prepare.sh`可以下载测试所需推理模型和数据(可以直接使用python预测所准备的数据集)。`prepare.sh` 根据不同的运行模式,配合从配置文件中解析得到的模型区别性名称,下载不同的数据和训练模型用于完成后续测试。
![](images/tipc_cpp_infer_prepare.png)
### 2.3 编写自动化测试代码
C++预测的自动化测试代码在脚本[test_inference_cpp.sh](https://github.com/PaddlePaddle/PaddleOCR/blob/dygraph/test_tipc/test_inference_cpp.sh)中,其中包含`编译opencv(可选)`、`编译可执行文件`、`运行测试`三个部分。
C++预测自动化测试的运行命令如下:
```shell
bash test_tipc/test_inference_cpp.sh test_tipc/configs/ppocr_det_mobile/model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt
```
理论上只需要修改配置文件和`prepare.sh`就可以完成自动化测试,本节将详细介绍如何修改配置文件,完成C++预测测试。运行脚本`test_inference_cpp.sh`将会在附录中详细介绍。
按如下方式在参数文件`model_linux_gpu_normal_normal_infer_cpp_linux_gpu_cpu.txt`中添加C++预测部分参数:
![](images/tipc_cpp_infer_params.png)
参数说明:
|行号 | 参数 | 参数介绍 |
|---|---|---|
|2 | model_name: ocr_det | 模型名称,该参数会在prepare.sh脚本中用到|
|3 | use_opencv: True/False | 表示是否使用use_opencv,如果代码仓库没有这个参数,可以设置为null。不需要opencv时,会跳过编译opencv的步骤|
|4 | infer_model: ./inference/ch_ppocr_mobile_v2.0_det_infer/ | 模型路径 |
|5 | infer_quant: True/False | 54行设置的模型路径是否是量化模型 |
|6 | inference: ./deploy/cpp_infer/build/ppocr det| C++预测命令|
|7 | --use_gpu:True|False | 设置GPU的参数,其他代码仓库可能是device参数,用于设置不同硬件的参数,作用类似。设置多个配置时,中间用|隔开,会分别运行不同配置下的预测 | --device:cpu|gpu|
|8 | --enable_mkldnn:True|False | 设置是否开启mkldnn |
|9 | --cpu_threads:1|6 | 设置CPU线程数,如果要测试CPU上不同线程下的预测速度和精度,可以设置多个值,不同值用|隔开 |
|10 | --rec_batch_num:1 | 设置batch_size 的参数 |
|11 | --use_tensorrt:False|True | 设置开启TRT的参数 |
|12 | --precision:fp32|fp16| 设置开启TRT后预测精度的参数 | --precision:fp32|int8|fp16 |
|13 | --det_model_dir: | 设置加载inference模型路径的参数,无需设置:后的部分 | null: |
|14 | --image_dir:./inference/ch_det_data_50/all-sum-510/ | 设置预测的数据路径 |
|15 | null:null | 预留位置,无需理会 | null:null:null |
|16 | --benchmark:True | 设置是否开启AutoLog的参数 |
## 3. 附录:C++预测自动化测试脚本test_inference_cpp.sh 函数介绍
[C++预测核心函数](https://github.com/PaddlePaddle/PaddleOCR/blob/dygraph/test_tipc/test_inference_cpp.sh#L43):
- func_cpp_inference() :执行C++预测的函数,如果是GPU上,会测试是否开启TensorRT和不同精度,不同batch_size下的情况;如果是CPU,会测试是否开启mkldnn,不同CPU线程数,不同batch_size下的情况。另外,如果是量化模型,则仅测试CPU+mkldnn和GPU+TensorRT+int8的组合。
[编译opencv](https://github.com/PaddlePaddle/PaddleOCR/blob/dygraph/test_tipc/test_inference_cpp.sh#L116):
- 如果不需要opencv,如语音或nlp任务,删除本段代码即可。
- 需要编译opencv时,首先检查是否已经存在指定版本的opencv,比较md5,如果已有,则跳过编译,避免多次测试时重复下载和编译。
[编译可执行文件](https://github.com/PaddlePaddle/PaddleOCR/blob/dygraph/test_tipc/test_inference_cpp.sh#L158):
- 可根据实际任务调整编译选项。