Welcome to Paddle-Inference’s documentation!

飞桨推理产品简介

作为飞桨生态重要的一部分,飞桨提供了多个推理产品,完整承接深度学习模型应用的最后一公里。

整体上分,推理产品主要包括如下子产品

名称 英文表示 适用场景
飞桨原生推理库 Paddle Inference 高性能服务器端、云端推理
飞桨服务化推理框架 Paddle Serving 自动服务、模型管理等高阶功能
飞桨轻量化推理引擎 Paddle Lite 移动端、物联网等
飞桨前端推理引擎 Paddle.js 浏览器中推理、小程序等

各产品在推理生态中的关系如下

_images/inference_ecosystem.png

用户使用飞桨推理产品的工作流 如下

  1. 获取一个飞桨的推理模型,其中有两种方法

    1. 利用飞桨训练得到一个推理模型

    2. 用 X2Paddle 工具从第三方框架(比如 TensorFlow 或者 Caffe 等)产出的模型转化

  2. (可选)对模型进行进一步优化, PaddleSlim 工具可以对模型进行压缩,量化,裁剪等工作,显著提升模型执行的速度性能,降低资源消耗

  3. 将模型部署到具体的推理产品上

Paddle Inference 简介

Paddle Inference 是飞桨的原生推理库, 作用于服务器端和云端,提供高性能的推理能力。

由于能力直接基于飞桨的训练算子,因此Paddle Inference 可以通用支持飞桨训练出的所有模型。

Paddle Inference 功能特性丰富,性能优异,针对不同平台不同的应用场景进行了深度的适配优化,做到高吞吐、低时延,保证了飞桨模型在服务器端即训即用,快速部署。

Paddle Inference的高性能实现

内存/显存复用提升服务吞吐量

在推理初始化阶段,对模型中的OP输出Tensor 进行依赖分析,将两两互不依赖的Tensor在内存/显存空间上进行复用,进而增大计算并行量,提升服务吞吐量。

细粒度OP横向纵向融合减少计算量

在推理初始化阶段,按照已有的融合模式将模型中的多个OP融合成一个OP,减少了模型的计算量的同时,也减少了 Kernel Launch的次数,从而能提升推理性能。目前Paddle Inference支持的融合模式多达几十个。

内置高性能的CPU/GPU Kernel

内置同Intel、Nvidia共同打造的高性能kernel,保证了模型推理高性能的执行。

子图集成TensorRT加快GPU推理速度

Paddle Inference采用子图的形式集成TensorRT,针对GPU推理场景,TensorRT可对一些子图进行优化,包括OP的横向和纵向融合,过滤冗余的OP,并为OP自动选择最优的kernel,加快推理速度。

子图集成Paddle Lite轻量化推理引擎

Paddle Lite 是飞桨深度学习框架的一款轻量级、低框架开销的推理引擎,除了在移动端应用外,还可以使用服务器进行 Paddle Lite 推理。Paddle Inference采用子图的形式集成 Paddle Lite,以方便用户在服务器推理原有方式上稍加改动,即可开启 Paddle Lite 的推理能力,得到更快的推理速度。并且,使用 Paddle Lite 可支持在百度昆仑等高性能AI芯片上执行推理计算。

支持加载PaddleSlim量化压缩后的模型

PaddleSlim是飞桨深度学习模型压缩工具,Paddle Inference可联动PaddleSlim,支持加载量化、裁剪和蒸馏后的模型并部署,由此减小模型存储空间、减少计算占用内存、加快模型推理速度。其中在模型量化方面,Paddle Inference在X86 CPU上做了深度优化,常见分类模型的单线程性能可提升近3倍,ERNIE模型的单线程性能可提升2.68倍。

Paddle Inference的通用性

主流软硬件环境兼容适配

支持服务器端X86 CPU、NVIDIA GPU芯片,兼容Linux/Mac/Windows系统。支持所有飞桨训练产出的模型,完全做到即训即用。

多语言环境丰富接口可灵活调用

支持C++, Python, C,接口简单灵活,20行代码即可完成部署。对于其他语言,提供了ABI稳定的C API, 用户可以很方便地扩展。

预测流程

一. 准备模型

Paddle Inference 原生支持由 PaddlePaddle 深度学习框架训练产出的推理模型。新版本 PaddlePaddle 用于推理的模型分别通过 paddle.jit.save (动态图) 与 paddle.static.save_inference_model (静态图) 或 paddle.Model().save (高层API) 保存下来;老版本的 PaddlePaddle 用于推理的模型通过 fluid.io.save_inference_model 这个API保存下来。更详细的说明请参考这里

如果您手中的模型是由诸如 Caffe、Tensorflow、PyTorch 等框架产出的,那么您可以使用 X2Paddle 工具将模型转换为 PadddlePaddle 格式。

二. 准备环境

1) Python 环境

请参照 官方主页-快速安装 页面进行自行安装或编译,当前支持 pip/conda 安装,docker镜像 以及源码编译等多种方式来准备 Paddle Inference 开发环境。

2) C++ 环境

Paddle Inference 提供了 Ubuntu/Windows/MacOS 平台的官方Release预测库下载,如果您使用的是以上平台,我们优先推荐您通过以下链接直接下载,或者您也可以参照文档进行源码编译

三. 开发预测程序

Paddle Inference采用 Predictor 进行预测。Predictor 是一个高性能预测引擎,该引擎通过对计算图的分析,完成对计算图的一系列的优化(如OP的融合、内存/显存的优化、 MKLDNN,TensorRT 等底层加速库的支持等),能够大大提升预测性能。

开发预测程序只需要简单的5个步骤 (这里以C++ API为例):

  1. 配置推理选项 paddle_infer::Config,包括设置模型路径、运行设备、开启/关闭计算图优化、使用MKLDNN/TensorRT进行部署的加速等。

  2. 创建推理引擎 paddle_infer::Predictor,通过调用 CreatePaddlePredictor(Config) 接口,一行代码即可完成引擎初始化,其中 Config 为第1步中生成的配置推理选项。

  3. 准备输入数据,需要以下几个步骤

    • 先通过 auto input_names = predictor->GetInputNames() 获取模型所有输入 Tensor 的名称

    • 再通过 auto tensor = predictor->GetInputTensor(input_names[i]) 获取输入 Tensor 的指针

    • 最后通过 tensor->copy_from_cpu(data),将 data 中的数据拷贝到 tensor 中

  4. 执行预测,只需要运行predictor->Run()一行代码,即可完成预测执行

  5. 获得预测结果,需要以下几个步骤

    • 先通过 auto out_names = predictor->GetOutputNames() 获取模型所有输出 Tensor 的名称

    • 再通过 auto tensor = predictor->GetOutputTensor(out_names[i]) 获取输出 Tensor的 指针

    • 最后通过 tensor->copy_to_cpu(data),将 tensor 中的数据 copy 到 data 指针上

Paddle Inference 提供了C, C++, Python, Golang 和 R 五种API的使用示例和开发说明文档,您可以参考示例中的说明快速了解使用方法,并集成到您自己的项目中去。

预测示例 (C++)

本章节包含2部分内容:(1) 运行 C++ 示例程序;(2) C++ 预测程序开发说明

运行 C++ 示例程序

1. 下载预编译 C++ 预测库

Paddle Inference 提供了 Ubuntu/Windows/MacOS 平台的官方Release预测库下载,如果您使用的是以上平台,我们优先推荐您通过以下链接直接下载,或者您也可以参照文档进行源码编译

下载完成并解压之后,目录下的 paddle_inference_install_dir 即为 C++ 预测库,目录结构如下:

paddle_inference/paddle_inference_install_dir/
├── CMakeCache.txt
├── paddle
│   ├── include                                    C++ 预测库头文件目录
│   │   ├── crypto
│   │   ├── internal
│   │   ├── paddle_analysis_config.h
│   │   ├── paddle_api.h
│   │   ├── paddle_infer_declare.h
│   │   ├── paddle_inference_api.h                 C++ 预测库头文件
│   │   ├── paddle_mkldnn_quantizer_config.h
│   │   └── paddle_pass_builder.h
│   └── lib
│       ├── libpaddle_inference.a                      C++ 静态预测库文件
│       └── libpaddle_inference.so                     C++ 动态态预测库文件
├── third_party
│   ├── install                                    第三方链接库和头文件
│   │   ├── cryptopp
│   │   ├── gflags
│   │   ├── glog
│   │   ├── mkldnn
│   │   ├── mklml
│   │   ├── protobuf
│   │   └── xxhash
│   └── threadpool
│       └── ThreadPool.h
└── version.txt

其中 version.txt 文件中记录了该预测库的版本信息,包括Git Commit ID、使用OpenBlas或MKL数学库、CUDA/CUDNN版本号,如:

GIT COMMIT ID: 1bf4836580951b6fd50495339a7a75b77bf539f6
WITH_MKL: ON
WITH_MKLDNN: ON
WITH_GPU: ON
CUDA version: 9.0
CUDNN version: v7.6
CXX compiler version: 4.8.5
WITH_TENSORRT: ON
TensorRT version: v6

2. 准备预测部署模型

下载 ResNet50 模型后解压,得到 Paddle 预测格式的模型,位于文件夹 ResNet50 下。如需查看模型结构,可将 inference.pdmodel 文件重命名为 __model__,然后通过模型可视化工具 Netron 打开。

wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar zxf resnet50.tgz

# 获得模型目录即文件如下
resnet50/
├── inference.pdmodel
├── inference.pdiparams.info
└── inference.pdiparams

3. 获取预测示例代码并编译

本章节 C++ 预测示例代码位于 Paddle-Inference-Demo/c++/resnet50。目录包含以下文件:

-rw-r--r-- 1 root root 2.6K Dec 11 07:26 resnet50_test.cc    预测 C++ 源码程序
-rw-r--r-- 1 root root 7.4K Dec 11 07:26 CMakeLists.txt      CMAKE 文件
-rwxr-xr-x 1 root root  650 Dec 11 07:26 run_impl.sh         编译脚本
-rw-r--r-- 1 root root 2.2K Dec 11 07:26 README.md           README 说明

编译运行预测样例之前,需要根据运行环境配置编译脚本 run_impl.sh

# 根据预编译库中的version.txt信息判断是否将以下三个标记打开
WITH_MKL=ON       
WITH_GPU=ON         
USE_TENSORRT=OFF

# 配置预测库的根目录,即为本章节第1步中下载/编译的 C++ 预测库
LIB_DIR=${YOUR_LIB_DIR}/paddle_inference_install_dir

# 如果上述的 WITH_GPU 或 USE_TENSORRT 设为ON,请设置对应的 CUDA, CUDNN, TENSORRT的路径,例如
CUDNN_LIB=/usr/lib/x86_64-linux-gnu
CUDA_LIB=/usr/local/cuda-10.2/lib64

运行脚本进行编译,会在目录下产生build目录,并生成 build/resnet50_test 可执行文件

bash run_impl.sh

4. 执行预测程序

注意:执行预测之前,需要先将动态库文件 libpaddle_inference.so 所在路径加入 LD_LIBRARY_PATH,否则会出现无法找到库文件的错误。而且,Paddle Inference 提供下载的C++预测库对应GCC 4.8,所以请检查您电脑中GCC版本是否一致,如果不一致可能出现未知错误。

# 设置 LD_LIBRARY_PATH
LIB_DIR=${YOUR_LIB_DIR}/paddle_inference_install_dir
export LD_LIBRARY_PATH=${LIB_DIR}/paddle/lib:$LD_LIBRARY_PATH

# 参数输入为本章节第2步中下载的 ResNet50 模型
./build/resnet50_test --model_file=./resnet50/inference.pdmodel --params_file=./resnet50/inference.pdiparams

成功执行之后,得到的预测输出结果如下:

# 程序输出结果如下
WARNING: Logging before InitGoogleLogging() is written to STDERR
E1211 08:29:39.840502 18792 paddle_pass_builder.cc:139] GPU not support MKLDNN yet
E1211 08:29:39.840647 18792 paddle_pass_builder.cc:139] GPU not support MKLDNN yet
E1211 08:29:40.997318 18792 paddle_pass_builder.cc:139] GPU not support MKLDNN yet
I1211 08:29:40.997367 18792 analysis_predictor.cc:139] Profiler is deactivated, and no profiling report will be generated.
I1211 08:29:41.016829 18792 analysis_predictor.cc:496] MKLDNN is enabled
--- Running analysis [ir_graph_build_pass]
--- Running analysis [ir_graph_clean_pass]
--- Running analysis [ir_analysis_pass]
--- Running IR pass [is_test_pass]
--- Running IR pass [simplify_with_basic_ops_pass]
--- Running IR pass [conv_affine_channel_fuse_pass]
--- Running IR pass [conv_eltwiseadd_affine_channel_fuse_pass]
--- Running IR pass [conv_bn_fuse_pass]
I1211 08:29:41.536377 18792 graph_pattern_detector.cc:101] ---  detected 53 subgraphs
--- Running IR pass [conv_eltwiseadd_bn_fuse_pass]
--- Running IR pass [embedding_eltwise_layernorm_fuse_pass]
--- Running IR pass [multihead_matmul_fuse_pass_v2]
--- Running IR pass [fc_fuse_pass]
I1211 08:29:41.577596 18792 graph_pattern_detector.cc:101] ---  detected 1 subgraphs
--- Running IR pass [fc_elementwise_layernorm_fuse_pass]
--- Running IR pass [conv_elementwise_add_act_fuse_pass]
I1211 08:29:41.599529 18792 graph_pattern_detector.cc:101] ---  detected 33 subgraphs
--- Running IR pass [conv_elementwise_add2_act_fuse_pass]
I1211 08:29:41.610285 18792 graph_pattern_detector.cc:101] ---  detected 16 subgraphs
--- Running IR pass [conv_elementwise_add_fuse_pass]
I1211 08:29:41.613446 18792 graph_pattern_detector.cc:101] ---  detected 4 subgraphs
--- Running IR pass [transpose_flatten_concat_fuse_pass]
--- Running IR pass [runtime_context_cache_pass]
--- Running analysis [ir_params_sync_among_devices_pass]
I1211 08:29:41.620128 18792 ir_params_sync_among_devices_pass.cc:45] Sync params from CPU to GPU
--- Running analysis [adjust_cudnn_workspace_size_pass]
--- Running analysis [inference_op_replace_pass]
--- Running analysis [ir_graph_to_program_pass]
I1211 08:29:41.688971 18792 analysis_predictor.cc:541] ======= optimize end =======
I1211 08:29:41.689072 18792 naive_executor.cc:102] ---  skip [feed], feed -> image
I1211 08:29:41.689968 18792 naive_executor.cc:102] ---  skip [save_infer_model/scale_0.tmp_0], fetch -> fetch
W1211 08:29:41.690475 18792 device_context.cc:338] Please NOTE: device: 0, CUDA Capability: 70, Driver API Version: 11.0, Runtime API Version: 9.0
W1211 08:29:41.690726 18792 device_context.cc:346] device: 0, cuDNN Version: 7.6.
WARNING: Logging before InitGoogleLogging() is written to STDERR
I1211 08:29:43.666896 18792 resnet50_test.cc:76] 0.000293902
I1211 08:29:43.667001 18792 resnet50_test.cc:76] 0.000453056
I1211 08:29:43.667009 18792 resnet50_test.cc:76] 0.000202802
I1211 08:29:43.667017 18792 resnet50_test.cc:76] 0.000109128
I1211 08:29:43.667255 18792 resnet50_test.cc:76] 0.000138924
...

C++ 预测程序开发说明

使用 Paddle Inference 开发 C++ 预测程序仅需以下五个步骤:

(1) 引用头文件

#include "paddle/include/paddle_inference_api.h"

(2) 创建配置对象,并根据需求配置,详细可参考 C++ API 文档 - Config

// 创建默认配置对象
paddle_infer::Config config;

// 设置预测模型路径,即为本小节第2步中下载的模型
config.SetModel(FLAGS_model_file, FLAGS_params_file);

// 启用 GPU 和 MKLDNN 预测
config.EnableUseGpu(100, 0);
config.EnableMKLDNN();

// 开启 内存/显存 复用
config.EnableMemoryOptim();

(3) 根据Config创建预测对象,详细可参考 C++ API 文档 - Predictor

auto predictor = paddle_infer::CreatePredictor(config);

(4) 设置模型输入 Tensor,详细可参考 C++ API 文档 - Tensor

// 获取输入 Tensor
auto input_names = predictor->GetInputNames();
auto input_tensor = predictor->GetInputHandle(input_names[0]);

// 设置输入 Tensor 的维度信息
std::vector<int> INPUT_SHAPE = {1, 3, 224, 224};
input_tensor->Reshape(INPUT_SHAPE);

// 准备输入数据
int input_size = 1 * 3 * 224 * 224;
std::vector<float> input_data(input_size, 1);
// 设置输入 Tensor 数据
input_tensor->CopyFromCpu(input_data.data());

(5) 执行预测,详细可参考 C++ API 文档 - Predictor

// 执行预测
predictor->Run();

(6) 获得预测结果,详细可参考 C++ API 文档 - Tensor

// 获取 Output Tensor
auto output_names = predictor->GetOutputNames();
auto output_tensor = predictor->GetOutputHandle(output_names[0]);

// 获取 Output Tensor 的维度信息
std::vector<int> output_shape = output_tensor->shape();
int output_size = std::accumulate(output_shape.begin(), output_shape.end(), 1,
                                  std::multiplies<int>());

// 获取 Output Tensor 的数据
std::vector<float> output_data;
output_data.resize(output_size);
output_tensor->CopyToCpu(output_data.data());

预测示例 (Python)

本章节包含2部分内容:(1) 运行 Python 示例程序;(2) Python 预测程序开发说明

运行 Python 示例程序

1. 安装 Python 预测库

请参照 官方主页-快速安装 页面进行自行安装或编译,当前支持 pip/conda 安装,docker镜像 以及源码编译等多种方式来准备 Paddle Inference 开发环境。

2. 准备预测部署模型

下载 ResNet50 模型后解压,得到 Paddle 预测格式的模型,位于文件夹 ResNet50 下。如需查看模型结构,可将 inference.pdmodel 文件重命名为 __model__,然后通过模型可视化工具 Netron 打开。

wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar zxf resnet50_model.tar.gz

# 获得模型目录即文件如下
resnet50/
├── inference.pdmodel
├── inference.pdiparams.info
└── inference.pdiparams

3. 准备预测部署程序

将以下代码保存为 python_demo.py 文件:

import argparse
import numpy as np

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

def main():
    args = parse_args()

    # 创建 config
    config = paddle_infer.Config(args.model_file, args.params_file)

    # 根据 config 创建 predictor
    predictor = paddle_infer.create_predictor(config)

    # 获取输入的名称
    input_names = predictor.get_input_names()
    input_handle = predictor.get_input_handle(input_names[0])

    # 设置输入
    fake_input = np.random.randn(args.batch_size, 3, 318, 318).astype("float32")
    input_handle.reshape([args.batch_size, 3, 318, 318])
    input_handle.copy_from_cpu(fake_input)

    # 运行predictor
    predictor.run()

    # 获取输出
    output_names = predictor.get_output_names()
    output_handle = predictor.get_output_handle(output_names[0])
    output_data = output_handle.copy_to_cpu() # numpy.ndarray类型
    print("Output data size is {}".format(output_data.size))
    print("Output data shape is {}".format(output_data.shape))

def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument("--model_file", type=str, help="model filename")
    parser.add_argument("--params_file", type=str, help="parameter filename")
    parser.add_argument("--batch_size", type=int, default=1, help="batch size")
    return parser.parse_args()

if __name__ == "__main__":
    main()

4. 执行预测程序

# 参数输入为本章节第2步中下载的 ResNet50 模型
python python_demo.py --model_file ./resnet50/inference.pdmodel --params_file ./resnet50/inference.pdiparams --batch_size 2

成功执行之后,得到的预测输出结果如下:

# 程序输出结果如下
grep: warning: GREP_OPTIONS is deprecated; please use an alias or script
I1211 11:12:40.869632 20942 analysis_predictor.cc:139] Profiler is deactivated, and no profiling report will be generated.
--- Running analysis [ir_graph_build_pass]
--- Running analysis [ir_graph_clean_pass]
--- Running analysis [ir_analysis_pass]
--- Running IR pass [simplify_with_basic_ops_pass]
--- Running IR pass [attention_lstm_fuse_pass]
--- Running IR pass [seqconv_eltadd_relu_fuse_pass]
--- Running IR pass [seqpool_cvm_concat_fuse_pass]
--- Running IR pass [mul_lstm_fuse_pass]
--- Running IR pass [fc_gru_fuse_pass]
--- Running IR pass [mul_gru_fuse_pass]
--- Running IR pass [seq_concat_fc_fuse_pass]
--- Running IR pass [fc_fuse_pass]
I1211 11:12:41.327713 20942 graph_pattern_detector.cc:100] ---  detected 1 subgraphs
--- Running IR pass [repeated_fc_relu_fuse_pass]
--- Running IR pass [squared_mat_sub_fuse_pass]
--- Running IR pass [conv_bn_fuse_pass]
--- Running IR pass [conv_eltwiseadd_bn_fuse_pass]
I1211 11:12:41.550542 20942 graph_pattern_detector.cc:100] ---  detected 53 subgraphs
--- Running IR pass [conv_transpose_bn_fuse_pass]
--- Running IR pass [conv_transpose_eltwiseadd_bn_fuse_pass]
--- Running IR pass [is_test_pass]
--- Running IR pass [runtime_context_cache_pass]
--- Running analysis [ir_params_sync_among_devices_pass]
--- Running analysis [adjust_cudnn_workspace_size_pass]
--- Running analysis [inference_op_replace_pass]
--- Running analysis [ir_graph_to_program_pass]
I1211 11:12:41.594254 20942 analysis_predictor.cc:537] ======= optimize end =======
I1211 11:12:41.594414 20942 naive_executor.cc:102] ---  skip [feed], feed -> data
I1211 11:12:41.595824 20942 naive_executor.cc:102] ---  skip [AddmmBackward190.fc.output.1.tmp_1], fetch -> fetch
Output data size is 1024
Output data shape is (2, 512)

Python 预测程序开发说明

使用 Paddle Inference 开发 Python 预测程序仅需以下五个步骤:

(1) 引用 paddle inference 预测库

import paddle.inference as paddle_infer

(2) 创建配置对象,并根据需求配置,详细可参考 Python API 文档 - Config

# 创建 config,并设置预测模型路径
config = paddle_infer.Config(args.model_file, args.params_file)

(3) 根据Config创建预测对象,详细可参考 Python API 文档 - Predictor

predictor = paddle_infer.create_predictor(config)

(4) 设置模型输入 Tensor,详细可参考 Python API 文档 - Tensor

# 获取输入的名称
input_names = predictor.get_input_names()
input_handle = predictor.get_input_handle(input_names[0])

# 设置输入
fake_input = np.random.randn(args.batch_size, 3, 318, 318).astype("float32")
input_handle.reshape([args.batch_size, 3, 318, 318])
input_handle.copy_from_cpu(fake_input)

(5) 执行预测,详细可参考 Python API 文档 - Predictor

predictor.run()

(5) 获得预测结果,详细可参考 Python API 文档 - Tensor

output_names = predictor.get_output_names()
output_handle = predictor.get_output_handle(output_names[0])
output_data = output_handle.copy_to_cpu() # numpy.ndarray类型

预测示例 (C)

本章节包含2部分内容:(1) 运行 C 示例程序;(2) C 预测程序开发说明

运行 C 示例程序

1. 源码编译 C 预测库

Paddle Inference 的 C 预测库需要以源码编译的方式进行获取,请参照以下两个文档进行源码编译

编译完成后,在编译目录下的 paddle_inference_c_install_dir 即为 C 预测库,目录结构如下:

paddle_inference_c_install_dir
├── paddle
│   ├── include               C 预测库头文件目录
│   │   └── pd_common.h
│   │   └── pd_config.h
│   │   └── pd_inference_api.h         C 预测库头文件
│   │   └── pd_predictor.h
│   │   └── pd_tensor.h
│   │   └── pd_types.h
│   │   └── pd_utils.h
│   └── lib
│       ├── libpaddle_inference_c.a          C 静态预测库文件
│       └── libpaddle_inference_c.so         C 动态预测库文件
├── third_party
│   └── install                          第三方链接库和头文件
│       ├── cryptopp
│       ├── gflags
│       ├── glog
│       ├── mkldnn
│       ├── mklml
│       ├── protobuf
│       └── xxhash
└── version.txt                          版本信息与编译选项信息

其中 version.txt 文件中记录了该预测库的版本信息,包括Git Commit ID、使用OpenBlas或MKL数学库、CUDA/CUDNN版本号,如:

GIT COMMIT ID: 1bf4836580951b6fd50495339a7a75b77bf539f6
WITH_MKL: ON
WITH_MKLDNN: ON
WITH_GPU: ON
CUDA version: 9.0
CUDNN version: v7.6
CXX compiler version: 4.8.5
WITH_TENSORRT: ON
TensorRT version: v6

2. 准备预测部署模型

下载 ResNet50 模型后解压,得到 Paddle 预测格式的模型,位于文件夹 ResNet50 下。如需查看模型结构,可将 inference.pdmodel 文件重命名为 __model__,然后通过模型可视化工具 Netron 打开。

wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar zxf resnet50_model.tar.gz

# 获得模型目录即文件如下
resnet50/
├── inference.pdmodel
├── inference.pdiparams.info
└── inference.pdiparams

3. 准备预测部署程序

将以下代码保存为 c_demo.c 文件:

#include "pd_inference_api.h"
#include <memory.h>
#include <malloc.h>

int main() {
  // 创建 Config 对象
  PD_Config* config = PD_ConfigCreate();

  // 设置预测模型路径,即为本小节第2步中下载的模型
  const char* model_path  = "./resnet50/inference.pdmodel";
  const char* params_path = "./resnet50/inference.pdiparams";
  PD_ConfigSetModel(config, model_path, params_path);
  
  // 根据 Config 创建 Predictor, 并销毁 Config 对象
  PD_Predictor* predictor = PD_PredictorCreate(config);

  // 准备输入数据
  float input_shape[4] = {1, 3, 244, 244};
  float input_data = (float*)calloc(1 * 3 * 224 * 224, sizeof(float));

  // 获取输入 Tensor
  PD_OneDimArrayCstr* input_names = PD_PredictorGetInputNames(predictor);
  PD_Tensor* input_tensor = PD_PredictorGetInputHandle(predictor, input_names->data[0]);

  // 设置输入 Tensor 的维度信息及数据
  PD_TensorReshape(input_tensor, 4, input_shape);
  PD_TensorCopyFromCpuFloat(input_tensor, input_data);

  // 执行预测
  PD_PredictorRun(pd_predictor);

  // 获取预测输出 Tensor
  PD_OneDimArrayCstr* output_names = PD_PredictorGetOutputNames(predictor);
  PD_Tensor* output_tensor = PD_PredictorGetOutputHandle(predictor, output_names->data[0]);

  // 获取预测输出 Tensor 信息
  PD_OneDimArrayInt32* output_shape = PD_TensorGetShape(output_tensor);
  int32_t out_size = 1;
  for (size_t i = 0; i < output_shape->size; ++i) {
    out_size = out_size * output_shape->data[i];
  }

  // 打印输出 Tensor 信息
  printf("Output Tensor Name: %s\n", output_names->data[0]);
  printf("Output Tensor Size: %d\n", out_size);

  // 获取预测输出 Tensor 数据
  out_data = (float*)malloc(out_size * sizeof(float));
  PD_TensorCopyToCpuFloat(output_tensor, out_data);

  // 销毁相关对象, 回收相关内存
  free(out_data)
  PD_OneDimArrayInt32Destroy(output_shape);
  PD_TensorDestroy(output_tensor);
  PD_OneDimArrayCstrDestroy(output_names);
  PD_TensorDestroy(input_tensor);
  PD_OneDimArrayCstrDestroy(input_names);
  free(input_data);
  PD_PredictorDestroy(predictor);

  return 0;
}

4. 编译预测部署程序

paddle_inference_c_install_dir/paddle/include 目录下的所有头文件和动态库文件 paddle_inference_c_install_dir/paddle/lib/libpaddle_inference_c.so 拷贝到与预测源码同一目录,然后使用 GCC 进行编译:

# GCC 编译命令
gcc c_demo.c libpaddle_inference_c.so -o c_demo_prog

# 编译完成之后生成 c_demo_prog 可执行文件,编译目录内容如下
c_demo_dir/
│
├── c_demo.c                 预测 C 源码程序,内容如本小节第3步所示
├── c_demo_prog              编译后的预测可执行程序
│
├── pd_inference_api.h         C 预测库头文件
├── pd_common.h
├── pd_config.h
├── pd_utils.h
├── pd_predictor.h
├── pd_tensor.h
├── pd_types.h
├── libpaddle_fluid_c.so     C 动态预测库文件
│
├── resnet50_model.tar.gz    本小节第2步中下载的预测模型
└── resnet50                 本小节第2步中下载的预测模型解压后的模型文件
    ├── inference.pdmodel
    ├── inference.pdiparams.info
    └── inference.pdiparams

5. 执行预测程序

注意:需要先将动态库文件 libpaddle_inference_c.so 所在路径加入 LD_LIBRARY_PATH,否则会出现无法找到库文件的错误。

# 执行预测程序
export LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH
./c_demo_prog

成功执行之后,得到的预测输出结果如下:

# 程序输出结果如下
WARNING: Logging before InitGoogleLogging() is written to STDERR
I1211 05:57:48.939208 16443 pd_config.cc:43] ./resnet50/inference.pdmodel
I1211 05:57:48.939507 16443 pd_config.cc:48] ./resnet50/inference.pdiparams
PaddleBuf empty: true
W1211 05:57:48.941076 16443 analysis_predictor.cc:1052] Deprecated. Please use CreatePredictor instead.
I1211 05:57:48.941124 16443 analysis_predictor.cc:139] Profiler is deactivated, and no profiling report will be generated.
--- Running analysis [ir_graph_build_pass]
--- Running analysis [ir_graph_clean_pass]
--- Running analysis [ir_analysis_pass]
--- Running IR pass [simplify_with_basic_ops_pass]
--- Running IR pass [attention_lstm_fuse_pass]
--- Running IR pass [seqconv_eltadd_relu_fuse_pass]
--- Running IR pass [seqpool_cvm_concat_fuse_pass]
--- Running IR pass [mul_lstm_fuse_pass]
--- Running IR pass [fc_gru_fuse_pass]
--- Running IR pass [mul_gru_fuse_pass]
--- Running IR pass [seq_concat_fc_fuse_pass]
--- Running IR pass [fc_fuse_pass]
I1211 05:57:49.481595 16443 graph_pattern_detector.cc:101] ---  detected 1 subgraphs
--- Running IR pass [repeated_fc_relu_fuse_pass]
--- Running IR pass [squared_mat_sub_fuse_pass]
--- Running IR pass [conv_bn_fuse_pass]
--- Running IR pass [conv_eltwiseadd_bn_fuse_pass]
I1211 05:57:49.698067 16443 graph_pattern_detector.cc:101] ---  detected 53 subgraphs
--- Running IR pass [conv_transpose_bn_fuse_pass]
--- Running IR pass [conv_transpose_eltwiseadd_bn_fuse_pass]
--- Running IR pass [is_test_pass]
--- Running IR pass [runtime_context_cache_pass]
--- Running analysis [ir_params_sync_among_devices_pass]
--- Running analysis [adjust_cudnn_workspace_size_pass]
--- Running analysis [inference_op_replace_pass]
--- Running analysis [ir_graph_to_program_pass]
I1211 05:57:49.741832 16443 analysis_predictor.cc:541] ======= optimize end =======
Output Tensor Name: AddmmBackward190.fc.output.1.tmp_1
Output Tensor Size: 1

C 预测程序开发说明

使用 Paddle Inference 开发 C 预测程序仅需以下七个步骤:

(1) 引用头文件

#include "pd_inference_api.h"

(2) 创建配置对象,并指定预测模型路径,详细可参考 C API 文档 - Config 方法

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 设置预测模型路径,即为本小节第2步中下载的模型
const char* model_path  = "./resnet50/inference.pdmodel";
const char* params_path = "./resnet50/inference.pdiparams";
PD_ConfigSetModel(config, model_path, params_path);

(3) 根据Config创建预测对象,详细可参考 C API 文档 - Predictor 方法

// 根据 Config 创建 Predictor, 并销毁 Config 对象
PD_Predictor* predictor = PD_PredictorCreate(config);

(4) 设置模型输入Tensor,详细可参考 C API 文档 - Tensor 方法

// 准备输入数据
float input_shape[4] = {1, 3, 244, 244};
float input_data = (float*)calloc(1 * 3 * 224 * 224, sizeof(float));

// 获取输入 Tensor
PD_OneDimArrayCstr* input_names = PD_PredictorGetInputNames(predictor);
PD_Tensor* input_tensor = PD_PredictorGetInputHandle(predictor, input_names->data[0]);

// 设置输入 Tensor 的维度信息及数据
PD_TensorReshape(input_tensor, 4, input_shape);
PD_TensorCopyFromCpuFloat(input_tensor, input_data);

(5) 执行预测引擎,详细可参考 C API 文档 - Predictor 方法

// 执行预测
PD_PredictorRun(pd_predictor);

(6) 获得预测结果,详细可参考 C API 文档 - Tensor 方法

// 获取预测输出 Tensor
PD_OneDimArrayCstr* output_names = PD_PredictorGetOutputNames(predictor);
PD_Tensor* output_tensor = PD_PredictorGetOutputHandle(predictor, output_names->data[0]);

// 获取预测输出 Tensor 信息
PD_OneDimArrayInt32* output_shape = PD_TensorGetShape(output_tensor);
int32_t out_size = 1;
for (size_t i = 0; i < output_shape->size; ++i) {
  out_size = out_size * output_shape->data[i];
}

// 打印输出 Tensor 信息
printf("Output Tensor Name: %s\n", output_names->data[0]);
printf("Output Tensor Size: %d\n", out_size);

// 获取预测输出 Tensor 数据
out_data = (float*)malloc(out_size * sizeof(float));
PD_TensorCopyToCpuFloat(output_tensor, out_data);

(7) 销毁相关对象,回收相关内存

// 销毁相关对象, 回收相关内存
free(out_data)
PD_OneDimArrayInt32Destroy(output_shape);
PD_TensorDestroy(output_tensor);
PD_OneDimArrayCstrDestroy(output_names);
PD_TensorDestroy(input_tensor);
PD_OneDimArrayCstrDestroy(input_names);
free(input_data);
PD_PredictorDestroy(predictor);

源码编译

什么时候需要源码编译?

深度学习的发展十分迅速,对科研或工程人员来说,可能会遇到一些需要自己开发op的场景,可以在python层面编写op,但如果对性能有严格要求的话则必须在C++层面开发op,对于这种情况,需要用户源码编译飞桨,使之生效。 此外对于绝大多数使用C++将模型部署上线的工程人员来说,您可以直接通过飞桨官网下载已编译好的预测库,快捷开启飞桨使用之旅。飞桨官网 提供了多个不同环境下编译好的预测库。如果用户环境与官网提供环境不一致(如cuda 、cudnn、tensorrt版本不一致等),或对飞桨源代码有修改需求,或希望进行定制化构建,可查阅本文档自行源码编译得到预测库。

编译原理

一:目标产物

飞桨框架的源码编译包括源代码的编译和链接,最终生成的目标产物包括:

  • 含有 C++ 接口的头文件及其二进制库:用于C++环境,将文件放到指定路径即可开启飞桨使用之旅。

  • Python Wheel 形式的安装包:用于Python环境,也就是说,前面讲的pip安装属于在线安装,这里属于本地安装。

二:基础概念

飞桨主要由C++语言编写,通过pybind工具提供了Python端的接口,飞桨的源码编译主要包括编译和链接两步。 * 编译过程由编译器完成,编译器以编译单元(后缀名为 .cc 或 .cpp 的文本文件)为单位,将 C++ 语言 ASCII 源代码翻译为二进制形式的目标文件。一个工程通常由若干源码文件组织得到,所以编译完成后,将生成一组目标文件。 * 链接过程使分离编译成为可能,由链接器完成。链接器按一定规则将分离的目标文件组合成一个能映射到内存的二进制程序文件,并解析引用。由于这个二进制文件通常包含源码中指定可被外部用户复用的函数接口,所以也被称作函数库。根据链接规则不同,链接可分为静态和动态链接。静态链接对目标文件进行归档;动态链接使用地址无关技术,将链接放到程序加载时进行。 配合包含声明体的头文件(后缀名为 .h 或 .hpp),用户可以复用程序库中的代码开发应用。静态链接构建的应用程序可独立运行,而动态链接程序在加载运行时需到指定路径下搜寻其依赖的二进制库。

三:编译方式

飞桨框架的设计原则之一是满足不同平台的可用性。然而,不同操作系统惯用的编译和链接器是不一样的,使用它们的命令也不一致。比如,Linux 一般使用 GNU 编译器套件(GCC),Windows 则使用 Microsoft Visual C++(MSVC)。为了统一编译脚本,飞桨使用了支持跨平台构建的 CMake,它可以输出上述编译器所需的各种 Makefile 或者 Project 文件。 为方便编译,框架对常用的CMake命令进行了封装,如仿照 Bazel工具封装了 cc_binary 和 cc_library ,分别用于可执行文件和库文件的产出等,对CMake感兴趣的同学可在 cmake/generic.cmake 中查看具体的实现逻辑。Paddle的CMake中集成了生成python wheel包的逻辑,对如何生成wheel包感兴趣的同学可参考 相关文档

编译步骤

飞桨分为 CPU 版本和 GPU 版本。如果您的计算机没有 Nvidia GPU,请选择 CPU 版本构建安装。如果您的计算机含有 Nvidia GPU( 1.0 且预装有 CUDA / CuDNN,也可选择 GPU 版本构建安装。

推荐配置及依赖项

1、稳定的 Github 连接,主频 1 GHz 以上的多核处理器,9 GB 以上磁盘空间。 2、GCC 版本 4.8 或者 8.2;或者 Visual Studio 2015 Update 3。 3、Python 版本 2.7 或 3.5 以上,pip 版本 9.0 及以上;CMake v3.10 及以上;Git 版本 2.17 及以上。请将可执行文件放入系统环境变量中以方便运行。 4、GPU 版本额外需要 Nvidia CUDA 9 / 10,CuDNN v7 及以上版本。根据需要还可能依赖 TensorRT。

基于 Ubuntu 18.04

一:环境准备

除了本节开头提到的依赖,在 Ubuntu 上进行飞桨的源码编译,您还需要准备 GCC8 编译器等工具,可使用下列命令安装:

sudo apt-get install gcc g++ make cmake git vim unrar python3 python3-dev python3-pip swig wget patchelf libopencv-dev
pip3 install numpy protobuf wheel setuptools

若需启用 cuda 加速,需准备 cuda、cudnn。上述工具的安装请参考 nvidia 官网,以 cuda10.1,cudnn7.6 为例配置 cuda 环境。

# cuda
sh cuda_10.1.168_418.67_linux.run
export PATH=/usr/local/cuda-10.1/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-10.1/${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}

# cudnn
tar -xzvf cudnn-10.1-linux-x64-v7.6.4.38.tgz
sudo cp -a cuda/include/cudnn.h /usr/local/cuda/include/
sudo cp -a cuda/lib64/libcudnn* /usr/local/cuda/lib64/

编译飞桨过程中可能会打开很多文件,Ubuntu 18.04 默认设置最多同时打开的文件数是1024(参见 ulimit -a),需要更改这个设定值。

在 /etc/security/limits.conf 文件中添加两行。

* hard noopen 102400
* soft noopen 102400

重启计算机,重启后执行以下指令,请将${user}切换成当前用户名。

su ${user}
ulimit -n 102400

若在 TensorRT 依赖编译过程中出现头文件虚析构函数报错,请在 NvInfer.h 文件中为 class IPluginFactory 和 class IGpuAllocator 分别添加虚析构函数:

virtual ~IPluginFactory() {};
virtual ~IGpuAllocator() {};

二:编译命令

使用 Git 将飞桨代码克隆到本地,并进入目录,切换到稳定版本(git tag显示的标签名,如 release/2.0)。 飞桨使用 develop 分支进行最新特性的开发,使用 release 分支发布稳定版本。在 GitHub 的 Releases 选项卡中,可以看到飞桨版本的发布记录。

git clone https://github.com/PaddlePaddle/Paddle.git
cd Paddle
git checkout release/2.0

下面以 GPU 版本为例说明编译命令。其他环境可以参考“CMake编译选项表”修改对应的cmake选项。比如,若编译 CPU 版本,请将 WITH_GPU 设置为 OFF。

# 创建并进入 build 目录
mkdir build_cuda && cd build_cuda
# 执行cmake指令
cmake .. -DPY_VERSION=3 \
        -DWITH_TESTING=OFF \
        -DWITH_MKL=ON \
        -DWITH_GPU=ON \
        -DON_INFER=ON \
        ..

使用make编译

make -j4

编译成功后可在dist目录找到生成的.whl包

pip3 install python/dist/paddlepaddle-2.0.0-cp38-cp38-linux_x86_64.whl

预测库编译

make inference_lib_dist -j4

cmake编译环境表

以下介绍的编译方法都是通用步骤,根据环境对应修改cmake选项即可。

选项

说明

默认值

WITH_GPU

是否支持GPU

ON

WITH_AVX

是否编译含有AVX指令集的飞桨二进制文件

ON

WITH_PYTHON

是否内嵌PYTHON解释器并编译Wheel安装包

ON

WITH_TESTING

是否开启单元测试

OFF

WITH_MKL

是否使用MKL数学库,如果为否,将使用OpenBLAS

ON

WITH_SYSTEM_BLAS

是否使用系统自带的BLAS

OFF

WITH_DISTRIBUTE

是否编译带有分布式的版本

OFF

WITH_BRPC_RDMA

是否使用BRPC,RDMA作为RPC协议

OFF

ON_INFER

是否打开预测优化

OFF

CUDA_ARCH_NAME

是否只针对当前CUDA架构编译

All:编译所有可支持的CUDA架构;Auto:自动识别当前环境的架构编译

TENSORRT_ROOT

TensorRT_lib的路径,该路径指定后会编译TRT子图功能eg:/paddle/nvidia/TensorRT/

/usr

三:NVIDIA Jetson嵌入式硬件预测库源码编译

NVIDIA Jetson是NVIDIA推出的嵌入式AI平台,Paddle Inference支持在 NVIDIA Jetson平台上编译预测库。具体步骤如下:

1、准备环境:

# 开启硬件性能模式
sudo nvpmodel -m 0 && sudo jetson_clocks
# 增加 DDR 可用空间,Xavier 默认内存为 16 GB,所以内存足够,如在 Nano 上尝试,请执行如下操作。
sudo fallocate -l 5G /var/swapfile
sudo chmod 600 /var/swapfile
sudo mkswap /var/swapfile
sudo swapon /var/swapfile
sudo bash -c 'echo "/var/swapfile swap swap defaults 0 0" >> /etc/fstab'

2、编译预测库:

cd Paddle
mkdir build
cd build
cmake .. \
-DWITH_CONTRIB=OFF \
-DWITH_MKL=OFF  \
-DWITH_MKLDNN=OFF \
-DWITH_TESTING=OFF \
-DCMAKE_BUILD_TYPE=Release \
-DON_INFER=ON \
-DWITH_PYTHON=OFF \
-DWITH_XBYAK=OFF  \
-DWITH_NV_JETSON=ON
make -j4

# 生成预测lib
make inference_lib_dist -j4

3、参照 官网样例 进行测试。

基于 Windows 10

一:环境准备

除了本节开头提到的依赖,在 Windows 10 上编译飞桨,您还需要准备 Visual Studio 2015 Update 3。飞桨正在对更高版本的编译支持做完善支持。

在命令提示符输入下列命令,安装必需的 Python 组件。

pip3 install numpy protobuf wheel

二:编译命令

使用 Git 将飞桨代码克隆到本地,并进入目录,切换到稳定版本(git tag显示的标签名,如 release/2.0)。 飞桨使用 develop 分支进行最新特性的开发,使用 release 分支发布稳定版本。在 GitHub 的 Releases 选项卡中,可以看到飞桨版本的发布记录。

git clone https://github.com/PaddlePaddle/Paddle.git
cd Paddle
git checkout release/2.0

创建一个构建目录,并在其中执行 CMake,生成解决方案文件 Solution File,以编译 CPU 版本为例说明编译命令,其他环境可以参考“CMake编译选项表”修改对应的cmake选项。

mkdir build
cd build
cmake .. -G "Visual Studio 14 2015 Win64" -A x64 -DWITH_GPU=OFF -DWITH_TESTING=OFF -DON_INFER=ON
        -DCMAKE_BUILD_TYPE=Release -DPY_VERSION=3

使用 Visual Studio 打开解决方案文件,在窗口顶端的构建配置菜单中选择 Release x64,单击生成解决方案,等待构建完毕即可。

cmake编译环境表

选项

说明

默认值

WITH_GPU

是否支持GPU

ON

WITH_AVX

是否编译含有AVX指令集的飞桨二进制文件

ON

WITH_PYTHON

是否内嵌PYTHON解释器并编译Wheel安装包

ON

WITH_TESTING

是否开启单元测试

OFF

WITH_MKL

是否使用MKL数学库,如果为否,将使用OpenBLAS

ON

WITH_SYSTEM_BLAS

是否使用系统自带的BLAS

OFF

WITH_DISTRIBUTE

是否编译带有分布式的版本

OFF

WITH_BRPC_RDMA

是否使用BRPC,RDMA作为RPC协议

OFF

ON_INFER

是否打开预测优化

OFF

CUDA_ARCH_NAME

是否只针对当前CUDA架构编译

All:编译所有可支持的CUDA架构;Auto:自动识别当前环境的架构编译

TENSORRT_ROOT

TensorRT_lib的路径,该路径指定后会编译TRT子图功能eg:/paddle/nvidia/TensorRT/

/usr

结果验证

一:python whl包

编译完毕后,会在 python/dist 目录下生成一个 Python Wheel 安装包,安装测试的命令为:

pip3 install paddlepaddle-2.0.0-cp38-cp38-win_amd64.whl

安装完成后,可以使用 python3 进入python解释器,输入以下指令,出现 `Your Paddle Fluid is installed successfully! ` ,说明安装成功。

import paddle.fluid as fluid
fluid.install_check.run_check()

二:c++ lib

预测库编译后,所有产出均位于build目录下的paddle_inference_install_dir目录内,目录结构如下。version.txt 中记录了该预测库的版本信息,包括Git Commit ID、使用OpenBlas或MKL数学库、CUDA/CUDNN版本号。

build/paddle_inference_install_dir
├── CMakeCache.txt
├── paddle
│   ├── include
│   │   ├── paddle_anakin_config.h
│   │   ├── paddle_analysis_config.h
│   │   ├── paddle_api.h
│   │   ├── paddle_inference_api.h
│   │   ├── paddle_mkldnn_quantizer_config.h
│   │   └── paddle_pass_builder.h
│   └── lib
│       ├── libpaddle_inference.a (Linux)
│       ├── libpaddle_inference.so (Linux)
│       └── libpaddle_inference.lib (Windows)
├── third_party
│   ├── boost
│   │   └── boost
│   ├── eigen3
│   │   ├── Eigen
│   │   └── unsupported
│   └── install
│       ├── gflags
│       ├── glog
│       ├── mkldnn
│       ├── mklml
│       ├── protobuf
│       ├── xxhash
│       └── zlib
└── version.txt

Include目录下包括了使用飞桨预测库需要的头文件,lib目录下包括了生成的静态库和动态库,third_party目录下包括了预测库依赖的其它库文件。

您可以编写应用代码,与预测库联合编译并测试结果。请参考 C++ 预测库 API 使用 一节。

基于 MacOSX 10.14

一:环境准备

在编译 Paddle 前,需要在 MacOSX 预装 Apple Clang 11.0 和 Python 3.8,以及 python-pip。请使用下列命令安装 Paddle 编译必需的 Python 组件包。

pip3 install numpy protobuf wheel setuptools

二:编译命令

使用 Git 将飞桨代码克隆到本地,并进入目录,切换到稳定版本(git tag显示的标签名,如 release/2.0)。 飞桨使用 develop 分支进行最新特性的开发,使用 release 分支发布稳定版本。在 GitHub 的 Releases 选项卡中,可以看到飞桨版本的发布记录。

git clone https://github.com/PaddlePaddle/Paddle.git
cd Paddle
git checkout release/2.0

下面以 CPU-MKL 版本为例说明编译命令。

# 创建并进入 build 目录
mkdir build && cd build
# 执行cmake指令
cmake .. -DPY_VERSION=3 \
        -DWITH_TESTING=OFF \
        -DWITH_MKL=ON \
        -DON_INFER=ON \
        ..

使用make编译

make -j4

编译成功后可在dist目录找到生成的.whl包

pip3 install python/dist/paddlepaddle-2.0.0-cp38-cp38-macosx_10_14_x86_64.whl

预测库编译

make inference_lib_dist -j4

cmake编译环境表

以下介绍的编译方法都是通用步骤,根据环境对应修改cmake选项即可。

选项

说明

默认值

WITH_GPU

是否支持GPU

ON

WITH_AVX

是否编译含有AVX指令集的飞桨二进制文件

ON

WITH_PYTHON

是否内嵌PYTHON解释器并编译Wheel安装包

ON

WITH_TESTING

是否开启单元测试

OFF

WITH_MKL

是否使用MKL数学库,如果为否,将使用OpenBLAS

ON

WITH_SYSTEM_BLAS

是否使用系统自带的BLAS

OFF

WITH_DISTRIBUTE

是否编译带有分布式的版本

OFF

WITH_BRPC_RDMA

是否使用BRPC,RDMA作为RPC协议

OFF

ON_INFER

是否打开预测优化

OFF

CUDA_ARCH_NAME

是否只针对当前CUDA架构编译

All:编译所有可支持的CUDA架构;Auto:自动识别当前环境的架构编译

TENSORRT_ROOT

TensorRT_lib的路径,该路径指定后会编译TRT子图功能eg:/paddle/nvidia/TensorRT/

/usr

飞腾/鲲鹏下从源码编译

环境准备

  • 处理器:FT2000+/Kunpeng 920 2426SK

  • 操作系统:麒麟v10/UOS

  • Python 版本 2.7.15+/3.5.1+/3.6/3.7/3.8 (64 bit)

  • pip 或 pip3 版本 9.0.1+ (64 bit)

飞腾FT2000+和鲲鹏920处理器均为ARMV8架构,在该架构上编译Paddle的方式一致,本文以FT2000+为例,介绍Paddle的源码编译。

安装步骤

目前在FT2000+处理器加国产化操作系统(麒麟UOS)上安装Paddle,只支持源码编译的方式,接下来详细介绍各个步骤。

源码编译

  1. Paddle依赖cmake进行编译构建,需要cmake版本>=3.10,如果操作系统提供的源包括了合适版本的cmake,直接安装即可,否则需要源码安装

     ```
     wget https://github.com/Kitware/CMake/releases/download/v3.16.8/cmake-3.16.8.tar.gz
     tar -xzf cmake-3.16.8.tar.gz && cd cmake-3.16.8
     ./bootstrap && make && sudo make install
     ```
    
  2. Paddle内部使用patchelf来修改动态库的rpath,如果操作系统提供的源包括了patchelf,直接安装即可,否则需要源码安装,请参考patchelf官方文档,后续会考虑在ARM上移出该依赖。

     ```
     ./bootstrap.sh
     ./configure
     make
     make check
     sudo make install
     ```
    
  3. 根据requirments.txt安装Python依赖库,在飞腾加国产化操作系统环境中,pip安装可能失败或不能正常工作,主要依赖通过源或源码安装的方式安装依赖库,建议使用系统提供源的方式安装依赖库。

  4. 将Paddle的源代码克隆到当下目录下的Paddle文件夹中,并进入Paddle目录

    git clone https://github.com/PaddlePaddle/Paddle.git
    
    cd Paddle
    
  5. 切换到较稳定release分支下进行编译:

    git checkout [分支名]
    

    例如:

    git checkout release/2.0-rc1
    
  6. 并且请创建并进入一个叫build的目录下:

    mkdir build && cd build
    
  7. 链接过程中打开文件数较多,可能超过系统默认限制导致编译出错,设置进程允许打开的最大文件数:

    ulimit -n 4096
    
  8. 执行cmake:

    具体编译选项含义请参见编译选项表

    For Python2:

    cmake .. -DPY_VERSION=2 -DPYTHON_EXECUTABLE=`which python2` -DWITH_ARM=ON -DWITH_TESTING=OFF -DCMAKE_BUILD_TYPE=Release -DON_INFER=ON -DWITH_XBYAK=OFF
    

    For Python3:

    cmake .. -DPY_VERSION=3 -DPYTHON_EXECUTABLE=`which python3` -DWITH_ARM=ON -DWITH_TESTING=OFF -DCMAKE_BUILD_TYPE=Release -DON_INFER=ON -DWITH_XBYAK=OFF
    
  9. 使用以下命令来编译,注意,因为处理器为ARM架构,如果不加TARGET=ARMV8则会在编译的时候报错。

    make TARGET=ARMV8 -j$(nproc)
    
  10. 编译成功后进入Paddle/build/python/dist目录下找到生成的.whl包。

  11. 在当前机器或目标机器安装编译好的.whl包:

    pip install -U(whl包的名字)`或`pip3 install -U(whl包的名字)
    

恭喜,至此您已完成PaddlePaddle在FT环境下的编译安装。

验证安装

安装完成后您可以使用 pythonpython3 进入python解释器,输入import paddle.fluid as fluid ,再输入 fluid.install_check.run_check()

如果出现Your Paddle Fluid is installed succesfully!,说明您已成功安装。

在mobilenetv1和resnet50模型上测试

wget -O profile.tar https://paddle-cetc15.bj.bcebos.com/profile.tar?authorization=bce-auth-v1/4409a3f3dd76482ab77af112631f01e4/2020-10-09T10:11:53Z/-1/host/786789f3445f498c6a1fd4d9cd3897ac7233700df0c6ae2fd78079eba89bf3fb
tar xf profile.tar && cd profile
python resnet.py --model_file ResNet50_inference/model --params_file ResNet50_inference/params
# 正确输出应为:[0.0002414  0.00022418 0.00053661 0.00028639 0.00072682 0.000213
#              0.00638718 0.00128127 0.00013535 0.0007676 ]
python mobilenetv1.py --model_file mobilenetv1/model --params_file mobilenetv1/params
# 正确输出应为:[0.00123949 0.00100392 0.00109539 0.00112206 0.00101901 0.00088412
#              0.00121536 0.00107679 0.00106071 0.00099605]
python ernie.py --model_dir ernieL3H128_model/
# 正确输出应为:[0.49879393 0.5012061 ]

如何卸载

请使用以下命令卸载PaddlePaddle:

pip uninstall paddlepaddle` 或 `pip3 uninstall paddlepaddle

备注

已在ARM架构下测试过resnet50, mobilenetv1, ernie, ELMo等模型,基本保证了预测使用算子的正确性,如果您在使用过程中遇到计算结果错误,编译失败等问题,请到issue中留言,我们会及时解决。

预测文档见doc,使用示例见Paddle-Inference-Demo

申威下从源码编译

环境准备

  • 处理器:SW6A

  • 操作系统:普华, iSoft Linux 5

  • Python 版本 2.7.15+/3.5.1+/3.6/3.7/3.8 (64 bit)

  • pip 或 pip3 版本 9.0.1+ (64 bit)

申威机器为SW架构,目前生态支持的软件比较有限,本文以比较trick的方式在申威机器上源码编译Paddle,未来会随着申威软件的完善不断更新。

安装步骤

本文在申威处理器下安装Paddle,接下来详细介绍各个步骤。

源码编译

  1. 将Paddle的源代码克隆到当下目录下的Paddle文件夹中,并进入Paddle目录

    git clone https://github.com/PaddlePaddle/Paddle.git
    cd Paddle
    
  2. 切换到较稳定release分支下进行编译:

    git checkout [分支/标签名]
    

    例如:

    git checkout release/2.0-rc1
    
  3. Paddle依赖cmake进行编译构建,需要cmake版本>=3.10,检查操作系统源提供cmake的版本,使用源的方式直接安装cmake, apt install cmake, 检查cmake版本, cmake --version, 如果cmake >= 3.10则不需要额外的操作,否则请修改Paddle主目录的CMakeLists.txt, cmake_minimum_required(VERSION 3.10) 修改为 cmake_minimum_required(VERSION 3.0).

  4. 由于申威暂不支持openblas,所以在此使用blas + cblas的方式,在此需要源码编译blas和cblas。

    pushd /opt
    wget http://www.netlib.org/blas/blas-3.8.0.tgz
    wget http://www.netlib.org/blas/blast-forum/cblas.tgz
    tar xzf blas-3.8.0.tgz
    tar xzf cblas.tgz
    pushd BLAS-3.8.0
    make
    popd
    pushd CBLAS
    # 修改Makefile.in中BLLIB为BLAS-3.8.0的编译产物blas_LINUX.a
    make
    pushd lib
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
    ln -s cblas_LINUX.a libcblas.a
    cp ../../BLAS-3.8.0/blas_LINUX.a .
    ln -s blas_LINUX.a libblas.a
    popd
    popd
    popd
    
  5. 根据requirments.txt安装Python依赖库,注意在申威系统中一般无法直接使用pip或源码编译安装python依赖包,建议使用源的方式安装,如果遇到部分依赖包无法安装的情况,请联系操作系统服务商提供支持。此外也可以通过pip安装的时候加–no-deps的方式来避免依赖包的安装,但该种方式可能导致包由于缺少依赖不可用。

  6. 请创建并进入一个叫build的目录下:

    mkdir build && cd build
    
  7. 链接过程中打开文件数较多,可能超过系统默认限制导致编译出错,设置进程允许打开的最大文件数:

    ulimit -n 4096
    
  8. 执行cmake:

    具体编译选项含义请参见编译选项表

     ```
     CBLAS_ROOT=/opt/CBLAS
     # For Python2:
     cmake .. -DPY_VERSION=2 -DPYTHON_EXECUTABLE=`which python2` -DWITH_MKL=OFF -DWITH_TESTING=OFF -DCMAKE_BUILD_TYPE=Release -DON_INFER=ON -DWITH_PYTHON=ON -DREFERENCE_CBLAS_ROOT=${CBLAS_ROOT} -DWITH_CRYPTO=OFF -DWITH_XBYAK=OFF -DWITH_SW=ON -DCMAKE_CXX_FLAGS="-Wno-error -w"
     # For Python3:
     cmake .. -DPY_VERSION=3 -DPYTHON_EXECUTABLE=`which python3` -DWITH_MKL=OFF -DWITH_TESTING=OFF -DCMAKE_BUILD_TYPE=Release -DON_INFER=ON -DWITH_PYTHON=ON -DREFERENCE_CBLAS_ROOT=${CBLAS_ROOT} -DWITH_CRYPTO=OFF -DWITH_XBYAK=OFF -DWITH_SW=ON -DCMAKE_CXX_FLAGS="-Wno-error -w"
     ```
    
  9. 编译。

    make -j$(nproc)
    
  10. 编译成功后进入Paddle/build/python/dist目录下找到生成的.whl包。

  11. 在当前机器或目标机器安装编译好的.whl包:

    python2 -m pip install -U(whl包的名字)`或`python3 -m pip install -U(whl包的名字)
    

恭喜,至此您已完成PaddlePaddle在FT环境下的编译安装。

验证安装

安装完成后您可以使用 pythonpython3 进入python解释器,输入import paddle.fluid as fluid ,再输入 fluid.install_check.run_check()

如果出现Your Paddle Fluid is installed succesfully!,说明您已成功安装。

在mobilenetv1和resnet50模型上测试

wget -O profile.tar https://paddle-cetc15.bj.bcebos.com/profile.tar?authorization=bce-auth-v1/4409a3f3dd76482ab77af112631f01e4/2020-10-09T10:11:53Z/-1/host/786789f3445f498c6a1fd4d9cd3897ac7233700df0c6ae2fd78079eba89bf3fb
tar xf profile.tar && cd profile
python resnet.py --model_file ResNet50_inference/model --params_file ResNet50_inference/params
# 正确输出应为:[0.0002414  0.00022418 0.00053661 0.00028639 0.00072682 0.000213
#              0.00638718 0.00128127 0.00013535 0.0007676 ]
python mobilenetv1.py --model_file mobilenetv1/model --params_file mobilenetv1/params
# 正确输出应为:[0.00123949 0.00100392 0.00109539 0.00112206 0.00101901 0.00088412
#              0.00121536 0.00107679 0.00106071 0.00099605]
python ernie.py --model_dir ernieL3H128_model/
# 正确输出应为:[0.49879393 0.5012061 ]

如何卸载

请使用以下命令卸载PaddlePaddle:

python3 -m pip uninstall paddlepaddle` 或 `python3 -m pip uninstall paddlepaddle

备注

已在申威下测试过resnet50, mobilenetv1, ernie, ELMo等模型,基本保证了预测使用算子的正确性,但可能会遇到浮点异常的问题,该问题我们后续会和申威一起解决,如果您在使用过程中遇到计算结果错误,编译失败等问题,请到issue中留言,我们会及时解决。

预测文档见doc,使用示例见Paddle-Inference-Demo

兆芯下从源码编译

环境准备

  • 处理器:ZHAOXIN KaiSheng KH-37800D

  • 操作系统:centos7

  • Python 版本 2.7.15+/3.5.1+/3.6/3.7/3.8 (64 bit)

  • pip 或 pip3 版本 9.0.1+ (64 bit)

兆芯为x86架构,编译方法与Linux下从源码编译cpu版一致。

安装步骤

本文在ZHAOXIN处理器下安装Paddle,接下来详细介绍各个步骤。

源码编译

  1. Paddle依赖cmake进行编译构建,需要cmake版本>=3.10,如果操作系统提供的源包括了合适版本的cmake,直接安装即可,否则需要源码安装

     ```
     wget https://github.com/Kitware/CMake/releases/download/v3.16.8/cmake-3.16.8.tar.gz
     tar -xzf cmake-3.16.8.tar.gz && cd cmake-3.16.8
     ./bootstrap && make && sudo make install
     ```
    
  2. Paddle内部使用patchelf来修改动态库的rpath,如果操作系统提供的源包括了patchelf,直接安装即可,否则需要源码安装,请参考patchelf官方文档

     ```
     ./bootstrap.sh
     ./configure
     make
     make check
     sudo make install
     ```
    
  3. 将Paddle的源代码克隆到当下目录下的Paddle文件夹中,并进入Paddle目录

    git clone https://github.com/PaddlePaddle/Paddle.git
    cd Paddle
    
  4. 切换到较稳定release分支下进行编译:

    git checkout [分支/标签名]
    

    例如:

    git checkout release/2.0-rc1
    
  5. 根据requirments.txt安装Python依赖库。

    pip install -r python/requirments.txt
    
  6. 请创建并进入一个叫build的目录下:

    mkdir build && cd build
    
  7. 链接过程中打开文件数较多,可能超过系统默认限制导致编译出错,设置进程允许打开的最大文件数:

    ulimit -n 4096
    
  8. 执行cmake:

    具体编译选项含义请参见编译选项表

     ```
     # For Python2:
     cmake .. -DPY_VERSION=2 -DPYTHON_EXECUTABLE=`which python2` -DWITH_MKL=ON -DWITH_TESTING=OFF -DCMAKE_BUILD_TYPE=Release -DON_INFER=ON -DWITH_PYTHON=ON
     # For Python3:
     cmake .. -DPY_VERSION=3 -DPYTHON_EXECUTABLE=`which python3` -DWITH_MKL=ON -DWITH_TESTING=OFF -DCMAKE_BUILD_TYPE=Release -DON_INFER=ON -DWITH_PYTHON=ON
     ```
    
  9. 编译。

    make -j$(nproc)
    
  10. 编译成功后进入Paddle/build/python/dist目录下找到生成的.whl包。

  11. 在当前机器或目标机器安装编译好的.whl包:

    python2 -m pip install -U(whl包的名字)`或`python3 -m pip install -U(whl包的名字)
    

恭喜,至此您已完成PaddlePaddle在FT环境下的编译安装。

验证安装

安装完成后您可以使用 pythonpython3 进入python解释器,输入import paddle.fluid as fluid ,再输入 fluid.install_check.run_check()

如果出现Your Paddle Fluid is installed succesfully!,说明您已成功安装。

在mobilenetv1和resnet50模型上测试

wget -O profile.tar https://paddle-cetc15.bj.bcebos.com/profile.tar?authorization=bce-auth-v1/4409a3f3dd76482ab77af112631f01e4/2020-10-09T10:11:53Z/-1/host/786789f3445f498c6a1fd4d9cd3897ac7233700df0c6ae2fd78079eba89bf3fb
tar xf profile.tar && cd profile
python resnet.py --model_file ResNet50_inference/model --params_file ResNet50_inference/params
# 正确输出应为:[0.0002414  0.00022418 0.00053661 0.00028639 0.00072682 0.000213
#              0.00638718 0.00128127 0.00013535 0.0007676 ]
python mobilenetv1.py --model_file mobilenetv1/model --params_file mobilenetv1/params
# 正确输出应为:[0.00123949 0.00100392 0.00109539 0.00112206 0.00101901 0.00088412
#              0.00121536 0.00107679 0.00106071 0.00099605]
python ernie.py --model_dir ernieL3H128_model/
# 正确输出应为:[0.49879393 0.5012061 ]

如何卸载

请使用以下命令卸载PaddlePaddle:

python3 -m pip uninstall paddlepaddle` 或 `python3 -m pip uninstall paddlepaddle

备注

已在ZHAOXIN下测试过resnet50, mobilenetv1, ernie, ELMo等模型,基本保证了预测使用算子的正确性,如果您在使用过程中遇到计算结果错误,编译失败等问题,请到issue中留言,我们会及时解决。

预测文档见doc,使用示例见Paddle-Inference-Demo

龙芯下从源码编译

环境准备

  • 处理器:Loongson-3A R4 (Loongson-3A4000)

  • 操作系统:Loongnix release 1.0

  • Python 版本 2.7.15+/3.5.1+/3.6/3.7/3.8 (64 bit)

  • pip 或 pip3 版本 20.2.2+ (64 bit)

本文以Loongson-3A4000为例,介绍Paddle在MIPS架构下的源码编译。

安装步骤

目前在MIPS龙芯处理器加龙芯国产化操作系统上安装Paddle,只支持源码编译的方式,接下来详细介绍各个步骤。

源码编译

  1. 龙芯操作系统Loongnix release 1.0默认安装的gcc版本是4.9,但yum源提供了gcc-7的工具链,在此处安装gcc-7。可以参考龙芯龙芯开源社区文章

    sudo yum install devtoolset-7-gcc.mips64el devtoolset-7-gcc-c++.mips64el devtoolset-7.mips64el
    

    设置环境变量使得gcc-7生效

    source /opt/rh/devtoolset-7/enable
    
  2. 龙芯系统自带的python都是基于gcc4.9,在第1步时选择使用了gcc-7.3,此处需要源码安装Python,此处以Python3.7为例。

    sudo yum install libffi-devel.mips64el openssl-devel.mips64el libsqlite3x-devel.mips64el sqlite-devel.mips64el lbzip2-utils.mips64el lzma.mips64el tk.mips64el uuid.mips64el gdbm-devel.mips64el gdbm.mips64el openjpeg-devel.mips64el zlib-devel.mips64el libjpeg-turbo-devel.mips64el openjpeg-devel.mips64el
    
    wget https://www.python.org/ftp/python/3.7.5/Python-3.7.5.tgz && tar xzf Python-3.7.5.tgz && cd Python-3.7.5
    
    ./configure –prefix $HOME/python37–enable−shared
    
    make -j
    
    make install
    

    设置环境变量,使得python37生效

    export PATH=$HOME/python37/bin:$PATH
    export LD_LIBRARY_PATH=$HOME/python37/lib:$LD_LIBRARY_PATH
    
  3. Paddle依赖cmake进行编译构建,需要cmake版本>=3.10,龙芯操作系统源提供cmake的版本是3.9,且尝试源码编译cmake失败,此处临时的处理方式是修改Paddle主目录的CMakeLists.txt, cmake_minimum_required(VERSION 3.10) 修改为 cmake_minimum_required(VERSION 3.9)。等到龙芯系统支持cmake >= 3.10后则不需要其它操作。

  4. Paddle内部使用patchelf来修改动态库的rpath,操作系统提供的源包括了patchelf,直接安装即可,后续会考虑在MIPS上移出该依赖。

    sudo yum install patchelf.mips64el
    
  5. 将Paddle的源代码克隆到当下目录下的Paddle文件夹中,并进入Paddle目录

    git clone https://github.com/PaddlePaddle/Paddle.git
    
    cd Paddle
    
  6. 根据requirments.txt安装Python依赖库。

  7. 切换到develop分支下进行编译:

    git checkout develop
    
  8. 并且请创建并进入一个叫build的目录下:

    mkdir build && cd build
    
  9. 链接过程中打开文件数较多,可能超过系统默认限制导致编译出错,设置进程允许打开的最大文件数:

    ulimit -n 4096
    
  10. 执行cmake:

    具体编译选项含义请参见编译选项表

    For Python2:

    cmake .. -DPY_VERSION=2 -DPYTHON_EXECUTABLE=`which python2` -DWITH_MIPS=ON -DWITH_TESTING=OFF -DCMAKE_BUILD_TYPE=Release -DON_INFER=ON -DWITH_XBYAK=OFF -DWITH_MKL=OFF
    

    For Python3:

    cmake .. -DPY_VERSION=3 -DPYTHON_EXECUTABLE=`which python3` -DWITH_MIPS=ON -DWITH_TESTING=OFF -DCMAKE_BUILD_TYPE=Release -DON_INFER=ON -DWITH_XBYAK=OFF -DWITH_MKL=OFF
    
  11. 编译。

    make -j$(nproc)
    
  12. 编译成功后进入Paddle/build/python/dist目录下找到生成的.whl包。

  13. 在当前机器或目标机器安装编译好的.whl包:

    python -m pip install -U(whl包的名字)`或`python3 -m pip install -U(whl包的名字)
    

恭喜,至此您已完成PaddlePaddle在龙芯环境下的编译安装。

验证安装

安装完成后您可以使用 pythonpython3 进入python解释器,输入import paddle ,再输入 paddle.utils.run_check()

如果出现PaddlePaddle is installed successfully!,说明您已成功安装。

在mobilenetv1和resnet50模型上测试

wget -O profile.tar https://paddle-cetc15.bj.bcebos.com/profile.tar?authorization=bce-auth-v1/4409a3f3dd76482ab77af112631f01e4/2020-10-09T10:11:53Z/-1/host/786789f3445f498c6a1fd4d9cd3897ac7233700df0c6ae2fd78079eba89bf3fb
tar xf profile.tar && cd profile
python resnet.py --model_file ResNet50_inference/model --params_file ResNet50_inference/params
# 正确输出应为:[0.0002414  0.00022418 0.00053661 0.00028639 0.00072682 0.000213
#              0.00638718 0.00128127 0.00013535 0.0007676 ]
python mobilenetv1.py --model_file mobilenetv1/model --params_file mobilenetv1/params
# 正确输出应为:[0.00123949 0.00100392 0.00109539 0.00112206 0.00101901 0.00088412
#              0.00121536 0.00107679 0.00106071 0.00099605]
python ernie.py --model_dir ernieL3H128_model/
# 正确输出应为:[0.49879393 0.5012061 ]

如何卸载

请使用以下命令卸载PaddlePaddle:

python -m pip uninstall paddlepaddle

python3 -m pip uninstall paddlepaddle

备注

已在MIPS架构下测试过resnet50, mobilenetv1, ernie, ELMo等模型,基本保证了预测使用算子的正确性,如果您在使用过程中遇到计算结果错误,编译失败等问题,请到issue中留言,我们会及时解决。

预测文档见doc,使用示例见Paddle-Inference-Demo

下载安装Linux预测库

C++预测库

版本说明 预测库(1.8.5版本) 预测库(2.1.1版本) 预测库(develop版本)
manylinux_cpu_avx_mkl_gcc8.2 fluid_inference.tgz paddle_inference.tgz paddle_inference.tgz
manylinux_cpu_avx_mkl_gcc5.4 paddle_inference.tgz
manylinux_cpu_avx_openblas_gcc8.2 fluid_inference.tgz paddle_inference.tgz paddle_inference.tgz
manylinux_cpu_avx_openblas_gcc5.4 paddle_inference.tgz
manylinux_cpu_noavx_openblas_gcc8.2 fluid_inference.tgz paddle_inference.tgz paddle_inference.tgz
manylinux_cpu_noavx_openblas_gcc5.4 paddle_inference.tgz
manylinux_cuda10.1_cudnn7.6_avx_mkl_trt6_gcc8.2 paddle_inference.tgz
manylinux_cuda10.1_cudnn7.6_avx_mkl_trt6_gcc5.4 paddle_inference.tgz
manylinux_cuda10.2_cudnn8.1_avx_mkl_trt7_gcc8.2 paddle_inference.tgz
manylinux_cuda10.2_cudnn8.1_avx_mkl_trt7_gcc5.4 paddle_inference.tgz
manylinux_cuda11.1_cudnn8.1_avx_mkl_trt7_gcc8.2 paddle_inference.tgz
manylinux_cuda11.1_cudnn8.1_avx_mkl_trt7_gcc5.4 paddle_inference.tgz
Jetpack4.4: nv-jetson-cuda10.2-cudnn8-trt7(all) paddle_inference.tgz
Jetpack4.4: nv-jetson-cuda10.2-cudnn8-trt7(nano) paddle_inference.tgz
Jetpack4.4: nv-jetson-cuda10.2-cudnn8-trt7(tx2) paddle_inference.tgz
Jetpack4.4: nv-jetson-cuda10.2-cudnn8-trt7(xavier) paddle_inference.tgz

C预测库

版本说明 预测库(2.1.1版本)
manylinux_cpu_avx_mkl_gcc8.2 paddle_inference.tgz
manylinux_cpu_avx_mkl_gcc5.4 paddle_inference.tgz
manylinux_cpu_avx_openblas_gcc8.2 paddle_inference.tgz
manylinux_cpu_avx_openblas_gcc5.4 paddle_inference.tgz
manylinux_cpu_noavx_openblas_gcc8.2 paddle_inference.tgz
manylinux_cpu_noavx_openblas_gcc5.4 paddle_inference.tgz
manylinux_cuda10.1_cudnn7.6_avx_mkl_trt6_gcc8.2 paddle_inference.tgz
manylinux_cuda10.1_cudnn7.6_avx_mkl_trt6_gcc5.4 paddle_inference.tgz
manylinux_cuda10.2_cudnn8.1_avx_mkl_trt7_gcc8.2 paddle_inference.tgz
manylinux_cuda10.2_cudnn8.1_avx_mkl_trt7_gcc5.4 paddle_inference.tgz
manylinux_cuda11.1_cudnn8.1_avx_mkl_trt7_gcc8.2 paddle_inference.tgz
manylinux_cuda11.1_cudnn8.1_avx_mkl_trt7_gcc5.4 paddle_inference.tgz

Python预测库

版本说明 python3.6 python3.7 python3.8 python3.9
linux-cuda10.1-trt6-gcc8.2 paddlepaddle-cp36m.whl paddlepaddle-cp37m.whl paddlepaddle-cp38m.whl paddlepaddle-cp39m.whl
linux-cuda10.2-trt6-gcc8.2 paddlepaddle-cp36m.whl paddlepaddle-cp37m.whl paddlepaddle-cp38m.whl paddlepaddle-cp39m.whl
linux-cuda11.1-trt7-gcc8.2 paddlepaddle-cp36m.whl paddlepaddle-cp37m.whl paddlepaddle-cp38m.whl paddlepaddle-cp39m.whl
nv_jetson-cuda10.2-trt7-all paddlepaddle_gpu-2.1.1-cp36-cp36m-linux_aarch64.whl
nv_jetson-cuda10.2-trt7-maxwell paddlepaddle_gpu-2.1.1-cp36-cp36m-linux_aarch64.whl
nv_jetson-cuda10.2-trt7-pascal paddlepaddle_gpu-2.1.1-cp36-cp36m-linux_aarch64.whl
nv_jetson-cuda10.2-trt7-volta paddlepaddle_gpu-2.1.1-cp36-cp36m-linux_aarch64.whl

下载安装Windows预测库

请确认您的VS版本与下载链接对应的版本完全一致,目前暂不保证在其它VS版本的可用性

环境硬件配置:

操作系统 win10 家庭版本
CPU I7-8700K
内存 16G
硬盘 1T hdd + 256G ssd
显卡 GTX1080 8G

C++预测库

版本说明 预测库(1.8.4版本) 预测库(2.1.1版本) 编译器 cuDNN CUDA
cpu_avx_mkl fluid_inference.zip paddle_inference.zip MSVC 2017 - -
cpu_avx_openblas fluid_inference.zip paddle_inference.zip MSVC 2017 - -
cuda10.1_cudnn7.6_avx_mkl_trt6 paddle_inference.zip MSVC 2017 7.6 10.1
cuda10.2_cudnn7.6_avx_mkl_trt7 paddle_inference.zip MSVC 2017 7.6 10.2
cuda11.0_cudnn8.0_avx_mkl_trt7 paddle_inference.zip MSVC 2017 8.0 11.0

C预测库

版本说明 预测库(2.1.1版本) 编译器 cuDNN CUDA
cpu_avx_mkl paddle_inference.zip MSVC 2017 - -
cpu_avx_openblas paddle_inference.zip MSVC 2017 - -
cuda10.1_cudnn7.6_avx_mkl_trt6 paddle_inference.zip MSVC 2017 7.6 10.0
cuda10.2_cudnn7.6_avx_mkl_trt7 paddle_inference.zip MSVC 2017 7.6 10.2
cuda11.0_cudnn8.0_avx_mkl_trt7 paddle_inference.zip MSVC 2017 8.0 11.0

下载安装Mac预测库

C++预测库

版本说明 预测库(2.1.1版本)
cpu_avx_openblas paddle_inference.tgz

C预测库

版本说明 预测库(2.1.1版本)
cpu_avx_openblas paddle_inference.tgz

使用Paddle-TensorRT库预测

NVIDIA TensorRT 是一个高性能的深度学习预测库,可为深度学习推理应用程序提供低延迟和高吞吐量。PaddlePaddle 采用子图的形式对TensorRT进行了集成,即我们可以使用该模块来提升Paddle模型的预测性能。在这篇文章中,我们会介绍如何使用Paddle-TRT子图加速预测。

如果您需要安装[TensorRT](https://developer.nvidia.com/nvidia-tensorrt-6x-download),请参考[trt文档](https://docs.nvidia.com/deeplearning/tensorrt/archives/tensorrt-601/tensorrt-install-guide/index.html).

概述

当模型加载后,神经网络可以表示为由变量和运算节点组成的计算图。如果我们打开TRT子图模式,在图分析阶段,Paddle会对模型图进行分析同时发现图中可以使用TensorRT优化的子图,并使用TensorRT节点替换它们。在模型的推断期间,如果遇到TensorRT节点,Paddle会调用TensorRT库对该节点进行优化,其他的节点调用Paddle的原生实现。TensorRT除了有常见的OP融合以及显存/内存优化外,还针对性的对OP进行了优化加速实现,降低预测延迟,提升推理吞吐。

目前Paddle-TRT支持静态shape模式以及/动态shape模式。在静态shape模式下支持图像分类,分割,检测模型,同时也支持Fp16, Int8的预测加速。在动态shape模式下,除了对动态shape的图像模型(FCN, Faster rcnn)支持外,同时也对NLP的Bert/Ernie模型也进行了支持。

Paddle-TRT的现有能力:

1)静态shape:

支持模型:

分类模型

检测模型

分割模型

Mobilenetv1

yolov3

ICNET

Resnet50

SSD

UNet

Vgg16

Mask-rcnn

FCN

Resnext

Faster-rcnn

AlexNet

Cascade-rcnn

Se-ResNext

Retinanet

GoogLeNet

Mobilenet-SSD

DPN

Fp16:

Calib Int8:

优化信息序列化:

加载PaddleSlim Int8模型:

2)动态shape:

支持模型:

图像

NLP

FCN

Bert

Faster_RCNN

Ernie

Fp16:

Calib Int8:

优化信息序列化:

加载PaddleSlim Int8模型:

Note:

  1. 从源码编译时,TensorRT预测库目前仅支持使用GPU编译,且需要设置编译选项TENSORRT_ROOT为TensorRT所在的路径。

  2. Windows支持需要TensorRT 版本5.0以上。

  3. 使用Paddle-TRT的动态shape输入功能要求TRT的版本在6.0以上。

一:环境准备

使用Paddle-TRT功能,我们需要准备带TRT的Paddle运行环境,我们提供了以下几种方式:

1)linux下通过pip安装

请从[whl list](https://www.paddlepaddle.org.cn/documentation/docs/zh/install/Tables.html#whl-release)下载带trt且与自己环境一致的whl包,并通过pip安装

2)使用docker镜像

# 拉取镜像,该镜像预装Paddle 1.8 Python环境,并包含c++的预编译库,lib存放在主目录~/ 下。
docker pull hub.baidubce.com/paddlepaddle/paddle:1.8.0-gpu-cuda10.0-cudnn7-trt6

export CUDA_SO="$(\ls /usr/lib64/libcuda* | xargs -I{} echo '-v {}:{}') $(\ls /usr/lib64/libnvidia* | xargs -I{} echo '-v {}:{}')"
export DEVICES=$(\ls /dev/nvidia* | xargs -I{} echo '--device {}:{}')
export NVIDIA_SMI="-v /usr/bin/nvidia-smi:/usr/bin/nvidia-smi"

docker run $CUDA_SO $DEVICES $NVIDIA_SMI --name trt_open --privileged --security-opt seccomp=unconfined --net=host -v $PWD:/paddle -it hub.baidubce.com/paddlepaddle/paddle:1.8.0-gpu-cuda10.0-cudnn7-trt6 /bin/bash

3)手动编译 编译的方式请参照 编译文档

Note1: cmake 期间请设置 TENSORRT_ROOT (即TRT lib的路径), WITH_PYTHON (是否产出python whl包, 设置为ON)选项。

Note2: 编译期间会出现TensorRT相关的错误。

需要手动在 NvInfer.h (trt5) 或 NvInferRuntime.h (trt6) 文件中为 class IPluginFactory 和 class IGpuAllocator 分别添加虚析构函数:

virtual ~IPluginFactory() {};
virtual ~IGpuAllocator() {};

需要将 NvInferRuntime.h (trt6)中的 protected: ~IOptimizationProfile() noexcept = default;

改为

virtual ~IOptimizationProfile() noexcept = default;

二:API使用介绍

预测流程 一节中,我们了解到Paddle Inference预测包含了以下几个方面:

  • 配置推理选项

  • 创建predictor

  • 准备模型输入

  • 模型推理

  • 获取模型输出

使用Paddle-TRT 也是遵照这样的流程。我们先用一个简单的例子来介绍这一流程(我们假设您已经对Paddle Inference有一定的了解,如果您刚接触Paddle Inference,请访问 这里 对Paddle Inference有个初步认识。):

import numpy as np
import paddle.inference as paddle_infer

def create_predictor():
    config = paddle_infer.Config("./resnet50/model", "./resnet50/params")
    config.enable_memory_optim()
    config.enable_use_gpu(1000, 0)

    # 打开TensorRT。此接口的详细介绍请见下文
    config.enable_tensorrt_engine(workspace_size = 1 << 30,
                                  max_batch_size = 1,
                                  min_subgraph_size = 3,
                                  precision_mode=paddle_infer.PrecisionType.Float32,
                                  use_static = False, use_calib_mode = False)

    predictor = paddle_infer.create_predictor(config)
    return predictor

def run(predictor, img):
    # 准备输入
    input_names = predictor.get_input_names()
    for i,  name in enumerate(input_names):
        input_tensor = predictor.get_input_handle(name)
        input_tensor.reshape(img[i].shape)
        input_tensor.copy_from_cpu(img[i].copy())
    # 预测
    predictor.run()
    results = []
    # 获取输出
    output_names = predictor.get_output_names()
    for i, name in enumerate(output_names):
        output_tensor = predictor.get_output_handle(name)
        output_data = output_tensor.copy_to_cpu()
        results.append(output_data)
    return results

if __name__ == '__main__':
    pred = create_predictor()
    img = np.ones((1, 3, 224, 224)).astype(np.float32)
    result = run(pred, [img])
    print ("class index: ", np.argmax(result[0][0]))

通过例子我们可以看出,我们通过 enable_tensorrt_engine 接口来打开TensorRT选项的。

config.enable_tensorrt_engine(workspace_size = 1 << 30,
                              max_batch_size = 1,
                              min_subgraph_size = 3,
                              precision_mode=paddle_infer.PrecisionType.Float32,
                              use_static = False, use_calib_mode = False)

接下来让我们看下该接口中各个参数的作用:

  • workspace_size,类型:int,默认值为1 << 30 (1G)。指定TensorRT使用的工作空间大小,TensorRT会在该大小限制下筛选最优的kernel执行预测运算。

  • max_batch_size,类型:int,默认值为1。需要提前设置最大的batch大小,运行时batch大小不得超过此限定值。

  • min_subgraph_size,类型:int,默认值为3。Paddle-TRT是以子图的形式运行,为了避免性能损失,当子图内部节点个数大于 min_subgraph_size 的时候,才会使用Paddle-TRT运行。

  • precision_mode,类型:paddle_infer.PrecisionType, 默认值为 paddle_infer.PrecisionType.Float32。指定使用TRT的精度,支持FP32(Float32),FP16(Half),Int8(Int8)。若需要使用Paddle-TRT int8离线量化校准,需设定precision为 paddle_infer.PrecisionType.Int8 , 且设置 use_calib_mode 为True。

  • use_static,类型:bool, 默认值为False。如果指定为True,在初次运行程序的时候会将TRT的优化信息进行序列化到磁盘上,下次运行时直接加载优化的序列化信息而不需要重新生成。

  • use_calib_mode,类型:bool, 默认值为False。若要运行Paddle-TRT int8离线量化校准,需要将此选项设置为True。

Int8量化预测

神经网络的参数在一定程度上是冗余的,在很多任务上,我们可以在保证模型精度的前提下,将Float32的模型转换成Int8的模型,从而达到减小计算量降低运算耗时、降低计算内存、降低模型大小的目的。使用Int8量化预测的流程可以分为两步:1)产出量化模型;2)加载量化模型进行Int8预测。下面我们对使用Paddle-TRT进行Int8量化预测的完整流程进行详细介绍。

1. 产出量化模型

目前,我们支持通过两种方式产出量化模型:

  1. 使用TensorRT自带Int8离线量化校准功能。校准即基于训练好的FP32模型和少量校准数据(如500~1000张图片)生成校准表(Calibration table),预测时,加载FP32模型和此校准表即可使用Int8精度预测。生成校准表的方法如下:

  • 指定TensorRT配置时,将 precision_mode 设置为 paddle_infer.PrecisionType.Int8 并且设置 use_calib_modeTrue

    config.enable_tensorrt_engine(
      workspace_size=1<<30,
      max_batch_size=1, min_subgraph_size=5,
      precision_mode=paddle_infer.PrecisionType.Int8,
      use_static=False, use_calib_mode=True)
    
  • 准备500张左右的真实输入数据,在上述配置下,运行模型。(Paddle-TRT会统计模型中每个tensor值的范围信息,并将其记录到校准表中,运行结束后,会将校准表写入模型目录下的 _opt_cache 目录中)

如果想要了解使用TensorRT自带Int8离线量化校准功能生成校准表的完整代码,请参考 这里 的demo。

  1. 使用模型压缩工具库PaddleSlim产出量化模型。PaddleSlim支持离线量化和在线量化功能,其中,离线量化与TensorRT离线量化校准原理相似;在线量化又称量化训练(Quantization Aware Training, QAT),是基于较多数据(如>=5000张图片)对预训练模型进行重新训练,使用模拟量化的思想,在训练阶段更新权重,实现减小量化误差的方法。使用PaddleSlim产出量化模型可以参考文档:

离线量化的优点是无需重新训练,简单易用,但量化后精度可能受影响;量化训练的优点是模型精度受量化影响较小,但需要重新训练模型,使用门槛稍高。在实际使用中,我们推荐先使用TRT离线量化校准功能生成量化模型,若精度不能满足需求,再使用PaddleSlim产出量化模型。

2. 加载量化模型进行Int8预测

加载量化模型进行Int8预测,需要在指定TensorRT配置时,将 precision_mode 设置为 paddle_infer.PrecisionType.Int8

若使用的量化模型为TRT离线量化校准产出的,需要将 use_calib_mode 设为 True

config.enable_tensorrt_engine(
  workspace_size=1<<30,
  max_batch_size=1, min_subgraph_size=5,
  precision_mode=paddle_infer.PrecisionType.Int8,
  use_static=False, use_calib_mode=True)

完整demo请参考 这里

若使用的量化模型为PaddleSlim量化产出的,需要将 use_calib_mode 设为 False

config.enable_tensorrt_engine(
  workspace_size=1<<30,
  max_batch_size=1, min_subgraph_size=5,
  precision_mode=paddle_infer.PrecisionType.Int8,
  use_static=False, use_calib_mode=False)

完整demo请参考 这里

运行Dynamic shape

从1.8 版本开始, Paddle对TRT子图进行了Dynamic shape的支持。 使用接口如下:

config.enable_tensorrt_engine(
        workspace_size = 1<<30,
        max_batch_size=1, min_subgraph_size=5,
        precision_mode=paddle_infer.PrecisionType.Float32,
        use_static=False, use_calib_mode=False)

min_input_shape = {"image":[1,3, 10, 10]}
max_input_shape = {"image":[1,3, 224, 224]}
opt_input_shape = {"image":[1,3, 100, 100]}

config.set_trt_dynamic_shape_info(min_input_shape, max_input_shape, opt_input_shape)

从上述使用方式来看,在 config.enable_tensorrt_engine 接口的基础上,新加了一个config.set_trt_dynamic_shape_info 的接口。

该接口用来设置模型输入的最小,最大,以及最优的输入shape。 其中,最优的shape处于最小最大shape之间,在预测初始化期间,会根据opt shape对op选择最优的kernel。

调用了 config.set_trt_dynamic_shape_info 接口,预测器会运行TRT子图的动态输入模式,运行期间可以接受最小,最大shape间的任意的shape的输入数据。

三:测试样例

我们在github上提供了使用TRT子图预测的更多样例:

  • Python 样例请访问此处 链接

  • C++ 样例地址请访问此处 链接

四:Paddle-TRT子图运行原理

PaddlePaddle采用子图的形式对TensorRT进行集成,当模型加载后,神经网络可以表示为由变量和运算节点组成的计算图。Paddle TensorRT实现的功能是对整个图进行扫描,发现图中可以使用TensorRT优化的子图,并使用TensorRT节点替换它们。在模型的推断期间,如果遇到TensorRT节点,Paddle会调用TensorRT库对该节点进行优化,其他的节点调用Paddle的原生实现。TensorRT在推断期间能够进行Op的横向和纵向融合,过滤掉冗余的Op,并对特定平台下的特定的Op选择合适的kernel等进行优化,能够加快模型的预测速度。

下图使用一个简单的模型展示了这个过程:

原始网络

https://raw.githubusercontent.com/NHZlX/FluidDoc/add_trt_doc/doc/fluid/user_guides/howto/inference/image/model_graph_original.png

转换的网络

https://raw.githubusercontent.com/NHZlX/FluidDoc/add_trt_doc/doc/fluid/user_guides/howto/inference/image/model_graph_trt.png

我们可以在原始模型网络中看到,绿色节点表示可以被TensorRT支持的节点,红色节点表示网络中的变量,黄色表示Paddle只能被Paddle原生实现执行的节点。那些在原始网络中的绿色节点被提取出来汇集成子图,并由一个TensorRT节点代替,成为转换后网络中的 block-25 节点。在网络运行过程中,如果遇到该节点,Paddle将调用TensorRT库来对其执行。

X86 CPU 上部署量化模型

1 概述

众所周知,模型量化可以有效加快模型预测性能,飞桨也提供了强大的模型量化功能。所以,本文主要介绍在X86 CPU部署PaddleSlim产出的量化模型。

对于常见图像分类模型,在Casecade Lake机器上(例如Intel® Xeon® Gold 6271、6248,X2XX等),INT8模型进行推理的速度通常是FP32模型的3-3.7倍;在SkyLake机器上(例如Intel® Xeon® Gold 6148、8180,X1XX等),INT8模型进行推理的速度通常是FP32模型的1.5倍。

X86 CPU部署量化模型的步骤:

  • 产出量化模型:使用PaddleSlim训练并产出量化模型

  • 转换量化模型:将量化模型转换成最终部署的量化模型

  • 部署量化模型:使用Paddle Inference预测库部署量化模型

2 图像分类INT8模型在 Xeon(R) 6271 上的精度和性能

图像分类INT8模型在 Intel(R) Xeon(R) Gold 6271 上精度

Model FP32 Top1 Accuracy INT8 Top1 Accuracy Top1 Diff FP32 Top5 Accuracy INT8 Top5 Accuracy Top5 Diff
MobileNet-V1 70.78% 70.74% -0.04% 89.69% 89.43% -0.26%
MobileNet-V2 71.90% 72.21% 0.31% 90.56% 90.62% 0.06%
ResNet101 77.50% 77.60% 0.10% 93.58% 93.55% -0.03%
ResNet50 76.63% 76.50% -0.13% 93.10% 92.98% -0.12%
VGG16 72.08% 71.74% -0.34% 90.63% 89.71% -0.92%
VGG19 72.57% 72.12% -0.45% 90.84% 90.15% -0.69%

图像分类INT8模型在 Intel(R) Xeon(R) Gold 6271 单核上性能

Model FP32 (images/s) INT8 (images/s) Ratio (INT8/FP32)
MobileNet-V1 74.05 216.36 2.92
MobileNet-V2 88.60 205.84 2.32
ResNet101 7.20 26.48 3.68
ResNet50 13.23 50.02 3.78
VGG16 3.47 10.67 3.07
VGG19 2.83 9.09 3.21

自然语言处理INT8模型在 Xeon(R) 6271 上的精度和性能

I. Ernie INT8 DNNL 在 Intel(R) Xeon(R) Gold 6271 的精度结果

Model FP32 Accuracy INT8 Accuracy Accuracy Diff
Ernie 80.20% 79.44% -0.76%

II. Ernie INT8 DNNL 在 Intel(R) Xeon(R) Gold 6271 上单样本耗时

Threads FP32 Latency (ms) INT8 Latency (ms) Ratio (FP32/INT8)
1 thread 237.21 79.26 2.99X
20 threads 22.08 12.57 1.76X

3 PaddleSlim 产出量化模型

X86 CPU预测端支持PaddleSlim量化训练方法和静态离线量化方法产出的量化模型。

关于使用PaddleSlim产出量化模型,请参考文档:

在产出部署在X86 CPU预测端的模型时,需要注意:

  • 静态离线量化方法支持的量化OP有conv2d, depthwise_conv2d, mul和matmul,所以 quant_post_static的输入参数 quantizable_op_type可以是这四个op的组合。

  • 量化训练方法支持的量化OP有conv2d, depthwise_conv2d, mul和matmul,所以 quant_aware 输入配置config中的quantize_op_types可以是这四个op的组合。

4 转换量化模型

在X86 CPU预测端上部署量化模型之前,需要对量化模型进行转换和优化操作。

安装Paddle

参考Paddle官网,安装Paddle最新CPU或者GPU版本。

准备脚本

下载脚本到本地.

wget https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/fluid/contrib/slim/tests/save_quant_model.py

save_quant_model.py脚本的参数说明:

  • quant_model_path: 为输入参数,必填。为PaddleSlim产出的量化模型。

  • int8_model_save_path: 量化模型转换后保存的路径。

转换量化模型

使用脚本转化量化模型,比如:

python save_quant_model.py \
    --quant_model_path=/PATH/TO/SAVE/FLOAT32/QUANT/MODEL \
    --int8_model_save_path=/PATH/TO/SAVE/INT8/MODEL

5 Paddle Inference 部署量化模型

检查机器

大家可以通过在命令行红输入lscpu查看本机支持指令。

  • 在支持avx512_vnni的CPU服务器上,如:Casecade Lake, Model name: Intel(R) Xeon(R) Gold X2XX,INT8精度和性能最高,INT8性能提升为FP32模型的3~3.7倍。

  • 在支持avx512但是不支持avx512_vnni的CPU服务器上,如:SkyLake, Model name:Intel(R) Xeon(R) Gold X1XX,INT8性能为FP32性能的1.5倍左右。

  • 请确保机器支持完整的avx512指令集。

预测部署

参考X86 Linux上预测部署示例X86 Windows上预测部署示例,准备预测库,对模型进行部署。

请注意,在X86 CPU预测端部署量化模型,必须开启MKLDNN,不要开启IrOptim。

C++ API举例如下。

paddle_infer::Config config;
if (FLAGS_model_dir == "") {
config.SetModel(FLAGS_model_file, FLAGS_params_file); // Load combined model
} else {
config.SetModel(FLAGS_model_dir); // Load no-combined model
}
config.EnableMKLDNN();
config.SwitchIrOptim(false);
config.SetCpuMathLibraryNumThreads(FLAGS_threads);
config.EnableMemoryOptim();

auto predictor = paddle_infer::CreatePredictor(config);

Python API举例如下。

if args.model_dir == "":
    config = Config(args.model_file, args.params_file)
else:
    config = Config(args.model_dir)
config.enable_mkldnn()
config.set_cpu_math_library_num_threads(args.threads)
config.switch_ir_optim(False)
config.enable_memory_optim()

predictor = create_predictor(config)

模型可视化

通过 快速开始 一节,我们了解到,预测模型包含了两个文件,一部分为模型结构文件,通常以 model__model__ 文件存在;另一部分为参数文件,通常以params 文件或一堆分散的文件存在。

模型结构文件,顾名思义,存储了模型的拓扑结构,其中包括模型中各种OP的计算顺序以及OP的详细信息。很多时候,我们希望能够将这些模型的结构以及内部信息可视化,方便我们进行模型分析。接下来将会通过两种方式来讲述如何对Paddle 预测模型进行可视化。

一: 通过 VisualDL 可视化

1) 安装

VisualDL是飞桨可视化分析工具,以丰富的图表呈现训练参数变化趋势、模型结构、数据样本、高维数据分布等,帮助用户更清晰直观地理解深度学习模型训练过程及模型结构,实现高效的模型优化。 我们可以进入 GitHub主页 进行下载安装。

2)可视化

点击 下载测试模型。

支持两种启动方式:

  • 前端拖拽上传模型文件:

    • 无需添加任何参数,在命令行执行 visualdl 后启动界面上传文件即可:

https://user-images.githubusercontent.com/48054808/88628504-a8b66980-d0e0-11ea-908b-196d02ed1fa2.png
  • 后端透传模型文件:

    • 在命令行加入参数 –model 并指定 模型文件 路径(非文件夹路径),即可启动:

visualdl --model ./log/model --port 8080
https://user-images.githubusercontent.com/48054808/88621327-b664f280-d0d2-11ea-9e76-e3fcfeea4e57.png

Graph功能详细使用,请见 Graph使用指南

二: 通过代码方式生成dot文件

1)pip 安装Paddle

2)生成dot文件

点击 下载测试模型。

#!/usr/bin/env python
import paddle.fluid as fluid
from paddle.fluid import core
from paddle.fluid.framework import IrGraph

def get_graph(program_path):
    with open(program_path, 'rb') as f:
            binary_str = f.read()
    program =   fluid.framework.Program.parse_from_string(binary_str)
    return IrGraph(core.Graph(program.desc), for_test=True)

if __name__ == '__main__':
    program_path = './lecture_model/__model__'
    offline_graph = get_graph(program_path)
    offline_graph.draw('.', 'test_model', [])

3)生成svg

Note:需要环境中安装graphviz

dot -Tsvg ./test_mode.dot -o test_model.svg

然后将test_model.svg以浏览器打开预览即可。

https://user-images.githubusercontent.com/5595332/81796500-19b59e80-9540-11ea-8c70-31122e969683.png

模型转换工具 X2Paddle

X2Paddle可以将caffe、tensorflow、onnx模型转换成Paddle支持的模型。

X2Paddle 支持将Caffe/TensorFlow模型转换为PaddlePaddle模型。目前X2Paddle支持的模型参考 x2paddle_model_zoo

多框架支持

模型

caffe

tensorflow

onnx

mobilenetv1

Y

Y

F

mobilenetv2

Y

Y

Y

resnet18

Y

Y

F

resnet50

Y

Y

Y

mnasnet

Y

Y

F

efficientnet

Y

Y

Y

squeezenetv1.1

Y

Y

Y

shufflenet

Y

Y

F

mobilenet_ssd

Y

Y

F

mobilenet_yolov3

F

Y

F

inceptionv4

F

F

F

mtcnn

Y

Y

F

facedetection

Y

F

F

unet

Y

Y

F

ocr_attention

F

F

F

vgg16

F

F

F

安装

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 的形式提交给我们,我们会实时跟进。

X86 Linux上预测部署示例

1 C++预测部署示例

C++示例代码在链接,下面从流程解析编译运行示例两方面介绍。

1.1 流程解析

1.1.1 准备预测库

请参考下载安装预测库文档下载Paddle Inference C++预测库,或者参考源码编译文档编译Paddle Inference C++预测库。

1.1.2 准备预测模型

使用Paddle训练结束后,得到预测模型,可以用于预测部署。

本示例准备了mobilenet_v1预测模型,可以从链接下载,或者wget下载。

wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/mobilenetv1.tgz
1.1.3 包含头文件

使用Paddle预测库,只需要包含 paddle_inference_api.h 头文件。

#include "paddle/include/paddle_inference_api.h"
1.1.4 设置Config

根据预测部署的实际情况,设置Config,用于后续创建Predictor。

Config默认是使用CPU预测,可以设置开启MKLDNN加速、设置CPU的线程数、开启IR优化、开启内存优化。

paddle_infer::Config config;
if (FLAGS_model_dir == "") {
config.SetModel(FLAGS_model_file, FLAGS_params_file); // Load combined model
} else {
config.SetModel(FLAGS_model_dir); // Load no-combined model
}
config.EnableMKLDNN();
config.SetCpuMathLibraryNumThreads(FLAGS_threads);
config.SwitchIrOptim();
config.EnableMemoryOptim();
1.1.5 创建Predictor
std::shared_ptr<paddle_infer::Predictor> predictor = paddle_infer::CreatePredictor(config);
1.1.6 设置输入

从Predictor中获取输入的names和handle,然后设置输入数据。

auto input_names = predictor->GetInputNames();
auto input_t = predictor->GetInputHandle(input_names[0]);
std::vector<int> input_shape = {1, 3, 224, 224};
std::vector<float> input_data(1 * 3 * 224 * 224, 1);
input_t->Reshape(input_shape);
input_t->CopyFromCpu(input_data.data());
1.1.7 执行Predictor
predictor->Run();
1.1.8 获取输出
auto output_names = predictor->GetOutputNames();
auto output_t = predictor->GetOutputHandle(output_names[0]);
std::vector<int> output_shape = output_t->shape();
int out_num = std::accumulate(output_shape.begin(), output_shape.end(), 1,
                              std::multiplies<int>());
std::vector<float> out_data;
out_data.resize(out_num);
output_t->CopyToCpu(out_data.data());

1.2 编译运行示例

1.2.1 编译示例

文件model_test.cc 为预测的样例程序(程序中的输入为固定值,如果您有opencv或其他方式进行数据读取的需求,需要对程序进行一定的修改)。文件CMakeLists.txt 为编译构建文件。脚本run_impl.sh 包含了第三方库、预编译库的信息配置。

根据前面步骤下载Paddle预测库和mobilenetv1模型。

打开 run_impl.sh 文件,设置 LIB_DIR 为下载的预测库路径,比如 LIB_DIR=/work/Paddle/build/paddle_inference_install_dir

运行 sh run_impl.sh, 会在当前目录下编译产生build目录。

注意:Paddle Inference 提供下载的C++预测库对应GCC 4.8,所以请检查您电脑中GCC版本是否一致,如果不一致可能出现未知错误。

1.2.2 运行示例

进入build目录,运行样例。

cd build
./model_test --model_file inference.pdmodel --params_file inference.pdiparams

运行结束后,程序会将模型结果打印到屏幕,说明运行成功。

2 Python预测部署示例

Python预测部署示例代码在链接,下面从流程解析编译运行示例两方面介绍。

2.1 流程解析

2.1.1 准备环境

请参考飞桨官网安装2.0及以上版本的Paddle。

Python安装opencv:pip install opencv-python

2.1.2 准备预测模型

使用Paddle训练结束后,得到预测模型,可以用于预测部署。

本示例准备了mobilenet_v1预测模型,可以从链接下载,或者wget下载。

wget https://paddle-inference-dist.cdn.bcebos.com/PaddleInference/mobilenetv1_fp32.tar.gz
tar zxf mobilenetv1_fp32.tar.gz
2.1.3 Python导入
from paddle.inference import Config
from paddle.inference import create_predictor
2.1.4 设置Config

根据预测部署的实际情况,设置Config,用于后续创建Predictor。

Config默认是使用CPU预测,可以设置开启MKLDNN加速、设置CPU的线程数、开启IR优化、开启内存优化。

# args 是解析的输入参数
if args.model_dir == "":
    config = Config(args.model_file, args.params_file)
else:
    config = Config(args.model_dir)
config.enable_mkldnn()
config.set_cpu_math_library_num_threads(args.threads)
config.switch_ir_optim()
config.enable_memory_optim()
2.1.5 创建Predictor
predictor = create_predictor(config)
2.1.6 设置输入

从Predictor中获取输入的names和handle,然后设置输入数据。

img = cv2.imread(args.img_path)
img = preprocess(img)
input_names = predictor.get_input_names()
input_tensor = predictor.get_input_handle(input_names[0])
input_tensor.reshape(img.shape)
input_tensor.copy_from_cpu(img.copy())
2.1.7 执行Predictor
predictor.run();
2.1.8 获取输出
output_names = predictor.get_output_names()
output_tensor = predictor.get_output_handle(output_names[0])
output_data = output_tensor.copy_to_cpu()

2.2 编译运行示例

文件img_preprocess.py是对图像进行预处理。 文件model_test.py是示例程序。

参考前面步骤准备环境、下载预测模型。

下载预测图片。

wget https://paddle-inference-dist.bj.bcebos.com/inference_demo/python/resnet50/ILSVRC2012_val_00000247.jpeg

执行预测命令。

python model_test.py --model_dir mobilenetv1_fp32 --img_path ILSVRC2012_val_00000247.jpeg

运行结束后,程序会将模型结果打印到屏幕,说明运行成功。

X86 Windows上预测部署示例

1 C++预测部署示例

C++示例代码在链接,下面从流程解析编译运行示例两方面介绍。

1.1 流程解析

1.1.1 准备预测库

请参考下载安装预测库文档下载Paddle Inference C++预测库,或者参考源码编译文档编译Paddle Inference C++预测库。

1.1.2 准备预测模型

使用Paddle训练结束后,得到预测模型,可以用于预测部署。

本示例准备了mobilenet_v1预测模型,可以从链接下载,或者wget下载。

wget https://paddle-inference-dist.cdn.bcebos.com/PaddleInference/mobilenetv1_fp32.tar.gz
1.1.3 包含头文件

使用Paddle预测库,只需要包含 paddle_inference_api.h 头文件。

#include "paddle/include/paddle_inference_api.h"
1.1.4 设置Config

根据预测部署的实际情况,设置Config,用于后续创建Predictor。

Config默认是使用CPU预测,可以设置开启MKLDNN加速、设置CPU的线程数、开启IR优化、开启内存优化。

paddle_infer::Config config;
if (FLAGS_model_dir == "") {
config.SetModel(FLAGS_model_file, FLAGS_params_file); // Load combined model
} else {
config.SetModel(FLAGS_model_dir); // Load no-combined model
}
config.EnableMKLDNN();
config.SetCpuMathLibraryNumThreads(FLAGS_threads);
config.SwitchIrOptim();
config.EnableMemoryOptim();
1.1.5 创建Predictor
std::shared_ptr<paddle_infer::Predictor> predictor = paddle_infer::CreatePredictor(config);
1.1.6 设置输入

从Predictor中获取输入的names和handle,然后设置输入数据。

auto input_names = predictor->GetInputNames();
auto input_t = predictor->GetInputHandle(input_names[0]);
std::vector<int> input_shape = {1, 3, 224, 224};
std::vector<float> input_data(1 * 3 * 224 * 224, 1);
input_t->Reshape(input_shape);
input_t->CopyFromCpu(input_data.data());
1.1.7 执行Predictor
predictor->Run();
1.1.8 获取输出
auto output_names = predictor->GetOutputNames();
auto output_t = predictor->GetOutputHandle(output_names[0]);
std::vector<int> output_shape = output_t->shape();
int out_num = std::accumulate(output_shape.begin(), output_shape.end(), 1,
                              std::multiplies<int>());
std::vector<float> out_data;
out_data.resize(out_num);
output_t->CopyToCpu(out_data.data());

1.2 编译运行示例

1.2.1 编译示例

文件model_test.cc 为预测的样例程序(程序中的输入为固定值,如果您有opencv或其他方式进行数据读取的需求,需要对程序进行一定的修改)。文件CMakeLists.txt 为编译构建文件。

根据前面步骤下载Paddle预测库和mobilenetv1模型。

使用cmake-gui程序生成vs工程:

  • 选择源代码路径,及编译产物路径,如图所示

_images/win_x86_cpu_cmake_1.pngwin_x86_cpu_cmake_1

  • 点击Configure,选择Visual Studio且选择x64版本如图所示,点击Finish,由于我们没有加入必要的CMake Options,会导致configure失败,请继续下一步。

_images/win_x86_cpu_cmake_2.pngwin_x86_cpu_cmake_2

  • 设置CMake Options,点击Add Entry,新增PADDLE_LIB,CMAKE_BUILD_TYPE,DEMO_NAME等选项。具体配置项如下图所示,其中PADDLE_LIB为您下载的预测库路径。

_images/win_x86_cpu_cmake_3.pngwin_x86_cpu_cmake_3

  • 点击Configure,log信息显示Configure done代表配置成功,接下来点击Generate生成vs工程,log信息显示Generate done,代表生成成功,最后点击Open Project打开Visual Studio.

  • 设置为Release/x64,编译,编译产物在build/Release目录下。

_images/win_x86_cpu_vs_1.pngwin_x86_cpu_vs_1

1.2.2 运行示例

首先设置model_test工程为启动首选项。

_images/win_x86_cpu_vs_2.pngwin_x86_cpu_vs_2

配置输入flags,即设置您之前下载的模型路径。点击Debug选项卡的model_test Properities..

_images/win_x86_cpu_vs_3.pngwin_x86_cpu_vs_3

点击Debug选项卡下的Start Without Debugging选项开始执行程序。

_images/win_x86_cpu_vs_4.pngwin_x86_cpu_vs_4

2 Python预测部署示例

Python预测部署示例代码在链接,下面从流程解析编译运行示例两方面介绍。

2.1 流程解析

2.1.1 准备环境

请参考飞桨官网安装2.0及以上版本的Paddle。

Python安装opencv:pip install opencv-python

2.1.2 准备预测模型

使用Paddle训练结束后,得到预测模型,可以用于预测部署。

本示例准备了mobilenet_v1预测模型,可以从链接下载,或者wget下载。

wget https://paddle-inference-dist.cdn.bcebos.com/PaddleInference/mobilenetv1_fp32.tar.gz
tar zxf mobilenetv1_fp32.tar.gz
2.1.3 Python导入
from paddle.inference import Config
from paddle.inference import create_predictor
2.1.4 设置Config

根据预测部署的实际情况,设置Config,用于后续创建Predictor。

Config默认是使用CPU预测,可以设置开启MKLDNN加速、设置CPU的线程数、开启IR优化、开启内存优化。

# args 是解析的输入参数
if args.model_dir == "":
    config = Config(args.model_file, args.params_file)
else:
    config = Config(args.model_dir)
config.enable_mkldnn()
config.set_cpu_math_library_num_threads(args.threads)
config.switch_ir_optim()
config.enable_memory_optim()
2.1.5 创建Predictor
predictor = create_predictor(config)
2.1.6 设置输入

从Predictor中获取输入的names和handle,然后设置输入数据。

img = cv2.imread(args.img_path)
img = preprocess(img)
input_names = predictor.get_input_names()
input_tensor = predictor.get_input_handle(input_names[0])
input_tensor.reshape(img.shape)
input_tensor.copy_from_cpu(img.copy())
2.1.7 执行Predictor
predictor.run();
2.1.8 获取输出
output_names = predictor.get_output_names()
output_tensor = predictor.get_output_handle(output_names[0])
output_data = output_tensor.copy_to_cpu()

2.2 编译运行示例

文件img_preprocess.py是对图像进行预处理。 文件model_test.py是示例程序。

参考前面步骤准备环境、下载预测模型。

下载预测图片。

wget https://paddle-inference-dist.bj.bcebos.com/inference_demo/python/resnet50/ILSVRC2012_val_00000247.jpeg

执行预测命令。

python model_test.py --model_dir mobilenetv1_fp32 --img_path ILSVRC2012_val_00000247.jpeg

运行结束后,程序会将模型结果打印到屏幕,说明运行成功。

使用昆仑预测

百度的昆仑芯⽚是⼀款⾼性能的AI SoC芯⽚,⽀持推理和训练。昆仑芯⽚采⽤百度的先进AI架构,⾮常适合常⽤的深度学习和机器学习算法的云端计算需求,并能适配诸如⾃然语⾔处理、⼤规模语⾳识别、⾃动驾驶、⼤规模推荐等多种终端场景的计算需求。

Paddle Inference集成了Paddle-Lite预测引擎在昆仑xpu上进行预测部署。

编译注意事项

请确保编译的时候设置了WITH_LITE=ON,且XPU_SDK_ROOT设置了正确的路径。

使用介绍

在使用Predictor时,我们通过配置Config中的接口,在XPU上运行。

config->EnableLiteEngine(
    precision_mode=PrecisionType::kFloat32,
    zero_copy=false,
    passes_filter={},
    ops_filter={},
)
  • precision_mode,类型:enum class PrecisionType {kFloat32 = 0, kHalf, kInt8,};, 默认值为PrecisionType::kFloat32。指定lite子图的运行精度。

  • zero_copy,类型:bool,lite子图与Paddle之间的数据传递是否是零拷贝模式。

  • passes_filter,类型:std::vector<std::string>,默认为空,扩展借口,暂不使用。

  • ops_filer,类型:std::vector<std::string>,默认为空,显示指定哪些op不使用lite子图运行。

Python接口如下:

config.enable_lite_engine(
    precision_mode=PrecisionType.Float32,
    zero_copy=False,
    passes_filter=[],
    ops_filter=[]
)

Python demo

因目前Paddle-Inference目前未将xpu sdk打包到whl包内,所以需要用户下载xpu sdk,并加入到环境变量中,之后会考虑解决该问题。

下载xpu_tool_chain,解压后将shlib加入到LD_LIBRARY_PATH

tar xzf xpu_tool_chain.tgz
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/output/XTDK/shlib/:$PWD/output/XTDK/runtime/shlib/

下载resnet50模型,并解压,运行如下命令将会调用预测引擎

python resnet50_subgraph.py --model_file ./ResNet50/model --params_file ./ResNet50/params

resnet50_subgraph.py的内容是:

import argparse
import time
import numpy as np
from paddle.inference import Config, PrecisionType
from paddle.inference import create_predictor

def main():
    args = parse_args()

    config = set_config(args)

    predictor = create_predictor(config)

    input_names = predictor.get_input_names()
    input_hanlde = predictor.get_input_handle(input_names[0])

    fake_input = np.ones((args.batch_size, 3, 224, 224)).astype("float32")
    input_hanlde.reshape([args.batch_size, 3, 224, 224])
    input_hanlde.copy_from_cpu(fake_input)

    for i in range(args.warmup):
      predictor.run()

    start_time = time.time()
    for i in range(args.repeats):
      predictor.run()

    output_names = predictor.get_output_names()
    output_handle = predictor.get_output_handle(output_names[0])
    output_data = output_handle.copy_to_cpu()
    end_time = time.time()
    print(output_data[0, :10])
    print('time is: {}'.format((end_time-start_time)/args.repeats * 1000))

def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument("--model_dir", type=str, help="model dir")
    parser.add_argument("--model_file", type=str, help="model filename")
    parser.add_argument("--params_file", type=str, help="parameter filename")
    parser.add_argument("--batch_size", type=int, default=1, help="batch size")
    parser.add_argument("--warmup", type=int, default=0, help="warmup")
    parser.add_argument("--repeats", type=int, default=1, help="repeats")
    parser.add_argument("--math_thread_num", type=int, default=1, help="math_thread_num")

    return parser.parse_args()

def set_config(args):
    config = Config(args.model_file, args.params_file)
    config.enable_lite_engine(PrecisionType.Float32, True)
    # use lite xpu subgraph
    config.enable_xpu(10 * 1024 * 1024)
    # use lite cuda subgraph
    # config.enable_use_gpu(100, 0)
    config.set_cpu_math_library_num_threads(args.math_thread_num)
    return config

if __name__ == "__main__":
    main()

Linux上GPU预测部署示例

1 C++预测部署示例

C++示例代码在链接,下面从流程解析编译运行示例两方面介绍。

1.1 流程解析

1.1.1 准备预测库

请参考推理库下载文档下载Paddle C++预测库,名称中带有 cuda 的为用于GPU的预测库。

1.1.2 准备预测模型

使用Paddle训练结束后,得到预测模型,可以用于预测部署。

本示例准备了mobilenet_v1预测模型,可以从链接下载,或者wget下载。

wget https://paddle-inference-dist.cdn.bcebos.com/PaddleInference/mobilenetv1_fp32.tar.gz
1.1.3 包含头文件

使用Paddle预测库,只需要包含 paddle_inference_api.h 头文件。

#include "paddle/include/paddle_inference_api.h"
1.1.4 设置Config

根据预测部署的实际情况,设置Config,用于后续创建Predictor。

Config默认是使用CPU预测,若要使用GPU预测,需要手动开启,设置运行的GPU卡号和分配的初始显存。可以设置开启TensorRT加速、开启IR优化、开启内存优化。使用Paddle-TensorRT相关说明和示例可以参考文档

paddle_infer::Config config;
if (FLAGS_model_dir == "") {
config.SetModel(FLAGS_model_file, FLAGS_params_file); // Load combined model
} else {
config.SetModel(FLAGS_model_dir); // Load no-combined model
}
config.EnableUseGpu(500, 0);
config.SwitchIrOptim(true);
config.EnableMemoryOptim();
config.EnableTensorRtEngine(1 << 30, FLAGS_batch_size, 10, PrecisionType::kFloat32, false, false);
1.1.5 创建Predictor
std::shared_ptr<paddle_infer::Predictor> predictor = paddle_infer::CreatePredictor(config);
1.1.6 设置输入

从Predictor中获取输入的names和handle,然后设置输入数据。

auto input_names = predictor->GetInputNames();
auto input_t = predictor->GetInputHandle(input_names[0]);
std::vector<int> input_shape = {1, 3, 224, 224};
std::vector<float> input_data(1 * 3 * 224 * 224, 1);
input_t->Reshape(input_shape);
input_t->CopyFromCpu(input_data.data());
1.1.7 执行Predictor
predictor->Run();
1.1.8 获取输出
auto output_names = predictor->GetOutputNames();
auto output_t = predictor->GetOutputHandle(output_names[0]);
std::vector<int> output_shape = output_t->shape();
int out_num = std::accumulate(output_shape.begin(), output_shape.end(), 1,
                              std::multiplies<int>());
std::vector<float> out_data;
out_data.resize(out_num);
output_t->CopyToCpu(out_data.data());

1.2 编译运行示例

1.2.1 编译示例

文件model_test.cc 为预测的样例程序(程序中的输入为固定值,如果您有opencv或其他方式进行数据读取的需求,需要对程序进行一定的修改)。文件CMakeLists.txt 为编译构建文件。脚本run_impl.sh 包含了第三方库、预编译库的信息配置。

根据前面步骤下载Paddle预测库和mobilenetv1模型。

打开 run_impl.sh 文件,设置 LIB_DIR 为下载的预测库路径,比如 LIB_DIR=/work/Paddle/build/paddle_inference_install_dir

运行 sh run_impl.sh, 会在当前目录下编译产生build目录。

1.2.2 运行示例

进入build目录,运行样例。

cd build
./model_test --model_dir=mobilenetv1_fp32_dir

运行结束后,程序会将模型结果打印到屏幕,说明运行成功。

2 Python预测部署示例

Python预测部署示例代码在链接,下面从流程解析编译运行示例两方面介绍。

2.1 流程解析

2.1.1 准备环境

请参考飞桨官网安装2.0及以上版本的paddlepaddle-gpu。

Python安装opencv:pip install opencv-python

2.1.2 准备预测模型

使用Paddle训练结束后,得到预测模型,可以用于预测部署。

本示例准备了mobilenet_v1预测模型,可以从链接下载,或者wget下载。

wget https://paddle-inference-dist.cdn.bcebos.com/PaddleInference/mobilenetv1_fp32.tar.gz
tar zxf mobilenetv1_fp32.tar.gz
2.1.3 Python导入
import paddle.inference as paddle_infer
2.1.4 设置Config

根据预测部署的实际情况,设置Config,用于后续创建Predictor。

Config默认是使用CPU预测,若要使用GPU预测,需要手动开启,设置运行的GPU卡号和分配的初始显存。可以设置开启TensorRT加速、开启IR优化、开启内存优化。使用Paddle-TensorRT相关说明和示例可以参考文档

# args 是解析的输入参数
if args.model_dir == "":
    config = paddle_infer.Config(args.model_file, args.params_file)
else:
    config = paddle_infer.Config(args.model_dir)
config.enable_use_gpu(500, 0)
config.switch_ir_optim()
config.enable_memory_optim()
config.enable_tensorrt_engine(workspace_size=1 << 30, precision_mode=paddle_infer.PrecisionType.Float32,max_batch_size=1, min_subgraph_size=5, use_static=False, use_calib_mode=False)
2.1.5 创建Predictor
predictor = paddle_infer.create_predictor(config)
2.1.6 设置输入

从Predictor中获取输入的names和handle,然后设置输入数据。

img = cv2.imread(args.img_path)
img = preprocess(img)
input_names = predictor.get_input_names()
input_tensor = predictor.get_input_handle(input_names[0])
input_tensor.reshape(img.shape)
input_tensor.copy_from_cpu(img.copy())
2.1.7 执行Predictor
predictor.run();
2.1.8 获取输出
output_names = predictor.get_output_names()
output_tensor = predictor.get_output_handle(output_names[0])
output_data = output_tensor.copy_to_cpu()

2.2 编译运行示例

文件img_preprocess.py是对图像进行预处理。 文件model_test.py是示例程序。

参考前面步骤准备环境、下载预测模型。

下载预测图片。

wget https://paddle-inference-dist.bj.bcebos.com/inference_demo/python/resnet50/ILSVRC2012_val_00000247.jpeg

执行预测命令。

python model_test.py --model_dir mobilenetv1_fp32 --img_path ILSVRC2012_val_00000247.jpeg

运行结束后,程序会将模型结果打印到屏幕,说明运行成功。

NV Jetson上预测部署示例

1 C++预测部署示例

C++示例代码在链接,下面从流程解析编译运行示例两方面介绍。

1.1 流程解析

1.1.1 准备预测库

请参考推理库下载文档下载Paddle C++预测库,名称前缀包含 nv_jetson 的为用于NV Jetson平台的预测库。

1.1.2 准备预测模型

使用Paddle训练结束后,得到预测模型,可以用于预测部署。

本示例准备了mobilenet_v1预测模型,可以从链接下载,或者wget下载。

wget https://paddle-inference-dist.cdn.bcebos.com/PaddleInference/mobilenetv1_fp32.tar.gz
1.1.3 包含头文件

使用Paddle预测库,只需要包含 paddle_inference_api.h 头文件。

#include "paddle/include/paddle_inference_api.h"
1.1.4 设置Config

根据预测部署的实际情况,设置Config,用于后续创建Predictor。

Config默认是使用CPU预测,若要使用GPU预测,需要手动开启,设置运行的GPU卡号和分配的初始显存。可以设置开启TensorRT加速、开启IR优化、开启内存优化。使用Paddle-TensorRT相关说明和示例可以参考文档

paddle_infer::Config config;
if (FLAGS_model_dir == "") {
config.SetModel(FLAGS_model_file, FLAGS_params_file); // Load combined model
} else {
config.SetModel(FLAGS_model_dir); // Load no-combined model
}
config.EnableUseGpu(500, 0);
config.SwitchIrOptim(true);
config.EnableMemoryOptim();
config.EnableTensorRtEngine(1 << 30, FLAGS_batch_size, 10, PrecisionType::kFloat32, false, false);
1.1.5 创建Predictor
std::shared_ptr<paddle_infer::Predictor> predictor = paddle_infer::CreatePredictor(config);
1.1.6 设置输入

从Predictor中获取输入的names和handle,然后设置输入数据。

auto input_names = predictor->GetInputNames();
auto input_t = predictor->GetInputHandle(input_names[0]);
std::vector<int> input_shape = {1, 3, 224, 224};
std::vector<float> input_data(1 * 3 * 224 * 224, 1);
input_t->Reshape(input_shape);
input_t->CopyFromCpu(input_data.data());
1.1.7 执行Predictor
predictor->Run();
1.1.8 获取输出
auto output_names = predictor->GetOutputNames();
auto output_t = predictor->GetOutputHandle(output_names[0]);
std::vector<int> output_shape = output_t->shape();
int out_num = std::accumulate(output_shape.begin(), output_shape.end(), 1,
                              std::multiplies<int>());
std::vector<float> out_data;
out_data.resize(out_num);
output_t->CopyToCpu(out_data.data());

1.2 编译运行示例

1.2.1 编译示例

文件model_test.cc 为预测的样例程序(程序中的输入为固定值,如果您有opencv或其他方式进行数据读取的需求,需要对程序进行一定的修改)。文件CMakeLists.txt 为编译构建文件。脚本run_impl.sh 包含了第三方库、预编译库的信息配置。

根据前面步骤下载Paddle预测库和mobilenetv1模型。

打开 run_impl.sh 文件,设置 LIB_DIR 为下载的预测库路径,比如 LIB_DIR=/work/Paddle/build/paddle_inference_install_dir

运行 sh run_impl.sh, 会在当前目录下编译产生build目录。

1.2.2 运行示例

进入build目录,运行样例。

cd build
./model_test --model_dir=mobilenetv1_fp32_dir

运行结束后,程序会将模型结果打印到屏幕,说明运行成功。

2 Python预测部署示例

Python预测部署示例代码在链接,下面从流程解析编译运行示例两方面介绍。

2.1 流程解析

2.1.1 准备环境

请参考飞桨官网安装2.0及以上版本的paddlepaddle-gpu。

Python安装opencv:pip install opencv-python

2.1.2 准备预测模型

使用Paddle训练结束后,得到预测模型,可以用于预测部署。

本示例准备了mobilenet_v1预测模型,可以从链接下载,或者wget下载。

wget https://paddle-inference-dist.cdn.bcebos.com/PaddleInference/mobilenetv1_fp32.tar.gz
tar zxf mobilenetv1_fp32.tar.gz
2.1.3 Python导入
import paddle.inference as paddle_infer
2.1.4 设置Config

根据预测部署的实际情况,设置Config,用于后续创建Predictor。

Config默认是使用CPU预测,若要使用GPU预测,需要手动开启,设置运行的GPU卡号和分配的初始显存。可以设置开启TensorRT加速、开启IR优化、开启内存优化。使用Paddle-TensorRT相关说明和示例可以参考文档

# args 是解析的输入参数
if args.model_dir == "":
    config = ipaddle_infer.Config(args.model_file, args.params_file)
else:
    config = paddle_infer.Config(args.model_dir)
config.enable_use_gpu(500, 0)
config.switch_ir_optim()
config.enable_memory_optim()
config.enable_tensorrt_engine(workspace_size=1 << 30, precision_mode=paddle_infer.PrecisionType.Float32,max_batch_size=1, min_subgraph_size=5, use_static=False, use_calib_mode=False)
2.1.5 创建Predictor
predictor = paddle_infer.create_predictor(config)
2.1.6 设置输入

从Predictor中获取输入的names和handle,然后设置输入数据。

img = cv2.imread(args.img_path)
img = preprocess(img)
input_names = predictor.get_input_names()
input_tensor = predictor.get_input_handle(input_names[0])
input_tensor.reshape(img.shape)
input_tensor.copy_from_cpu(img.copy())
2.1.7 执行Predictor
predictor.run();
2.1.8 获取输出
output_names = predictor.get_output_names()
output_tensor = predictor.get_output_handle(output_names[0])
output_data = output_tensor.copy_to_cpu()

2.2 编译运行示例

文件img_preprocess.py是对图像进行预处理。 文件model_test.py是示例程序。

参考前面步骤准备环境、下载预测模型。

下载预测图片。

wget https://paddle-inference-dist.bj.bcebos.com/inference_demo/python/resnet50/ILSVRC2012_val_00000247.jpeg

执行预测命令。

python model_test.py --model_dir mobilenetv1_fp32 --img_path ILSVRC2012_val_00000247.jpeg

运行结束后,程序会将模型结果打印到屏幕,说明运行成功。

Windows上GPU预测部署示例

1 C++预测部署示例

C++示例代码在链接,下面从流程解析编译运行示例两方面介绍。

1.1 流程解析

1.1.1 准备预测库

请参考推理库下载文档下载windows平台的Paddle GPU C++预测库。

1.1.2 准备预测模型

使用Paddle训练结束后,得到预测模型,可以用于预测部署。

本示例准备了mobilenet_v1预测模型,可以从链接下载,或者wget下载。

wget https://paddle-inference-dist.cdn.bcebos.com/PaddleInference/mobilenetv1_fp32.tar.gz
1.1.3 包含头文件

使用Paddle预测库,只需要包含 paddle_inference_api.h 头文件。

#include "paddle/include/paddle_inference_api.h"
1.1.4 设置Config

根据预测部署的实际情况,设置Config,用于后续创建Predictor。

Config默认是使用CPU预测,若要使用GPU预测,需要手动开启,设置运行的GPU卡号和分配的初始显存。可以设置开启IR优化、开启内存优化。

paddle_infer::Config config;
if (FLAGS_model_dir == "") {
config.SetModel(FLAGS_model_file, FLAGS_params_file); // Load combined model
} else {
config.SetModel(FLAGS_model_dir); // Load no-combined model
}
config.EnableUseGpu(500, 0);
config.SwitchIrOptim(true);
config.EnableMemoryOptim();
1.1.5 创建Predictor
std::shared_ptr<paddle_infer::Predictor> predictor = paddle_infer::CreatePredictor(config);
1.1.6 设置输入

从Predictor中获取输入的names和handle,然后设置输入数据。

auto input_names = predictor->GetInputNames();
auto input_t = predictor->GetInputHandle(input_names[0]);
std::vector<int> input_shape = {1, 3, 224, 224};
std::vector<float> input_data(1 * 3 * 224 * 224, 1);
input_t->Reshape(input_shape);
input_t->CopyFromCpu(input_data.data());
1.1.7 执行Predictor
predictor->Run();
1.1.8 获取输出
auto output_names = predictor->GetOutputNames();
auto output_t = predictor->GetOutputHandle(output_names[0]);
std::vector<int> output_shape = output_t->shape();
int out_num = std::accumulate(output_shape.begin(), output_shape.end(), 1,
                              std::multiplies<int>());
std::vector<float> out_data;
out_data.resize(out_num);
output_t->CopyToCpu(out_data.data());

1.2 编译运行示例

1.2.1 编译示例

文件model_test.cc 为预测的样例程序(程序中的输入为固定值,如果您有opencv或其他方式进行数据读取的需求,需要对程序进行一定的修改)。文件CMakeLists.txt 为编译构建文件。

根据前面步骤下载Paddle预测库和mobilenetv1模型。

使用cmake-gui程序生成vs工程:

  • 选择源代码路径,及编译产物路径,如图所示

_images/win_x86_cpu_cmake_1.pngwin_x86_cpu_cmake_1

  • 点击Configure,选择Visual Studio且选择x64版本如图所示,点击Finish,由于我们没有加入必要的CMake Options,会导致configure失败,请继续下一步。

_images/win_x86_cpu_cmake_2.pngwin_x86_cpu_cmake_2

  • 设置CMake Options,点击Add Entry,新增PADDLE_LIB,CMAKE_BUILD_TYPE,DEMO_NAME等选项。具体配置项如下图所示,其中PADDLE_LIB为您下载的预测库路径。

_images/win_x86_cpu_cmake_3.pngwin_x86_cpu_cmake_3

  • 点击Configure,log信息显示Configure done代表配置成功,接下来点击Generate生成vs工程,log信息显示Generate done,代表生成成功,最后点击Open Project打开Visual Studio.

  • 设置为Release/x64,编译,编译产物在build/Release目录下。

_images/win_x86_cpu_vs_1.pngwin_x86_cpu_vs_1

1.2.2 运行示例

首先设置model_test工程为启动首选项。

_images/win_x86_cpu_vs_2.pngwin_x86_cpu_vs_2

配置输入flags,即设置您之前下载的模型路径。点击Debug选项卡的model_test Properities..

_images/win_x86_cpu_vs_3.pngwin_x86_cpu_vs_3

点击Debug选项卡下的Start Without Debugging选项开始执行程序。

_images/win_x86_cpu_vs_4.pngwin_x86_cpu_vs_4

2 Python预测部署示例

Python预测部署示例代码在链接,下面从流程解析编译运行示例两方面介绍。

2.1 流程解析

2.1.1 准备环境

请参考飞桨官网安装2.0及以上版本的paddlepaddle-gpu。

Python安装opencv:pip install opencv-python

2.1.2 准备预测模型

使用Paddle训练结束后,得到预测模型,可以用于预测部署。

本示例准备了mobilenet_v1预测模型,可以从链接下载,或者wget下载。

wget https://paddle-inference-dist.cdn.bcebos.com/PaddleInference/mobilenetv1_fp32.tar.gz
tar zxf mobilenetv1_fp32.tar.gz
2.1.3 Python导入
from paddle.inference import Config
from paddle.inference import create_predictor
2.1.4 设置Config

根据预测部署的实际情况,设置Config,用于后续创建Predictor。

Config默认是使用CPU预测,若要使用GPU预测,需要手动开启,设置运行的GPU卡号和分配的初始显存。可以设置开启IR优化、开启内存优化。

# args 是解析的输入参数
if args.model_dir == "":
    config = Config(args.model_file, args.params_file)
else:
    config = Config(args.model_dir)
config.enable_use_gpu(500, 0)
config.switch_ir_optim()
config.enable_memory_optim()
2.1.5 创建Predictor
predictor = create_predictor(config)
2.1.6 设置输入

从Predictor中获取输入的names和handle,然后设置输入数据。

img = cv2.imread(args.img_path)
img = preprocess(img)
input_names = predictor.get_input_names()
input_tensor = predictor.get_input_handle(input_names[0])
input_tensor.reshape(img.shape)
input_tensor.copy_from_cpu(img.copy())
2.1.7 执行Predictor
predictor.run();
2.1.8 获取输出
output_names = predictor.get_output_names()
output_tensor = predictor.get_output_handle(output_names[0])
output_data = output_tensor.copy_to_cpu()

2.2 编译运行示例

文件img_preprocess.py是对图像进行预处理。 文件model_test.py是示例程序。

参考前面步骤准备环境、下载预测模型。

下载预测图片。

wget https://paddle-inference-dist.bj.bcebos.com/inference_demo/python/resnet50/ILSVRC2012_val_00000247.jpeg

执行预测命令。

python model_test.py --model_dir mobilenetv1_fp32 --img_path ILSVRC2012_val_00000247.jpeg

运行结束后,程序会将模型结果打印到屏幕,说明运行成功。

性能数据

测试条件

  • 测试模型

    • Mobilenetv2

    • Resnet101

    • Yolov3

    • Faster-RCNN

    • Deeplabv3

    • Unet

    • Bert/Ernie

  • 测试机器

    • P4

    • T4

    • cpu

  • 测试说明

    • 测试Paddle版本:release/2.0-rc1

    • warmup=10, repeats=1000,统计平均时间,单位ms。

数据

model_name batch_size num_samples device ir_optim enable_tensorrt enable_mkldnn trt_precision cpu_math_library_num_threads Average_latency QPS
bert_emb_v1-paddle 1 1000 P4 true true fp32 8.5914 116.396
bert_emb_v1-paddle 2 1000 P4 true false 15.3364 130.409
bert_emb_v1-paddle 1 1000 P4 true false 8.09328 123.559
bert_emb_v1-paddle 4 1000 P4 true false 27.0221 148.027
bert_emb_v1-paddle 2 1000 P4 true true fp32 14.9749 133.556
deeplabv3p_xception_769_fp32-paddle 4 1000 P4 true false 458.679 8.7207
deeplabv3p_xception_769_fp32-paddle 4 1000 P4 true true fp32 379.832 10.531
deeplabv3p_xception_769_fp32-paddle 1 1000 P4 true true fp32 96.0014 10.4165
deeplabv3p_xception_769_fp32-paddle 2 1000 P4 true true fp32 193.826 10.3185
deeplabv3p_xception_769_fp32-paddle 1 1000 P4 true false 114.996 8.69596
deeplabv3p_xception_769_fp32-paddle 2 1000 P4 true false 227.272 8.80004
faster_rcnn_r50_1x-paddle 2 1000 P4 true true fp32 162.795 12.2854
faster_rcnn_r50_1x-paddle 1 1000 P4 true true fp32 141.49 7.06762
faster_rcnn_r50_1x-paddle 4 1000 P4 true false 320.018 12.4993
faster_rcnn_r50_1x-paddle 2 1000 P4 true false 162.685 12.2937
faster_rcnn_r50_1x-paddle 1 1000 P4 true false 140.516 7.11662
faster_rcnn_r50_1x-paddle 4 1000 P4 true true fp32 318.193 12.571
mobilenet_ssd-paddle 1 1000 P4 true false 5.34364 187.138
mobilenet_ssd-paddle 4 1000 P4 true false 10.0709 397.185
mobilenet_ssd-paddle 2 1000 P4 true false 6.45996 309.6
mobilenet_v2-paddle 4 1000 P4 true true fp32 3.74114 1069.19
mobilenet_v2-paddle 1 1000 P4 true true fp32 1.77892 562.14
mobilenet_v2-paddle 2 1000 P4 true true fp32 2.44298 818.673
mobilenet_v2-paddle 4 1000 P4 true false 7.19198 556.175
mobilenet_v2-paddle 2 1000 P4 true false 4.53171 441.335
mobilenet_v2-paddle 1 1000 P4 true false 3.45571 289.376
resnet101-paddle 1 1000 P4 true false 13.1659 75.9538
resnet101-paddle 4 1000 P4 true false 21.1129 189.457
resnet101-paddle 2 1000 P4 true true fp32 11.7751 169.849
resnet101-paddle 1 1000 P4 true true fp32 7.79821 128.234
resnet101-paddle 4 1000 P4 true true fp32 18.3 218.58
resnet101-paddle 2 1000 P4 true false 15.4095 129.79
unet-paddle 4 1000 P4 true true fp32 155.15 25.7814
unet-paddle 1 1000 P4 true true fp32 36.8867 27.11
unet-paddle 2 1000 P4 true true fp32 75.5283 26.4801
yolov3_darknet 1 1000 P4 true false 84.2696 11.8667
yolov3_darknet 2 1000 P4 true false 139.273 14.3603
yolov3_darknet 4 1000 P4 true false 208.45 19.1893
yolov3_darknet 1 1000 P4 true true fp32 43.5201 22.9779
yolov3_darknet 2 1000 P4 true true fp32 86.456 23.1331
yolov3_darknet 4 1000 P4 true true fp32 170.954 23.3981

C++ API 文档

CreatePredictor 方法

API定义如下:

// 根据 Config 构建预测执行对象 Predictor
// 参数: config - 用于构建 Predictor 的配置信息
// 返回: std::shared_ptr<Predictor> - 预测对象的智能指针
std::shared_ptr<Predictor> CreatePredictor(const Config& config);

代码示例:

// 创建 Config
paddle_infer::Config config("../assets/models/mobilenet_v1");

// 根据 Config 创建 Predictor
auto predictor = paddle_infer::CreatePredictor(config);

GetVersion 方法

API定义如下:

// 获取 Paddle 版本信息
// 参数: NONE
// 返回: std::string - Paddle 版本信息
std::string GetVersion();

代码示例:

// 获取 Paddle 版本信息
std::string paddle_version = paddle_infer::GetVersion();

Config 类

Config 构造函数

Config 类为用于配置构建 Predictor 对象的配置信息,如模型路径、是否开启gpu等等。

构造函数定义如下:

// 创建 Config 对象,默认构造函数
Config();

// 创建 Config 对象,输入为其他 Config 对象
Config(const Config& other);

// 创建 Config 对象,输入为非 Combine 模型的文件夹路径
Config(const std::string& model_dir);

// 创建 Config 对象,输入分别为 Combine 模型的模型文件路径和参数文件路径
Config(const std::string& prog_file, const std::string& params_file);

代码示例 (1):默认构造函数,通过API加载预测模型 - 非Combined模型

// 字符串 model_dir 为非 Combine 模型文件夹路径
std::string model_dir = "../assets/models/mobilenet_v1";

// 创建默认 Config 对象
paddle_infer::Config config();

// 通过 API 设置模型文件夹路径
config.SetModel(model_dir);

// 根据 Config 对象创建预测器对象
auto predictor = paddle_infer::CreatePredictor(config);

代码示例 (2):通过构造函数加载预测模型 - 非Combined模型

// 字符串 model_dir 为非 Combine 模型文件夹路径
std::string model_dir = "../assets/models/mobilenet_v1";

// 根据非 Combine 模型的文件夹路径构造 Config 对象
paddle_infer::Config config(model_dir);

// 根据 Config 对象创建预测器对象
auto predictor = paddle_infer::CreatePredictor(config);

代码示例 (3):通过构造函数加载预测模型 - Combined模型

// 字符串 prog_file 为 Combine 模型文件所在路径
std::string prog_file = "../assets/models/mobilenet_v1/__model__";
// 字符串 params_file 为 Combine 模型参数文件所在路径
std::string params_file = "../assets/models/mobilenet_v1/__params__";

// 根据 Combine 模型的模型文件和参数文件构造 Config 对象
paddle_infer::Config config(prog_file, params_file);

// 根据 Config 对象创建预测器对象
auto predictor = paddle_infer::CreatePredictor(config);

设置预测模型

从文件中加载预测模型 - 非Combined模型

API定义如下:

// 设置模型文件路径,当需要从磁盘加载非 Combined 模型时使用
// 参数:model_dir - 模型文件夹路径
// 返回:None
void SetModel(const std::string& model_dir);

// 获取非combine模型的文件夹路径
// 参数:None
// 返回:string - 模型文件夹路径
const std::string& model_dir();

代码示例:

// 字符串 model_dir 为非 Combined 模型文件夹路径
std::string model_dir = "../assets/models/mobilenet_v1";

// 创建默认 Config 对象
paddle_infer::Config config();

// 通过 API 设置模型文件夹路径
config.SetModel(model_dir);

// 通过 API 获取 config 中的模型路径
std::cout << "Model Path is: " << config.model_dir() << std::endl;

// 根据Config对象创建预测器对象
auto predictor = paddle_infer::CreatePredictor(config);
从文件中加载预测模型 - Combined 模型

API定义如下:

// 设置模型文件路径,当需要从磁盘加载 Combined 模型时使用
// 参数:prog_file_path - 模型文件路径
//      params_file_path - 参数文件路径
// 返回:None
void SetModel(const std::string& prog_file_path,
              const std::string& params_file_path);

// 设置模型文件路径,当需要从磁盘加载 Combined 模型时使用。
// 参数:x - 模型文件路径
// 返回:None
void SetProgFile(const std::string& x);

// 设置参数文件路径,当需要从磁盘加载 Combined 模型时使用
// 参数:x - 参数文件路径
// 返回:None
void SetParamsFile(const std::string& x);

// 获取 Combined 模型的模型文件路径
// 参数:None
// 返回:string - 模型文件路径
const std::string& prog_file();

// 获取 Combined 模型的参数文件路径
// 参数:None
// 返回:string - 参数文件路径
const std::string& params_file();

代码示例:

// 字符串 prog_file 为 Combined 模型的模型文件所在路径
std::string prog_file = "../assets/models/mobilenet_v1/__model__";
// 字符串 params_file 为 Combined 模型的参数文件所在路径
std::string params_file = "../assets/models/mobilenet_v1/__params__";

// 创建默认 Config 对象
paddle_infer::Config config();
// 通过 API 设置模型文件夹路径,
config.SetModel(prog_file, params_file);
// 注意:SetModel API与以下2行代码等同
// config.SetProgFile(prog_file);
// config.SetParamsFile(params_file);

// 通过 API 获取 config 中的模型文件和参数文件路径
std::cout << "Model file path is: " << config.prog_file() << std::endl;
std::cout << "Model param path is: " << config.params_file() << std::endl;

// 根据 Config 对象创建预测器对象
auto predictor = paddle_infer::CreatePredictor(config);
从内存中加载预测模型

API定义如下:

// 从内存加载模型
// 参数:prog_buffer - 内存中模型结构数据
//      prog_buffer_size - 内存中模型结构数据的大小
//      params_buffer - 内存中模型参数数据
//      params_buffer_size - 内存中模型参数数据的大小
// 返回:None
void SetModelBuffer(const char* prog_buffer, size_t prog_buffer_size,
                    const char* params_buffer, size_t params_buffer_size);

// 判断是否从内存中加载模型
// 参数:None
// 返回:bool - 是否从内存中加载模型
bool model_from_memory() const;

代码示例:

// 定义文件读取函数
std::string read_file(std::string filename) {
  std::ifstream file(filename);
  return std::string((std::istreambuf_iterator<char>(file)),
                     std::istreambuf_iterator<char>());
}

// 设置模型文件和参数文件所在路径
std::string prog_file = "../assets/models/mobilenet_v1/__model__";
std::string params_file = "../assets/models/mobilenet_v1/__params__";

// 加载模型文件到内存
std::string prog_str = read_file(prog_file);
std::string params_str = read_file(params_file);

// 创建默认 Config 对象
paddle_infer::Config config();
// 从内存中加载模型
config.SetModelBuffer(prog_str.c_str(), prog_str.size(),
                      params_str.c_str(), params_str.size());

// 通过 API 获取 config 中 model_from_memory 的值
std::cout << "Load model from memory is: " << config.model_from_memory() << std::endl;

// 根据 Confi 对象创建预测器对象
auto predictor = paddle_infer::CreatePredictor(config);

使用 CPU 进行预测

注意:

  1. 在 CPU 型号允许的情况下,进行预测库下载或编译试尽量使用带 AVX 和 MKL 的版本

  2. 可以尝试使用 Intel 的 MKLDNN 进行 CPU 预测加速,默认 CPU 不启用 MKLDNN

  3. 在 CPU 可用核心数足够时,可以通过设置 SetCpuMathLibraryNumThreads 将线程数调高一些,默认线程数为 1

CPU 设置

API定义如下:

// 设置 CPU Blas 库计算线程数
// 参数:cpu_math_library_num_threads - blas库计算线程数
// 返回:None
void SetCpuMathLibraryNumThreads(int cpu_math_library_num_threads);

// 获取 CPU Blas 库计算线程数
// 参数:None
// 返回:int - cpu blas库计算线程数。
int cpu_math_library_num_threads() const;

代码示例:

// 创建默认 Config 对象
paddle_infer::Config config();

// 设置 CPU Blas 库线程数为 10
config.SetCpuMathLibraryNumThreads(10);

// 通过 API 获取 CPU 信息
int num_thread = config.cpu_math_library_num_threads();
std::cout << "CPU blas thread number is: " << num_thread << std::endl; // 10
MKLDNN 设置

注意:

  1. 启用 MKLDNN 的前提为已经使用 CPU 进行预测,否则启用 MKLDNN 无法生效

  2. 启用 MKLDNN BF16 要求 CPU 型号可以支持 AVX512,否则无法启用 MKLDNN BF16

  3. SetMkldnnCacheCapacity 请参考 MKLDNN cache设计文档

API定义如下:

// 启用 MKLDNN 进行预测加速
// 参数:None
// 返回:None
void EnableMKLDNN();

// 判断是否启用 MKLDNN 
// 参数:None
// 返回:bool - 是否启用 MKLDNN
bool mkldnn_enabled() const;

// 设置 MKLDNN 针对不同输入 shape 的 cache 容量大小
// 参数:int - cache 容量大小
// 返回:None
void SetMkldnnCacheCapacity(int capacity);

// 指定使用 MKLDNN 加速的 OP 列表
// 参数:std::unordered_set<std::string> - 使用 MKLDNN 加速的 OP 列表
// 返回:None
void SetMKLDNNOp(std::unordered_set<std::string> op_list);

// 启用 MKLDNN BFLOAT16
// 参数:None
// 返回:None
void EnableMkldnnBfloat16();

// 判断是否启用 MKLDNN BFLOAT16
// 参数:None
// 返回:bool - 是否启用 MKLDNN BFLOAT16
bool mkldnn_bfloat16_enabled() const;

// 指定使用 MKLDNN BFLOAT16 加速的 OP 列表
// 参数:std::unordered_set<std::string> - 使用 MKLDNN BFLOAT16 加速的 OP 列表
// 返回:None
void SetBfloat16Op(std::unordered_set<std::string> op_list);

代码示例 (1):使用 MKLDNN 进行预测

// 创建 Config 对象
paddle_infer::Config config(FLAGS_infer_model + "/mobilenet");

// 启用 MKLDNN 进行预测
config.EnableMKLDNN();
// 通过 API 获取 MKLDNN 启用结果 - true
std::cout << "Enable MKLDNN is: " << config.mkldnn_enabled() << std::endl;

// 设置 MKLDNN 的 cache 容量大小
config.SetMkldnnCacheCapacity(1);

// 设置启用 MKLDNN 进行加速的 OP 列表
std::unordered_set<std::string> op_list = {"softmax", "elementwise_add", "relu"};
config.SetMKLDNNOp(op_list);

代码示例 (2):使用 MKLDNN BFLOAT16 进行预测

// 创建 Config 对象
paddle_infer::Config config(FLAGS_infer_model + "/mobilenet");

// 启用 MKLDNN 进行预测
config.EnableMKLDNN();

// 启用 MKLDNN BFLOAT16 进行预测
config.EnableMkldnnBfloat16();
// 设置启用 MKLDNN BFLOAT16 的 OP 列表
config.SetBfloat16Op({"conv2d"});

// 通过 API 获取 MKLDNN BFLOAT16 启用结果 - true
std::cout << "Enable MKLDNN BF16 is: " << config.mkldnn_bfloat16_enabled() << std::endl;

使用 GPU 进行预测

注意:

  1. Config 默认使用 CPU 进行预测,需要通过 EnableUseGpu 来启用 GPU 预测

  2. 可以尝试启用 CUDNN 和 TensorRT 进行 GPU 预测加速

GPU 设置

API定义如下:

// 启用 GPU 进行预测
// 参数:memory_pool_init_size_mb - 初始化分配的gpu显存,以MB为单位
//      device_id - 设备id
// 返回:None
void EnableUseGpu(uint64_t memory_pool_init_size_mb, int device_id = 0);

// 禁用 GPU 进行预测
// 参数:None
// 返回:None
void DisableGpu();

// 判断是否启用 GPU 
// 参数:None
// 返回:bool - 是否启用 GPU 
bool use_gpu() const;

// 获取 GPU 的device id
// 参数:None
// 返回:int -  GPU 的device id
int gpu_device_id() const;

// 获取 GPU 的初始显存大小
// 参数:None
// 返回:int -  GPU 的初始的显存大小
int memory_pool_init_size_mb() const;

// 初始化显存占总显存的百分比
// 参数:None
// 返回:float - 初始的显存占总显存的百分比
float fraction_of_gpu_memory_for_pool() const;

// 开启线程流,目前的行为是为每一个线程绑定一个流,在将来该行为可能改变
// 参数:None
// 返回:None
void EnableGpuMultiStream();

// 判断是否开启线程流
// 参数:None
// 返回:bool - 是否是否开启线程流
bool thread_local_stream_enabled() const;

GPU设置代码示例:

// 创建默认 Config 对象
paddle_infer::Config config;

// 启用 GPU 进行预测 - 初始化 GPU 显存 100M, Deivce_ID 为 0
config.EnableUseGpu(100, 0);
// 通过 API 获取 GPU 信息
std::cout << "Use GPU is: " << config.use_gpu() << std::endl; // true
std::cout << "Init mem size is: " << config.memory_pool_init_size_mb() << std::endl;
std::cout << "Init mem frac is: " << config.fraction_of_gpu_memory_for_pool() << std::endl;
std::cout << "GPU device id is: " << config.gpu_device_id() << std::endl;

// 禁用 GPU 进行预测
config.DisableGpu();
// 通过 API 获取 GPU 信息
std::cout << "Use GPU is: " << config.use_gpu() << std::endl; // false

开启多线程流代码示例:

// 自定义 Barrier 类,用于线程间同步
class Barrier {
 public:
  explicit Barrier(std::size_t count) : _count(count) {}
  void Wait() {
    std::unique_lock<std::mutex> lock(_mutex);
    if (--_count) {
      _cv.wait(lock, [this] { return _count == 0; });
    } else {
      _cv.notify_all();
    }
  }
 private:
  std::mutex _mutex;
  std::condition_variable _cv;
  std::size_t _count;
};

int test_main(const paddle_infer::Config& config, Barrier* barrier = nullptr) {
  static std::mutex mutex;
  // 创建 Predictor 对象
  std::shared_ptr<paddle_infer::Predictor> predictor;
  {
    std::unique_lock<std::mutex> lock(mutex);
    predictor = std::move(paddle_infer::CreatePredictor(config));
  }
  if (barrier) {
    barrier->Wait();
  }
  // 准备输入数据
  int input_num = shape_production(INPUT_SHAPE);
  std::vector<float> input_data(input_num, 1);
  auto input_names = predictor->GetInputNames();
  auto input_tensor = predictor->GetInputHandle(input_names[0]);
  input_tensor->Reshape(INPUT_SHAPE);
  input_tensor->CopyFromCpu(input_data.data());
  // 执行预测
  predictor->Run();
  // 获取预测输出
  auto output_names = predictor->GetOutputNames();
  auto output_tensor = predictor->GetOutputHandle(output_names[0]);
  std::vector<int> output_shape = output_tensor->shape();
  std::cout << "Output shape is " << shape_to_string(output_shape) << std::endl;
}

int main(int argc, char **argv) {
  const size_t thread_num = 5;
  std::vector<std::thread> threads(thread_num);
  Barrier barrier(thread_num);
  // 创建 5 个线程,并为每个线程开启一个单独的GPU Stream
  for (size_t i = 0; i < threads.size(); ++i) {
    threads[i] = std::thread([&barrier, i]() {
      paddle_infer::Config config;
      config.EnableUseGpu(100, 0);
      config.SetModel(FLAGS_infer_model);
      config.EnableGpuMultiStream();
      test_main(config, &barrier);
    });
  }
  for (auto& th : threads) {
    th.join();
  }
}
CUDNN 设置

注意: 启用 CUDNN 的前提为已经启用 GPU,否则启用 CUDNN 无法生效。

API定义如下:

// 启用 CUDNN 进行预测加速
// 参数:None
// 返回:None
void EnableCUDNN();

// 判断是否启用 CUDNN 
// 参数:None
// 返回:bool - 是否启用 CUDNN
bool cudnn_enabled() const;

代码示例:

// 创建默认 Config 对象
paddle_infer::Config config();

// 启用 GPU 进行预测
config.EnableUseGpu(100, 0);
// 启用 CUDNN 进行预测加速
config.EnableCUDNN();
// 通过 API 获取 CUDNN 启用结果
std::cout << "Enable CUDNN is: " << config.cudnn_enabled() << std::endl; // true

// 禁用 GPU 进行预测
config.DisableGpu();
// 启用 CUDNN 进行预测加速 - 因为 GPU 被禁用,因此 CUDNN 启用不生效
config.EnableCUDNN();
// 通过 API 获取 CUDNN 启用结果
std::cout << "Enable CUDNN is: " << config.cudnn_enabled() << std::endl; // false
TensorRT 设置

注意:

  1. 启用 TensorRT 的前提为已经启用 GPU,否则启用 TensorRT 无法生效

  2. 对存在LoD信息的模型,如Bert, Ernie等NLP模型,必须使用动态 Shape

  3. 启用 TensorRT OSS 可以支持更多 plugin,详细参考 TensorRT OSS

更多 TensorRT 详细信息,请参考 使用Paddle-TensorRT库预测

API定义如下:

// 启用 TensorRT 进行预测加速
// 参数:workspace_size     - 指定 TensorRT 使用的工作空间大小
//      max_batch_size     - 设置最大的 batch 大小,运行时 batch 大小不得超过此限定值
//      min_subgraph_size  - Paddle-TRT 是以子图的形式运行,为了避免性能损失,当子图内部节点个数
//                           大于 min_subgraph_size 的时候,才会使用 Paddle-TRT 运行
//      precision          - 指定使用 TRT 的精度,支持 FP32(kFloat32),FP16(kHalf),Int8(kInt8)
//      use_static         - 若指定为 true,在初次运行程序的时候会将 TRT 的优化信息进行序列化到磁盘上,
//                           下次运行时直接加载优化的序列化信息而不需要重新生成
//      use_calib_mode     - 若要运行 Paddle-TRT INT8 离线量化校准,需要将此选项设置为 true
// 返回:None
void EnableTensorRtEngine(int workspace_size = 1 << 20,
                          int max_batch_size = 1, int min_subgraph_size = 3,
                          Precision precision = Precision::kFloat32,
                          bool use_static = false,
                          bool use_calib_mode = true);
// 判断是否启用 TensorRT 
// 参数:None
// 返回:bool - 是否启用 TensorRT
bool tensorrt_engine_enabled() const;

// 设置 TensorRT 的动态 Shape
// 参数:min_input_shape          - TensorRT 子图支持动态 shape 的最小 shape
//      max_input_shape          - TensorRT 子图支持动态 shape 的最大 shape
//      optim_input_shape        - TensorRT 子图支持动态 shape 的最优 shape
//      disable_trt_plugin_fp16  - 设置 TensorRT 的 plugin 不在 fp16 精度下运行
// 返回:None
void SetTRTDynamicShapeInfo(
      std::map<std::string, std::vector<int>> min_input_shape,
      std::map<std::string, std::vector<int>> max_input_shape,
      std::map<std::string, std::vector<int>> optim_input_shape,
      bool disable_trt_plugin_fp16 = false);

// 启用 TensorRT OSS 进行预测加速
// 参数:None
// 返回:None
void EnableTensorRtOSS();

// 判断是否启用 TensorRT OSS
// 参数:None
// 返回:bool - 是否启用 TensorRT OSS
bool tensorrt_oss_enabled();

/// 启用TensorRT DLA进行预测加速
/// 参数:dla_core - DLA设备的id,可选0,1,...,DLA设备总数 - 1
/// 返回:None
void EnableTensorRtDLA(int dla_core = 0);

/// 判断是否已经开启TensorRT DLA加速
/// 参数:None
/// 返回:bool - 是否已开启TensorRT DLA加速
bool tensorrt_dla_enabled();

代码示例 (1):使用 TensorRT FP32 / FP16 / INT8 进行预测

// 创建 Config 对象
paddle_infer::Config config(FLAGS_infer_model + "/mobilenet");

// 启用 GPU 进行预测
config.EnableUseGpu(100, 0);

// 启用 TensorRT 进行预测加速 - FP32
config.EnableTensorRtEngine(1 << 20, 1, 3, 
                            paddle_infer::PrecisionType::kFloat32, false, false);
// 通过 API 获取 TensorRT 启用结果 - true
std::cout << "Enable TensorRT is: " << config.tensorrt_engine_enabled() << std::endl;

// 启用 TensorRT 进行预测加速 - FP16
config.EnableTensorRtEngine(1 << 20, 1, 3, 
                            paddle_infer::PrecisionType::kHalf, false, false);
// 通过 API 获取 TensorRT 启用结果 - true
std::cout << "Enable TensorRT is: " << config.tensorrt_engine_enabled() << std::endl;

// 启用 TensorRT 进行预测加速 - Int8
config.EnableTensorRtEngine(1 << 20, 1, 3, 
                            paddle_infer::PrecisionType::kInt8, false, true);
// 通过 API 获取 TensorRT 启用结果 - true
std::cout << "Enable TensorRT is: " << config.tensorrt_engine_enabled() << std::endl;

代码示例 (2):使用 TensorRT 动态 Shape 进行预测

// 创建 Config 对象
paddle_infer::Config config(FLAGS_infer_model + "/mobilenet");

// 启用 GPU 进行预测
config.EnableUseGpu(100, 0);

// 启用 TensorRT 进行预测加速 - Int8
config.EnableTensorRtEngine(1 << 30, 1, 1,
                            paddle_infer::PrecisionType::kInt8, false, true);
// 设置模型输入的动态 Shape 范围
std::map<std::string, std::vector<int>> min_input_shape = {{"image", {1, 1, 3, 3}}};
std::map<std::string, std::vector<int>> max_input_shape = {{"image", {1, 1, 10, 10}}};
std::map<std::string, std::vector<int>> opt_input_shape = {{"image", {1, 1, 3, 3}}};
// 设置 TensorRT 的动态 Shape
config.SetTRTDynamicShapeInfo(min_input_shape, max_input_shape, opt_input_shape);

代码示例 (3):使用 TensorRT OSS 进行预测

// 创建 Config 对象
paddle_infer::Config config(FLAGS_infer_model + "/mobilenet");

// 启用 GPU 进行预测
config.EnableUseGpu(100, 0);

// 启用 TensorRT 进行预测加速
config.EnableTensorRtEngine();
// 启用 TensorRT OSS 进行预测加速
config.EnableTensorRtOSS();

// 通过 API 获取 TensorRT OSS 启用结果 - true
std::cout << "Enable TensorRT is: " << config.tensorrt_oss_enabled() << std::endl;

使用 XPU 进行预测

API定义如下:

// 启用 XPU 进行预测
// 参数:l3_workspace_size - l3 cache 分配的显存大小,最大为16M
// 参数:locked - 分配的L3 cache是否可以锁定。如果为false,表示不锁定L3 cache,则分配的L3 cache可以多个模型共享,多个共享L3 cache的模型在卡上将顺序执行
// 参数:autotune - 是否对模型中的conv算子进行autotune。如果为true,则在第一次执行到某个维度的conv算子时,将自动搜索更优的算法,用以提升后续相同维度的conv算子的性能
// 参数:autotune_file - 指定autotune文件路径。如果指定autotune_file,则使用文件中指定的算法,不再重新进行autotune
// 参数:precision - multi_encoder的计算精度
// 参数:adaptive_seqlen - multi_encoder的输入是否可变长
// 返回:None
void EnableXpu(int l3_workspace_size = 0xfffc00, bool locked = false,
               bool autotune = true, const std::string& autotune_file = "",
               const std::string& precision = "int16", bool adaptive_seqlen = false);

代码示例:

// 创建 Config 对象
paddle_infer::Config config(FLAGS_model_dir);

// 启用 XPU,并设置 l3 cache 大小为 10M
config.EnableXpu(10*1024*1024);

设置模型优化方法

IR 优化

注意: 关于自定义 IR 优化 Pass,请参考 PaddlePassBuilder 类

API定义如下:

// 启用 IR 优化
// 参数:x - 是否开启 IR 优化,默认打开
// 返回:None
void SwitchIrOptim(int x = true);

// 判断是否开启 IR 优化 
// 参数:None
// 返回:bool - 是否开启 IR 优化
bool ir_optim() const;

// 设置是否在图分析阶段打印 IR,启用后会在每一个 PASS 后生成 dot 文件
// 参数:x - 是否打印 IR,默认关闭
// 返回:None
void SwitchIrDebug(int x = true);

// 返回 pass_builder,用来自定义图分析阶段选择的 IR
// 参数:None
// 返回:PassStrategy - pass_builder对象
PassStrategy* pass_builder() const;

代码示例:

// 创建 Config 对象
paddle_infer::Config config(FLAGS_model_dir);

// 开启 IR 优化
config.SwitchIrOptim();
// 开启 IR 打印
config.SwitchIrDebug();

// 得到 pass_builder 对象
auto pass_builder = config.pass_builder();
// 在 IR 优化阶段,去除 fc_fuse_pass
pass_builder->DeletePass("fc_fuse_pass");

// 通过 API 获取 IR 优化是否开启 - true
std::cout << "IR Optim is: " << config.ir_optim() << std::endl;

// 根据Config对象创建预测器对象
auto predictor = paddle_infer::CreatePredictor(config);

运行结果示例:

# SwitchIrOptim 开启 IR 优化后,运行中会有如下 LOG 输出
--- Running analysis [ir_graph_build_pass]
--- Running analysis [ir_graph_clean_pass]
--- Running analysis [ir_analysis_pass]
--- Running IR pass [simplify_with_basic_ops_pass]
--- Running IR pass [attention_lstm_fuse_pass]
--- Running IR pass [seqconv_eltadd_relu_fuse_pass]
...
--- Running analysis [inference_op_replace_pass]
--- Running analysis [ir_graph_to_program_pass]

# SwitchIrDebug 开启 IR 打印后,运行结束之后会在目录下生成如下 DOT 文件
-rw-r--r-- 1 root root  70K Nov 17 10:47 0_ir_simplify_with_basic_ops_pass.dot
-rw-r--r-- 1 root root  72K Nov 17 10:47 10_ir_fc_gru_fuse_pass.dot
-rw-r--r-- 1 root root  72K Nov 17 10:47 11_ir_graph_viz_pass.dot
...
-rw-r--r-- 1 root root  72K Nov 17 10:47 8_ir_mul_lstm_fuse_pass.dot
-rw-r--r-- 1 root root  72K Nov 17 10:47 9_ir_graph_viz_pass.dot
Lite 子图
// 启用 Lite 子图
// 参数:precision_mode - Lite 子图的运行精度,默认为 FP32
//      zero_copy      - 启用 zero_copy,lite 子图与 paddle inference 之间共享数据
//      Passes_filter  - 设置 lite 子图的 pass
//      ops_filter     - 设置不使用 lite 子图运行的 op
// 返回:None
void EnableLiteEngine(
      AnalysisConfig::Precision precision_mode = Precision::kFloat32,
      bool zero_copy = false,
      const std::vector<std::string>& passes_filter = {},
      const std::vector<std::string>& ops_filter = {});

// 判断是否启用 Lite 子图
// 参数:None
// 返回:bool - 是否启用 Lite 子图
bool lite_engine_enabled() const;

示例代码:

// 创建 Config 对象
paddle_infer::Config config(FLAGS_model_dir);

config.EnableUseGpu(100, 0);
config.EnableLiteEngine(paddle_infer::PrecisionType::kFloat32);

// 通过 API 获取 Lite 子图启用信息 - true
std::cout << "Lite Engine is: " << config.lite_engine_enabled() << std::endl;

启用内存优化

API定义如下:

// 开启内存/显存复用,具体降低内存效果取决于模型结构
// 参数:None
// 返回:None
void EnableMemoryOptim();

// 判断是否开启内存/显存复用
// 参数:None
// 返回:bool - 是否开启内/显存复用
bool enable_memory_optim() const;

代码示例:

// 创建 Config 对象
paddle_infer::Config config(FLAGS_infer_model + "/mobilenet");

// 开启 CPU 显存优化
config.EnableMemoryOptim();
// 通过 API 获取 CPU 是否已经开启显存优化 - true
std::cout << "CPU Mem Optim is: " << config.enable_memory_optim() << std::endl;

// 启用 GPU 进行预测
config.EnableUseGpu(100, 0);
// 开启 GPU 显存优化
config.EnableMemoryOptim();
// 通过 API 获取 GPU 是否已经开启显存优化 - true
std::cout << "GPU Mem Optim is: " << config.enable_memory_optim() << std::endl;

设置缓存路径

注意: 如果当前使用的为 TensorRT INT8 且设置从内存中加载模型,则必须通过 SetOptimCacheDir 来设置缓存路径。

API定义如下:

// 设置缓存路径
// 参数:opt_cache_dir - 缓存路径
// 返回:None
void SetOptimCacheDir(const std::string& opt_cache_dir);

代码示例:

// 创建 Config 对象
paddle_infer::Config config(FLAGS_infer_model + "/mobilenet");

// 设置缓存路径
config.SetOptimCacheDir(FLAGS_infer_model + "/OptimCacheDir");

FC Padding

API定义如下:

// 禁用 FC Padding
// 参数:None
// 返回:None
void DisableFCPadding();

// 判断是否启用 FC Padding
// 参数:None
// 返回:bool - 是否启用 FC Padding
bool use_fc_padding() const;

代码示例:

// 创建 Config 对象
paddle_infer::Config config(FLAGS_infer_model + "/mobilenet");

// 禁用 FC Padding
config.DisableFCPadding();

// 通过 API 获取是否禁用 FC Padding - false
std::cout << "Disable FC Padding is: " << config.use_fc_padding() << std::endl;

Profile 设置

API定义如下:

// 打开 Profile,运行结束后会打印所有 OP 的耗时占比。
// 参数:None
// 返回:None
void EnableProfile();

// 判断是否开启 Profile
// 参数:None
// 返回:bool - 是否开启 Profile
bool profile_enabled() const;

代码示例:

// 创建 Config 对象
paddle_infer::Config config(FLAGS_infer_model + "/mobilenet");

// 打开 Profile
config.EnableProfile();

// 判断是否开启 Profile - true
std::cout << "Profile is: " << config.profile_enabled() << std::endl;

执行预测之后输出的 Profile 的结果如下:

------------------------->     Profiling Report     <-------------------------

Place: CPU
Time unit: ms
Sorted by total time in descending order in the same thread

-------------------------     Overhead Summary      -------------------------

Total time: 1085.33
  Computation time       Total: 1066.24     Ratio: 98.2411%
  Framework overhead     Total: 19.0902     Ratio: 1.75893%

-------------------------     GpuMemCpy Summary     -------------------------

GpuMemcpy                Calls: 0           Total: 0           Ratio: 0%

-------------------------       Event Summary       -------------------------

Event                            Calls       Total       Min.        Max.        Ave.        Ratio.
thread0::conv2d                  210         319.734     0.815591    6.51648     1.52254     0.294595
thread0::load                    137         284.596     0.114216    258.715     2.07735     0.26222
thread0::depthwise_conv2d        195         266.241     0.955945    2.47858     1.36534     0.245308
thread0::elementwise_add         210         122.969     0.133106    2.15806     0.585568    0.113301
thread0::relu                    405         56.1807     0.021081    0.585079    0.138718    0.0517635
thread0::batch_norm              195         25.8073     0.044304    0.33896     0.132345    0.0237783
thread0::fc                      15          7.13856     0.451674    0.714895    0.475904    0.0065773
thread0::pool2d                  15          1.48296     0.09054     0.145702    0.0988637   0.00136636
thread0::softmax                 15          0.941837    0.032175    0.460156    0.0627891   0.000867786
thread0::scale                   15          0.240771    0.013394    0.030727    0.0160514   0.000221841

Log 设置

API定义如下:

// 去除 Paddle Inference 运行中的 LOG
// 参数:None
// 返回:None
void DisableGlogInfo();

// 判断是否禁用 LOG
// 参数:None
// 返回:bool - 是否禁用 LOG
bool glog_info_disabled() const;

代码示例:

// 创建 Config 对象
paddle_infer::Config config(FLAGS_infer_model + "/mobilenet");

// 去除 Paddle Inference 运行中的 LOG
config.DisableGlogInfo();

// 判断是否禁用 LOG - true
std::cout << "GLOG INFO is: " << config.glog_info_disabled() << std::endl;

PaddlePassBuilder 类

注意: PaddlePassBuilder 对象通过 Configpass_builder 方法进行获取。其中存在2个成员对象 AnalysisPasses 和 Passes,AnalysisPasses 独立于 Passes 之外,仅 AppendAnalysisPassAnalysisPasses 两个 API 能对其进行修改和读取,其余 API 的操作对象都仅限于Passes。

类及方法定义如下:

// 设置 IR 图分析阶段的 passes
// 参数:passes - IR 图分析阶段的 passes 的字符串列表
// 返回:None
void SetPasses(std::initializer_list<std::string> passes);

// 在 Passes 末尾添加 pass
// 参数:pass_type - 需要添加的 pass 字符串
// 返回:None
void AppendPass(const std::string &pass_type);

// 在 Passes 中的第 idx 位置插入 pass
// 参数:idx - 插入的 index 位置
//      pass_type - 需要插入的 pass 字符串
// 返回:None
void InsertPass(size_t idx, const std::string &pass_type);

// 删除第 idx 位置的pass
// 参数:idx - 删除的 index 位置
// 返回:None
void DeletePass(size_t idx);

// 删除字符串匹配为 pass_type 的 pass
// 参数:pass_type - 需要删除的 pass 字符串
// 返回:None
void DeletePass(const std::string &pass_type);

// 清空所有 IR 优化中的 Passes
// 参数:None
// 返回:None
void ClearPasses();

// 启用Debug, 会在每一个 PASS 优化后生成当前计算图 DOT
// 即在每一个 fuse pass 之后添加一个 graph_viz_pass 进行 pass 可视化
// 参数:None
// 返回:None
void TurnOnDebug();

// 获取 IR 图分析阶段的 Passes 中的可读信息
// 参数:None
// 返回:std::string - 所有 Passes 的可读信息
std::string DebugString();

// 获取 IR 图分析阶段的所有 Passes
// 参数:None
// 返回:std::vector<std::string> - 所有 Passes 字符串列表
const std::vector<std::string> &AllPasses();

// 添加 Analysis Pass
// 参数:pass - 需要添加的 Analysis Pass 字符串表示
// 返回:None
void AppendAnalysisPass(const std::string &pass);

// 获取 IR 图分析阶段的所有 Analysis Passes
// 参数:None
// 返回:std::vector<std::string> - 所有 Analysis Passes 字符串列表
std::vector<std::string> AnalysisPasses() const;

自定义 IR Pass 代码示例:

// 构造 Config 对象
paddle_infer::Config config(FLAGS_infer_model);

// 开启 IR 优化
config.SwitchIrOptim();

// 得到 pass_builder 对象
auto pass_builder = config.pass_builder();

// 获取 pass_builder 中的所有 Passes
const std::vector<std::string> all_passes = pass_builder->AllPasses();

// all_passes 中返回结果如下:
// simplify_with_basic_ops_pass
// attention_lstm_fuse_pass
// ...
// runtime_context_cache_pass

// 清空所有 Passes
pass_builder->ClearPasses();
// 设置 Passes
pass_builder->SetPasses({"attention_lstm_fuse_pass", "fc_gru_fuse_pass"});
// 在末尾处添加pass
pass_builder->AppendPass("fc_fuse_pass");
// 删除 Passes
pass_builder->DeletePass("fc_fuse_pass");
// 在 idx = 0 的位置添加 pass
pass_builder->InsertPass(0, "fc_fuse_pass");
// 删除 idx = 0 所在位置的 pass
pass_builder->DeletePass(0);
// 启用Debug, 会在每一个 PASS 优化后生成当前计算图 DOT
// 即在每一个 pass 之后添加一个 graph_viz_pass
pass_builder->TurnOnDebug();
// 获取 IR 图分析阶段的 Passes 中的可读信息
std::cout << pass_builder->DebugString() << std::endl;

// 运行以上代码得到的输出结果如下:
//  - attention_lstm_fuse_pass
//  - graph_viz_pass
//  - fc_gru_fuse_pass
//  - graph_viz_pass

对 Analysis Pass 进行操作和读取示例:

// 构造 Config 对象
paddle_infer::Config config(FLAGS_infer_model);

// 开启 IR 优化
config.SwitchIrOptim();

// 得到 pass_builder 对象
auto pass_builder = config.pass_builder();

// 添加 analysis pass
pass_builder->AppendAnalysisPass("ir_analysis_pass");

// 获取 pass_builder 中的所有 Analysis Passes
const std::vector<std::string> analysis_passes = pass_builder->AnalysisPasses();

// analysis_passes 中返回结果如下:
// ir_graph_build_pass
// ir_graph_clean_pass
// ...
// ir_graph_to_program_pass

Predictor 类

Paddle Inference的预测器,由 CreatePredictor 根据 Config 进行创建。用户可以根据Predictor提供的接口设置输入数据、执行模型预测、获取输出等。

获取输入输出

API 定义如下:

// 获取所有输入 Tensor 的名称
// 参数:None
// 返回:std::vector<std::string> - 所有输入 Tensor 的名称
std::vector<std::string> GetInputNames();

// 根据名称获取输入 Tensor 的句柄
// 参数:name - Tensor 的名称
// 返回:std::unique_ptr<Tensor> - 指向 Tensor 的指针
std::unique_ptr<Tensor> GetInputHandle(const std::string& name);

// 获取所有输出 Tensor 的名称
// 参数:None
// 返回:std::vector<std::string> - 所有输出 Tensor 的名称
std::vector<std::string> GetOutputNames();

// 根据名称获取输出 Tensor 的句柄
// 参数:name - Tensor 的名称
// 返回:std::unique_ptr<Tensor> - 指向 Tensor 的指针
std::unique_ptr<Tensor> GetOutputHandle(const std::string& name);

代码示例:

// 构造 Config 对象
paddle_infer::Config config(FLAGS_infer_model);

// 创建 Predictor
auto predictor = paddle_infer::CreatePredictor(config);

// 准备输入数据
int input_num = shape_production(INPUT_SHAPE);
std::vector<float> input_data(input_num, 1);

// 准备输入 Tensor
auto input_names = predictor->GetInputNames();
auto input_tensor = predictor->GetInputHandle(input_names[0]);
input_tensor->Reshape({1, 3, 224, 224});
input_tensor->CopyFromCpu(input_data.data());

// 执行预测
predictor->Run();

// 获取 Output Tensor
auto output_names = predictor->GetOutputNames();
auto output_tensor = predictor->GetOutputHandle(output_names[0]);

运行和生成

API 定义如下:

// 执行模型预测,需要在设置输入数据后调用
// 参数:None
// 返回:None
bool Run();

// 根据该Predictor,克隆一个新的Predictor,两个Predictor之间共享权重
// 参数:None
// 返回:std::unique_ptr<Predictor> - 新的 Predictor
std::unique_ptr<Predictor> Clone();

// 释放中间Tensor
// 参数:None
// 返回:None
void ClearIntermediateTensor();

// 释放内存池中的所有临时 Tensor
// 参数:None
// 返回:uint64_t - 释放的内存字节数
uint64_t TryShrinkMemory();

代码示例:

// 创建 Predictor
auto predictor = paddle_infer::CreatePredictor(config);

// 准备输入数据
int input_num = shape_production(INPUT_SHAPE);
std::vector<float> input_data(input_num, 1);

// 准备输入 Tensor
auto input_names = predictor->GetInputNames();
auto input_tensor = predictor->GetInputHandle(input_names[0]);
input_tensor->Reshape({1, 3, 224, 224});
input_tensor->CopyFromCpu(input_data.data());

// 执行预测
predictor->Run();

// 获取 Output Tensor
auto output_names = predictor->GetOutputNames();
auto output_tensor = predictor->GetOutputHandle(output_names[0]);
std::vector<int> output_shape = output_tensor->shape();
int out_num = std::accumulate(output_shape.begin(), output_shape.end(), 
                              1, std::multiplies<int>());
// 获取 Output 数据
std::vector<float> out_data;
out_data.resize(out_num);
output_tensor->CopyToCpu(out_data.data());

// 释放中间Tensor
predictor->ClearIntermediateTensor();

// 释放内存池中的所有临时 Tensor
predictor->TryShrinkMemory();

PredictorPool 类

PredictorPoolPredictor 进行了简单的封装,通过传入config和thread的数目来完成初始化,在每个线程中,根据自己的线程id直接从池中取出对应的 Predictor 来完成预测过程。

构造函数和API定义如下:

// PredictorPool构造函数
// 参数:config - Config 对象
//      size - Predictor 对象数量
PredictorPool(const Config& config, size_t size = 1);

// 根据线程 ID 取出该线程对应的 Predictor
// 参数:idx - 线程 ID
// 返回:Predictor* - 线程 ID 对应的 Predictor 指针
Predictor* Retrive(size_t idx);

代码示例

// 构造 Config 对象
paddle_infer::Config config(FLAGS_infer_model);
// 启用 GPU 预测
config.EnableUseGpu(100, 0);

// 根据 Config 对象创建 PredictorPool
paddle_infer::PredictorPool pred_pool(config, 4);

// 获取 ID 为 2 的 Predictor 对象
auto pred = pred_pool.Retrive(2);

Tensor 类

Tensor 是 Paddle Inference 的数据组织形式,用于对底层数据进行封装并提供接口对数据进行操作,包括设置 Shape、数据、LoD 信息等。

注意: 应使用 PredictorGetInputHandleGetOuputHandle 接口获取输入输出 Tensor

Tensor 类的API定义如下:

// 设置 Tensor 的维度信息
// 参数:shape - 维度信息
// 返回:None
void Reshape(const std::vector<int>& shape);

// 从 CPU 获取数据,设置到 Tensor 内部
// 参数:data - CPU 数据指针
// 返回:None
template <typename T>
void CopyFromCpu(const T* data);

// 从 Tensor 中获取数据到 CPU
// 参数:data - CPU 数据指针
// 返回:None
template <typename T>
void CopyToCpu(T* data);

// 获取 Tensor 底层数据指针,用于设置 Tensor 输入数据
// 在调用这个 API 之前需要先对输入 Tensor 进行 Reshape
// 参数:place - 获取 Tensor 的 PlaceType
// 返回:数据指针
template <typename T>
T* mutable_data(PlaceType place);

// 获取 Tensor 底层数据的常量指针,用于读取 Tensor 输出数据
// 参数:place - 获取 Tensor 的 PlaceType
//      size - 获取 Tensor 的 size
// 返回:数据指针
template <typename T>
T* data(PlaceType* place, int* size) const;

// 设置 Tensor 的 LoD 信息
// 参数:x - Tensor 的 LoD 信息
// 返回:None
void SetLoD(const std::vector<std::vector<size_t>>& x);

// 获取 Tensor 的 LoD 信息
// 参数:None
// 返回:std::vector<std::vector<size_t>> - Tensor 的 LoD 信息
std::vector<std::vector<size_t>> lod() const;

// 获取 Tensor 的 DataType
// 参数:None
// 返回:DataType - Tensor 的 DataType
DataType type() const;

// 获取 Tensor 的维度信息
// 参数:None
// 返回:std::vector<int> - Tensor 的维度信息
std::vector<int> shape() const;

// 获取 Tensor 的 Name
// 参数:None
// 返回:std::string& - Tensor 的 Name
const std::string& name() const;

代码示例:

// 构造 Config 对象
paddle_infer::Config config(FLAGS_infer_model);

// 创建 Predictor
auto predictor = paddle_infer::CreatePredictor(config);

// 准备输入数据
int input_num = shape_production(INPUT_SHAPE);
std::vector<float> input_data(input_num, 1);

// 获取输入 Tensor
auto input_names = predictor->GetInputNames();
auto input_tensor = predictor->GetInputHandle(input_names[0]);

// 设置输入 Tensor 的维度信息
input_tensor->Reshape(INPUT_SHAPE);
// 获取输入 Tensor 的 Name
auto name = input_tensor->name();

//  方式1: 通过 mutable_data 设置输入数据
std::copy_n(input_data.begin(), input_data.size(),
            input_tensor->mutable_data<float>(PaddlePlace::kCPU));

//  方式2: 通过 CopyFromCpu 设置输入数据
input_tensor->CopyFromCpu(input_data.data());

// 执行预测
predictor->Run();

// 获取 Output Tensor
auto output_names = predictor->GetOutputNames();
auto output_tensor = predictor->GetOutputHandle(output_names[0]);

// 获取 Output Tensor 的维度信息
std::vector<int> output_shape = output_tensor->shape();

// 方式1: 通过 data 获取 Output Tensor 的数据
paddle_infer::PlaceType place;
int size = 0;
auto* out_data = output_tensor->data<float>(&place, &size);

// 方式2: 通过 CopyToCpu 获取 Output Tensor 的数据
std::vector<float> output_data;
output_data.resize(output_size);
output_tensor->CopyToCpu(output_data.data());

枚举类型

DataType

DataType为模型中Tensor的数据精度,默认值为 FLOAT32。枚举变量与 API 定义如下:

// DataType 枚举类型定义
enum DataType {
  FLOAT32,
  INT64,
  INT32,
  UINT8,
};

// 获取各个 DataType 对应的字节数
// 参数:dtype - DataType 枚举
// 输出:int - 字节数
int GetNumBytesOfDataType(DataType dtype)

代码示例:

// 创建 FLOAT32 类型 DataType
auto data_type = paddle_infer::DataType::FLOAT32;

// 输出 data_type 的字节数 - 4
std::cout << paddle_infer::GetNumBytesOfDataType(data_type) << std::endl;

PrecisionType

PrecisionType设置模型的运行精度,默认值为 kFloat32(float32)。枚举变量定义如下:

// PrecisionType 枚举类型定义
enum class PrecisionType {
  kFloat32 = 0,  ///< fp32
  kInt8,         ///< int8
  kHalf,         ///< fp16
};

代码示例:

// 创建 Config 对象
paddle_infer::Config config(FLAGS_infer_model + "/mobilenet");

// 启用 GPU 进行预测
config.EnableUseGpu(100, 0);

// 启用 TensorRT 进行预测加速 - FP16
config.EnableTensorRtEngine(1 << 20, 1, 3, 
                            paddle_infer::PrecisionType::kHalf, false, false);

PlaceType

PlaceType为目标设备硬件类型,用户可以根据应用场景选择硬件平台类型。枚举变量定义如下:

// PlaceType 枚举类型定义
enum class PlaceType { kUNK = -1, kCPU, kGPU };

代码示例:

// 创建 Config 对象
paddle_infer::Config config;

// 启用 GPU 预测
config.EnableUseGpu(100, 0);
config.SetModel(model_dir);

// 创建 Predictor
auto predictor = paddle_infer::CreatePredictor(config);

// 准备输入数据
int input_num = shape_production(INPUT_SHAPE);
std::vector<float> input_data(input_num, 1);

// 准备输入 Tensor
auto input_names = predictor->GetInputNames();
auto input_tensor = predictor->GetInputHandle(input_names[0]);
input_tensor->Reshape({1, 3, 224, 224});
input_tensor->CopyFromCpu(input_data.data());

// 执行预测
predictor->Run();

// 获取 Output Tensor
auto output_names = predictor->GetOutputNames();
auto output_tensor = predictor->GetOutputHandle(output_names[0]);

// 获取 Output Tensor 的 PlaceType 和 数据指针
paddle_infer::PlaceType place;
int size = 0;
auto* out_data = output_tensor->data<float>(&place, &size);

// 输出 Place 结果 - true
std::cout << (place == paddle_infer::PlaceType::kGPU) << std::endl;
std::cout << size / sizeof(float) << std::endl;

Python API 文档

create_predictor 方法

API定义如下:

# 根据 Config 构建预测执行器 Predictor
# 参数: config - 用于构建 Predictor 的配置信息
# 返回: Predictor - 预测执行器
paddle.inference.create_predictor(config: Config)

代码示例:

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 根据 config 创建 predictor
predictor = paddle_infer.create_predictor(config)

get_version 方法

API定义如下:

# 获取 Paddle 版本信息
# 参数: NONE
# 返回: str - Paddle 版本信息
paddle.inference.get_version()

代码示例:

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 获取 Paddle 版本信息
paddle_infer.get_version()

# 获得输出如下:
# version: 2.0.0-rc0
# commit: 97227e6
# branch: HEAD

Config 类

Config 类定义

Config 类为用于配置构建 Predictor 对象的配置信息,如模型路径、是否开启gpu等等。

构造函数定义如下:

# Config 类定义,输入为 None
class paddle.inference.Config()

# Config 类定义,输入为其他 Config 对象
class paddle.inference.Config(config: Config)

# Config 类定义,输入为非 Combine 模型的文件夹路径
class paddle.inference.Config(model_dir: str)

# Config 类定义,输入分别为 Combine 模型的模型文件路径和参数文件路径
class paddle.inference.Config(prog_file: str, params_file: str)

代码示例 (1):加载预测模型 - 非Combined模型

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config()

# 加载非Combined模型
config.set_model("./mobilenet_v1")

# 根据 config 创建 predictor
predictor = paddle_infer.create_predictor(config)

代码示例 (2):加载预测模型 - 非Combined模型

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 根据 config 创建 predictor
predictor = paddle_infer.create_predictor(config)

代码示例 (3):加载预测模型 - Combined模型

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v2/__model__", "./mobilenet_v2/__params__")

# 根据 config 创建 predictor
predictor = paddle_infer.create_predictor(config)

设置预测模型

从文件中加载预测模型 - 非Combined模型

API定义如下:

# 设置模型文件路径,当需要从磁盘加载非 Combined 模型时使用
# 参数:model_dir - 模型文件夹路径 - str 类型
# 返回:None
paddle.inference.Config.set_model(model_dir: str)

# 获取非combine模型的文件夹路径
# 参数:None
# 返回:str - 模型文件夹路径
paddle.inference.Config.model_dir()

代码示例:

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config()

# 设置非combine模型的文件夹路径
config.set_model("./mobilenet_v1")

# 获取非combine模型的文件夹路径
print(config.model_dir())

# 根据 config 创建 predictor
predictor = paddle_infer.create_predictor(config)
从文件中加载预测模型 - Combined 模型

API定义如下:

# 设置模型文件路径,当需要从磁盘加载 Combined 模型时使用
# 参数:prog_file_path - 模型文件路径
#      params_file_path - 参数文件路径
# 返回:None
paddle.inference.Config.set_model(prog_file_path: str, params_file_path: str)

# 设置模型文件路径,当需要从磁盘加载 Combined 模型时使用。
# 参数:x - 模型文件路径
# 返回:None
paddle.inference.Config.set_prog_file(x: str)

# 设置参数文件路径,当需要从磁盘加载 Combined 模型时使用
# 参数:x - 参数文件路径
# 返回:None
paddle.inference.Config.set_params_file(x: str)

# 获取 Combined 模型的模型文件路径
# 参数:None
# 返回:str - 模型文件路径
paddle.inference.Config.prog_file()

# 获取 Combined 模型的参数文件路径
# 参数:None
# 返回:str - 参数文件路径
paddle.inference.Config.params_file()

代码示例:

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config()

# 通过 API 设置模型文件夹路径
config.set_prog_file("./mobilenet_v2/__model__")
config.set_params_file("./mobilenet_v2/__params__")

# 通过 API 获取 config 中的模型文件和参数文件路径
print(config.prog_file())
print(config.params_file())

# 根据 config 创建 predictor
predictor = paddle_infer.create_predictor(config)
从内存中加载预测模型

API定义如下:

# 从内存加载模型
# 参数:prog_buffer - 内存中模型结构数据
#      prog_buffer_size - 内存中模型结构数据的大小
#      params_buffer - 内存中模型参数数据
#      params_buffer_size - 内存中模型参数数据的大小
# 返回:None
paddle.inference.Config.set_model_buffer(prog_buffer: str, prog_buffer_size: int, 
                                         params_buffer: str, params_buffer_size: int)

# 判断是否从内存中加载模型
# 参数:None
# 返回:bool - 是否从内存中加载模型
paddle.inference.Config.model_from_memory()

代码示例:

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config()

# 加载模型文件到内存
with open('./mobilenet_v2/__model__', 'rb') as prog_file:
    prog_data=prog_file.read()
    
with open('./mobilenet_v2/__params__', 'rb') as params_file:
    params_data=params_file.read()

# 从内存中加载模型
config.set_model_buffer(prog_data, len(prog_data), params_data, len(params_data))

# 通过 API 获取 config 中 model_from_memory 的值 - True
print(config.model_from_memory())

# 根据 config 创建 predictor
predictor = paddle_infer.create_predictor(config)

使用 CPU 进行预测

注意:

  1. 在 CPU 型号允许的情况下,进行预测库下载或编译试尽量使用带 AVX 和 MKL 的版本

  2. 可以尝试使用 Intel 的 MKLDNN 进行 CPU 预测加速,默认 CPU 不启用 MKLDNN

  3. 在 CPU 可用核心数足够时,可以通过设置 set_cpu_math_library_num_threads 将线程数调高一些,默认线程数为 1

CPU 设置

API定义如下:

# 设置 CPU Blas 库计算线程数
# 参数:cpu_math_library_num_threads - blas库计算线程数
# 返回:None
paddle.inference.Config.set_cpu_math_library_num_threads(cpu_math_library_num_threads: int)

# 获取 CPU Blas 库计算线程数
# 参数:None
# 返回:int - cpu blas库计算线程数
paddle.inference.Config.cpu_math_library_num_threads()

代码示例:

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config()

# 设置 CPU Blas 库线程数为 10
config.set_cpu_math_library_num_threads(10)

# 通过 API 获取 CPU 信息 - 10
print(config.cpu_math_library_num_threads())
MKLDNN 设置

注意:

  1. 启用 MKLDNN 的前提为已经使用 CPU 进行预测,否则启用 MKLDNN 无法生效

  2. 启用 MKLDNN BF16 要求 CPU 型号可以支持 AVX512,否则无法启用 MKLDNN BF16

  3. set_mkldnn_cache_capacity 请参考 MKLDNN cache设计文档

API定义如下:

# 启用 MKLDNN 进行预测加速
# 参数:None
# 返回:None
paddle.inference.Config.enable_mkldnn()

# 判断是否启用 MKLDNN 
# 参数:None
# 返回:bool - 是否启用 MKLDNN
paddle.inference.Config.mkldnn_enabled()

# 设置 MKLDNN 针对不同输入 shape 的 cache 容量大小
# 参数:int - cache 容量大小
# 返回:None
paddle.inference.Config.set_mkldnn_cache_capacity(capacity: int=0)

# 指定使用 MKLDNN 加速的 OP 集合
# 参数:使用 MKLDNN 加速的 OP 集合
# 返回:None
paddle.inference.Config.set_mkldnn_op(op_list: Set[str])

# 启用 MKLDNN BFLOAT16
# 参数:None
# 返回:None
paddle.inference.Config.enable_mkldnn_bfloat16()

# 指定使用 MKLDNN BFLOAT16 加速的 OP 集合
# 参数:使用 MKLDNN BFLOAT16 加速的 OP 集合
# 返回:None
paddle.inference.Config.set_bfloat16_op(op_list: Set[str])

代码示例 (1):使用 MKLDNN 进行预测

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 启用 MKLDNN 进行预测
config.enable_mkldnn()

# 通过 API 获取 MKLDNN 启用结果 - true
print(config.mkldnn_enabled())

# 设置 MKLDNN 的 cache 容量大小
config.set_mkldnn_cache_capacity(1)

# 设置启用 MKLDNN 进行加速的 OP 列表
config.set_mkldnn_op({"softmax", "elementwise_add", "relu"})

代码示例 (2):使用 MKLDNN BFLOAT16 进行预测

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 启用 MKLDNN 进行预测
config.enable_mkldnn()

# 启用 MKLDNN BFLOAT16 进行预测
config.enable_mkldnn_bfloat16()

# 设置启用 MKLDNN BFLOAT16 的 OP 列表
config.set_bfloat16_op({"conv2d"})

使用 GPU 进行预测

注意:

  1. Config 默认使用 CPU 进行预测,需要通过 EnableUseGpu 来启用 GPU 预测

  2. 可以尝试启用 CUDNN 和 TensorRT 进行 GPU 预测加速

GPU 设置

API定义如下:

# 启用 GPU 进行预测
# 参数:memory_pool_init_size_mb - 初始化分配的gpu显存,以MB为单位
#      device_id - 设备id
# 返回:None
paddle.inference.Config.enable_use_gpu(memory_pool_init_size_mb: int, device_id: int)

# 禁用 GPU 进行预测
# 参数:None
# 返回:None
paddle.inference.Config.disable_gpu()

# 判断是否启用 GPU 
# 参数:None
# 返回:bool - 是否启用 GPU 
paddle.inference.Config.use_gpu()

# 获取 GPU 的device id
# 参数:None
# 返回:int -  GPU 的device id
paddle.inference.Config.gpu_device_id()

# 获取 GPU 的初始显存大小
# 参数:None
# 返回:int -  GPU 的初始的显存大小
paddle.inference.Config.memory_pool_init_size_mb()

# 初始化显存占总显存的百分比
# 参数:None
# 返回:float - 初始的显存占总显存的百分比
paddle.inference.Config.fraction_of_gpu_memory_for_pool()

GPU设置代码示例:

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 启用 GPU 进行预测 - 初始化 GPU 显存 100M, Deivce_ID 为 0
config.enable_use_gpu(100, 0)
# 通过 API 获取 GPU 信息
print("Use GPU is: {}".format(config.use_gpu())) # True
print("Init mem size is: {}".format(config.memory_pool_init_size_mb())) # 100
print("Init mem frac is: {}".format(config.fraction_of_gpu_memory_for_pool())) # 0.003
print("GPU device id is: {}".format(config.gpu_device_id())) # 0

# 禁用 GPU 进行预测
config.disable_gpu()
# 通过 API 获取 GPU 信息
print("Use GPU is: {}".format(config.use_gpu())) # False
TensorRT 设置

注意:

  1. 启用 TensorRT 的前提为已经启用 GPU,否则启用 TensorRT 无法生效

  2. 对存在LoD信息的模型,如Bert, Ernie等NLP模型,必须使用动态 Shape

  3. 启用 TensorRT OSS 可以支持更多 plugin,详细参考 TensorRT OSS

更多 TensorRT 详细信息,请参考 使用Paddle-TensorRT库预测

API定义如下:

# 启用 TensorRT 进行预测加速
# 参数:workspace_size     - 指定 TensorRT 使用的工作空间大小
#      max_batch_size     - 设置最大的 batch 大小,运行时 batch 大小不得超过此限定值
#      min_subgraph_size  - Paddle-TRT 是以子图的形式运行,为了避免性能损失,当子图内部节点个数
#                           大于 min_subgraph_size 的时候,才会使用 Paddle-TRT 运行
#      precision          - 指定使用 TRT 的精度,支持 FP32(kFloat32),FP16(kHalf),Int8(kInt8)
#      use_static         - 若指定为 true,在初次运行程序的时候会将 TRT 的优化信息进行序列化到磁盘上,
#                           下次运行时直接加载优化的序列化信息而不需要重新生成
#      use_calib_mode     - 若要运行 Paddle-TRT INT8 离线量化校准,需要将此选项设置为 true
# 返回:None
paddle.inference.Config.enable_tensorrt_engine(workspace_size: int = 1 << 20,
                                               max_batch_size: int,
                                               min_subgraph_size: int,
                                               precision_mode: PrecisionType,
                                               use_static: bool,
                                               use_calib_mode: bool)

# 判断是否启用 TensorRT 
# 参数:None
# 返回:bool - 是否启用 TensorRT
paddle.inference.Config.tensorrt_engine_enabled()

# 设置 TensorRT 的动态 Shape
# 参数:min_input_shape          - TensorRT 子图支持动态 shape 的最小 shape
#      max_input_shape          - TensorRT 子图支持动态 shape 的最大 shape
#      optim_input_shape        - TensorRT 子图支持动态 shape 的最优 shape
#      disable_trt_plugin_fp16  - 设置 TensorRT 的 plugin 不在 fp16 精度下运行
# 返回:None
paddle.inference.Config.set_trt_dynamic_shape_info(min_input_shape: Dict[str, List[int]]={}, 
                                                   max_input_shape: Dict[str, List[int]]={}, 
                                                   optim_input_shape: Dict[str, List[int]]={}, 
                                                   disable_trt_plugin_fp16: bool=False)

# 启用 TensorRT OSS 进行预测加速
# 参数:None
# 返回:None
paddle.inference.Config.enable_tensorrt_oss()

# 判断是否启用 TensorRT OSS
# 参数:None
# 返回:bool - 是否启用 TensorRT OSS
paddle.inference.Config.tensorrt_oss_enabled()

# 启用TensorRT DLA进行预测加速
# 参数:dla_core - DLA设备的id,可选0,1,...,DLA设备总数 - 1
# 返回:None
paddle.inference.Config.enable_tensorrt_dla(dla_core: int = 0)

# 判断是否已经开启TensorRT DLA加速
# 参数:None
# 返回:bool - 是否已开启TensorRT DLA加速
paddle.inference.Config.tensorrt_dla_enabled()

代码示例 (1):使用 TensorRT FP32 / FP16 / INT8 进行预测

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 启用 GPU 进行预测 - 初始化 GPU 显存 100M, Deivce_ID 为 0
config.enable_use_gpu(100, 0)

# 启用 TensorRT 进行预测加速 - FP32
config.enable_tensorrt_engine(workspace_size = 1 << 30, 
                              max_batch_size = 1, 
                              min_subgraph_size = 3, 
                              precision_mode=paddle_infer.PrecisionType.Float32, 
                              use_static = False, use_calib_mode = False)
# 通过 API 获取 TensorRT 启用结果 - true
print("Enable TensorRT is: {}".format(config.tensorrt_engine_enabled()))

# 启用 TensorRT 进行预测加速 - FP16
config.enable_tensorrt_engine(workspace_size = 1 << 30, 
                              max_batch_size = 1, 
                              min_subgraph_size = 3, 
                              precision_mode=paddle_infer.PrecisionType.Half, 
                              use_static = False, use_calib_mode = False)
# 通过 API 获取 TensorRT 启用结果 - true
print("Enable TensorRT is: {}".format(config.tensorrt_engine_enabled()))

# 启用 TensorRT 进行预测加速 - Int8
config.enable_tensorrt_engine(workspace_size = 1 << 30, 
                              max_batch_size = 1, 
                              min_subgraph_size = 3, 
                              precision_mode=paddle_infer.PrecisionType.Int8, 
                              use_static = False, use_calib_mode = False)
# 通过 API 获取 TensorRT 启用结果 - true
print("Enable TensorRT is: {}".format(config.tensorrt_engine_enabled()))

代码示例 (2):使用 TensorRT 动态 Shape 进行预测

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 启用 GPU 进行预测 - 初始化 GPU 显存 100M, Deivce_ID 为 0
config.enable_use_gpu(100, 0)

# 启用 TensorRT 进行预测加速 - Int8
config.enable_tensorrt_engine(workspace_size = 1 << 30, 
                              max_batch_size = 1, 
                              min_subgraph_size = 1, 
                              precision_mode=paddle_infer.PrecisionType.Int8, 
                              use_static = False, use_calib_mode = True)

# 设置 TensorRT 的动态 Shape
config.set_trt_dynamic_shape_info(min_input_shape={"image": [1, 1, 3, 3]},
                                  max_input_shape={"image": [1, 1, 10, 10]},
                                  optim_input_shape={"image": [1, 1, 3, 3]})

代码示例 (3):使用 TensorRT OSS 进行预测

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 启用 GPU 进行预测 - 初始化 GPU 显存 100M, Deivce_ID 为 0
config.enable_use_gpu(100, 0)

# 启用 TensorRT 进行预测加速
config.enable_tensorrt_engine()

# 启用 TensorRT OSS 进行预测加速
config.enable_tensorrt_oss()

# 通过 API 获取 TensorRT OSS 启用结果 - true
print("Enable TensorRT OSS is: {}".format(config.tensorrt_oss_enabled()))

使用 XPU 进行预测

API定义如下:

# 启用 XPU 进行预测
# 参数:l3_workspace_size - l3 cache 分配的显存大小,最大为16M
# 参数:locked - 分配的L3 cache是否可以锁定。如果为false,表示不锁定L3 cache,则分配的L3 cache可以多个模型共享,多个共享L3 cache的模型在卡上将顺序执行
# 参数:autotune - 是否对模型中的conv算子进行autotune。如果为true,则在第一次执行到某个维度的conv算子时,将自动搜索更优的算法,用以提升后续相同维度的conv算子的性能
# 参数:autotune_file - 指定autotune文件路径。如果指定autotune_file,则使用文件中指定的算法,不再重新进行autotune
# 参数:precision - multi_encoder的计算精度
# 参数:adaptive_seqlen - multi_encoder的输入是否可变长
# 返回:None
paddle.inference.Config.enable_xpu(l3_workspace_size: int = 0xfffc00,
                                   locked: bool = False,
                                   autotune: bool = True,
                                   autotune_file: string = "",
                                   precision: string = "int16",
                                   adaptive_seqlen: bool = False)

代码示例:

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 启用 XPU,并设置 l3 cache 大小为 10M
config.enable_xpu(10 * 1024 * 1024)

设置模型优化方法

IR 优化

API定义如下:

# 启用 IR 优化
# 参数:x - 是否开启 IR 优化,默认打开
# 返回:None
paddle.inference.Config.switch_ir_optim(x: bool = True)

# 判断是否开启 IR 优化 
# 参数:None
# 返回:bool - 是否开启 IR 优化
paddle.inference.Config.ir_optim()

# 设置是否在图分析阶段打印 IR,启用后会在每一个 PASS 后生成 dot 文件
# 参数:x - 是否打印 IR,默认关闭
# 返回:None
paddle.inference.Config.switch_ir_debug(x: int=True)

# 返回 pass_builder,用来自定义图分析阶段选择的 IR
# 参数:None
# 返回:PassStrategy - pass_builder对象
paddle.inference.Config.pass_builder()

# 删除字符串匹配为 pass 的 pass
# 参数:pass - 需要删除的 pass 字符串
# 返回:None
paddle.inference.Config.delete_pass(pass: str)

代码示例:

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 开启 IR 优化
config.switch_ir_optim()
# 开启 IR 打印
config.switch_ir_debug()

# 得到 pass_builder 对象
pass_builder = config.pass_builder()

# 或者直接通过 config 去除 fc_fuse_pass
config.delete_pass("fc_fuse_pass")

# 通过 API 获取 IR 优化是否开启 - true
print("IR Optim is: {}".format(config.ir_optim()))

# 根据 config 创建 predictor
predictor = paddle_infer.create_predictor(config)

运行结果示例:

# switch_ir_optim 开启 IR 优化后,运行中会有如下 LOG 输出
--- Running analysis [ir_graph_build_pass]
--- Running analysis [ir_graph_clean_pass]
--- Running analysis [ir_analysis_pass]
--- Running IR pass [simplify_with_basic_ops_pass]
--- Running IR pass [attention_lstm_fuse_pass]
--- Running IR pass [seqconv_eltadd_relu_fuse_pass]
...
--- Running analysis [inference_op_replace_pass]
--- Running analysis [ir_graph_to_program_pass]

# switch_ir_debug 开启 IR 打印后,运行结束之后会在目录下生成如下 DOT 文件
-rw-r--r-- 1 root root  70K Nov 17 10:47 0_ir_simplify_with_basic_ops_pass.dot
-rw-r--r-- 1 root root  72K Nov 17 10:47 10_ir_fc_gru_fuse_pass.dot
-rw-r--r-- 1 root root  72K Nov 17 10:47 11_ir_graph_viz_pass.dot
...
-rw-r--r-- 1 root root  72K Nov 17 10:47 8_ir_mul_lstm_fuse_pass.dot
-rw-r--r-- 1 root root  72K Nov 17 10:47 9_ir_graph_viz_pass.dot
Lite 子图
# 启用 Lite 子图
# 参数:precision_mode - Lite 子图的运行精度,默认为 FP32
#      zero_copy      - 启用 zero_copy,lite 子图与 paddle inference 之间共享数据
#      Passes_filter  - 设置 lite 子图的 pass
#      ops_filter     - 设置不使用 lite 子图运行的 op
# 返回:None
paddle.inference.Config.enable_lite_engine(precision_mode: PrecisionType = paddle_infer.PrecisionType.Float32, 
                                           zero_copy: bool = False, 
                                           passes_filter: List[str]=[], 
                                           ops_filter: List[str]=[])

# 判断是否启用 Lite 子图
# 参数:None
# 返回:bool - 是否启用 Lite 子图
paddle.inference.Config.lite_engine_enabled()

示例代码:

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 启用 GPU 进行预测
config.enable_use_gpu(100, 0)

# 启用 Lite 子图
config.enable_lite_engine(paddle_infer.PrecisionType.Float32)

# 通过 API 获取 Lite 子图启用信息 - true
print("Lite Engine is: {}".format(config.lite_engine_enabled()))

启用内存优化

API定义如下:

# 开启内存/显存复用,具体降低内存效果取决于模型结构。
# 参数:None
# 返回:None
paddle.inference.Config.enable_memory_optim()

代码示例:

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 开启 CPU 显存优化
config.enable_memory_optim()

# 启用 GPU 进行预测
config.enable_use_gpu(100, 0)
# 开启 GPU 显存优化
config.enable_memory_optim()

设置缓存路径

注意: 如果当前使用的为 TensorRT INT8 且设置从内存中加载模型,则必须通过 SetOptimCacheDir 来设置缓存路径。

API定义如下:

# 设置缓存路径
# 参数:opt_cache_dir - 缓存路径
# 返回:None
paddle.inference.Config.set_optim_cache_dir(opt_cache_dir: str)

代码示例:

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 设置缓存路径
config.set_optim_cache_dir("./OptimCacheDir")

Profile 设置

API定义如下:

# 打开 Profile,运行结束后会打印所有 OP 的耗时占比。
# 参数:None
# 返回:None
paddle.inference.Config.enable_profile()

代码示例:

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 打开 Profile
config.enable_profile()

执行预测之后输出的 Profile 的结果如下:

------------------------->     Profiling Report     <-------------------------

Place: CPU
Time unit: ms
Sorted by total time in descending order in the same thread

-------------------------     Overhead Summary      -------------------------

Total time: 1085.33
  Computation time       Total: 1066.24     Ratio: 98.2411%
  Framework overhead     Total: 19.0902     Ratio: 1.75893%

-------------------------     GpuMemCpy Summary     -------------------------

GpuMemcpy                Calls: 0           Total: 0           Ratio: 0%

-------------------------       Event Summary       -------------------------

Event                            Calls       Total       Min.        Max.        Ave.        Ratio.
thread0::conv2d                  210         319.734     0.815591    6.51648     1.52254     0.294595
thread0::load                    137         284.596     0.114216    258.715     2.07735     0.26222
thread0::depthwise_conv2d        195         266.241     0.955945    2.47858     1.36534     0.245308
thread0::elementwise_add         210         122.969     0.133106    2.15806     0.585568    0.113301
thread0::relu                    405         56.1807     0.021081    0.585079    0.138718    0.0517635
thread0::batch_norm              195         25.8073     0.044304    0.33896     0.132345    0.0237783
thread0::fc                      15          7.13856     0.451674    0.714895    0.475904    0.0065773
thread0::pool2d                  15          1.48296     0.09054     0.145702    0.0988637   0.00136636
thread0::softmax                 15          0.941837    0.032175    0.460156    0.0627891   0.000867786
thread0::scale                   15          0.240771    0.013394    0.030727    0.0160514   0.000221841

Log 设置

API定义如下:

# 去除 Paddle Inference 运行中的 LOG
# 参数:None
# 返回:None
paddle.inference.Config.disable_glog_info()

# 判断是否禁用 LOG
# 参数:None
# 返回:bool - 是否禁用 LOG
paddle.inference.Config.glog_info_disabled()

代码示例:

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 去除 Paddle Inference 运行中的 LOG
config.disable_glog_info()

# 判断是否禁用 LOG - true
print("GLOG INFO is: {}".format(config.glog_info_disabled()))

Predictor 类

Paddle Inference的预测器,由 create_predictor 根据 Config 进行创建。用户可以根据Predictor提供的接口设置输入数据、执行模型预测、获取输出等。

类及方法定义如下:

# Predictor 类定义
class paddle.inference.Predictor

# 获取所有输入 Tensor 的名称
# 参数:None
# 返回:List[str] - 所有输入 Tensor 的名称
paddle.inference.Predictor.get_input_names()

# 根据名称获取输入 Tensor 的句柄
# 参数:name - Tensor 的名称
# 返回:Tensor - 输入 Tensor
paddle.inference.Predictor.get_input_handle(name: str)

# 获取所有输出 Tensor 的名称
# 参数:None
# 返回:List[str] - 所有输出 Tensor 的名称
paddle.inference.Predictor.get_output_names()

# 根据名称获取输出 Tensor 的句柄
# 参数:name - Tensor 的名称
# 返回:Tensor - 输出 Tensor
paddle.inference.Predictor.get_output_handle(name: str)

# 执行模型预测,需要在设置输入数据后调用
# 参数:None
# 返回:None
paddle.inference.Predictor.run()

# 根据该 Predictor,克隆一个新的 Predictor,两个 Predictor 之间共享权重
# 参数:None
# 返回:Predictor - 新的 Predictor
paddle.inference.Predictor.clone()

# 释放中间 Tensor
# 参数:None
# 返回:None
paddle.inference.Predictor.clear_intermediate_tensor()

# 释放内存池中的所有临时 Tensor
# 参数:None
# 返回:int - 释放的内存字节数
paddle.inference.Predictor.try_shrink_memory()

代码示例

import numpy

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 根据 config 创建 predictor
predictor = paddle_infer.create_predictor(config)

# 获取输入 Tensor
input_names = predictor.get_input_names()
input_tensor = predictor.get_input_handle(input_names[0])

# 从 CPU 获取数据,设置到 Tensor 内部
fake_input = numpy.random.randn(1, 3, 224, 224).astype("float32")
input_tensor.copy_from_cpu(fake_input)

# 执行预测
predictor.run()

# 获取输出 Tensor
output_names = predictor.get_output_names()
output_tensor = predictor.get_output_handle(output_names[0])

# 释放中间Tensor
predictor.clear_intermediate_tensor()

# 释放内存池中的所有临时 Tensor
predictor.try_shrink_memory()

PredictorPool 类

PredictorPoolPredictor 进行了简单的封装,通过传入config和thread的数目来完成初始化,在每个线程中,根据自己的线程id直接从池中取出对应的 Predictor 来完成预测过程。

类及方法定义如下:

# PredictorPool 类定义
# 参数:config - Config 类型
#      size - Predictor 对象数量
class paddle.inference.PredictorPool(config: Config, size: int)

# 根据线程 ID 取出该线程对应的 Predictor
# 参数:idx - 线程 ID
# 返回:Predictor - 线程 ID 对应的 Predictor
paddle.inference.PredictorPool.retrive(idx: int)

代码示例

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 Config
config = paddle_infer.Config("./mobilenet_v1")

# 创建 PredictorPool
pred_pool = paddle_infer.PredictorPool(config, 4)

# 获取 ID 为 2 的 Predictor 对象
predictor = pred_pool.retrive(2)

Tensor 类

Tensor是Paddle Inference的数据组织形式,用于对底层数据进行封装并提供接口对数据进行操作,包括设置Shape、数据、LoD信息等。

注意: 应使用 Predictorget_input_handleget_output_handle 接口获取输入输出 Tensor

类及方法定义如下:

# Tensor 类定义
class paddle.inference.Tensor

# 设置 Tensor 的维度信息
# 参数:shape - 维度信息
# 返回:None
paddle.inference.Tensor.reshape(shape: numpy.ndarray|List[int])

# 从 CPU 获取数据,设置到 Tensor 内部
# 参数:data - CPU 数据 - 支持float, int32, int64
# 返回:None
paddle.inference.Tensor.copy_from_cpu(data: numpy.ndarray)

# 从 Tensor 中获取数据到 CPU
# 参数:None
# 返回:numpy.ndarray - CPU 数据
paddle.inference.Tensor.copy_to_cpu()

# 获取 Tensor 的维度信息
# 参数:None
# 返回:List[int] - Tensor 的维度信息
paddle.inference.Tensor.shape()

# 设置 Tensor 的 LoD 信息
# 参数:x - Tensor 的 LoD 信息
# 返回:None
paddle.inference.Tensor.set_lod(x: numpy.ndarray|List[List[int]])

# 获取 Tensor 的 LoD 信息
# 参数:None
# 返回:List[List[int]] - Tensor 的 LoD 信息
paddle.inference.Tensor.lod()

# 获取 Tensor 的数据类型
# 参数:None
# 返回:DataType - Tensor 的数据类型
paddle.inference.Tensor.type()

代码示例:

import numpy

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 根据 config 创建 predictor
predictor = paddle_infer.create_predictor(config)

# 准备输入数据
fake_input = numpy.random.randn(1, 3, 224, 224).astype("float32")

# 获取输入 Tensor
input_names = predictor.get_input_names()
input_tensor = predictor.get_input_handle(input_names[0])

# 设置 Tensor 的维度信息
input_tensor.reshape([1, 3, 224, 224])

# 从 CPU 获取数据,设置到 Tensor 内部
input_tensor.copy_from_cpu(fake_input)

# 执行预测
predictor.run()

# 获取输出 Tensor
output_names = predictor.get_output_names()
output_tensor = predictor.get_output_handle(output_names[0])

# 从 Tensor 中获取数据到 CPU
output_data = output_tensor.copy_to_cpu()

# 获取 Tensor 的维度信息
output_shape = output_tensor.shape()

# 获取 Tensor 的数据类型
output_type = output_tensor.type()

枚举类型

DataType

DataType定义了Tensor的数据类型,由传入Tensor的numpy数组类型确定。

# DataType 枚举定义
class paddle.inference.DataType:

# 获取各个 DataType 对应的字节数
# 参数:dtype - DataType 枚举
# 输出:dtype 对应的字节数
paddle.inference.get_num_bytes_of_data_type(dtype: DataType)

DataType 中包括以下成员:

  • INT64: 64位整型

  • INT32: 32位整型

  • FLOAT32: 32位浮点型

代码示例:

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 FLOAT32 类型 DataType
data_type = paddle_infer.DataType.FLOAT32

# 输出 data_type 的字节数 - 4
paddle_infer.get_num_bytes_of_data_type(data_type)

PrecisionType

PrecisionType设置模型的运行精度,默认值为 kFloat32(float32)。枚举变量定义如下:

# PrecisionType 枚举定义
class paddle.inference.PrecisionType

PrecisionType 中包括以下成员:

  • Float32: FP32 模式运行

  • Half: FP16 模式运行

  • Int8: INT8 模式运行

代码示例:

# 引用 paddle inference 预测库
import paddle.inference as paddle_infer

# 创建 config
config = paddle_infer.Config("./mobilenet_v1")

# 启用 GPU, 初始化100M显存,使用gpu id为0
config.enable_use_gpu(100, 0)

# 开启 TensorRT 预测,精度为 FP32,开启 INT8 离线量化校准
config.enable_tensorrt_engine(precision_mode=paddle_infer.PrecisionType.Float32,
                              use_calib_mode=True)

C API 文档

枚举类型

DataType

DataType 为模型中 Tensor 的数据精度, 默认值为 PD_DATA_FLOAT32。枚举变量定义如下:

// DataType 枚举类型定义
PD_ENUM(PD_DataType){
    PD_DATA_UNK = -1,
    PD_DATA_FLOAT32,
    PD_DATA_INT32,
    PD_DATA_INT64,
    PD_DATA_UINT8,
};

PrecisionType

PrecisionType 为模型的运行精度,默认值为 PD_PRECISION_FLOAT32。枚举变量定义如下:

// PrecisionType 枚举类型定义
PD_ENUM(PD_PrecisionType){PD_PRECISION_FLOAT32 = 0, PD_PRECISION_INT8,
                          PD_PRECISION_HALF};

PlaceType

PlaceType 为目标设备硬件类型,用户可以根据应用场景选择硬件平台类型。枚举变量定义如下:

// PlaceType 枚举类型定义
PD_ENUM(PD_PlaceType){PD_PLACE_UNK = -1, PD_PLACE_CPU, PD_PLACE_GPU,
                      PD_PLACE_XPU};

动态数组结构体

动态数组结构体是 Paddle Inference 为了兼顾效率和安全,创建的一组 C 结构体类型,用来进行上层和底层的数据交互。它的来源分为两种,一种是用户自己创建,这种情况由用户自己负责进行内存的回收。另外一种是来自于 Paddle Inference C api的返回值,这种情况需要用户显式地调用相应的 Destroy 函数进行对象的销毁。

OneDimArrayInt32

OneDimArrayInt32 是 int32_t 类型的一维数组,结构体与 API 定义如下:

// OneDimArrayInt32 结构体定义
typedef struct PD_OneDimArrayInt32 {
  size_t size;   // 数组长度
  int32_t* data; // 数组元素指针
} PD_OneDimArrayInt32;

// 销毁由 paddle inferecen C api 返回的 OneDimArrayInt32 对象
// 参数:array - 需要销毁的 OneDimArrayInt32 对象指针
// 返回:None
void PD_OneDimArrayInt32Destroy(PD_OneDimArrayInt32* array);

OneDimArraySize

OneDimArraySize 是 size_t 类型的一维数组,结构体与 API 定义如下:

// OneDimArraySize 结构体定义
typedef struct PD_OneDimArraySize {
  size_t size;   // 数组长度
  size_t* data;  // 数组元素指针
} PD_OneDimArraySize;

// 销毁由 paddle inferecen C api 返回的 OneDimArraySize 对象
// 参数:array - 需要销毁的 OneDimArraySize 对象指针
// 返回:None
void PD_OneDimArraySizeDestroy(PD_OneDimArraySize* array);

OneDimArrayCstr

OneDimArrayCstr 是 const char* 类型的一维数组,结构体与 API 定义如下:

// OneDimArrayCstr 结构体定义
typedef struct PD_OneDimArrayCstr {
  size_t size;   // 数组长度
  char** data;   // 数组元素指针
} PD_OneDimArraySize;

// 销毁由 paddle inferecen C api 返回的 OneDimArrayCstr 对象
// 参数:array - 需要销毁的 OneDimArrayCstr 对象指针
// 返回:None
void PD_OneDimArrayCstrDestroy(PD_OneDimArrayCstr* array);

TwoDimArraySize

TwoDimArraySize 是 size_t 类型的二维数组,也可以理解为是OneDimArraySize指针类型的一维数组,结构体与 API 定义如下:

// TwoDimArraySize 结构体定义
typedef struct PD_TwoDimArraySize {
  size_t size;   // 数组长度
  PD_OneDimArraySize** data;  // 数组元素指针
} PD_TwoDimArraySize;

// 销毁由 paddle inferecen C api 返回的 TwoDimArraySize 对象
// 参数:array - 需要销毁的 TwoDimArraySize 对象指针
// 返回:None
void PD_TwoDimArraySizeDestroy(PD_TwoDimArraySize* array);

Config 方法

创建 Config

Config 对象相关方法用于创建预测相关配置,构建 Predictor 对象的配置信息,如模型路径、是否开启gpu等等。

相关方法定义如下:

// 创建 Config 对象
// 参数:None
// 返回:PD_Config* - Config 对象指针
PD_Config* PD_ConfigCreate();

// 销毁 Config 对象
// 参数:pd_config - Config 对象指针
// 返回:None
void PD_ConfigDestroy(PD_Config* pd_config);

代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 销毁 Config 对象
PD_ConfigDestroy(config);

设置预测模型

从文件中加载预测模型 - 非 Combined 模型

注意: 非 Combined 模型主要是为了兼容2.0之前版本的模型格式,从paddle 2.0开始,模型的默认保存格式是 Combined。

API定义如下:

// 设置模型文件路径,当需要从磁盘加载非 Combined 模型时使用
// 参数:pd_config - Config 对象指针
//      model_dir - 模型文件夹路径
// 返回:None
void PD_ConfigSetModelDir(PD_Config* pd_config, const char* model_dir);

// 获取非combine模型的文件夹路径
// 参数:pd_config - Config 对象指针
// 返回:const char * - 模型文件夹路径
const char* PD_ConfigGetModelDir(const PD_Config* pd_config);

代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 设置预测模型路径,这里为非 Combined 模型
const char* model_dir  = "./mobilenet_v1";
PD_ConfigSetModelDir(config, model_dir);

// 输出模型路径
printf("Non-combined model dir is: %s\n", PD_ConfigGetModelDir(config));

// 销毁 Config 对象
PD_ConfigDestroy(config);
从文件中加载预测模型 - Combined 模型

API定义如下:

// 设置模型文件路径
// 参数:pd_config        - Config 对象指针
//      prog_file_path   - Combined 模型文件所在路径
//      params_file_path - Combined 模型参数文件所在路径
// 返回:None
void PD_ConfigSetModel(PD_Config* pd_config, const char* prog_file_path, const char* params_file_path);

// 设置模型文件路径,当需要从磁盘加载 Combined 模型时使用。
// 参数:pd_config      - Config 对象指针
//      prog_file_path - 模型文件路径
// 返回:None
void PD_ConfigSetProgFile(PD_Config* pd_config, const char* prog_file_path);

// 设置参数文件路径,当需要从磁盘加载 Combined 模型时使用
// 参数:pd_config        - Config 对象指针
//      params_file_path - 参数文件路径
// 返回:None
void PD_ConfigSetParamsFile(PD_Config* pd_config, const char* params_file_path);

// 获取 Combined 模型的模型文件路径
// 参数:pd_config - Config 对象指针
// 返回:const char* - 模型文件路径
const char* PD_ConfigGetProgFile(PD_Config* pd_config);

// 获取 Combined 模型的参数文件路径
// 参数:pd_config - Config 对象指针
// 返回:const char* - 参数文件路径
const char* PD_ConfigGetParamsFile(PD_Config* pd_config);

代码示例 (1):

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 设置预测模型路径,这里为 Combined 模型
const char* model_path  = "./model/inference.pdmodel";  
const char* params_path = "./model/inference.pdiparams";
PD_ConfigSetModel(config, model_path, params_path);

// 输出模型路径
printf("Non-combined model path is: %s\n", PD_ConfigGetProgFile(config));
printf("Non-combined param path is: %s\n", PD_ConfigGetParamsFile(config));

// 销毁 Config 对象
PD_ConfigDestroy(config);

代码示例 (2):

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 设置预测模型路径,这里为 Combined 模型
const char* model_path  = "./model/inference.pdmodel";  
const char* params_path = "./model/inference.pdiparams";
PD_ConfigSetProgFile(config, model_path);
PD_ConfigSetParamsFile(config, params_path);

// 输出模型路径
printf("Non-combined model path is: %s\n", PD_ConfigGetProgFile(config));
printf("Non-combined param path is: %s\n", PD_ConfigGetParamsFile(config));

// 销毁 Config 对象
PD_ConfigDestroy(config);
从内存中加载预测模型

API定义如下:

// 从内存加载模型
// 参数:pd_config          - Config 对象指针
//      prog_buffer        - 内存中模型结构数据
//      prog_buffer_size   - 内存中模型结构数据的大小
//      params_buffer      - 内存中模型参数数据
//      params_buffer_size - 内存中模型参数数据的大小
// 返回:None
void PD_ConfigSetModelBuffer(PD_Config* pd_config,
                             const char* prog_buffer, size_t prog_buffer_size,
                             const char* params_buffer, size_t params_buffer_size);

// 判断是否从内存中加载模型
// 参数:pd_config - Config 对象指针
// 返回:PD_Bool - 是否从内存中加载模型
PD_Bool PD_ConfigModelFromMemory(PD_Config* pd_config);

代码示例:

// 定义文件读取函数
void read_file(const char * filename, char ** filedata, size_t * filesize) {
  FILE *file = fopen(filename, "rb");
  if (file == NULL) {
    printf("Failed to open file: %s\n", filename);
    return;
  }
  fseek(file, 0, SEEK_END);
  int64_t size = ftell(file);
  if (size == 0) {
    printf("File %s should not be empty, size is: %ld\n", filename, size);
    return;
  }
  rewind(file);
  *filedata = calloc(1, size+1);
  if (!(*filedata)) {
    printf("Failed to alloc memory.\n");
    return;
  }
  *filesize = fread(*filedata, 1, size, file);
  if ((*filesize) != size) {
    printf("Read binary file bytes do not match with fseek!\n");
    return;
  }
  fclose(file);
}

int main() {
  // 创建 Config 对象
  PD_Config* config = PD_ConfigCreate();

  // 设置预测模型路径,这里为 Combined 模型
  const char* model_path  = "./model/inference.pdmodel";  
  const char* params_path = "./model/inference.pdiparams";

  // 加载模型文件到内存,并获取文件大小
  char * model_buffer = NULL;
  char * param_buffer = NULL;
  size_t model_size, param_size;
  read_file(model_path, &model_buffer, &model_size);
  read_file(params_path, &param_buffer, &param_size);

  if(model_buffer == NULL) {
    printf("Failed to load model buffer.\n");
    return 1;
  }
  if(param_buffer == NULL) {
    printf("Failed to load param buffer.\n");
    return 1;
  }

  // 从内存中加载模型
  PD_ConfigSetModelBuffer(config, model_buffer, model_size, param_buffer, param_size);

  // 输出是否从内存中加载模型
  printf("Load model from memory is: %s\n", PD_ConfigModelFromMemory(config) ? "true" : "false");

 // 销毁 Config 对象
  PD_ConfigDestroy(config);

  free(model_buffer);
  free(param_buffer);
}

使用 CPU 进行预测

注意:

  1. 在 CPU 型号允许的情况下,进行预测库下载或编译试尽量使用带 AVX 和 MKL 的版本

  2. 可以尝试使用 Intel 的 MKLDNN 进行 CPU 预测加速,默认 CPU 不启用 MKLDNN

  3. 在 CPU 可用核心数足够时,可以通过设置 PD_ConfigSetCpuMathLibraryNumThreads 将线程数调高一些,默认线程数为 1

CPU 设置

API定义如下:

// 设置 CPU Blas 库计算线程数
// 参数:config - Config 对象指针
//      cpu_math_library_num_threads - blas库计算线程数
// 返回:None
void PD_ConfigSetCpuMathLibraryNumThreads(PD_Config* pd_config, int32_t cpu_math_library_num_threads);

// 获取 CPU Blas 库计算线程数
// 参数:pd_config - Config 对象指针
// 返回:int32_t - cpu blas 库计算线程数
int32_t PD_ConfigGetCpuMathLibraryNumThreads(PD_Config* pd_config);

代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 设置 CPU Blas 库线程数为 10
PD_ConfigSetCpuMathLibraryNumThreads(config, 10);

// 通过 API 获取 CPU 信息
printf("CPU Math Lib Thread Num is: %d\n", PD_ConfigGetCpuMathLibraryNumThreads(config));

// 销毁 Config 对象
PD_ConfigDestroy(config);
MKLDNN 设置

注意:

  1. 启用 MKLDNN 的前提为已经使用 CPU 进行预测,否则启用 MKLDNN 无法生效

  2. 启用 MKLDNN BF16 要求 CPU 型号可以支持 AVX512,否则无法启用 MKLDNN BF16

  3. PD_ConfigSetMkldnnCacheCapacity 请参考 MKLDNN cache设计文档

API定义如下:

// 启用 MKLDNN 进行预测加速
// 参数:pd_config - Config 对象指针
// 返回:None
void PD_ConfigEnableMKLDNN(PD_Config* pd_config);

// 判断是否启用 MKLDNN 
// 参数:pd_config - Config 对象指针
// 返回:PD_Bool - 是否启用 MKLDNN
PD_Bool PD_ConfigMkldnnEnabled(PD_Config* pd_config);

// 设置 MKLDNN 针对不同输入 shape 的 cache 容量大小
// 参数:pd_config - Config 对象指针
//      capacity  - cache 容量大小
// 返回:None
void PD_ConfigSetMkldnnCacheCapacity(PD_Config* pd_config, int32_t capacity);

// 指定使用 MKLDNN 加速的 OP 列表
// 参数:pd_config - Config 对象指针
//      ops_num   - 使用 MKLDNN 加速的 OP 数量
//      op_list   - 使用 MKLDNN 加速的 OP 列表
// 返回:None
void PD_ConfigSetMkldnnOp(PD_Config* pd_config, size_t ops_num, const char** op_list);

// 启用 MKLDNN BFLOAT16
// 参数:pd_config - Config 对象指针
// 返回:None
void PD_ConfigEnableMkldnnBfloat16(PD_Config* pd_config);

// 判断是否启用 MKLDNN BFLOAT16
// 参数:pd_config - Config 对象指针
// 返回:PD_Bool - 是否启用 MKLDNN BFLOAT16
PD_Bool PD_ConfigMkldnnBfloat16Enabled(PD_Config* pd_config);

// 指定使用 MKLDNN BFLOAT16 加速的 OP 列表
// 参数:pd_config - Config 对象指针
//      ops_num   - 使用 MKLDNN BFLOAT16 加速的 OP 数量
//      op_list   - 使用 MKLDNN BFLOAT16 加速的 OP 列表
// 返回:None
PD_ConfigSetBfloat16Op(PD_Config* pd_config, size_t ops_num, const char** op_list);

代码示例 (1):使用 MKLDNN 进行预测

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 启用 MKLDNN 进行预测
PD_ConfigEnableMKLDNN(config);

// 通过 API 获取 MKLDNN 启用结果 - True
printf("Enable MKLDNN is: %s\n", PD_MkldnnEnabled(config) ? "True" : "False");

// 设置 MKLDNN 的 cache 容量大小
PD_ConfigSetMkldnnCacheCapacity(config, 1);

// 设置启用 MKLDNN 进行加速的 OP 列表
const char* op_list[3] = {"softmax", "elementwise_add", "relu"};
PD_ConfigSetMkldnnOp(config, 3, op_list);

// 销毁 Config 对象
PD_ConfigDestroy(config);

代码示例 (2):使用 MKLDNN BFLOAT16 进行预测

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 启用 MKLDNN 进行预测
PD_ConfigEnableMKLDNN(config);

// 启用 MKLDNN BFLOAT16 进行预测
PD_EnableMkldnnBfloat16(config);

// 设置启用 MKLDNN BFLOAT16 进行加速的 OP 列表
const char* op_list[1] = {"conv2d"};
PD_ConfigSetBfloat16Op(config, 1, op_list);

// 通过 API 获取 MKLDNN 启用结果 - true
printf("Enable MKLDNN BF16 is: %s\n", PD_ConfigMkldnnBfloat16Enabled(config) ? "True" : "False");

// 销毁 Config 对象
PD_ConfigDestroy(config);

使用 GPU 进行预测

注意:

  1. Config 默认使用 CPU 进行预测,需要通过 PD_ConfigEnableUseGpu 来启用 GPU 预测

  2. 可以尝试启用 CUDNN 和 TensorRT 进行 GPU 预测加速

GPU 设置

API定义如下:

// 启用 GPU 进行预测
// 参数:pd_config                - Config 对象指针
//      memory_pool_init_size_mb - 初始化分配的gpu显存,以MB为单位
//      device_id                - 设备id
// 返回:None
PD_ConfigEnableUseGpu(PD_Config* pd_config, uint64_t memory_pool_init_size_mb, int32_t device_id);

// 禁用 GPU 进行预测
// 参数:pd_config - Config 对象指针
// 返回:None
void PD_ConfigDisableGpu(PD_Config* pd_config);

// 判断是否启用 GPU 
// 参数:pd_config - Config 对象指针
// 返回:PD_Bool - 是否启用 GPU 
PD_Bool PD_ConfigUseGpu(PD_Config* pd_config);

// 获取 GPU 的device id
// 参数:pd_config - Config 对象指针
// 返回:int32_t -  GPU 的device id
int32_t PD_ConfigGpuDeviceId(PD_Config* pd_config);

// 获取 GPU 的初始显存大小
// 参数:pd_config - Config 对象指针
// 返回:int32_t -  GPU 的初始的显存大小
int32_t PD_ConfigMemoryPoolInitSizeMb(PD_Config* pd_config);

// 初始化显存占总显存的百分比
// 参数:pd_config - Config 对象指针
// 返回:float - 初始的显存占总显存的百分比
float PD_ConfigFractionOfGpuMemoryForPool(PD_Config* pd_config);

// 开启线程流,目前的行为是为每一个线程绑定一个流,在将来该行为可能改变
// 参数:pd_config - Config 对象指针
// 返回:None
void PD_ConfigEnableGpuMultiStream(PD_Config* pd_config);

// 判断是否开启线程流
// 参数:pd_config - Config 对象指针
// 返回:PD_Bool - 是否是否开启线程流
PD_Bool PD_ConfigThreadLocalStreamEnabled(PD_Config* pd_config);

代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 启用 GPU 进行预测 - 初始化 GPU 显存 100M, Deivce_ID 为 0
PD_ConfigEnableUseGpu(config, 100, 0);

// 通过 API 获取 GPU 信息
printf("Use GPU is: %s\n", PD_ConfigUseGpu(config) ? "True" : "False"); // True
printf("GPU deivce id is: %d\n", PD_ConfigGpuDeviceId(config));
printf("GPU memory size is: %d\n", PD_ConfigMemoryPoolInitSizeMb(config));
printf("GPU memory frac is: %f\n", PD_ConfigFractionOfGpuMemoryForPool(config));

// 开启线程流
PD_ConfigEnableGpuMultiStream(config);

// 判断是否开启线程流 - True
printf("Thread local stream enabled: %s\n", PD_ConfigThreadLocalStreamEnabled(config) ? "True" : "False");

// 禁用 GPU 进行预测
PD_ConfigDisableGpu(config);

// 通过 API 获取 GPU 信息
printf("Use GPU is: %s\n", PD_ConfigUseGpu(config) ? "True" : "False"); // False

// 销毁 Config 对象
PD_ConfigDestroy(config);
CUDNN 设置

注意: 启用 CUDNN 的前提为已经启用 GPU,否则启用 CUDNN 无法生效。

API定义如下:

// 启用 CUDNN 进行预测加速
// 参数:pd_config - Config 对象指针
// 返回:None
void PD_ConfigEnableCudnn(PD_Config* pd_config);

// 判断是否启用 CUDNN 
// 参数:pd_config - Config 对象指针
// 返回:PD_Bool - 是否启用 CUDNN
PD_Bool PD_ConfigCudnnEnabled(PD_Config* pd_config);

GPU设置代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 启用 GPU 进行预测 - 初始化 GPU 显存 100M, Deivce_ID 为 0
PD_ConfigEnableUseGpu(config, 100, 0);

// 启用 CUDNN 进行预测加速
PD_ConfigEnableCudnn(config);

// 通过 API 获取 CUDNN 启用结果 - True
printf("Enable CUDNN is: %s\n", PD_ConfigCudnnEnabled(config) ? "True" : "False");

// 禁用 GPU 进行预测
PD_ConfigDisableGpu(config);

// 启用 CUDNN 进行预测加速 - 因为 GPU 被禁用,因此 CUDNN 启用不生效
PD_ConfigEnableCudnn(config);

// 通过 API 获取 CUDNN 启用结果 - False
printf("Enable CUDNN is: %s\n", PD_ConfigCudnnEnabled(config) ? "True" : "False");

// 销毁 Config 对象
PD_ConfigDestroy(config);
TensorRT 设置

注意:

  1. 启用 TensorRT 的前提为已经启用 GPU,否则启用 TensorRT 无法生效

  2. 对存在LoD信息的模型,如Bert, Ernie等NLP模型,必须使用动态 Shape

  3. 启用 TensorRT OSS 可以支持更多 plugin,详细参考 TensorRT OSS

更多 TensorRT 详细信息,请参考 使用Paddle-TensorRT库预测

API定义如下:

// 启用 TensorRT 进行预测加速
// 参数:pd_config          - Config 对象指针
//      workspace_size     - 指定 TensorRT 使用的工作空间大小
//      max_batch_size     - 设置最大的 batch 大小,运行时 batch 大小不得超过此限定值
//      min_subgraph_size  - Paddle-TRT 是以子图的形式运行,为了避免性能损失,当子图内部节点个数
//                           大于 min_subgraph_size 的时候,才会使用 Paddle-TRT 运行
//      precision          - 指定使用 TRT 的精度,支持 FP32(kFloat32),FP16(kHalf),Int8(kInt8)
//      use_static         - 若指定为 TRUE,在初次运行程序的时候会将 TRT 的优化信息进行序列化到磁盘上,
//                           下次运行时直接加载优化的序列化信息而不需要重新生成
//      use_calib_mode     - 若要运行 Paddle-TRT INT8 离线量化校准,需要将此选项设置为 TRUE
// 返回:None
void PD_ConfigEnableTensorRtEngine(PD_Config* pd_config,
                                   int32_t workspace_size,
                                   int32_t max_batch_size,
                                   int32_t min_subgraph_size,
                                   PD_PrecisionType precision,
                                   PD_Bool use_static,
                                   PD_Bool use_calib_mode);

// 判断是否启用 TensorRT 
// 参数:pd_config - Config 对象指针
// 返回:PD_Bool - 是否启用 TensorRT
PD_Bool PD_ConfigTensorRtEngineEnabled(PD_Config* pd_config);

// 设置 TensorRT 的动态 Shape
// 参数:pd_config               - Config 对象指针
//      tensor_num              - TensorRT 子图支持动态 shape 的 Tensor 数量
//      tensor_name             - TensorRT 子图支持动态 shape 的 Tensor 名称
//      shapes_num              - TensorRT 子图支持动态 shape 的 Tensor 对应的 shape 的长度
//      min_shape               - TensorRT 子图支持动态 shape 的 Tensor 对应的最小 shape
//      max_shape               - TensorRT 子图支持动态 shape 的 Tensor 对应的最大 shape
//      optim_shape             - TensorRT 子图支持动态 shape 的 Tensor 对应的最优 shape
//      disable_trt_plugin_fp16 - 设置 TensorRT 的 plugin 不在 fp16 精度下运行
// 返回:None
void PD_ConfigSetTrtDynamicShapeInfo(PD_Config* pd_config,
                                     size_t tensor_num,
                                     const char** tensor_name,
                                     size_t* shapes_num,
                                     int32_t** min_shape,
                                     int32_t** max_shape,
                                     int32_t** optim_shape,
                                     PD_Bool disable_trt_plugin_fp16);

// 启用 TensorRT OSS 进行预测加速
// 参数:pd_config - Config 对象指针
// 返回:None
void PD_ConfigEnableTensorRtOSS(PD_Config* pd_config);

// 判断是否启用 TensorRT OSS
// 参数:pd_config - Config 对象指针
// 返回:PD_Bool - 是否启用 TensorRT OSS
PD_Bool PD_ConfigTensorRtOssEnabled(PD_Config* pd_config);

// 启用TensorRT DLA进行预测加速
// 参数:pd_config - Config 对象指针
//      dla_core - DLA设备的id,可选0,1,...,DLA设备总数 - 1
// 返回:None
void PD_ConfigEnableTensorRtDla(PD_Config* pd_config, int32_t dla_core);

// 判断是否已经开启TensorRT DLA加速
// 参数:pd_config - Config 对象指针
// 返回:PD_Bool - 是否已开启TensorRT DLA加速
PD_Bool PD_ConfigTensorRtDlaEnabled(PD_Config* pd_config);

代码示例 (1):使用 TensorRT FP32 / FP16 / INT8 进行预测

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 启用 GPU 进行预测 - 初始化 GPU 显存 100M, Deivce_ID 为 0
PD_ConfigEnableUseGpu(config, 100, 0);

// 启用 TensorRT 进行预测加速 - FP32
PD_ConfigEnableTensorRtEngine(config, 1 << 20, 1, 3,
                              PD_PRECISION_FLOAT32, FALSE, FALSE);

// 启用 TensorRT 进行预测加速 - FP16
PD_ConfigEnableTensorRtEngine(config, 1 << 20, 1, 3,
                              PD_PRECISION_HALF, FALSE, FALSE);

// 启用 TensorRT 进行预测加速 - Int8
PD_ConfigEnableTensorRtEngine(config, 1 << 20, 1, 3,
                              PD_PRECISION_INT8, FALSE, FALSE);

// 通过 API 获取 TensorRT 启用结果 - True
printf("Enable TensorRT is: %s\n", PD_ConfigTensorRtEngineEnabled(config) ? "True" : "False");

// 销毁 Config 对象
PD_ConfigDestroy(config);

代码示例 (2):使用 TensorRT 动态 Shape 进行预测

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 设置预测模型路径,这里为 Combined 模型
const char* model_path  = "./model/inference.pdmodel";  
const char* params_path = "./model/inference.pdiparams";
PD_ConfigSetModel(config, model_path, params_path);

// 启用 GPU 进行预测 - 初始化 GPU 显存 100M, Deivce_ID 为 0
PD_ConfigEnableUseGpu(config, 100, 0);

// 启用 TensorRT 进行预测加速 - Int8
PD_ConfigEnableTensorRtEngine(config, 1 << 30, 1, 2, PD_PRECISION_INT8, FALSE, TRUE);

// 设置模型输入的动态 Shape 范围
const char * tensor_name[1] = {"image"};
size_t shapes_num[1] = {4};
int32_t image_min_shape[4] = {1, 1, 3, 3};
int32_t image_max_shape[4] = {1, 1, 10, 10};
int32_t image_opt_shape[4] = {1, 1, 3, 3};
int32_t* min_shape[1] = {image_min_shape};
int32_t* max_shape[1] = {image_max_shape};
int32_t* opt_shape[1] = {image_opt_shape};
PD_ConfigSetTrtDynamicShapeInfo(config, 1, tensor_name, shapes_num, 
                                min_shape, max_shape, opt_shape, FALSE); 

// 销毁 Config 对象
PD_ConfigDestroy(config);

代码示例 (3):使用 TensorRT OSS 进行预测

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 设置预测模型路径,这里为 Combined 模型
const char* model_path  = "./model/inference.pdmodel";  
const char* params_path = "./model/inference.pdiparams";
PD_ConfigSetModel(config, model_path, params_path);

// 启用 GPU 进行预测 - 初始化 GPU 显存 100M, Deivce_ID 为 0
PD_ConfigEnableUseGpu(config, 100, 0);

// 启用 TensorRT 进行预测加速 - FP32
PD_ConfigEnableTensorRtEngine(config, 1 << 20, 1, 3, PD_PRECISION_FLOAT32, FALSE, TRUE);

// 启用 TensorRT OSS 进行预测加速
PD_ConfigEnableTensorRtOSS(config);

// 通过 API 获取 TensorRT OSS 启用结果 - True
printf("Enable TensorRT is: %s\n", PD_ConfigTensorRtOssEnabled(config) ? "True" : "False");

// 销毁 Config 对象
PD_ConfigDestroy(config);

使用 XPU 进行预测

API定义如下:

// 启用 XPU 进行预测
// 参数:pd_config         - Config 对象指针
//      l3_workspace_size - l3 cache 分配的显存大小
// 返回:None
void PD_ConfigEnableXpu(PD_Config* pd_config, int32_t l3_workspace_size);

// 判断是否启用 XPU 
// 参数:pd_config - Config 对象指针
// 返回:PD_Bool - 是否启用 XPU 
PD_Bool PD_ConfigUseXpu(PD_Config* pd_config);

代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 启用 XPU,并设置 l3 cache 大小为 100M
PD_ConfigEnableXpu(config, 100);

// 判断是否开启 XPU - True
printf("Use XPU is: %s\n", PD_ConfigUseXpu(config) ? "True" : "False");

// 销毁 Config 对象
PD_ConfigDestroy(config);

设置模型优化方法

IR 优化

API定义如下:

// 启用 IR 优化
// 参数:pd_config - Config 对象指针
//      x         - 是否开启 IR 优化,默认打开
// 返回:None
void PD_ConfigSwitchIrOptim(PD_Config* pd_config, PD_Bool x);

// 判断是否开启 IR 优化 
// 参数:pd_config - Config 对象指针
// 返回:PD_Bool - 是否开启 IR 优化
PD_Bool PD_ConfigIrOptim(PD_Config* pd_config);

// 设置是否在图分析阶段打印 IR,启用后会在每一个 PASS 后生成 dot 文件
// 参数:pd_config - Config 对象指针
//      x         - 是否打印 IR,默认关闭
// 返回:None
void PD_ConfigSwitchIrDebug(PD_Config* pd_config, PD_Bool x);

代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 设置预测模型路径,这里为 Combined 模型
const char* model_path  = "./model/inference.pdmodel";  
const char* params_path = "./model/inference.pdiparams";
PD_ConfigSetModel(config, model_path, params_path);

// 开启 IR 优化
PD_ConfigSwitchIrOptim(config, TRUE);
// 开启 IR 打印
PD_ConfigSwitchIrDebug(config, TRUE);

// 通过 API 获取 IR 优化是否开启 - True
printf("IR Optim is: %s\n", PD_ConfigIrOptim(config) ? "True" : "False");

// 根据 Config 创建 Predictor, 并销毁该 Config 对象
PD_Predictor* predictor = PD_PredictorCreate(config);

// 利用该 Predictor 进行预测
.......

// 销毁 Predictor 对象
PD_PredictorDestroy(predictor);

运行结果示例:

# PD_ConfigSwitchIrOptim 开启 IR 优化后,运行中会有如下 LOG 输出
--- Running analysis [ir_graph_build_pass]
--- Running analysis [ir_graph_clean_pass]
--- Running analysis [ir_analysis_pass]
--- Running IR pass [simplify_with_basic_ops_pass]
--- Running IR pass [attention_lstm_fuse_pass]
--- Running IR pass [seqconv_eltadd_relu_fuse_pass]
...
--- Running analysis [inference_op_replace_pass]
--- Running analysis [ir_graph_to_program_pass]

# PD_ConfigSwitchIrDebug 开启 IR 打印后,运行结束之后会在目录下生成如下 DOT 文件
-rw-r--r-- 1 root root  70K Nov 17 10:47 0_ir_simplify_with_basic_ops_pass.dot
-rw-r--r-- 1 root root  72K Nov 17 10:47 10_ir_fc_gru_fuse_pass.dot
-rw-r--r-- 1 root root  72K Nov 17 10:47 11_ir_graph_viz_pass.dot
...
-rw-r--r-- 1 root root  72K Nov 17 10:47 8_ir_mul_lstm_fuse_pass.dot
-rw-r--r-- 1 root root  72K Nov 17 10:47 9_ir_graph_viz_pass.dot
Lite子图

API定义如下:

// 启用 Lite 子图
// 参数:pd_config         - Config 对象指针
//      precision         - Lite 子图的运行精度
//      zero_copy         - 启用 zero_copy,lite 子图与 paddle inference 之间共享数据
//      passes_filter_num - 设置 lite 子图的 pass 数量
//      passes_filter     - 设置 lite 子图的 pass 名称
//      ops_filter_num    - 设置不使用 lite 子图运行的 op 数量
//      ops_filter        - 设置不使用 lite 子图运行的 op
// 返回:None
void PD_ConfigEnableLiteEngine(PD_Config* pd_config,
                               PD_PrecisionType precision,
                               PD_Bool zero_copy,
                               size_t passes_filter_num,
                               const char** passes_filter,
                               size_t ops_filter_num,
                               const char** ops_filter);

// 判断是否启用 Lite 子图
// 参数:pd_config - Config 对象指针
// 返回:PD_Bool - 是否启用 Lite 子图
PD_Bool PD_ConfigLiteEngineEnabled(PD_Config* pd_config);

代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 启用 GPU 进行预测 - 初始化 GPU 显存 100M, Deivce_ID 为 0
PD_ConfigEnableUseGpu(config, 100, 0);

// 启用 Lite 子图
PD_ConfigEnableLiteEngine(config, PD_PRECISION_FLOAT32, FALSE, 0, NULL, 0, NULL);

// 通过 API 获取 Lite 子图启用信息 - True
printf("Lite Engine is: %s\n", PD_ConfigLiteEngineEnabled(config) ? "True" : "False");

// 销毁 Config 对象
PD_ConfigDestroy(config);

启用内存优化

API定义如下:

// 开启内存/显存复用,具体降低内存效果取决于模型结构
// 参数:pd_config - Config 对象指针
// 返回:None
void PD_ConfigEnableMemoryOptim(PD_Config* pd_config);

// 判断是否开启内存/显存复用
// 参数:pd_config - Config 对象指针
// 返回:PD_Bool - 是否开启内/显存复用
PD_Bool PD_ConfigMemoryOptimEnabled(PD_Config* pd_config);

代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 开启 CPU 内存优化
PD_ConfigEnableMemoryOptim(config);

// 通过 API 获取 CPU 是否已经开启内存优化 - True
printf("CPU Mem Optim is: %s\n", PD_ConfigMemoryOptimEnabled(config) ? "True" : "False");

// 启用 GPU 进行预测 - 初始化 GPU 显存 100M, Deivce_ID 为 0
PD_ConfigEnableUseGpu(config, 100, 0);

// 开启 GPU 显存优化
PD_ConfigEnableMemoryOptim(config);

// 通过 API 获取 GPU 是否已经开启显存优化 - True
printf("GPU Mem Optim is: %s\n", PD_ConfigMemoryOptimEnabled(config) ? "True" : "False");

// 销毁 Config 对象
PD_ConfigDestroy(config);

设置缓存路径

注意: 如果当前使用的为 TensorRT INT8 且设置从内存中加载模型,则必须通过 PD_ConfigSetOptimCacheDir 来设置缓存路径。

API定义如下:

// 设置缓存路径
// 参数:pd_config     - Config 对象指针
//      opt_cache_dir - 缓存路径
// 返回:None
void PD_ConfigSetOptimCacheDir(PD_Config* pd_config, const char* opt_cache_dir);

代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 设置预测模型路径,这里为 Combined 模型
const char* model_path  = "./model/inference.pdmodel";  
const char* params_path = "./model/inference.pdiparams";
PD_ConfigSetModel(config, model_path, params_path);

// 设置缓存路径
PD_ConfigSetOptimCacheDir(config,"./model/OptimCacheDir");

// 销毁 Config 对象
PD_ConfigDestroy(config);

FC Padding

API定义如下:

// 禁用 FC Padding
// 参数:pd_config - Config 对象指针
// 返回:None
void PD_ConfigDisableFCPadding(PD_Config* pd_config);

// 判断是否启用 FC Padding
// 参数:pd_config - Config 对象指针
// 返回:PD_Bool - 是否启用 FC Padding
PD_Bool PD_ConfigUseFcPadding(PD_Config* pd_config);

代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 禁用 FC Padding
PD_ConfigDisableFCPadding(config);

// 通过 API 获取是否启用 FC Padding - False
printf("FC Padding is: %s\n", PD_ConfigUseFcPadding(config) ? "True" : "False");

// 销毁 Config 对象
PD_ConfigDestroy(config);

Profile 设置

API定义如下:

// 打开 Profile,运行结束后会打印所有 OP 的耗时占比。
// 参数:pd_config - Config 对象指针
// 返回:None
void PD_ConfigEnableProfile(PD_Config* pd_config);

// 判断是否开启 Profile
// 参数:pd_config - Config 对象指针
// 返回:PD_Bool - 是否开启 Profile
PD_Bool PD_ConfigProfileEnabled(PD_Config* pd_config);

代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 打开 Profile
PD_ConfigEnableProfile(config);

// 通过 API 获取是否启用Profile - True
printf("Profile is: %s\n", PD_ConfigProfileEnabled(config) ? "True" : "False");

// 销毁 Config 对象
PD_ConfigDestroy(config);

执行预测之后输出的 Profile 的结果如下:

------------------------->     Profiling Report     <-------------------------

Place: CPU
Time unit: ms
Sorted by total time in descending order in the same thread

-------------------------     Overhead Summary      -------------------------

Total time: 1085.33
  Computation time       Total: 1066.24     Ratio: 98.2411%
  Framework overhead     Total: 19.0902     Ratio: 1.75893%

-------------------------     GpuMemCpy Summary     -------------------------

GpuMemcpy                Calls: 0           Total: 0           Ratio: 0%

-------------------------       Event Summary       -------------------------

Event                            Calls       Total       Min.        Max.        Ave.        Ratio.
thread0::conv2d                  210         319.734     0.815591    6.51648     1.52254     0.294595
thread0::load                    137         284.596     0.114216    258.715     2.07735     0.26222
thread0::depthwise_conv2d        195         266.241     0.955945    2.47858     1.36534     0.245308
thread0::elementwise_add         210         122.969     0.133106    2.15806     0.585568    0.113301
thread0::relu                    405         56.1807     0.021081    0.585079    0.138718    0.0517635
thread0::batch_norm              195         25.8073     0.044304    0.33896     0.132345    0.0237783
thread0::fc                      15          7.13856     0.451674    0.714895    0.475904    0.0065773
thread0::pool2d                  15          1.48296     0.09054     0.145702    0.0988637   0.00136636
thread0::softmax                 15          0.941837    0.032175    0.460156    0.0627891   0.000867786
thread0::scale                   15          0.240771    0.013394    0.030727    0.0160514   0.000221841

Log 设置

API定义如下:

// 去除 Paddle Inference 运行中的 LOG
// 参数:pd_config - Config 对象指针
// 返回:None
void PD_ConfigDisableGlogInfo(PD_Config* pd_config);

// 判断是否禁用 LOG
// 参数:pd_config - Config 对象指针
// 返回:PD_Bool - 是否禁用 LOG
PD_Bool PD_ConfigGlogInfoDisabled(PD_Config* pd_config);

代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 去除 Paddle Inference 运行中的 LOG
PD_ConfigDisableGlogInfo(config);

// 通过 API 获取是否启用LOG - False
printf("GLOG INFO is: %s\n", PD_ConfigGlogInfoDisabled(config) ? "True" : "False");

// 销毁 Config 对象
PD_ConfigDestroy(config);

Predictor 方法

Paddle Inference 的预测器,由 PD_PredictorCreate 根据 Config 进行创建。用户可以根据 Predictor 提供的接口设置输入数据、执行模型预测、获取输出等。

创建 Predictor

API定义如下:

// 根据 Config 构建预测执行对象 Predictor, 并销毁传入的Config对象
// 参数:pd_config - 用于构建 Predictor 的配置信息
// 返回:PD_Predictor* - 预测对象指针
PD_Predictor* PD_PredictorCreate(PD_Config* pd_config);

// 根据 已有的 Predictor 对象克隆一个新的 Predictor 对象
// 参数:pd_predictor - 用于克隆新对象的 Predictor 指针
// 返回:PD_Predictor* - 一个新的 Predictor 对象
PD_Predictor* PD_PredictorClone(PD_Predictor* pd_predictor);

// 销毁 Predictor 对象
// 参数:pd_predictor - Predictor 对象指针
// 返回:None
void PD_PredictorDestroy(PD_Predictor* pd_predictor);

代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 设置预测模型路径,这里为 Combined 模型
const char* model_path  = "./model/inference.pdmodel";  
const char* params_path = "./model/inference.pdiparams";
PD_ConfigSetModel(config, model_path, params_path);

// 根据 Config 创建 Predictor, 并销毁 Config 对象
PD_Predictor* predictor = PD_PredictorCreate(config);

// 根据已有的 Predictor 克隆出一个新的 Predictor 对象
PD_Predictor* new_predictor = PD_PredictorClone(predictor);

// 销毁 Predictor 对象
PD_PredictorDestroy(new_predictor);
PD_PredictorDestroy(predictor);

获取输入输出

API 定义如下:

// 获取输入 Tensor 名称
// 参数:pd_predictor - Predictor 对象指针
// 返回:PD_OneDimArrayCstr* - 由输入 Tensor 名称构成的一维字符串数组。
//      该一维字符串数组需要显式调用`PD_OneDimArrayCstrDestroy`来销毁。
PD_OneDimArrayCstr* PD_PredictorGetInputNames(PD_Predictor* pd_predictor);

// 获取输入 Tensor 数量
// 参数:pd_predictor - Predictor 对象指针
// 返回:size_t - Predictor 的输入 tensor 数量。
size_t PD_PredictorGetInputNum(PD_Predictor* pd_predictor);

// 根据名称获取输入 Tensor 的句柄
// 参数:pd_predictor - Predictor 对象指针
//      name - Tensor 的名称
// 返回:PD_Tensor* - 指向 Tensor 的指针。
//      该 Tensor 需要显式调用`PD_TensorDestroy`来销毁。
PD_Tensor* PD_PredictorGetInputHandle(PD_Predictor* pd_predictor, const char* name);

// 获取输出 Tensor 名称
// 参数:pd_predictor - Predictor 对象指针
// 返回:PD_OneDimArrayCstr* - 由输出 Tensor 名称构成的一维字符串数组。
//      该一维字符串数组需要显式调用`PD_OneDimArrayCstrDestroy`来销毁。
PD_OneDimArrayCstr* PD_PredictorGetOutputNames(PD_Predictor* pd_predictor);

// 获取输出 Tensor 数量
// 参数:pd_predictor - Predictor 输出tensor数量。
size_t PD_PredictorGetOutputNum(PD_Predictor* pd_predictor);

// 根据名称获取输出 Tensor 的句柄
// 参数:pd_predictor - Predictor 对象指针
//      name - Tensor 的名称
// 返回:PD_Tensor* - 指向 Tensor 的指针。
//      该 Tensor 需要显式调用`PD_TensorDestroy`来销毁。
PD_Tensor* PD_PredictorGetOutputHandle(PD_Predictor* pd_predictor, const char* name);

代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 设置预测模型路径,这里为 Combined 模型
const char* model_path  = "./model/inference.pdmodel";  
const char* params_path = "./model/inference.pdiparams";
PD_ConfigSetModel(config, model_path, params_path);

// 根据 Config 创建 Predictor, 并销毁 Config 对象
PD_Predictor* predictor = PD_PredictorCreate(config);

// 获取输入 tensor 的数量和名称
PD_OneDimArrayCstr* input_names = PD_PredictorGetInputNames(predictor);
printf("Input tensor number is: %d\n", input_names->size);
for(size_t index = 0; index < input_names->size; ++index) {
  printf("Input tensor %u name is: %s\n", index, input_names->data[index]);
}

// 根据名称获取第 0 个输入 Tensor
PD_Tensor* input_tensor = PD_PredictorGetInputHandle(predictor, input_names->data[0]);

// 获取输出 Tensor 的数量和名称
PD_OneDimArrayCstr* output_names = PD_PredictorGetOutputNames(predictor);
printf("Output tensor number is: %d\n", output_names->size);
for(size_t index = 0; index < output_names->size; ++index) {
  printf("Output tensor %u name is: %s\n", index, output_names->data[index]);
}

// 根据名称获取第 0 个输出 Tensor
PD_Tensor* output_tensor = PD_PredictorGetOutputHandle(predictor, output_names->data[0]);

// 销毁相应的对象
PD_TensorDestroy(output_tensor);
PD_OneDimArrayCstrDestroy(output_names);
PD_TensorDestroy(input_tensor);
PD_OneDimArrayCstrDestroy(input_names);
PD_PredictorDestroy(predictor);

执行预测

API 定义如下:

// 执行模型预测,需要在设置输入Tensor数据后调用
// 参数:pd_predictor - Predictor 对象指针
// 返回:PD_Bool - 执行预测是否成功
PD_Bool PD_PredictorRun(PD_Predictor* pd_predictor);

代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 设置预测模型路径,这里为 Combined 模型
const char* model_path  = "./model/inference.pdmodel";  
const char* params_path = "./model/inference.pdiparams";
PD_ConfigSetModel(config, model_path, params_path);

// 根据 Config 创建 Predictor, 并销毁 Config 对象
PD_Predictor* predictor = PD_PredictorCreate(config);

// 准备输入数据
float input_shape[4] = {1, 3, 244, 244};
float input_data = (float*)calloc(1 * 3 * 224 * 224, sizeof(float));

// 获取输入 Tensor 并进行赋值
PD_OneDimArrayCstr* input_names = PD_PredictorGetInputNames(predictor);
PD_Tensor* input_tensor = PD_PredictorGetInputHandle(predictor, input_names->data[0]);
PD_TensorReshape(input_tensor, 4, input_shape);
PD_TensorCopyFromCpuFloat(input_tensor, input_data);

// 执行预测
PD_PredictorRun(pd_predictor);

// 获取预测输出 Tensor
PD_OneDimArrayCstr* output_names = PD_PredictorGetOutputNames(predictor);
PD_Tensor* output_tensor = PD_PredictorGetOutputHandle(predictor, output_names->data[0]);

// 获取输出 Tensor 数据
PD_OneDimArrayInt32* output_shape = PD_TensorGetShape(output_tensor);
int32_t out_size = 1;
for (size_t i = 0; i < output_shape->size; ++i) {
  out_size = out_size * output_shape->data[i];
}
float* out_data = (float*)malloc(out_size * sizeof(float));
PD_TensorCopyToCpuFloat(output_tensor, out_data);

// 销毁相关对象, 回收相关内存
free(out_data);
PD_OneDimArrayInt32Destroy(output_shape);
PD_TensorDestroy(output_tensor);
PD_OneDimArrayCstrDestroy(output_names);
PD_TensorDestroy(input_tensor);
PD_OneDimArrayCstrDestroy(input_names);
PD_PredictorDestroy(predictor);
free(input_data);

Tensor 方法

Tensor 是 Paddle Inference 的数据组织形式,用于对底层数据进行封装并提供接口对数据进行操作,包括设置 Shape、数据、LoD 信息等。

注意: 应使用 PD_PredictorGetInputHandlePD_PredictorGetOutputHandle 接口获取输入输出 Tensor

Tensor 相关的API定义如下:

// 设置 Tensor 的维度信息
// 参数:pd_tensor - Tensor 对象指针
//      shape_size - 维度信息的长度
//      shape - 维度信息指针
// 返回:None
void PD_TensorReshape(PD_Tensor* pd_tensor, size_t shape_size, int32_t* shape);

// 从 CPU 获取 float / int64_t / int32_t / uint8_t / int8_t 类型数据,拷贝到 Tensor 内部
// 参数:pd_tensor - Tensor 对象指针
//      data - CPU 数据指针
// 返回:None
void PD_TensorCopyFromCpuFloat(PD_Tensor* pd_tensor, const float* data);
void PD_TensorCopyFromCpuInt64(PD_Tensor* pd_tensor, const int64_t* data);
void PD_TensorCopyFromCpuInt32(PD_Tensor* pd_tensor, const int32_t* data);
void PD_TensorCopyFromCpuUint8(PD_Tensor* pd_tensor, const uint8_t* data);
void PD_TensorCopyFromCpuInt8(PD_Tensor* pd_tensor, const int8_t* data);

// 从 Tensor 中获取 float / int64_t / int32_t / uint8_t / int8_t 类型数据,拷贝到 CPU
// 参数:pd_tensor - Tensor 对象指针
//      data - CPU 数据指针
// 返回:None
void PD_TensorCopyToCpuFloat(PD_Tensor* pd_tensor, const float* data);
void PD_TensorCopyToCpuInt64(PD_Tensor* pd_tensor, const int64_t* data);
void PD_TensorCopyToCpuInt32(PD_Tensor* pd_tensor, const int32_t* data);
void PD_TensorCopyToCpuUint8(PD_Tensor* pd_tensor, const uint8_t* data);
void PD_TensorCopyToCpuInt8(PD_Tensor* pd_tensor, const int8_t* data);

// 获取 Tensor 底层数据指针,用于设置 Tensor 输入数据
// 在调用这个 API 之前需要先对输入 Tensor 进行 Reshape
// 参数:pd_tensor - Tensor 对象指针
//      place - 获取 Tensor 的 PlaceType
// 返回:数据指针
float* PD_TensorMutableDataFloat(PD_Tensor* pd_tensor, PD_PlaceType place);
int64_t* PD_TensorMutableDataInt64(PD_Tensor* pd_tensor, PD_PlaceType place);
int32_t* PD_TensorMutableDataInt32(PD_Tensor* pd_tensor, PD_PlaceType place);
uint8_t* PD_TensorMutableDataUint8(PD_Tensor* pd_tensor, PD_PlaceType place);
int8_t* PD_TensorMutableDataInt8(PD_Tensor* pd_tensor, PD_PlaceType place);

// 获取 Tensor 底层数据的常量指针,用于读取 Tensor 输出数据
// 参数:pd_tensor - Tensor 对象指针
//      place - 获取 Tensor 的 PlaceType
//      size - 获取 Tensor 的 size
// 返回:数据指针
float* PD_TensorDataFloat(PD_Tensor* pd_tensor, PD_PlaceType* place, int32_t* size);
int64_t* PD_TensorDataInt64(PD_Tensor* pd_tensor, PD_PlaceType* place, int32_t* size);
int32_t* PD_TensorDataInt32(PD_Tensor* pd_tensor, PD_PlaceType* place, int32_t* size);
uint8_t* PD_TensorDataUint8(PD_Tensor* pd_tensor, PD_PlaceType* place, int32_t* size);
int8_t* PD_TensorDataInt8(PD_Tensor* pd_tensor, PD_PlaceType* place, int32_t* size);

// 设置 Tensor 的 LoD 信息
// 参数:pd_tensor - Tensor 对象指针
//      lod - Tensor 的 LoD 信息
// 返回:None
void PD_TensorSetLod(PD_Tensor* pd_tensor, PD_TwoDimArraySize* lod);

// 获取 Tensor 的 LoD 信息
// 参数:pd_tensor - Tensor 对象指针
// 返回:PD_TwoDimArraySize* - Tensor 的 LoD 信息。
//      该LoD信息对象需要通过 ’PD_TwoDimArraySizeDestroy‘ 进行回收。
PD_TwoDimArraySize* PD_TensorGetLod(PD_Tensor* pd_tensor);

// 获取 Tensor 的 DataType
// 参数:pd_tensor - Tensor 对象指针
// 返回:PD_DataType - Tensor 的 DataType
PD_DataType PD_TensorGetDataType(PD_Tensor* pd_tensor);

// 获取 Tensor 的维度信息
// 参数:pd_tensor - Tensor 对象指针
// 返回:PD_OneDimArrayInt32* - Tensor 的维度信息
//      该返回值需要通过 ’PD_OneDimArrayInt32Destroy‘ 进行回收。
PD_OneDimArrayInt32* PD_TensorGetShape(PD_Tensor* pd_tensor);

// 获取 Tensor 的 Name
// 参数:pd_tensor - Tensor 对象指针
// 返回:const char* - Tensor 的 Name
const char* PD_TensorGetName(PD_Tensor* pd_tensor);

// 销毁 Tensor 对象
// 参数:pd_tensor - Tensor 对象指针
// 返回:None
void PD_TensorDestroy(__pd_take PD_Tensor* pd_tensor);

代码示例:

// 创建 Config 对象
PD_Config* config = PD_ConfigCreate();

// 设置预测模型路径,这里为 Combined 模型
const char* model_path  = "./model/inference.pdmodel";  
const char* params_path = "./model/inference.pdiparams";
PD_ConfigSetModel(config, model_path, params_path);

// 根据 Config 创建 Predictor, 并销毁 Config 对象
PD_Predictor* predictor = PD_PredictorCreate(config);

// 准备输入数据
float input_shape[4] = {1, 3, 244, 244};
float input_data = (float*)calloc(1 * 3 * 224 * 224, sizeof(float));

// 获取输入 Tensor
PD_OneDimArrayCstr* input_names = PD_PredictorGetInputNames(predictor);
PD_Tensor* input_tensor = PD_PredictorGetInputHandle(predictor, input_names->data[0]);

// 设置输入 Tensor 的维度信息
PD_TensorReshape(input_tensor, 4, input_shape);

// 获取输入 Tensor 的 Name
const char* name = PD_TensorGetName(PD_Tensor* pd_tensor);

//  方式1: 通过 mutable_data 设置输入数据
float* data_ptr = PD_TensorMutableDataFloat(pd_tensor, PD_PLACE_CPU);
memcpy(data_ptr, input_data, 1 * 3 * 224 * 224 * sizeof(float));

//  方式2: 通过 CopyFromCpu 设置输入数据
PD_TensorCopyFromCpuFloat(input_tensor, input_data);

// 执行预测
PD_PredictorRun(pd_predictor);

// 获取预测输出 Tensor
PD_OneDimArrayCstr* output_names = PD_PredictorGetOutputNames(predictor);
PD_Tensor* output_tensor = PD_PredictorGetOutputHandle(predictor, output_names->data[0]);

// 方式1: 通过 data 获取 Output Tensor 的数据
PD_PlaceType place;
int32_t size;
float* out_data_ptr = PD_TensorDataFloat(output_tensor, &place, &size);
float* out_data = (float*)malloc(size * sizeof(float));
memcpy(out_data, out_data_ptr, size * sizeof(float));
free(out_data);

// 方式2: 通过 CopyToCpu 获取 Output Tensor 的数据
PD_OneDimArrayInt32* output_shape = PD_TensorGetShape(output_tensor);
int32_t out_size = 1;
for (size_t i = 0; i < output_shape->size; ++i) {
  out_size = out_size * output_shape->data[i];
}
out_data = (float*)malloc(out_size * sizeof(float));
PD_TensorCopyToCpuFloat(output_tensor, out_data);
free(out_data)

// 销毁相关对象, 回收相关内存
PD_OneDimArrayInt32Destroy(output_shape);
PD_TensorDestroy(output_tensor);
PD_OneDimArrayCstrDestroy(output_names);
PD_TensorDestroy(input_tensor);
PD_OneDimArrayCstrDestroy(input_names);
free(input_data);
PD_PredictorDestroy(predictor);

Paddle Inference FAQ

1、编译报错, 且出错语句没有明显语法错误。 答:请检查使用的GCC版本,目前PaddlePaddle支持的编译器版本为GCC 4.8.2和GCC 8.2.0。

2、编译时报错No CMAKE_CUDA_COMPILER could be found。 答:编译时未找到nvcc。设置编译选项-DCMAKE_CUDA_COMPILER=nvcc所在路径,注意需要与CUDA版本保持一致。

3、运行时报错RuntimeError: parallel_for failed: cudaErrorNoKernelImageForDevice: no kernel image is available for execution on the device。 答:这种情况一般出现在编译和运行在不同架构的显卡上,且cmake时未指定运行时需要的CUDA架构。可以在cmake时加上 -DCUDA_ARCH_NAME=All(或者特定的架构如Turing、Volta、Pascal等),否则会使用默认值Auto,此时只会当前的CUDA架构编译。

4、运行时报错PaddleCheckError: Expected id < GetCUDADeviceCount(), but received id:0 >= GetCUDADeviceCount():0 。 答:一般原因是找不到驱动文件。请检查环境设置,需要在LD_LIBRARY_PATH中加入/usr/lib64(驱动程序libcuda.so所在的实际路径)。

5、运行时报错Error: Cannot load cudnn shared library. 。 答:请在LD_LIBRARY_PATH中加入cuDNN库的路径。

6、运行时报错libstdc++.so.6中GLIBCXX版本过低。 答:运行时链接了错误的glibc。可以通过以下两种方式解决: 1)在编译时,通过在CXXFLAGS中加入”-Wl,–rpath=/opt/compiler/gcc-8.2/lib,–dynamic-linker=/opt/compiler/gcc-8.2/lib/ld-linux-x86-64.so.2”,来保证编译产出的可执行程序链接到正确的libc.so.6/libstdc++.so.6库。 2)在运行时,将正确的glibc的路径加入到LD_LIBRARY_PATH中。

7、使用CPU预测,开启MKLDNN时内存异常上涨。 答:请检查是否使用了变长输入,此时可以使用接口config.set_mkldnn_cache_capacity(capacity)接口,设置不同shape的缓存数,以防止缓存太多的优化信息占用较大的内存空间。

8、运行时报错CUDNN_STATUS_NOT_INITIALIZED at ...。 答:请检查运行时链接的cuDNN版本和编译预测库使用的cuDNN版本是否一致。

9、运行时报错OMP: Error #100 Fatal system error detected。 答:OMP的问题,可参考链接 https://unix.stackexchange.com/questions/302683/omp-error-bash-on-ubuntu-on-windows

10、初始化阶段报错Program terminated with signal SIGILL, Illegal instruction 答:请检查下是否在不支持AVX的机器上使用了AVX的预测库。

11、运行时报错PaddleCheckError: an illegal memory access was encounted at xxx 答:请检查输入Tensor 是否存在指针越界。

12、Predictor是否有Profile工具。 答: config.EnableProfile()可以打印op耗时,请参考API文档-Profile设置

13、同一个模型的推理耗时不稳定。 答:请按以下方向排查: 1)硬件资源(CPU、GPU等)是否没有他人抢占。 2)输入是否一致,某些模型推理时间跟输入有关,比如检测模型的候选框数量。 3)使用TensorRT时,初始的优化阶段比较耗时,可以通过少量数据warm up的方式解决。

14、ZeroCopyTensor和ZeroCopyRun的相关文档。 答:ZeroCopyTensor虽然在模型推理时不再有数据拷贝,但是构造时需要用户将数据拷贝至ZeroCopyTensor中,为避免歧义,该接口2.0rc1+版本已经隐藏,当前接口请参考API文档

15、在JetPack 4.4环境的Jetson开发套件上运行带卷积的模型报错terminate called after throwing an instance of 'std::logic_error' what(): basic_string::_M_construct null not valid。 答:这个是cuDNN 8.0在SM_72下的bug,在运行cudnnConvolutionBiasActivationForward的时候会出错,见https://forums.developer.nvidia.com/t/nx-jp44-cudnn-internal-logic-error/124805。 目前有以下两种解决方案: 1)通过config.pass_builder()->DeletPass()删除如下PASS:conv_elementwise_add_act_fuse_passconv_elementwise_add2_act_fuse_passconv_elementwise_add_fuse_pass, 来避免预测期间进行conv + bias + activation的融合。 2)将cuDNN 8.0降级为cuDNN 7.6。

16、运行时报错Expected static_cast<size_t>(col) < feed_list.size(), but received static_cast<size_t>(col):23 >= feed_list.size():0。 答:在2.0 rc1之前的版本,用户使用ZeroCopyTensor和ZeroCopyRun接口时,需要设置config.SwitchUseFeedFetchOps(false),后续版本已经隐藏ZeroCopyTensor的设计,无需手动设置。

17、如何开启CPU预测的多线程加速。 答:请使用config.EnableMKLDNN()config.SetCpuMathLibraryNumThreads(),请参考API文档-CPU预测

训练推理示例说明

本文档将向您介绍如何使用 Paddle 2.0 新接口训练和推理一个模型。

一、使用 Paddle 2.0 新接口训练一个简单模型

我们参考 LeNet 的 MNIST 数据集图像分类 ,使用 Paddle 2.0 接口训练一个简单的模型,分别存储成预训练和预测部署模型。我们将着重介绍如何生成模型文件。

  • 依赖包导入

import paddle
import paddle.nn.functional as F
from paddle.nn import Layer
from paddle.vision.datasets import MNIST
from paddle.metric import Accuracy
from paddle.nn import Conv2D,MaxPool2D,Linear
from paddle.static import InputSpec
from paddle.jit import to_static
from paddle.vision.transforms import ToTensor
  • 查看 Paddle 版本

print(paddle.__version__)
  • 数据集准备

train_dataset = MNIST(mode='train', transform=ToTensor())
test_dataset = MNIST(mode='test', transform=ToTensor())
  • 构建 LeNet 网络

class LeNet(paddle.nn.Layer):
    def __init__(self):
        super(LeNet, self).__init__()
        self.conv1 = paddle.nn.Conv2D(in_channels=1, out_channels=6, kernel_size=5, stride=1, padding=2)
        self.max_pool1 = paddle.nn.MaxPool2D(kernel_size=2,  stride=2)
        self.conv2 = paddle.nn.Conv2D(in_channels=6, out_channels=16, kernel_size=5, stride=1)
        self.max_pool2 = paddle.nn.MaxPool2D(kernel_size=2, stride=2)
        self.linear1 = paddle.nn.Linear(in_features=16*5*5, out_features=120)
        self.linear2 = paddle.nn.Linear(in_features=120, out_features=84)
        self.linear3 = paddle.nn.Linear(in_features=84, out_features=10)

    def forward(self, x):
        x = self.conv1(x)
        x = F.relu(x)
        x = self.max_pool1(x)
        x = F.relu(x)
        x = self.conv2(x)
        x = self.max_pool2(x)
        x = paddle.flatten(x, start_axis=1,stop_axis=-1)
        x = self.linear1(x)
        x = F.relu(x)
        x = self.linear2(x)
        x = F.relu(x)
        x = self.linear3(x)
        return x
  • 模型训练

train_loader = paddle.io.DataLoader(train_dataset, batch_size=64, shuffle=True)
model = LeNet()
optim = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters())
def train(model, optim):
    model.train()
    epochs = 2
    for epoch in range(epochs):
        for batch_id, data in enumerate(train_loader()):
            x_data = data[0]
            y_data = data[1]
            predicts = model(x_data)
            loss = F.cross_entropy(predicts, y_data)
            acc = paddle.metric.accuracy(predicts, y_data)
            loss.backward()
            if batch_id % 300 == 0:
                print("epoch: {}, batch_id: {}, loss is: {}, acc is: {}".format(epoch, batch_id, loss.numpy(), acc.numpy()))
            optim.step()
            optim.clear_grad()
train(model, optim)
  • 存储训练模型(训练格式):您可以参考 参数存储 ,了解如何在动态图下存储训练格式的模型。只需调用paddle.save接口即可。

paddle.save(model.state_dict(), 'lenet.pdparams')
paddle.save(optim.state_dict(), "lenet.pdopt")

二、预训练模型如何转换为预测部署模型

  • 加载预训练模型:您可以参考参数载入了解如何在动态图下加载训练格式的模型,此方法可帮助您完成恢复训练,即模型状态回到训练中断的时刻,恢复训练之后的梯度更新走向是和恢复训练前的梯度走向完全相同的。只需调用paddle.load接口加载训练格式的模型,再调用set_state_dict接口恢复模型训练中断时刻的状态。

model_state_dict = paddle.load('lenet.pdparams')
opt_state_dict = paddle.load('lenet.pdopt')
model.set_state_dict(model_state_dict)
optim.set_state_dict(opt_state_dict)
  • 存储为预测部署模型:实际部署时,您需要使用预测格式的模型,预测格式模型相对训练格式模型而言,在拓扑上进行了裁剪,去除了预测不需要的算子。您可以参考InputSpec来完成动转静功能。只需InputSpec标记模型的输入,调用paddle.jit.to_staticpaddle.jit.save即可得到预测格式的模型。

net = to_static(model, input_spec=[InputSpec(shape=[None, 1, 28, 28], name='x')])
paddle.jit.save(net, 'inference_model/lenet')

参考代码

import paddle
import paddle.nn.functional as F
from paddle.nn import Layer
from paddle.vision.datasets import MNIST
from paddle.metric import Accuracy
from paddle.nn import Conv2D, MaxPool2D, Linear
from paddle.static import InputSpec
from paddle.jit import to_static
from paddle.vision.transforms import ToTensor

class LeNet(paddle.nn.Layer):
    def __init__(self):
        super(LeNet, self).__init__()
        self.conv1 = paddle.nn.Conv2D(in_channels=1,
                                      out_channels=6,
                                      kernel_size=5,
                                      stride=1,
                                      padding=2)
        self.max_pool1 = paddle.nn.MaxPool2D(kernel_size=2, stride=2)
        self.conv2 = paddle.nn.Conv2D(in_channels=6,
                                      out_channels=16,
                                      kernel_size=5,
                                      stride=1)
        self.max_pool2 = paddle.nn.MaxPool2D(kernel_size=2, stride=2)
        self.linear1 = paddle.nn.Linear(in_features=16 * 5 * 5,
                                        out_features=120)
        self.linear2 = paddle.nn.Linear(in_features=120, out_features=84)
        self.linear3 = paddle.nn.Linear(in_features=84, out_features=10)

    def forward(self, x):
        # x = x.reshape((-1, 1, 28, 28))
        x = self.conv1(x)
        x = F.relu(x)
        x = self.max_pool1(x)
        x = F.relu(x)
        x = self.conv2(x)
        x = self.max_pool2(x)
        x = paddle.flatten(x, start_axis=1, stop_axis=-1)
        x = self.linear1(x)
        x = F.relu(x)
        x = self.linear2(x)
        x = F.relu(x)
        x = self.linear3(x)
        return x

def train(model, optim):
    model.train()
    epochs = 2
    for epoch in range(epochs):
        for batch_id, data in enumerate(train_loader()):
            x_data = data[0]
            y_data = data[1]
            predicts = model(x_data)
            loss = F.cross_entropy(predicts, y_data)
            # calc loss
            acc = paddle.metric.accuracy(predicts, y_data)
            loss.backward()
            if batch_id % 300 == 0:
                print("epoch: {}, batch_id: {}, loss is: {}, acc is: {}".format(
                    epoch, batch_id, loss.numpy(), acc.numpy()))
            optim.step()
            optim.clear_grad()

if __name__ == '__main__':
    # paddle version
    print(paddle.__version__)

    # prepare datasets
    train_dataset = MNIST(mode='train', transform=ToTensor())
    test_dataset = MNIST(mode='test', transform=ToTensor())

    # load dataset
    train_loader = paddle.io.DataLoader(train_dataset,
                                        batch_size=64,
                                        shuffle=True)

    # build network
    model = LeNet()
    # prepare optimizer
    optim = paddle.optimizer.Adam(learning_rate=0.001,
                                  parameters=model.parameters())

    # train network
    train(model, optim)

    # save training format model
    paddle.save(model.state_dict(), 'lenet.pdparams')
    paddle.save(optim.state_dict(), "lenet.pdopt")

    # load training format model
    model_state_dict = paddle.load('lenet.pdparams')
    opt_state_dict = paddle.load('lenet.pdopt')
    model.set_state_dict(model_state_dict)
    optim.set_state_dict(opt_state_dict)

    # save inferencing format model
    net = to_static(model,
                    input_spec=[InputSpec(shape=[None, 1, 28, 28], name='x')])
    paddle.jit.save(net, 'inference_model/lenet')

Paddle 2.0 默认保存的权重格式为 *.pdiparams 后缀的文件。若因特殊需求,希望沿用旧版本的分离权重方式,请参考以下示例进行另存为。Paddle 2.0 兼容支持这种旧格式推理部署模型的加载。

import paddle

if __name__ == '__main__':
    paddle.enable_static()
    place = paddle.CPUPlace()
    exe = paddle.static.Executor(place)

    # load combined params and model
    program, _, _ = paddle.static.load_inference_model(
        path_prefix='inference_model',
        executor=exe,
        model_filename='lenet.pdmodel',
        params_filename='lenet.pdiparams')

    # save as separate persistables
    paddle.static.save_vars(
        executor=exe,
        dirname="separate_persistables",
        main_program=program,
        vars=None,
        predicate=paddle.static.io.is_persistable)

三、使用 Paddle 2.0 Python 接口预测部署

我们使用存储好的预测部署模型,借助 Python 2.0 接口执行预测部署。

加载预测模型并进行预测配置

首先,我们加载预测模型,并配置预测时的一些选项,根据配置创建预测引擎:

config = Config("inference_model/lenet/lenet.pdmodel", "inference_model/lenet/lenet.pdiparams") # 通过模型和参数文件路径加载
config.disable_gpu() # 使用cpu预测
predictor = create_predictor(config) # 根据预测配置创建预测引擎predictor

更多配置选项可以参考官网文档

设置输入

我们先通过获取输入Tensor的名称,再根据名称获取到输入Tensor的句柄。

# 获取输入变量名称
input_names = predictor.get_input_names()
input_handle = predictor.get_input_handle(input_names[0])

下面我们准备输入数据,并将其拷贝至待预测的设备上。这里我们使用了随机数据,您在实际使用中可以将其换为需要预测的真实图片。

### 设置输入
fake_input = np.random.randn(1, 1, 28, 28).astype("float32")
input_handle.reshape([1, 1, 28, 28])
input_handle.copy_from_cpu(fake_input)

运行预测

predictor.run()

获取输出

# 获取输出变量名称
output_names = predictor.get_output_names()
output_handle = predictor.get_output_handle(output_names[0])
output_data = output_handle.copy_to_cpu()

获取输出句柄的方式与输入类似,我们最后获取到的输出是numpy.ndarray类型,方便使用numpy对其进行后续的处理。

完整可运行代码

import numpy as np
from paddle.inference import Config
from paddle.inference import create_predictor

config = Config("inference_model/lenet/lenet.pdmodel", "inference_model/lenet/lenet.pdiparams")
config.disable_gpu()

# 创建PaddlePredictor
predictor = create_predictor(config)

# 获取输入的名称
input_names = predictor.get_input_names()
input_handle = predictor.get_input_handle(input_names[0])

# 设置输入
fake_input = np.random.randn(1, 1, 28, 28).astype("float32")
input_handle.reshape([1, 1, 28, 28])
input_handle.copy_from_cpu(fake_input)

# 运行predictor
predictor.run()

# 获取输出
output_names = predictor.get_output_names()
output_handle = predictor.get_output_handle(output_names[0])
output_data = output_handle.copy_to_cpu() # numpy.ndarray类型

print(output_data)

四、使用 Paddle 2.0 C++ 接口预测部署

我们将存储好的模型使用 Paddle 2.0 C++ 接口执行预测部署。

准备预测库

首先,我们需要Paddle Inference预测库用于模型推理部署。这里下载2.0.0版本的用于x86 cpu的预测库:

wget https://paddle-inference-lib.bj.bcebos.com/2.0.0-cpu-avx-mkl/paddle_inference.tgz

下载预测样例包

下载预测样例代码包:

wget https://paddle-inference-dist.bj.bcebos.com/lenet_demo.tgz

其中,lenet_infer_test.cc为预测脚本,run.sh为执行脚本。

配置依赖库路径

我们需要在执行脚本run.sh中,配置预测库的路径:

LIB_DIR=/path/to/paddle_inference

加载预测模型并进行预测配置

首先,我们加载预测模型,并配置预测时的一些选项,根据配置创建预测引擎:

std::shared_ptr<Predictor> InitPredictor() {
  Config config;
  if (FLAGS_model_dir != "") {
    config.SetModel(FLAGS_model_dir);
  }
  config.SetModel(FLAGS_model_file, FLAGS_params_file);
  config.DisableGpu();
  return CreatePredictor(config);
}

这里设置使用CPU来进行预测,更多配置选项可以参考官网文档

设置输入

我们先通过获取输入Tensor的名称,再根据名称获取到输入Tensor的句柄:

  std::vector<float> input(1 * 1 * 28 * 28, 0);
  # 获取输入变量名称
  auto input_names = predictor->GetInputNames();
  auto input_t = predictor->GetInputHandle(input_names[0]);
  input_t->Reshape(input_shape);
  input_t->CopyFromCpu(input.data());

这里我们使用了全零数据,您在实际使用中可以将其换为需要预测的真实图片。

运行预测

predictor->Run();

获取输出

# 获取输出变量名称
  auto output_names = predictor->GetOutputNames();
  auto output_t = predictor->GetOutputHandle(output_names[0]);
  std::vector<int> output_shape = output_t->shape();
  int out_num = std::accumulate(output_shape.begin(), output_shape.end(), 1,
                                std::multiplies<int>());

  out_data->resize(out_num);
  output_t->CopyToCpu(out_data->data());

out_data即为所需输出,可以对其进行后续的分析和处理。

执行预测

配置好之后,在预测样例文件目录下,使用下列命令编译、执行预测样例。

sh run.sh
./build/lenet_infer_test --model_file=inference_model/lenet/lenet.pdmodel --params_file=inference_model/lenet/lenet.pdiparams

即可打印出预测结果: https://agroup-bos-bj.cdn.bcebos.com/bj-dc2e9237bf385ad86dea8efae001da13d81e2b2d图片