开始阅览 Paddle Inference 文档

使用指南

Paddle Inference 简介

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

由于 Paddle Inference 能力直接基于飞桨的训练算子,因此它支持飞桨训练出的所有模型的推理。

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

如果Paddle Inference可以满足您的部署需求,建议您先阅览 ** 推理流程 ** 了解标准的推理应用开发流程。

如果您不确定 Paddle Inference 是否是最佳选择,您可以阅览 ** 选择推理引擎 ** 来选择适合您的部署方式。

欢迎各位用户加入我们的交流群,与更多Paddle Inference的开发者交流讨论

_images/user_qq.png

如何选择正确的推理引擎

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

飞桨推理产品主要包括如下子产品:

名称 英文表示 适用场景 典型硬件
飞桨原生推理库 Paddle Inference 高性能服务器端、云端推理 X86 CPU、Nvidia GPU(含Jetson系列)、飞腾/鲲鹏、申威、兆芯、龙芯、AMD GPU,海光DCU,昆仑XPU,昇腾910NPU,Graphcore IPU 等
飞桨服务化推理框架 Paddle Serving 服务化部署、多模型管理等高阶功能;
其中的AI推理部分集成Paddle Inference
x86(Intel) CPU、ARM CPU、Nvidia GPU(含Jetson系列)、昆仑 XPU、华为昇腾310/910、海光 DCU 等
飞桨轻量化推理引擎 Paddle Lite 移动端、物联网等 Arm CPU、Arm Mali 系列 GPU、高通 Adreno 系列 GPU、华为麒麟 NPU、华为昇腾NPU、寒武纪MLU、瑞芯微NPU、昆仑芯XPU、晶晨NPU、Imagination NNA、比特大陆TPU、联发科APU、亿智NPU、百度 FPGA、Intel FPGA等硬件;
Intel OpenVINO、芯原 TIM-VX、Android NNAPI 等后端
飞桨前端推理引擎 Paddle.js 浏览器、Node.js、小程序等中做AI推理 浏览器:主流浏览器
小程序:百度小程序、微信小程序

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

_images/inference_ecosystem.png

推理流程

_images/workflow.png

一. 准备模型

Paddle Inference 原生支持由 PaddlePaddle 深度学习框架训练产出的推理模型。PaddlePaddle 用于推理的模型分别可通过 paddle.jit.save (动态图) 与 paddle.static.save_inference_model (静态图) 或 paddle.Model().save (高层API) 保存下来。如果您手中的模型是由诸如 TensorFlow、PyTorch 等框架产出的,那么您可以使用 X2Paddle 工具将模型转换为 PadddlePaddle 格式。

更详细的模型导出说明请参考模型导出文档

可以使用模型可视化工具来查看您的模型结构,以确认符合组网预期。

二. 准备环境

可参照 Paddle Inference 安装 页面,通过下载预编译库或源码编译的方式准备 Paddle Inference 的基础开发环境。

三. 开发推理程序

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

_images/predict.png

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

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

  2. 创建推理引擎 paddle_infer::Predictor,通过调用 CreatePredictor(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 四种 API 的使用示例和开发说明文档,您可以参考示例中的说明快速了解使用方法,并集成到您自己的项目中去。

四. 性能优化
1) 根据实际场景开启相应的推理优化配置项
  • 以 C++ API 为例,根据实际场景开启相关的优化开关,示例如下,具体请参考 C++ API 文档

    • CPU 推理:EnableMKLDNNEnableMkldnnBfloat16SetCpuMathLibraryNumThreadsEnableONNXRuntime

    • GPU 推理:EnableTensorRtEngine

    • 基础优化:SwitchIrOptimEnableMemoryOptim

  • 参考系统调优概述使用混合精度推理和多线程推理。

2) 使用 PaddleSlim 进行模型小型化

如果开启以上相关优化配置后,还需要进一步提升推理性能,可以在我们提供的深度学习模型压缩工具库 PaddleSlim 的帮助下,通过低比特量化、知识蒸馏、稀疏化和模型结构搜索等方式,进行模型小型化。

架构设计

Paddle Inference 的整体架构如下:

_images/inference_arch.png
Paddle Inference 的高性能实现
内存 / 显存复用

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

细粒度 OP 横向纵向融合

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

内置高性能的 CPU / GPU Kernel

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

子图集成 TensorRT

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

支持加载 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, Golang,接口简单灵活,20行代码即可完成部署。对于其他语言,提供了 ABI 稳定的 C API, 用户可以很方便地扩展。

Roadmap

Release Note

详细 Release Note 请参考 PaddlePadde ReleaseNote,近期重要 Roadmap 如下:

2.3.0 / 2.3.0-rc0
  • 新增 Java API 和 ONNX Runtime CPU 后端。

  • 针对 ERNIE 类结构模型性能深度优化。

2.2.2
  • 支持 relu、relu6、tanh、sigmoid、pool2d、concat、batch_norm、split、gelu、scale、swish、prelu、clip、reduce_sum、reduce_mean 算子在静态 shape 且2维输入情况下调用 TensorRT 推理。

  • 支持mish激活函数调用 TensorRT 推理。

2.2.0 / 2.2.0-rc0
  • 新增 TensorRT 子图模式下动态 shape 自动配置功能。使用离线 tune 动态 shape 的方式,提升模型被切分成多个 TensorRT 子图场景下动态 shape 的易用性。

  • 新增 pool3d 算子支持 TensorRT 推理。

  • Go API 重构。

  • 升级 oneDNN 版本为 2.3.2,优化 oneDNN 的 cache 机制。

  • 增加 TensorRT 8.0 的支持,在将来的某个版本我们会放弃对 TensorRT 6.x 的支持。

  • 支持 TensorRT 8.0 稀疏推理,ERNIE 模型变长输入在不同的 batch_size 下性能提升10% - 30%,ResNeXt101_32x4d 模型在不同的 batch_size 下性能提升 10%。

2.1.0
  • 发布 C API (experimental), 功能与 C++ API 基本对齐。

  • 预测框架python接口接入训练自定义算子。用户在训练过程中加载自定义算子后,即可像框架原生算子那样,通过 PaddlePredictor 直接执行包含此自定义算子的预测模型部署。

  • 支持从内存加载模型时TensorRT序列化和反序列化功能。

2.0.2
  • Paddle-TRT 适配由 Paddle 2.0 训练保存的 ERNIE / BERT 模型。

  • 升级 Paddle 的 oneDNN 版本到 oneDNN 2.2,多个模型预测性能有提升。

2.0.1
  • 增加了对采用 per-layer 方式量化的模型 TensorRT 量化预测的支持。

  • 新增 API paddle_infer::Config::EnableTensorRtDLA(),支持在开启 TensorRT 的基础上使用 NVIDIA 的硬件加速器 DLA。

  • C++ 和 Python 推理接口新增对昆仑 XPU 的原生支持,用户可因此获得更完备的算子种类支持。

2.0.0
  • 全面升级推理C++ API

  • C++ 接口新增 paddle_infer 命名空间,包含推理相关接口。

  • ZeroCopyTensor 更名为 Tensor,作为推理接口默认输入输出表示方式。

  • 简化 CreatePaddlePredictor 为 CreatePredictor,只保留对 AnalysisConfig 的支持,不再支持其他多种 Config。

  • 新增服务相关的工具类,比如 PredictorPool,便于创建多个 predictor 时使用。

  • 模型相关API

  • load_inference_model 和 save_inference_model 两个API迁移到 paddle.static 下,兼容旧接口,提升易用性。

  • 新增 serialize_program, deserialize_program, serialize_persistables, deserialize_persistables, save_to_file, load_from_file 六个API,用来满足用户执行序列化/反序列化 program,序列化/反序列化 params,以及将模型/参数保存到文件,或从文件中加载模型/参数的需求。

  • NV GPU 推理相关

  • 新增对 TensorRT 7.1 版本的适配支持。

  • 新增对 Jetson Xavier NX 硬件的适配支持。

  • Paddle-TRT 动态 shape 功能支持 PaddleSlim 量化 Int8 模型。

  • ERNIE 模型在 Nvidia Telsa T4 上使用 Paddle-TRT FP16 推理性能提升 15%。

  • ERNIE 模型在开启 TenorRT 时增加变长输入的支持,带来性能提升 147%。在软件版本 cuda10.1、cudnn 7.6、tensorrt 6.0、OSS 7.2.1,模型 ernie-base-2.0,数据集 QNLI,输入 BatchSize = 32 时,Nvidia Telsa T4 上的性能从 905 sentences/s 提升到 2237 sentences/s。

  • X86 CPU 推理相关

  • 添加了对 oneDNN BF16 的支持:支持 conv2d 和 gru bf16 计算,目前支持 resnet50、googlenet、mobilenetv1 和 mobilenetv2 模型的 BF16 预测。

  • 添加了一些oneDNN 算子的版本兼容性支持。

  • oneDNN 升级到 1.6。

  • 自定义OP

  • Python端推理新增对用户自定义OP支持。

  • 内存 / 显存相关

  • 新增 TryShrinkMemory 接口,通过释放临时 tensor 的方式减少应用内存 / 显存占用。

  • 动态图量化模型支持

  • X86 推理支持动态图量化模型。

  • NVIDIA GPU 推理支持动态图量化模型。

安装指南

本文介绍了 Paddle Inference 支持的硬件平台、操作系统环境、AI 软件加速库、多语言 API 等。

安装 Paddle Inference 主要包括 下载安装推理库源码编译 两种方式。

下载安装推理库 是最简单便捷的安装方式,Paddle Inference 提供了多种环境组合下的预编译库,如 cuda/cudnn 的多个版本组合、是否支持 TensorRT 、可选的 CPU 矩阵计算加速库等。详细内容可参考以下文档:

如果用户环境与官网提供环境不一致(如用户环境的 cuda, cudnn, tensorrt 组合与预编译库提供的组合版本不一致),或对飞桨源代码有修改需求(如发现并修复了算子的 bug , 需要编译推理库集成测试),或希望进行定制化构建(如需新增算子、Pass 优化)等,则您可选择 源码编译 的方式。

  • 源码编译:

    • 源码编译 : 在Linux、Windows、MacOS及其他平台上编译Paddle Inference

系统要求

本篇文档介绍了安装Paddle Inference的软硬件要求,您可以根据以下文档描述,判断您的硬件平台、系统环境及软件依赖是否满足安装要求。

注: 在不满足要求的环境中安装 Paddle Inference 可能会遇到兼容性问题

硬件平台

Paddle Inference 支持多种硬件平台,除了常规的 Intel CPU + NVIDIA GPU 组合外,还支持多种其它架构 CPU 和 AI 加速卡,如下列表所示。

CPU:

  1. X64: Intel(酷睿 Core, 志强 Xeon), AMD(Zen) 以及兆芯等;

  2. AArch64: 飞腾、鲲鹏;

  3. MIPS: 龙芯;

  4. SW: 申威。

AI 加速芯片:

  1. GPU: 主要指 NVIDIA(Kepler, Maxwell, Pascal, Volta, Turing, Ampere架构) 和 AMD 出产的 GPU;

  2. XPU: 昆仑加速卡;

  3. NPU: 昇腾加速卡;

  4. IPU: GraphCore加速卡

操作系统

Paddle Inference 适配了多种操作系统,支持主流的 Windows, Mac, Linux,如下列表所示。

  1. 主流 Linux 系统: Ubuntu, Centos, 统信 UOS, 银河麒麟 v10, 普华等

  2. MacOS: 10.x/11.x (64 bit)

  3. Windows10

AI 软件加速库

Paddle Inference 为追求更快的性能,通过子图集成接入和算子接入两个方面,适配了以下 AI 软件加速库。

  1. TensorRT: 以子图的方式接入;

  2. cuDNN: 以算子的方式接入;

  3. oneDNN: 以算子的方式接入;

  4. Paddle Lite: 以子图的方式接入。

多语言 API

Paddle Inference 基于 C++ 实现,提供了标准 C++ API 接口,在此基础上封装了其它多语言 API,支持多语言 API 如下列表所示。

  1. C++: 原生支持;

  2. C: 通过 extern "C" 的方式进行封装;

  3. Python: 通过 pybind 进行封装;

  4. Go: 通过 cgo 在 C API 基础上进行封装。

安装 Python API

本文主要介绍 Paddle Inference Python API 的安装。主要分为以下三个章节:环境准备、安装步骤和验证安装。三个章节分别说明了安装前的环境要求、安装的具体流程和成功安装后的验证方法。

环境准备
  • Python: 3.6 / 3.7 / 3.8 / 3.9

  • CUDA 10.1 / CUDA 10.2 / CUDA 11.0 / CUDA 11.2, cuDNN7.6+, TensorRT (仅在使用 GPU 版本的推理库时需要)

您可参考 NVIDIA 官方文档了解 CUDA 和 cuDNN 的安装流程和配置方法,请见 CUDAcuDNN,版本对应关系如下表所示:

CUDA 版本 cuDNN 版本 TensorRT 版本
10.2 7.6 7
11.0 8.0 7
11.2 8.2 8
开始安装

如果已经安装 PaddlePaddle,且不需要使用 TensorRT 进行推理加速,可以跳过此步骤,直接使用为训练安装的 PaddlePaddle 版本。

方式一:通过 pip 在线安装(不含TensorRT)
  • CPU版本

python3 -m pip install paddlepaddle==2.3.0 -i https://mirror.baidu.com/pypi/simple
  • GPU版本(以 CUDA11.0 为例)

python3 -m pip install paddlepaddle-gpu==2.3.0.post110 -f https://www.paddlepaddle.org.cn/whl/linux/mkl/avx/stable.html
方式二:下载 whl 包(可选 TensorRT)到本地,然后通过 pip 工具安装
方式三:源码安装

参考源码编译文档。

验证安装
静态验证方式

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

import paddle
paddle.utils.run_check()
动态验证方式

您可以编写应用代码并测试结果。请参考 推理示例(Python) 一节。

开始使用

请参考 推理示例(Python)Python API 文档

安装 C++ API

本文主要介绍 Paddle Inference C++ API 的安装。主要分为以下三个章节:环境准备、安装步骤和验证安装。三个章节分别说明了安装前的环境要求、安装的具体流程和成功安装后的验证方法。

环境准备
  • GCC 5.4+

  • CMake 3.0+

  • Visual Studio 2017 Update 3 (仅在使用 Windows 版本的推理库时需要,根据 Paddle 推理库所使用的 VS 版本选择,请参考 Visual Studio 不同版本二进制兼容性 )

  • CUDA 10.2 / CUDA 11.0 / CUDA 11.2, cuDNN7.6+, TensorRT (仅在使用 GPU 版本的推理库时需要)

您可参考 NVIDIA 官方文档了解 CUDA 和 cuDNN 的安装流程和配置方法,请见 CUDAcuDNN,版本对应关系如下表所示:

CUDA 版本 cuDNN 版本 TensorRT 版本
10.2 7.6 7
11.0 8.0 7
11.2 8.2 8
开始安装

Paddle Inference 提供了 Linux/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

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

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

GIT COMMIT ID: 47fa64004362b1d7d63048016911e62dc1d84f45
WITH_MKL: ON
WITH_MKLDNN: ON
WITH_GPU: ON
WITH_ROCM: OFF
WITH_ASCEND_CL: OFF
WITH_ASCEND_CXX11: OFF
WITH_IPU: OFF
CUDA version: 11.2
CUDNN version: v8.2
CXX compiler version: 8.2.0
WITH_TENSORRT: ON
TensorRT version: v8.2.4.2
动态验证方式

您可以编写应用代码,与推理库联合编译并测试结果。请参考 推理示例(C++) 一节。

开始使用

请参考 推理示例(C++)C++ API 文档

安装 C API

本文主要介绍 Paddle Inference C API 的安装。主要分为以下三个章节:环境准备、安装步骤和验证安装。三个章节分别说明了安装前的环境要求、安装的具体流程和成功安装后的验证方法。

环境准备
  • GCC 5.4+

  • CMake 3.0+

  • Visual Studio 2017 Update 3 (仅在使用 Windows 版本的推理库时需要,根据 Paddle 推理库所使用的 VS 版本选择,请参考 Visual Studio 不同版本二进制兼容性 )

  • CUDA 10.2 / CUDA 11.0 / CUDA 11.2, cuDNN7.6+, TensorRT (仅在使用 GPU 版本的推理库时需要)

您可参考 NVIDIA 官方文档了解 CUDA 和 cuDNN 的安装流程和配置方法,请见 CUDAcuDNN,版本对应关系如下表所示:

CUDA 版本 cuDNN 版本 TensorRT 版本
10.2 7.6 7
11.0 8.0 7
11.2 8.2 8
开始安装

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

验证安装
静态验证方式

下载完成并解压之后,目录下的 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                          版本信息与编译选项信息

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

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

GIT COMMIT ID: 47fa64004362b1d7d63048016911e62dc1d84f45
WITH_MKL: ON
WITH_MKLDNN: ON
WITH_GPU: ON
WITH_ROCM: OFF
WITH_ASCEND_CL: OFF
WITH_ASCEND_CXX11: OFF
WITH_IPU: OFF
CUDA version: 11.2
CUDNN version: v8.2
CXX compiler version: 8.2.0
WITH_TENSORRT: ON
TensorRT version: v8.2.4.2
动态验证方式

您可以编写应用代码,与推理库联合编译并测试结果。请参考 推理示例(C) 一节。

开始使用

请参考 推理示例(C)C API 文档

安装 Go API

本文主要介绍 Paddle Inference Go API 的安装。主要分为以下三个章节:环境准备,安装步骤和验证安装。

Paddle Inference 集成 Golang 的方式

Golang 为了能够尽可能的复用现有 C/C++ 库的软件,提供了 cgo 工具来作为 Golang 与 C/C++ 交互的方式。 Paddle Inference 提供了完善的 C API 接口,Golang 通过 cgo 直接接入,集成代码见code.

环境准备

Paddle Inference Go API 目前仅在 Linux 系统下 Golang 1.15 版本上进行了验证、测试和 CI 监控,如需其它环境和版本的支持,请在 issue 中描述需求,相关工作人员看到后会排期支持。

Golang 安装

安装 Golang, 您可直接访问 Golang 官网,下载对应版本的 Golang.

  1. 下载 Golang 1.15 版本。

wget https://go.dev/dl/go1.15.15.linux-amd64.tar.gz
  1. 新增或替换 Golang.(可能需要 sudo 权限)

rm -rf /usr/local/go && tar -C /usr/local -xzf go1.15.15.linux-amd64.tar.gz
  1. 修改 PATH 环境变量。

export PATH=$PATH:/usr/local/go/bin
  1. 验证 Golang 正确安装。 打印出正确的版本号即代表安装成功。

go version
安装步骤
Paddle Inference C 库安装

安装Paddle Inference C 库请参考Paddle Inference安装(C)

Paddle Inference 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: 47fa64004362b1d7d63048016911e62dc1d84f45
WITH_MKL: ON
WITH_MKLDNN: ON
WITH_GPU: ON
WITH_ROCM: OFF
WITH_ASCEND_CL: OFF
WITH_ASCEND_CXX11: OFF
WITH_IPU: OFF
CUDA version: 11.2
CUDNN version: v8.2
CXX compiler version: 8.2.0
WITH_TENSORRT: ON
TensorRT version: v8.2.4.2

此处需要记录 Git Commit ID, 请替换该变量为您 C 库的 Commit ID.

export COMMITID=47fa64004362b1d7d63048016911e62dc1d84f45
Paddle Inference Golang API 安装
  1. 确认使用 Paddle 的 Commit ID. 安装 C 库的过程中,记录下使用 Paddle 的 Commit ID.

  2. 使用 go get 获取 Golang Paddle API.

go env -w GO111MODULE=on
go get -d -v github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi@${COMMITID}
  1. 软链 C 库

go1.15 新增了 GOMODCACHE 环境变量,go get 默认会将代码下载到 GOMODCACHE 目录下,您可以通过 go env | grep GOMODCACHE 的方式,查看该路径,在官网发布的docker镜像中该路径一般默认为 /root/gopath/pkg/mod,进入到 Golang API 代码路径建立软连接,将 C 推理库命名为 paddle_inference_c.

eval $(go env | grep GOMODCACHE)
# 按需修改最后的goapi版本号
cd ${GOMODCACHE}/github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi\@v0.0.0-20210623023452-0722297d9b8c/
ln -s ${PADDLE_C_DOWNLOAD_DIR}/paddle_inference_c_install_dir paddle_inference_c
验证安装

在 Golang API 安装目录下存在test.sh脚本,用来检验安装是否成功,直接运行该脚本即可。

# 按需修改最后的goapi版本号
cd ${GOMODCACHE}/github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi\@v0.0.0-20210623023452-0722297d9b8c/

bash test.sh
开始使用

请参考 推理示例(Go)Go API 文档

下载安装 Linux 推理库

C++ 推理库
硬件后端 是否打开avx 数学库 gcc版本 CUDA/cuDNN/TensorRT版本 推理库(2.3.0版本)
CPU MKL 8.2 - paddle_inference.tgz
CPU MKL 5.4 - paddle_inference.tgz
CPU OpenBLAS 8.2 - paddle_inference.tgz
CPU OpenBLAS 5.4 - paddle_inference.tgz
CPU OpenBLAS 8.2 - paddle_inference.tgz
CPU OpenBLAS 5.4 - paddle_inference.tgz
GPU MKL 8.2 CUDA10.2/cuDNN8.1/TensorRT7.2 paddle_inference.tgz
GPU MKL 5.4 CUDA10.2/cuDNN8.1/TensorRT7.2 paddle_inference.tgz
GPU MKL 8.2 CUDA11.1/cuDNN8.1/TensorRT7.2 paddle_inference.tgz
GPU MKL 5.4 CUDA11.1/cuDNN8.1/TensorRT7.2 paddle_inference.tgz
GPU MKL 8.2 CUDA11.2/cuDNN8.2/TensorRT8.0 paddle_inference.tgz
GPU MKL 5.4 CUDA11.2/cuDNN8.2/TensorRT8.0 paddle_inference.tgz
Jetson(all) - - - Jetpack 4.5 paddle_inference.tgz
Jetson(Nano) - - - Jetpack 4.5 paddle_inference.tgz
Jetson(TX2) - - - Jetpack 4.5 paddle_inference.tgz
Jetson(Xavier) - - - Jetpack 4.5 paddle_inference.tgz
Jetson(all) - - - Jetpack 4.6 paddle_inference.tgz
Jetson(Nano) - - - Jetpack 4.6 paddle_inference.tgz
Jetson(TX2) - - - Jetpack 4.6 paddle_inference.tgz
Jetson(Xavier) - - - Jetpack 4.6 paddle_inference.tgz
Jetson(all) - - - Jetpack 4.6.1 paddle_inference.tgz
Jetson(Nano) - - - Jetpack 4.6.1 paddle_inference.tgz
Jetson(TX2) - - - Jetpack 4.6.1 paddle_inference.tgz
Jetson(Xavier) - - - Jetpack 4.6.1 paddle_inference.tgz
C 推理库
硬件后端 是否打开avx 数学库 gcc版本 CUDA/cuDNN/TensorRT版本 推理库(2.3.0版本)
CPU MKL 8.2 - paddle_inference_c.tgz
CPU MKL 5.4 - paddle_inference_c.tgz
CPU OpenBLAS 8.2 - paddle_inference_c.tgz
CPU OpenBLAS 5.4 - paddle_inference_c.tgz
CPU OpenBLAS 8.2 - paddle_inference_c.tgz
CPU OpenBLAS 5.4 - paddle_inference_c.tgz
GPU 8.2 CUDA10.2/cuDNN8.1/TensorRT7.2 paddle_inference_c.tgz
GPU 5.4 CUDA10.2/cuDNN8.1/TensorRT7.2 paddle_inference_c.tgz
GPU 8.2 CUDA11.1/cuDNN8.2/TensorRT7.2 paddle_inference_c.tgz
GPU 5.4 CUDA11.1/cuDNN8.1/TensorRT7.2 paddle_inference_c.tgz
GPU 8.2 CUDA11.2/cuDNN8.2/TensorRT8.0 paddle_inference_c.tgz
GPU 5.4 CUDA11.2/cuDNN8.2/TensorRT8.0 paddle_inference_c.tgz

下载安装 Windows 推理库

环境硬件配置:

操作系统 win10 家庭版本
CPU I7-8700K
内存 16G
硬盘 1T hdd + 256G ssd
显卡 GTX1080 8G
C++ 推理库
硬件后端 是否使用avx 编译器 CUDA/cuDNN/TensorRT版本 数学库 推理库(2.3.0版本)
CPU MSVC 2017 - MKL paddle_inference.zip
CPU MSVC 2017 - OpenBLAS paddle_inference.zip
GPU MSVC 2017 CUDA10.1/cuDNN7.6/no_trt MKL paddle_inference.zip
GPU MSVC 2017 CUDA10.2/cuDNN7.6/TensorRT7.0 MKL paddle_inference.zip
GPU MSVC 2017 CUDA11.0/cuDNN8.0/TensorRT7.2 MKL paddle_inference.zip
GPU MSVC 2017 CUDA11.2/cuDNN8.2/TensorRT8.0 MKL paddle_inference.zip
C 推理库
硬件后端 是否打开avx 数学库 编译器版本 CUDA/cuDNN/TensorRT版本 推理库(2.3.0版本)
CPU MKL MSVC 2017 - paddle_inference_c.zip
CPU OpenBLAS MSVC 2017 - paddle_inference_c.zip
GPU MKL MSVC 2017 CUDA10.1/cuDNN7.6/no_trt paddle_inference_c.zip
GPU MKL MSVC 2017 CUDA10.2/cuDNN7.6/TensorRT7.0 paddle_inference_c.zip
GPU MKL MSVC 2017 CUDA11.0/cuDNN8.0/TensorRT7.2 paddle_inference_c.zip
GPU MKL MSVC 2017 CUDA11.2/cuDNN8.2/TensorRT8.2 paddle_inference_c.zip
python 推理
版本说明 python3.8
cuda10.2_cudnn7.6.5_avx_mkl-trt7.0.0.11 paddlepaddle-cp38m.whl
cuda11.0_cudnn8.0_avx_mkl-trt7.2.1.6 paddlepaddle-cp38m.whl
cuda11.2_cudnn8.2_avx_mkl-trt8.0.1.6 paddlepaddle-cp38m.whl

下载安装 Mac 推理库

C++ 推理库
硬件后端 是否打开avx 数学库 推理库(2.3.0版本)
CPU OpenBLAS paddle_inference.tgz
C 推理库
硬件后端 是否打开avx 数学库 推理库(2.3.0版)
CPU OpenBLAS paddle_inference_c.tgz

源码编译

对于首次接触Paddle Inference源码编译, 或者不确定自己是否需要从源码编译的用户,我们建议您首先阅读 源码编译基础 进行了解。

如果您确定需要进行源码编译,请首先确定您的硬件平台:

(1) x86 CPU 或 Nvidia GPU

如果您的计算机没有 Nvidia GPU,请选择 CPU 版本构建安装。如果您的计算机含有 Nvidia GPU 且预装有 CUDA / cuDNN,也可选择 GPU 版本构建安装。下面提供下在不同OS平台上的编译步骤:

(2)其他硬件环境编译

如果您的硬件环境不同于以上介绍的通用环境,您可以前往 其他硬件部署 页面查阅您的硬件平台是否被Paddle Inference支持,并参考相应文档完成编译和推理应用开发。

源码编译基础
什么时候需要源码编译?

深度学习的发展十分迅速,对科研或工程人员来说,可能会遇到一些需要自己开发 OP 的场景,可以在 Python 层面编写 OP,但如果对性能有严格要求的话则必须在 C++ 层面开发 OP,对于这种情况,需要用户源码编译飞桨,使之生效。

此外对于绝大多数使用 C++ 将模型部署上线的工程人员来说,您可以直接通过飞桨官网下载已编译好的推理库,快捷开启飞桨使用之旅。飞桨官网 提供了多个不同环境下编译好的推理库。如果用户环境与官网提供环境不一致(如 CUDA、 cuDNN、 TensorRT 版本不一致等),或对飞桨源代码有修改需求,或希望进行定制化构建,可查阅本文档自行源码编译得到推理库。

目标产物

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

c++ lib

含有 C++ 接口的头文件及其二进制库:用于 C++ 环境,将文件放到指定路径即可开启飞桨使用之旅。 推理库编译后,所有产出均位于 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 使用 一节。

python whl 包

Python Wheel 形式的安装包:用于 Python 环境,也就是说,通过 pip 安装属于在线安装,这里属于本地安装。 编译完毕后,会在 python/dist 目录下生成一个 Python Wheel 安装包,安装测试的命令为:

pip3 install [wheel 包的名字]

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

import paddle
paddle.utils.run_check()
基础概念

飞桨深度学习框架主要由 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 且预装有 CUDA / cuDNN,也可选择 GPU 版本构建安装。下面提供下在不同平台上的编译步骤:

Linux 下从源码编译

Windows 下从源码编译

MacOs 下从源码编译

Linux 下从源码编译
环境准备

Linux 版本 (64 bit)

CentOS 6 (不推荐,不提供编译出现问题时的官方支持)
CentOS 7 (GPU 版本支持 CUDA 10.2/11.0/11.1/11.2)
Ubuntu 14.04 (不推荐,不提供编译出现问题时的官方支持)
Ubuntu 16.04 (GPU 版本支持 CUDA 10.2/11.0/11.1/11.2)
Ubuntu 18.04 (GPU 版本支持 CUDA 10.2/11.0/11.1/11.2)

Python 版本 3.6/3.7/3.8/3.9/3.10 (64 bit)

选择 CPU/GPU

如果您的计算机没有 NVIDIA GPU,请安装 CPU 版本的 PaddlePaddle。 如果您的计算机有 NVIDIA GPU,请确保满足以下条件以编译 GPU 版 PaddlePaddle:

CUDA 工具包10.2配合 cuDNN 7 (cuDNN 版本>=7.6.5)
CUDA 工具包11.0配合 cuDNN v8.0.4
CUDA 工具包11.1配合 cuDNN v8.1.1
CUDA 工具包11.2配合 cuDNN v8.1.1
GPU 运算能力超过3.5的硬件设备

您可参考 NVIDIA 官方文档了解 CUDA 和 cuDNN 的安装流程和配置方法,请见 CUDAcuDNN

安装步骤

在 Linux 的系统下有2种编译方式,推荐使用 Docker 编译。 Docker 环境中已预装好编译 Paddle 需要的各种依赖,相较本机编译环境更简单。

1. 使用 Docker 编译(不提供在 CentOS 6下编译中遇到问题的支持)
2. 本机编译(不提供在 CentOS 6下编译中遇到问题的支持)
使用 docker 编译

Docker 是一个开源的应用容器引擎。使用 Docker,既可以将 PaddlePaddle 的安装&使用与系统环境隔离,也可以与主机共享 GPU、网络等资源

使用 Docker 编译时,您需要:

1. 在本地主机上安装 Docker
2. 如需在 Linux 开启 GPU 支持,请安装 nvidia-docker

请您按照以下步骤编译安装:

1. 请首先选择您希望储存 PaddlePaddle 的路径,然后在该路径下使用以下命令将 PaddlePaddle 的源码从 github 克隆到本地当前目录下名为 Paddle 的文件夹中

git clone https://github.com/PaddlePaddle/Paddle.git

2. 进入 Paddle 目录下

cd Paddle

3. 拉取 PaddlePaddle 镜像

对于国内用户,因为网络问题下载 docker 比较慢时,可使用百度提供的镜像:

CPU 版的 PaddlePaddle:

docker pull registry.baidubce.com/paddlepaddle/paddle:latest-dev

GPU 版的 PaddlePaddle:

nvidia-docker pull registry.baidubce.com/paddlepaddle/paddle:latest-dev-cuda11.2-cuDNN8-gcc82

如果您的机器不在中国大陆地区,可以直接从 DockerHub 拉取镜像:

CPU 版的 PaddlePaddle:

docker pull paddlepaddle/paddle:latest-dev

GPU 版的 PaddlePaddle:

nvidia-docker pull paddlepaddle/paddle:latest-dev-cuda11.2-cuDNN8-gcc82

上例中,latest-dev-cuda11.2-cuDNN8-gcc82 仅作示意用,表示安装 GPU 版的镜像。如果您还想安装其他 cuda/cuDNN 版本的镜像,可以将其替换成 latest-gpu-cuda10.1-cuDNN7-gcc82-dev、latest-gpu-cuda10.1-cuDNN7-gcc54-dev 等。 您可以访问 DockerHub 获取与您机器适配的镜像。

4. 创建并进入已配置好编译环境的 Docker 容器

编译 CPU 版本的 PaddlePaddle:

docker run --name paddle-test -v $PWD:/paddle --network=host --privileged=true -it registry.baidubce.com/paddlepaddle/paddle:latest-dev /bin/bash

其中参数的意义为:

--name paddle-test:为您创建的 Docker 容器命名为 paddle-test;
-v $PWD:/paddle: 将当前目录挂载到 Docker 容器中的 /paddle 目录下(Linux 中 PWD 变量会展开为当前路径的绝对路径);
--privileged=true: container 内的 root用户 拥有真正的 root 权限
-it: 与宿主机保持交互状态;
registry.baidubce.com/paddlepaddle/paddle:latest-dev:使用名为 registry.baidubce.com/paddlepaddle/paddle:latest-dev 的镜像创建 Docker 容器,/bin/bash 进入容器后启动 /bin/bash 命令。

编译 GPU 版本的 PaddlePaddle:

nvidia-docke run --name paddle-test -v $PWD:/paddle --network=host --privileged=true -it registry.baidubce.com/paddlepaddle/paddle:latest-dev /bin/bash

注意: 请确保至少为 docker 分配 4g 以上的内存,否则编译过程可能因内存不足导致失败。

5. 进入 Docker 后进入 paddle 目录下

cd /paddle

6. 切换到较稳定版本下进行编译

git checkout [分支名]

例如:

git checkout release/2.3

7. 创建并进入 /paddle/build 路径下

mkdir -p /paddle/build && cd /paddle/build

8. 使用以下命令安装相关依赖

安装 protobuf。

pip3.7 install protobuf

注意:以上用 Python3.7 命令来举例,请将上述命令中的 pip3.7 改成对应的版本。

9. 执行 cmake

编译 CPU 版本:

cmake .. -DPY_VERSION=3.7 -DWITH_TESTING=OFF -DWITH_MKL=ON -DWITH_GPU=OFF -DON_INFER=ON

编译 GPU 版本:

cmake .. -DPY_VERSION=3.7 -DWITH_TESTING=OFF -DWITH_MKL=ON -DWITH_GPU=ON -DON_INFER=ON

使用 TensorRT:

如果想使用 TensorRT 进行推理,首先需要根据自己的需求下载对应版本的 TensorRT GA build, 下载解压后,在 cmake 中开启 WITH_TENSORRT, 并通过 TENSORRT_ROOT 指定刚刚解压的 TensorRT_lib 的路径。假设下载的 TensorRT lib 解压 目录为 /paddle/nvidia/TensorRT/, cmake 编译指令如下:

cmake .. -DPY_VERSION=3.7 -DWITH_TESTING=OFF -DWITH_MKL=ON -DWITH_GPU=ON -DON_INFER=ON \
					-DWITH_TENSORRT=ON -DTENSORRT_ROOT=/paddle/nvidia/TensorRT/

更多 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:自动识别当前环境的架构编译
WITH_TENSORRT 是否开启 TensorRT OFF
TENSORRT_ROOT TensorRT_lib的路径,该路径指定后会编译 TensorRT 子图功能eg:/paddle/nvidia/TensorRT/ /usr

10. 执行编译

make -j4

编译飞桨过程中可能会打开很多文件,如果编译过程中显示 “Too many open files” 错误时,请使用指令 ulimit -n 102400 来增大当前进程允许打开的文件数**

ulimit -n 102400

注意: 编译过程中需要从 github 上下载依赖,请确保您的编译环境能正常从 github 下载代码。

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

pip3 install python/dist/[wheel 包名字]

12. 推理库编译

make inference_lib_dist -j4

编译成功后,所有产出均位于 build 目录下的 paddle_inference_install_dir 目录内。

本机编译

本机编译与 docker 编译的区别只有环境准备不同, docker 中已经配置好了相关环境,本机编译中,需要用户自己配置编译依赖项

1. 安装必要的工具

以 Ubuntu 上为例, 安装编译依赖项可通过如下命令:

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

若需启用 CUDA 加速,需准备 CUDA、cuDNN。请参考 NVIDIA 官网文档了解 CUDA 和 cuDNN 的安装流程和配置方法,请见 CUDAcuDNN, 版本对应关系如下表所示:

CUDA 版本 cuDNN 版本 TensorRT 版本
10.2 7.6 7
11.0 8.0 7
11.2 8.2 8

以 CUDA 11.3,cuDNN 8.2 为例配置 CUDA 环境。

# cuda
sh cuda_11.3.0_465.19.01_linux.run
export PATH=/usr/local/cuda-11.3/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-11.3/${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}

# cuDNN
tar -xzvf cudnn-11.3-linux-x64-v8.2.1.32.tgz
sudo cp -a cuda/include/cuDNN.h /usr/local/cuda/include/
sudo cp -a cuda/lib64/libcuDNN* /usr/local/cuda/lib64/

2. 编译

2.1) 下载 Paddle

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

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

2.2)cmake

下面以 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 \
        ..

2.3) 使用 make 编译

make -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:自动识别当前环境的架构编译
WITH_TENSORRT 是否开启 TensorRT OFF
TENSORRT_ROOT TensorRT_lib的路径,该路径指定后会编译 TensorRT 子图功能eg:/paddle/nvidia/TensorRT/ /usr

3. 安装Wheel包

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

pip3 install python/dist/[wheel 包名字]

4. 编译C++推理库(按需)

make inference_lib_dist -j4

编译成功后,所有产出均位于 build 目录下的 paddle_inference_install_dir 目录内。

编译飞桨过程中可能会打开很多文件,如果编译过程中显示 “Too many open files” 错误时,请使用指令 ulimit -n 102400 来增大当前进程允许打开的文件数

ulimit -n 102400

5. 验证安装

安装完成后你可以使用 python 进入 python 解释器,输入:

import paddle
paddle.utils.run_check()

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

恭喜,至此你已完成 Paddle Inference 的编译安装

Windows 下从源码编译
环境准备
Windows 7/8/10 专业版/企业版 (64 bit)
Python 版本 3.6/3.7/3.8/3.9/3.10 (64 bit)
Visual Studio 2017 社区版/专业版/企业版
选择CPU/GPU
如果你的计算机硬件没有 NVIDIA GPU,请编译 CPU 版本的 Paddle Inference 预测库
如果你的计算机硬件有 NVIDIA GPU,推荐编译 GPU 版本的 Paddle Inference 预测库,建议安装 CUDA 10.2/11.0/11.1/11.2
本机编译过程

1. 安装必要的工具 cmake, git, python, Visual studio 2017:

  • cmake:建议安装 CMake3.17 版本, 官网下载链接。安装时注意勾选 Add CMake to the system PATH for all users,将 CMake 添加到环境变量中。

  • git:官网下载链接,使用默认选项安装。

  • python:官网链接,可选择 3.6/3.7/3.8/3.9/3.10 中任一版本的 Windows installer(64-bit) 安装。安装时注意勾选 Add Python 3.x to PATH,将 Python 添加到环境变量中。

  • Visual studio 2017:官网链接,需要登录后下载,建议下载 Community 社区版。在安装时需要在工作负荷一栏中勾选 使用 C++ 的桌面开发通用 Windows 平台开发,并在语言包一栏中选择 英语

2. 在 Windows 桌面下方的搜索栏中搜索 x64 Native Tools Command Prompt for VS 2017适用于 VS 2017 x64 本机工具命令提示符,右键以管理员身份打开终端。之后的命令均在该终端中执行。

3.使用 pip 命令安装 Python 依赖:

通过 python --version 检查默认 python 版本是否是预期版本,因为你的计算机可能安装有多个 python,你可通过修改系统环境变量的顺序来修改默认 Python 版本。

安装 numpy, protobuf, wheel, ninja

pip3 install numpy protobuf wheel ninja

4. 创建编译 Paddle 的文件夹(例如 D:\workspace),进入该目录并下载源码:

mkdir D:\workspace && cd /d D:\workspace
git clone https://github.com/PaddlePaddle/Paddle.git
cd Paddle

5. 切换到 2.3 分支下进行编译:

git checkout release/2.3

6. 创建名为 build 的目录并进入:

mkdir build
cd build

7. 执行 cmake:

编译 CPU 版本的 Paddle Inference:

cmake .. -GNinja -DWITH_GPU=OFF  -DCMAKE_BUILD_TYPE=Release -DWITH_UNITY_BUILD=ON -DWITH_TESTING=OFF -DON_INFER=ON

编译 GPU 版本的 Paddle Inference:

cmake .. -GNinja -DWITH_GPU=ON  -DCMAKE_BUILD_TYPE=Release -DWITH_UNITY_BUILD=ON -DWITH_TESTING=OFF -DON_INFER=ON

使用 TensorRT: 如果想使用 TensorRT 进行推理,首先需要下载并解压TensorRT, 在 cmake 中开启 WITH_TENSORRT, 并通过 TENSORRT_ROOT 指定刚刚解压的 TensorRT 路径。假设下载的 TensorRT lib 解压 到 D 盘目录下, cmake 编译指令如下:

cmake .. -GNinja -DWITH_GPU=ON  -DCMAKE_BUILD_TYPE=Release -DWITH_UNITY_BUILD=ON -DWITH_TESTING=OFF -DON_INFER=ON -DWITH_TENSORRT=ON -DTENSORRT_ROOT="D:/TensorRT"

其他编译选项含义请参见编译选项表。

选项 说明 默认值
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:自动识别当前环境的架构编译
WITH_TENSORRT 是否开启 TensorRT OFF
TENSORRT_ROOT TensorRT_lib的路径,该路径指定后会编译TRT子图功能eg:/paddle/nvidia/TensorRT/ /usr

注意:

如果本机安装了多个 CUDA,将使用最新安装的 CUDA 版本,且无法指定。
如果本机安装了多个 Python,将使用最新安装的 Python 版本。若需要指定 Python 版本,则需要指定 Python 路径,例如:

cmake .. -GNinja -DWITH_GPU=ON -DPYTHON_EXECUTABLE=C:\Python38\python.exe -DPYTHON_INCLUDE_DIR=C:\Python38\include -DPYTHON_LIBRARY=C:\Python38\libs\python38.lib -DCMAKE_BUILD_TYPE=Release -DWITH_UNITY_BUILD=ON -DWITH_TESTING=OFF -DON_INFER=ON

8. 执行编译:

ninja -j4

9. 编译成功后进入 python\dist 目录下找到生成的 .whl 包并安装:

cd python\dist
pip3 install(whl 包的名字)--force-reinstall

10. 在 paddle_inference_install_dir 目录下有生成的 paddle inference c++ 库和头文件等

11. 验证安装 安装完成后你可以使用 python 进入 python 解释器,输入:

import paddle
paddle.utils.run_check()

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

恭喜,至此你已完成 Paddle Inference 的编译安装

macOS 下从源码编译
环境准备

macOS 版本 10.x/11.x/12.x (64 bit) (不支持 GPU 版本)

Python 版本 3.6/3.7/3.8/3.9/3.10 (64 bit)

选择 CPU/GPU

目前仅支持在 macOS 环境下编译安装 CPU 版本的 Paddle Inference

安装步骤

在 macOS 的系统下有 2 种编译方式,推荐使用 Docker 编译。 Docker 环境中已预装好编译 Paddle 需要的各种依赖,相较本机编译环境更简单。

1. Docker源码编译(目前仅支持 mac x86)
2. 本机源码编译
使用 docker 编译(目前仅支持 x86)

Docker 是一个开源的应用容器引擎。使用 Docker,既可以将 Paddle Inference 的安装&使用与系统环境隔离,也可以与主机共享 GPU、网络等资源

使用 Docker 编译时,您需要在本地主机上安装 Docker

请您按照以下步骤安装:

1. 进入 Mac 的终端

2. 请首先选择您希望储存 PaddlePaddle 的路径,然后在该路径下使用以下命令将 PaddlePaddle 的源码从 github 克隆到本地当前目录下名为 Paddle 的文件夹中:

git clone https://github.com/PaddlePaddle/Paddle.git

3. 进入 Paddle 目录下:

cd Paddle

4. 拉取 PaddlePaddle 镜像

对于国内用户,因为网络问题下载 docker 比较慢时,可使用百度提供的镜像:

CPU 版的 PaddlePaddle:

docker pull registry.baidubce.com/paddlepaddle/paddle:latest-dev

如果您的机器不在中国大陆地区,可以直接从 DockerHub 拉取镜像:

CPU 版的 PaddlePaddle:

docker pull paddlepaddle/paddle:latest-dev

5. 创建并进入已配置好编译环境的 Docker 容器:

docker run --name paddle-test -v $PWD:/paddle --network=host --privileged=true -it registry.baidubce.com/paddlepaddle/paddle:latest-dev /bin/bash

其中参数的意义为:

--name paddle-test:为您创建的 Docker 容器命名为 paddle-test;
-v $PWD:/paddle: 将当前目录挂载到 Docker 容器中的 /paddle 目录下(Linux 中 PWD 变量会展开为当前路径的绝对路径);
--privileged=true: container 内的 root用户 拥有真正的 root 权限
-it: 与宿主机保持交互状态;
registry.baidubce.com/paddlepaddle/paddle:latest-dev:使用名为 registry.baidubce.com/paddlepaddle/paddle:latest-dev 的镜像创建 Docker 容器,/bin/bash 进入容器后启动 /bin/bash 命令。

注意: 请确保至少为 docker 分配 4g 以上的内存,否则编译过程可能因内存不足导致失败。

6. 进入 Docker 后进入 paddle 目录下:

cd /paddle

7. 切换到较稳定版本下进行编译:

git checkout [分支名]

例如:

git checkout release/2.3

8. 创建并进入 /paddle/build 路径下:

mkdir build && cd build

9. 使用以下命令安装相关依赖:

安装 protobuf 3.1.0。

pip3.7 install protobuf==3.1.0

注意:以上用 Python3.7 命令来举例,如您的 Python 版本为3.6/3.8/3.9/3.10,请将上述命令中的 pip3.7 改成对应的版本。

10. 执行 cmake:

对于需要编译 CPU 版本 PaddlePaddle 的用户(我们目前不支持 macOS 下 GPU 版本 PaddlePaddle 的编译):

cmake .. -DPY_VERSION=3.7 -DWITH_TESTING=OFF -DWITH_MKL=ON -DWITH_GPU=OFF -DON_INFER=ON

请注意修改参数 -DPY_VERSION 为您希望编译使用的 python 版本, 例如 -DPY_VERSION=3.7 表示 python 版本为3.7

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

选项 说明 默认值
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:自动识别当前环境的架构编译
WITH_TENSORRT 是否开启 TensorRT OFF
TENSORRT_ROOT TensorRT_lib的路径,该路径指定后会编译TRT子图功能eg:/paddle/nvidia/TensorRT/ /usr

11. 执行编译 使用多核编译

make -j4

注意: 编译过程中需要从 github 上下载依赖,请确保您的编译环境能正常从 github 下载代码。

编译飞桨过程中可能会打开很多文件,如果编译过程中显示 “Too many open files” 错误时,请使用指令 ulimit -n 102400 来增大当前进程允许打开的文件数

ulimit -n 102400

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

pip3 install python/dist/(安装包名字)

12. 预测库编译

make inference_lib_dist -j4

编译成功后,位于 build 目录下的 paddle_inference_install_dir 目录内生成 c++ 预测库和对应的头文件。

本机编译

请严格按照以下指令顺序执行

1. 检查您的计算机和操作系统是否符合我们支持的编译标准:

uname -m

并且在 关于本机 中查看系统版本

2. 安装 Python 以及 pip:

建议不要使用 macOS 中自带 Python,使用 python 官方下载 python3.6.x、python3.7.x、python3.8、python3.9、python3.10), pip以及其他的依赖,这将会使您高效编译

3. (Only For Python3 )设置 Python 相关的环境变量:

a. 首先使用

find `dirname $(dirname $(which python3.8))` -name "libpython3.*.dylib"

找到 Pythonlib 的路径(一般情况下弹出的第一个对应您需要使用的 python 的 dylib 路径),然后(下面 [python-lib-path] 替换为找到文件路径)

请注意,当您的 mac 上安装有多个 python 时请保证您正在使用的 python 是您希望使用的 python。

b. 设置 PYTHON_LIBRARIES:

export PYTHON_LIBRARY=[python-lib-path]

c. 其次使用找到 PythonInclude 的路径(通常是找到 [python-lib-path] 的上一级目录为同级目录的 include, 然后找到该目录下 python3.x 的路径),然后将下面 [python-include-path] 替换为找到路径。

d. 设置 PYTHON_INCLUDE_DIR:

export PYTHON_INCLUDE_DIRS=[python-include-path]

e. 设置系统环境变量路径:

export PATH=[python-bin-path]:$PATH

(这里 [python-bin-path] 为将 [python-lib-path] 的最后两级目录替换为 /bin/ 后的目录)

f. 设置动态库链接:

export LD_LIBRARY_PATH=[python-ld-path]
export DYLD_LIBRARY_PATH=[python-ld-path]

(这里 [python-ld-path] 为 [python-bin-path] 的上一级目录)

g. (可选)如果您是在 macOS 10.14 上编译 PaddlePaddle,请保证您已经安装了对应版本的 Xcode。

4. 执行编译前请您确认您的环境中安装有编译依赖表中提到的相关依赖,否则我们强烈推荐使用 Homebrew 安装相关依赖。

macOS 下如果您未自行修改或安装过“编译依赖表”中提到的依赖,则仅需要使用 pip 安装 PyYAML, numpy,protobuf,wheel,使用 homebrew 安装 wget,swig, unrar,另外安装 cmake 即可

a. 这里特别说明一下 CMake 的安装:

CMake 我们支持 3.15 以上版本,推荐使用 CMake3.16, 请从 CMake官方网站下载 CMake 镜像并安装

b. 如果您希望使用自己安装的 OpenBLAS 请 1)设置环境变量OPENBLAS_ROOT为您安装的 OpenBLAS 的路径;2)设置cmake编译选项-DWITH_SYSTEM_BLAS=OFF

5. 将 PaddlePaddle 的源码 clone 在当下目录下的 Paddle 的文件夹中,并进入 Padde 目录下:

git clone https://github.com/PaddlePaddle/Paddle.git
cd Paddle

6. 切换到较稳定 release 分支下进行编译:

git checkout [分支名]

例如:

git checkout release/2.3

7. 创建并进入 build 目录下:

mkdir build && cd build

8. 执行 cmake: 具体编译选项含义请参见编译选项表

若您的机器为 Mac M1 机器,需要编译Arm架构、CPU版本PaddlePaddle:

cmake .. -DPY_VERSION=3.8 -DPYTHON_INCLUDE_DIR=${PYTHON_INCLUDE_DIRS} \
-DPYTHON_LIBRARY=${PYTHON_LIBRARY} -DWITH_GPU=OFF -DWITH_TESTING=OFF \
-DWITH_AVX=OFF -DWITH_ARM=ON -DCMAKE_BUILD_TYPE=Release -DWITH_INFER=ON

若编译arm架构的paddlepaddle,需要cmake版本为 3.19.2 以上

若您的机器不是Mac M1机器,需要编译x86_64架构、CPU版本PaddlePaddle:

cmake .. -DPY_VERSION=3.8 -DPYTHON_INCLUDE_DIR=${PYTHON_INCLUDE_DIRS} \
-DPYTHON_LIBRARY=${PYTHON_LIBRARY} -DWITH_GPU=OFF -DWITH_TESTING=OFF  -DCMAKE_BUILD_TYPE=Release -DWITH_INFER=ON

9. 使用以下命令来编译:

若您的机器为 Mac M1 机器,需要编译 Arm 架构、CPU 版本 PaddlePaddle:

make TARGET=ARMV8 -j4

若您的机器不是 Mac M1 机器,需要编译 x86_64 架构、CPU版本 PaddlePaddle:

make -j4

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

pip3 install python/dist/[wheel 安装包]

11. 在 paddle_inference_install_dir 目录下有生成的 paddle inference c++ 库和头文件等

12. 验证安装 安装完成后你可以使用 python 进入 python 解释器,输入:

import paddle
paddle.utils.run_check()

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

恭喜,至此你已完成 Paddle Inference 的编译安装

快速开始

在本章节中,我们提供了基于Paddle Inference的 Python、 C++、 C、 GO API接口所构建的简单示例供您参考。 示例中演示了Paddle Inference的基本API的使用流程,并解释了部分重要API的使用方式。

Note

请确认您在参照示例进行操作前,已正确完成Paddle Inference的安装。如未完成可以参考 安装指南 选择适合您的方式安装Paddle Inference。 Paddle Inference的标准开发流程可以参考 推理流程

您可以根据您的项目环境要求或是个人开发习惯,参考适合您的推理示例;您也可以直接基于此示例进一步开发您的推理应用。

快速上手Python推理

本章节包含2部分内容,

运行 Python 示例程序

在此环节中,共包含以下4个步骤,

  • 环境准备

  • 模型准备

  • 推理代码

  • 执行程序

1. 环境准备

Paddle Inference 提供了 Ubuntu/Windows/MacOS 平台的官方 Release 推理库wheel包,用户需根据开发环境和硬件自行下载安装,具体可参阅Python推理环境安装

2. 模型准备

下载 ResNet50 模型后解压,得到 Paddle 推理格式的模型,位于文件夹 ResNet50 下。如需查看模型结构,可参考模型结构可视化文档

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. 推理代码

将以下代码保存为 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

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

# 程序输出结果如下
Output data size is 2000
Output data shape is (2, 1000)
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类型

至此 Paddle Inference 推理已跑通,如果想更进一步学习 Paddle Inference,可以根据硬件情况选择学习 GPU 推理、CPU 推理、进阶使用等章节。

快速上手C++推理

本章节包含2部分内容,

注意本章节文档和代码仅适用于Linux系统。

运行 C++ 示例程序

在此环节中,共包含以下5个步骤,

  • 环境准备

  • 模型准备

  • 推理代码

  • 编译代码

  • 执行程序

1. 环境准备

Paddle Inference 提供了 Ubuntu/Windows/MacOS/Jetson 平台的官方 Release 推理库,用户需根据开发环境和硬件自行下载安装,具体可参阅C++推理环境安装

2. 模型准备

下载 ResNet50 模型后解压,得到 Paddle 推理格式的模型,位于文件夹 ResNet50 下。如需查看模型结构,可参考模型结构可视化文档

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++/cpu/resnet50

# 获取部署 Demo 代码库
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git
cd Paddle-Inference-Demo/c++/cpu/resnet50

其中示例代码目录结构如下所示

Paddle-Inference-Demo/c++/resnet50/
├── resnet50_test.cc         推理 C++ 源码程序
├── README.md                README 说明
├── compile.sh               编译脚本
└── run.sh                   运行脚本 
4. 编译代码

在编译前,

  • 第1步环境准备下载解压后的预测库paddle_inference目录(如解压后的目录名称不同,也需重命名为paddle_inference)拷贝至Paddle-Inference-Demo/c++/lib目录下

  • 第2步模型准备下载解压后的模型目录resnet50目录拷贝至Paddle-Inference-Demo/c++/cpu/resnet50目录下

执行如下命令进行编译

bash compile.sh

编译后的二进制即在Paddle-Inference-Demo/c++/cpu/resnet50/build目录下

编译前,可根据部署的环境和硬件编辑compile.sh,配置推理方式。其中各参数含义如下所示,

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

# 配置推理库的根目录,即为本章节第1步中下载/编译的 C++ 推理库,可重命名为 paddle_inference 后置于 ../lib 目录下
LIB_DIR=${work_path}/../lib/paddle_inference

# 如果上述的 WITH_GPU 或 USE_TENSORRT 设为ON,请设置对应的 CUDA, CUDNN, TENSORRT 的路径,例如
CUDNN_LIB=/usr/lib/x86_64-linux-gnu/
CUDA_LIB=/usr/local/cuda/lib64
TENSORRT_ROOT=/usr/local/TensorRT-6.0.1.5
5. 执行程序

使用如下命令执行推理程序

./build/resnet50_test --model_file resnet50/inference.pdmodel --params_file resnet50/inference.pdiparams

如若推理时,提示找不到.so的问题,可将各个依赖动态库拷贝到执行路径(也可以将依赖库路径加入到环境变量中),再执行上述命令

# 将paddle inference中的动态库拷贝到执行路径下
find ../../lib/paddle_inference/ -name "*.so*" | xargs -i cp {} .

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

# 程序输出结果如下
I1202 06:53:18.979496  3411 resnet50_test.cc:73] run avg time is 257.678 ms
I1202 06:53:18.979645  3411 resnet50_test.cc:88] 0 : 0
I1202 06:53:18.979676  3411 resnet50_test.cc:88] 100 : 2.04164e-37
I1202 06:53:18.979728  3411 resnet50_test.cc:88] 200 : 2.12382e-33
I1202 06:53:18.979768  3411 resnet50_test.cc:88] 300 : 0
I1202 06:53:18.979779  3411 resnet50_test.cc:88] 400 : 1.68493e-35
I1202 06:53:18.979794  3411 resnet50_test.cc:88] 500 : 0
I1202 06:53:18.979802  3411 resnet50_test.cc:88] 600 : 1.05767e-19
I1202 06:53:18.979810  3411 resnet50_test.cc:88] 700 : 2.04094e-23
I1202 06:53:18.979820  3411 resnet50_test.cc:88] 800 : 3.85254e-25
I1202 06:53:18.979828  3411 resnet50_test.cc:88] 900 : 1.52393e-30
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());

至此 Paddle Inference 推理已跑通,如果想更进一步学习 Paddle Inference,可以根据硬件情况选择学习 GPU 推理、CPU 推理、进阶使用等章节。

快速上手C推理

本章节包含2部分内容,

注意本章节文档和代码仅适用于Linux系统。

运行 C 示例程序

在此环节中,共包含以下5个步骤,

  • 环境准备

  • 模型准备

  • 推理代码

  • 编译代码

  • 执行程序

1. 环境准备

Paddle Inference 提供了 Ubuntu/Windows/MacOS 平台的官方 Release 推理库下载, 用户需根据开发环境和硬件自行下载安装,具体可参阅 C 推理环境安装

2. 模型准备

下载 ResNet50 模型后解压,得到 Paddle 推理格式的模型,位于文件夹 ResNet50 下。如需查看模型结构,可参考模型结构可视化文档

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_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);

  // 准备输入数据
  int32_t 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(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 数据
  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);
  free(input_data);
  PD_PredictorDestroy(predictor);

  return 0;
}
4. 编译代码

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

  • paddle_inference_c/third_party/install/paddle2onnx/lib/libpaddle2onnx.so

  • paddle_inference_c/third_party/install/onnxruntime/lib/libonnxruntime.so.1.10.0

  • paddle_inference_c/third_party/install/mklml/lib/libmklml_intel.so

  • paddle_inference_c/third_party/install/mklml/lib/libiomp5.so

  • paddle_inference_c/third_party/install/mkldnn/lib/libdnnl.so.2

  • paddle_inference_c/paddle/lib/libpaddle_inference_c.so

# 拷贝所有的动态库到编译路径
find paddle_inference_c/ -name "*.so*" | xargs -i cp {} .

# GCC 编译命令
gcc c_demo.c -Ipaddle_inference_c/paddle/include \
    libpaddle2onnx.so libonnxruntime.so.1.10.0 \
    libiomp5.so libdnnl.so.2 libpaddle_inference_c.so \
    -o c_demo_prog

编译完成之后在当前目录生成 c_demo_prog 可执行文件

5. 执行程序

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

# 执行推理程序
export LD_LIBRARY_PATH=${PWD}:${LD_LIBRARY_PATH}
./c_demo_prog

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

# 程序输出结果如下
--- 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 [layer_norm_fuse_pass]
---    Fused 0 subgraphs into layer_norm op.
--- 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]
---    fused 0 pairs of fc gru patterns
--- Running IR pass [mul_gru_fuse_pass]
--- Running IR pass [seq_concat_fc_fuse_pass]
--- Running IR pass [squeeze2_matmul_fuse_pass]
--- Running IR pass [reshape2_matmul_fuse_pass]
WARNING: Logging before InitGoogleLogging() is written to STDERR
W1202 07:16:22.473459  3803 op_compat_sensible_pass.cc:219]  Check the Attr(transpose_Y) of Op(matmul) in pass(reshape2_matmul_fuse_pass) failed!
W1202 07:16:22.473500  3803 map_matmul_to_mul_pass.cc:668] Reshape2MatmulFusePass in op compat failed.
--- Running IR pass [flatten2_matmul_fuse_pass]
--- Running IR pass [map_matmul_v2_to_mul_pass]
--- Running IR pass [map_matmul_v2_to_matmul_pass]
--- Running IR pass [map_matmul_to_mul_pass]
I1202 07:16:22.476769  3803 fuse_pass_base.cc:57] ---  detected 1 subgraphs
--- Running IR pass [fc_fuse_pass]
I1202 07:16:22.478200  3803 fuse_pass_base.cc:57] ---  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]
I1202 07:16:22.526548  3803 fuse_pass_base.cc:57] ---  detected 53 subgraphs
--- Running IR pass [conv_eltwiseadd_bn_fuse_pass]
--- 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]
I1202 07:16:22.576740  3803 analysis_predictor.cc:717] ======= optimize end =======
I1202 07:16:22.579823  3803 naive_executor.cc:98] ---  skip [feed], feed -> inputs
I1202 07:16:22.581485  3803 naive_executor.cc:98] ---  skip [save_infer_model/scale_0.tmp_1], fetch -> fetch
Output Tensor Name: save_infer_model/scale_0.tmp_1
Output Tensor Size: 1000
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 方法

// 准备输入数据
int32_t 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(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 数据
float* 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);

至此 Paddle Inference 推理已跑通,如果想更进一步学习 Paddle Inference,可以根据硬件情况选择学习 GPU 推理、CPU 推理、进阶使用等章节。

快速上手GO推理

本章节包含2部分内容,

注意本章节文档和代码仅适用于Linux系统。

运行 GO 示例程序

在此环节中,共包含以下5个步骤,

  • 环境准备

  • 模型准备

  • 推理代码

  • 编译代码

  • 执行程序

1. 环境准备

go 语言推理需要下载Paddle Inference的 C 预编译推理库。Paddle Inference 提供了 Ubuntu/Windows/MacOS 平台的官方 Release 推理库下载,用户需根据开发环境和硬件自行下载安装,具体可参阅 C 推理环境安装

执行推理程序之前需要完成以下几个步骤

  1. 使用go get获取golang paddle api,go 的版本需要大于等于 1.15

# 此处使用对应tag的CommitId,假设为76e5724,可在步骤1中查看到
export GO111MODULE=on
go get -d -v github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi@590b4dbcdd989324089ce43c22ef151c746c92a3
  1. 软链

go get默认会将代码下载到GOMODCACHE目录下,您可以通过go env | grep GOMODCACHE的方式,查看该路径,在官网发布的docker镜像中该路径一般默认为/root/gopath/pkg/mod,进入到golang api代码路径建立软连接,将c推理库命名为paddle_inference_c

eval $(go env | grep GOMODCACHE)
# 按需修改最后的goapi版本号
cd ${GOMODCACHE}/github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi\@v0.0.0-20220523104455-d5b6eec273a9/
ln -s ${PADDLE_C_DOWNLOAD_DIR}/paddle_inference_c paddle_inference_c
  1. 进入到golang api代码路径后,运行单测,验证。

bash test.sh

单测通过后,即表示 go 环境准备完成。

2. 模型准备

下载 resnet50 模型后解压。

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

# 获得 resnet50 目录结构如下
resnet50/
├── inference.pdmodel
├── inference.pdiparams
└── inference.pdiparams.info
3. 推理代码

本章节 GO 推理示例代码位于 Paddle-Inference-Demo,目录下的resnet50子目录。

# 获取部署 Demo 代码库
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git
cd Paddle-Inference-Demo/go/resnet50

其中示例代码目录结构如下所示

Paddle-Inference-Demo/go/resnet50/
├── README.md                README 说明
└── demo.go                  示例代码
4. 编译代码

进入Paddle-Inference-Demo/go/resnet50/目录,执行

# 先将依赖库加入环境变量
export LD_LIBRARY_PATH=${PADDLE_C_DOWNLOAD_DIR}/paddle_inference_c/third_party/install/paddle2onnx/lib/:${LD_LIBRARY_PATH}
export LD_LIBRARY_PATH=${PADDLE_C_DOWNLOAD_DIR}/paddle_inference_c/third_party/install/onnxruntime/lib/:${LD_LIBRARY_PATH}
export LD_LIBRARY_PATH=${PADDLE_C_DOWNLOAD_DIR}/paddle_inference_c/third_party/install/mklml/lib/:${LD_LIBRARY_PATH}
export LD_LIBRARY_PATH=${PADDLE_C_DOWNLOAD_DIR}/paddle_inference_c/third_party/install/mkldnn/lib/:${LD_LIBRARY_PATH}
export LD_LIBRARY_PATH=${PADDLE_C_DOWNLOAD_DIR}/paddle_inference_c/paddle/lib/:${LD_LIBRARY_PATH}

go mod init demo
go get -d -v github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi@v0.0.0-20220523104455-d5b6eec273a9
go build .
5. 执行程序

在将模型resnet50拷贝至编译目录后,使用如下命令执行程序

./demo -thread_num 1 -work_num 1 -cpu_math 2
GO 推理程序开发说明

使用 Paddle Inference 开发 GO 推理程序仅需以下六个步骤:

(1) 引用 Paddle Inference 的 GO API

import pd "github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi"

(2) 创建配置对象,并指定推理模型路径,详细可参考 go API 文档 - Config

// 配置 PD_AnalysisConfig
config := paddle.NewConfig()

// 设置推理模型路径,即为本小节第2步中下载的模型
config.SetModel("resnet50/inference.pdmodel", "resnet50/inference.pdiparams")

(3) 根据Config创建推理对象,详细可参考 go API 文档 - Predictor

predictor := paddle.NewPredictor(config)

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

// 创建输入 Tensor
inNames := predictor.GetInputNames()
inHandle := predictor.GetInputHandle(inNames[0])

data := make([]float32, 1*3*224*224)
for i := 0; i < len(data); i++ {
    data[i] = float32(i%255) * 0.1
}
inHandle.Reshape([]int32{1, 3, 224, 224})
inHandle.CopyFromCpu(data)

(5) 执行推理引擎,详细可参考 go API 文档 - Predictor

predictor.Run()

(6) 获得推理结果,详细可参考 go API 文档 - Tensor

outNames := predictor.GetOutputNames()
outHandle := predictor.GetOutputHandle(outNames[0])
outData := make([]float32, numElements(outHandle.Shape()))
outHandle.CopyToCpu(outData)

func numElements(shape []int32) int32 {
	n := int32(1)
	for _, v := range shape {
		n *= v
	}
	return n
}

导出模型

Paddle Inference支持使用飞桨静态图模型进行推理,您可以通过以下两种方式获取静态图模型:

(1)飞桨框架导出推理模型

飞桨框架在训练模型过程中,会在本地存储最终训练产出的模型结构和权重参数,这个步骤中存储的模型文件包含了模型的前向、反向以及优化器等信息(即常说的动态图模型,模型参数文件名为*.pdparams和*.pdopt)。 而在进行模型部署时,我们只需要模型的前向结构,以及前向的权重参数,并且会针对网络结构做部署优化(如算子融合等),以保证部署性能更优,因此在模型部署阶段,需要进行模型导出(即常说的静态图模型,模型参数文件名为*.pdmodel和*.pdiparams)。 您可以参考此篇文档导出用于推理的飞桨模型:

(2)导入其他框架模型(X2Paddle)

通过X2Paddle工具,目前支持将Pytorch、ONNX、TensorFlow、Caffe的模型转换成飞桨静态图模型结构,具体使用方法请参考以下文档:

(可选)模型结构可视化

在得到用于Paddle Inference推理的 飞桨静态图模型 后,推荐您使用 VisualDL 或其他类似工具对您的模型进行查看,方便您后续的推理应用开发。 您可以参考以下文档可视化您的模型:

飞桨框架模型导出

本节以LeNet网络为例,介绍从训练LeNet网络存储动态图模型,到存储部署模型流程。包含PaddleSlim输出压缩模型部分,分为以下三部分:

1.Paddle训练模型

该节参考 LeNet 的 MNIST 数据集图像分类 ,使用 Paddle 训练LeNet模型,并存储成训练模型(即动态图模型,模型参数文件名为*.pdparams和*.pdopt)。

  • 依赖包导入

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)
  • 存储训练模型(训练格式):该操作会保存动态图模型(模型参数文件名为*.pdparams和*.pdopt),您可以参考 参数保存 ,了解如何在动态图下存储训练格式的模型。只需调用paddle.save接口即可。

paddle.save(model.state_dict(), 'lenet.pdparams')
paddle.save(optim.state_dict(), "lenet.pdopt")
2.训练模型转换为预测部署模型
  • 加载预训练模型:您可以参考参数载入了解如何在动态图下加载训练格式的模型,此方法可帮助您完成恢复训练,即模型状态回到训练中断的时刻,恢复训练之后的梯度更新走向是和恢复训练前的梯度走向完全相同的。只需调用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即可得到预测格式的模型(即保存成静态图模型,模型参数文件名为*.pdmodel和*.pdiparams)。

  • 注:InputSpec中shape第一个维度设置成None,表示推理时接受任意batch的输入。更详细的InputSpec使用可参考InputSpec

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')
3.PaddleSlim导出预测部署模型

因为PaddleSlim每种压缩策略导出推理模型的接口有差异,若使用PaddleSlim产出推理部署模型,请参考PaddleSlim相关文档:

其他框架模型导出

Pytorch、ONNX、TensorFlow、Caffe模型,可以通过 X2Paddle 工具完成模型转换,转到 Paddle 模型后,即可使用 Paddle Inference 完成部署。

X2Paddle 是飞桨生态下的模型转换工具,致力于帮助你快速迁移其他深度学习框架至飞桨框架。目前支持 推理模型的框架转换PyTorch训练代码迁移 ,除此之外还提供了详细的不同框架间API对比文档,降低你上手飞桨核心的学习成本。

1.安装模型转换工具X2Padlde

使用pip安装

pip install x2paddle

使用源码安装

git clone https://github.com/PaddlePaddle/X2Paddle.git
cd X2Paddle
python setup.py install
2.模型转换
2.1 Pytorch模型转换
from x2paddle.convert import pytorch2paddle
pytorch2paddle(module=torch_module,
                        save_dir="./pd_model",
                        jit_type="trace",
                        input_examples=[torch_input])
# module (torch.nn.Module): PyTorch的Module。
# save_dir (str): 转换后模型的保存路径。
# jit_type (str): 转换方式。默认为"trace"。
# input_examples (list[torch.tensor]): torch.nn.Module的输入示例,list的长度必须与输入的长度一致。默认为None。

script 模式以及更多细节可参考 PyTorch模型转换文档

2.2 ONNX模型转换
x2paddle --framework onnx --model onnx_model.onnx --save_dir pd_model
2.3 TensorFlow模型转换
x2paddle --framework tensorflow --model model.pb --save_dir pd_model
2.4 Caffe模型转换
x2paddle --framework caffe --prototxt model.proto --weight model.caffemodel --save_dir pd_model
转换参数说明

参数

作用

–framework

源模型类型 (pytorch、tensorflow、caffe、onnx)

–prototxt

当framework为caffe时,该参数指定caffe模型的proto文件路径

–weight

当framework为caffe时,该参数指定caffe模型的参数文件路径

–save_dir

指定转换后的模型保存目录路径

–model

当framework为tensorflow/onnx时,该参数指定tensorflow的pb模型文件或onnx模型路径

–caffe_proto

[可选] 由caffe.proto编译成caffe_pb2.py文件的存放路径,当存在自定义Layer时使用,默认为None

–define_input_shape

[可选] For TensorFlow, 当指定该参数时,强制用户输入每个Placeholder的shape,见 文档

–enable_code_optim

[可选] For PyTorch, 是否对生成代码进行优化,默认为True

更多参数可参考 X2Paddle官网

X2Paddle API

目前X2Paddle提供API方式转换模型,可参考 X2PaddleAPI

转换结果说明

在指定的 save_dir 以下目录以及文件

  1. inference_model : 目录下有静态图模型结构以及参数

  2. x2paddle_code.py : 自动生成的动态图组网代码

  3. model.pdparams : 动态图模型参数

问题反馈

X2Paddle使用时存在问题时,欢迎您将问题或Bug报告以 Github Issues 的形式提交给我们,我们会实时跟进。

模型结构可视化

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

模型结构文件(*.pdmodel文件),顾名思义,存储了模型的拓扑结构,其中包括模型中各种OP的计算顺序以及OP的详细信息。很多时候,我们希望能够将这些模型的结构以及内部信息可视化,方便我们进行模型分析。

飞桨提供了 VisualDL 帮助我们来可视化模型结构,除此之外,对 Netron 熟悉的用户也可直接使用 Netron 进行模型结构的可视化。 安装可视化工具VisualDL >>>>>>>>>>>>>>

使用pip安装

python -m pip install visualdl -i https://mirror.baidu.com/pypi/simple

使用代码安装

git clone https://github.com/PaddlePaddle/VisualDL.git
cd VisualDL

python setup.py bdist_wheel
pip install --upgrade dist/visualdl-*.whl
可视化

支持两种启动方式:

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

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

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

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

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

更多具体细节可参考 VisualDL使用指南

x86 CPU部署

本章节介绍了如何使用Paddle Inference在x86 CPU平台上部署模型,请根据您的模型格式以及您期望的运行方式选择对应文档。

在x86 CPU上开发推理应用

简介

Paddle Inference在CPU上有:原生CPU、oneDNN和ONNX Runtime后端三种推理方式。还支持量化和低精度推理,加快模型推理速度。

本文档主要介绍使用Paddle Inference原生CPU、oneDNN和ONNX Runtime后端进行推理时,如何调用API进行配置。详细代码请参考:X86 Linux上预测部署示例X86 Windows上预测部署示例

CPU原生推理

原生CPU推理在推理时,使用飞桨核心框架的标准OP实现进行推理计算,不依赖第三方计算库,推理时也无需额外配置。

配置文件开发说明

C++示例:

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

// 设置预测模型路径
config.SetModel(FLAGS_model_file, FLAGS_params_file);

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

// 通过 API 获取 CPU 信息
int num_thread = config.cpu_math_library_num_threads();

python示例:

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

# 创建 config
config = paddle_infer.Config()

# 设置模型的文件夹路径
config.set_model("model.pdmodel", "model.pdiparam")

# 设置 CPU Blas 库线程数为 10
config.set_cpu_math_library_num_threads(10)

# 通过 API 获取 CPU 信息 - 10
print(config.cpu_math_library_num_threads())
oneDNN推理加速

oneDNN(原MKL-DNN)是由英特尔开发的开源深度学习软件包,支持神经网络在CPU上的高性能计算,在Paddle Inference中可通过一行配置打开oneDNN加速。

配置文件开发说明

C++示例:

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

// 设置预测模型路径
config.SetModel(FLAGS_model_file, FLAGS_params_file);

// 启用 oneDNN 进行预测
config.EnableoneDNN();

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

// 设置 oneDNN 的 cache 数量
// 当动态shape推理时,能缓存n个最新输入shape对应的oneDNN配置,减少shape变换时重新生成配置带来的开销
config.SetoneDNNCacheCapacity(1);

python示例:

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

# 创建 config
config = paddle_infer.Config()

# 设置模型的文件夹路径
config.set_model("model.pdmodel", "model.pdiparam")

# 启用 oneDNN 进行预测
config.enable_oneDNN()

# 通过 API 获取 oneDNN 启用结果 - true
print(config.oneDNN_enabled())

# 设置 oneDNN 的 cache 数量
# 当动态shape推理时,能缓存n个最新输入shape对应的oneDNN配置,减少shape变换时重新生配置带来的开销
config.set_oneDNN_cache_capacity(1)

SetMkldnnCacheCapacity缓存机制的详细介绍: 官网文档

ONNX Runtime推理

ONNX Runtime是由微软开源的一款推理引擎,Paddle Inference通过Paddle2ONNX集成ONNX Runtime作为推理的后端之一,开发者在使用时,只需一行配置代码即可让模型通过ONNX Runtime进行推理。

配置文件开发说明

C++示例:

// 创建 Config 对象
paddle_infer::Config config(FLAGS_model_file, FLAGS_params_file);

// 启用 ONNXRuntime
config.EnableONNXRuntime();

// 通过 API 获取 ONNXRuntime 信息
std::cout << "Use ONNXRuntime is: " << config.use_onnxruntime() << std::endl; // true

// 开启ONNXRuntime优化
config.EnableORTOptimization();

// 设置 ONNXRuntime 算子计算线程数为 10
config.SetCpuMathLibraryNumThreads(10);

// 禁用 ONNXRuntime 进行预测
config.DisableONNXRuntime();

python示例:

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

# 创建 config
config = paddle_infer.Config("model.pdmodel", "model.pdiparams")

# 启用 ONNXRuntime 进行预测
config.enable_onnxruntime()

# 通过 API 获取 ONNXRuntime 信息
print("Use ONNXRuntime is: {}".format(config.onnxruntime_enabled())) # True

# 开启ONNXRuntime优化
config.enable_ort_optimization();

# 设置 ONNXRuntime 算子计算线程数为 10
config.set_cpu_math_library_num_threads(10)

# 禁用 ONNXRuntime 进行预测
config.DisableONNXRuntime();
# 通过 API 获取 ONNXRuntime 信息

print("Use ONNXRuntime is: {}".format(config.onnxruntime_enabled())) # false

在x86 CPU上部署BF16模型

概述

bfloat16 (Brain float Point, BF16)浮点格式是一种计算机内存中占用16位的计算机数字格式。该格式是32位IEEE 754单精度浮点格式(float32)的截断(16位)版本,它保留了符号位的1位,指数部分的8位和尾数部分的7位,舍弃了尾数部分不重要的后16位尾数(在float32中,尾数是23位)。Bfloat16用于降低存储需求,提高机器学习算法的计算速度。关于bfloat16数据类型的更多细节可以在这里找到。目前,X86 CPU bfloat16已在PaddlePaddle推理中支持,能提升推理性能。

使用BF16部署模型的步骤:

安装Paddle

参考Paddle官网Inference章节,安装Paddle Inference最新CPU版本(如果已经安装Paddle训练框架,无需在安装Paddle Inference)。

检查机器
  • 可以通过在命令行输入lscpu查看本机支持指令。

  • 在Intel支持avx512_bf16指令的机型上,(目前Cooper Lake机型支持avx512_bf16,如Intel(R) Xeon(R) Platinum 8371HC CPU, Intel(R) d on(R) Gold 6348H CPU),bfloat16性能会获得如上表的性能提升。Cooper Lake机型列表

  • 在Intel支持avx512bwavx512vlavx512dq指令但是不支持avx512_bf16的机型上,如:SkyLake, CasCade Lake等,可以顺利运行不报错,但是性能无法达到下面的benchmark性能。

  • 为了防止在非配套机器上测试bfloat16功能,应进行适当检查:

Python
import paddle
paddle.fluid.core.supports_bfloat16() // 如果为true, bf16可以顺利运行不报错,性能未知。
paddle.fluid.core.supports_bfloat16_fast_performance() // 如果为true, bf16可以顺利运行,且可获得上表所示的性能。

c++
#include "paddle/fluid/platform/cpu_info.h"
platform::MayIUse(platform::cpu_isa_t::avx512_core) // 如果为true, bf16可以顺利运行不报错,性能未知。
platform::MayIUse(platform::cpu_isa_t::avx512_bf16) // 如果为true, bf16可以顺利运行,且可获得上表所示的性能。
预测部署

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

请注意:

  • 在X86 CPU预测端部署FP16模型,必须开启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(true);
config.SetCpuMathLibraryNumThreads(FLAGS_threads);
// 将所可转为BF16的op转为BF16
config.EnableMkldnnBfloat16();
// 如果您想自己决定要替换哪些操作符,可以使用SetBfloat16Op选项
//config.SetBfloat16Op({“conv2d”、“pool2d”})

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.switch_ir_optim(True)
config.set_cpu_math_library_num_threads(args.threads)
config.enable_mkldnn_bfloat16()
# 如果您想自己决定要替换哪些操作符,可以使用set_bfloat16_op选项
# config.set_bfloat16_op({"conv2d", "pool2d"})
predictor = create_predictor(config)
性能benchmark
图像分类和自然语言处理模型bfloat16在Intel(R)机型上预测的精度和性能

图像分类模型在 Intel(R) Xeon(R) Platinum 8371HC CPU @ 3.30GHz 上预测的精度和性能

Full dataset BF16 fps improvement compared to MKLDNN FP32 TOP1 acc MKLDNN FP32 TOP1 acc MKLDNN BF16 TOP1 acc drop
resnet50 1.85x 0.7663 0.7656 0.00091
googlenet 1.61x 0.705 0.7049 0.00014
mobilenetV1 1.71x 0.7078 0.7071 0.00099
mobilenetV2 1.52x 0.719 0.7171 0.00264

Note: Clas models batch_size=1 nr_threads=1

自然语言处理模型在 Intel(R) Xeon(R) Platinum 8371HC CPU @ 3.30GHz 上预测的精度和性能

GRU Accuracy FP32 BF16 diff
Precision 0.89211 0.89225 -0.00014
Recall 0.89442 0.89457 -0.00015
F1 score 0.89326 0.89341 -0.00015
GRU Performance (QPS) Naive FP32 FP32 BF16 (BF16/FP32)
thread = 1 2794.97 2700.45 4210.27 1.56x
thread = 4 3076.66 4756.45 6186.94 1.30x

Note: Gru model batch size = 50 iterations = 160

X86 CPU 上部署量化模型

概述

本文主要介绍在X86 CPU部署PaddleSlim产出的量化模型。

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

产出量化模型

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的组合。

转换量化模型

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

安装Paddle

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

准备脚本

下载脚本到本地.

wget https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/fluid/contrib/slim/tests/save_quant_model.py
转换量化模型

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

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

save_quant_model.py脚本的参数说明:

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

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

部署量化模型
检查机器
  • 大家可以通过在命令行输入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++ 修改如下:

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(true);
config.SetCpuMathLibraryNumThreads(FLAGS_threads);

auto predictor = paddle_infer::CreatePredictor(config);

Python 修改如下:

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

predictor = create_predictor(config)
性能benchmark

对于常见图像分类模型,在Casecade Lake机器上(例如Intel® Xeon® Gold 6271、6248,X2XX等),图片分类模型INT8模型预测性能可达FP32模型的3-3.7倍, 自然语言处理模型INT8模型预测性能可达到FP32的1.5-3倍;在SkyLake机器上(例如Intel® Xeon® Gold 6148、8180,X1XX等),图片分类INT8模型预测性能可达FP32模型的1.5倍左右。

图像分类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
  • Accuracy: 准确率

  • images/s: 每秒推理的图片数量

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

自然语言处理INT8模型 Ernie, GRU, LSTM 模型在 Xeon(R) 6271 上的性能

Ernie Latency FP32 Latency (ms) INT8 Latency (ms) Ratio (FP32/INT8)
Ernie 1 thread 237.21 79.26 2.99X
Ernie 20 threads 22.08 12.57 1.76X
GRU Performance (QPS) Naive FP32 INT8 Int8/Native FP32
GRU bs 1, thread 1 1108 1393 1.26
GRU repeat 1, bs 50, thread 1 2175 3199 1.47
GRU repeat 10, bs 50, thread 1 2165 3334 1.54
LSTM Performance (QPS) FP32 INT8 INT8 /FP32
LSTM 1 thread 4895.65 7190.55 1.47
LSTM 4 threads 6370.86 7942.51 1.25
  • ms: 毫秒

  • QPS: 每秒执行的推理次数

自然语言处理INT8模型 Ernie, GRU, LSTM 模型在 Xeon(R) 6271 上的精度

Ernie FP32 Accuracy INT8 Accuracy Accuracy Diff
accuracy 80.20% 79.44% -0.76%
LAC (GRU) FP32 INT8 Accuracy diff
accuracy 0.89326 0.89323 -0.00007
LSTM FP32 INT8
HX_ACC 0.933 0.925
CTC_ACC 0.999 1.000

Note:

NVIDIA-GPU部署

Paddle Inference 支持通过以下两种方式在Nvidia-GPU上运行推理任务:

(1)GPU 原生推理 : PaddlePaddle 深度学习开源框架中存在大量用 CUDA 实现的算子,如果您选择用 GPU 原生推理,那么用于部署的模型会在一系列内部优化之后直接调用这些原生算子实现;

(2)GPU TensorRT 加速推理 : TensorRT 是一个针对 NVIDIA GPU 及 Jetson 系列硬件的高性能机器学习推理 SDK,可以使得深度学习模型在这些硬件上的部署获得更好的性能。Paddle Inference 采用子图的方式对 TensorRT 进行了集成,即我们可以使用该模块来提升 Paddle Inference 的推理性能。TensorRT 接入方式支持 fp32,fp16,int8 精度的推理,除了 CUDA 和 cuDNN,使用前您还需确保您的机器上已经安装了 TensoRT 。这通常会带给您比 GPU 原生推理更好的性能;

Note

目前Paddle Inference可以支持绝大多数的 NVIDIA-GPU平台,如果您不确定您的GPU平台是否被支持,请查阅本篇文档附录中的硬件支持列表

本章节为您详细介绍了如何实现基于原生推理和TensorRT推理,并提供了可参考的示例代码,主要分为以下三个部分:

GPU 原生推理 : 介绍如何使用 GPU 原生推理将模型部署在 GPU 硬件上,包括根据示例代码介绍 Paddle Inference C++/Python API 的使用流程,如何安装 Paddle Inference 推理库,以及如何在 Ubuntu、Windows 等操作系统上编译和执行示例代码。

Note

GPU 原生推理仅支持 fp32,fp16 精度目前处于实验阶段。使用前,您需要确保您的机器上已经安装了 CUDA 和 cuDNN。

GPU TensorRT 加速推理(NV-GPU/Jetson) : 介绍如何使用 TensorRT 加速模型推理,根据示例代码介绍启用 TensorRT 加速的 API、 保存优化后的模型降低首帧耗时、支持动态 shape 的 API 等内容。还会介绍 Paddle Inference 接入 TensorRT 的原理。

GPU TensorRT 低精度或量化推理 : 介绍 Paddle Inference 使用 TensorRT情况下,实现对低精度和量化推理的支持。

GPU 原生推理

使用 GPU 原生推理前必须确保您的机器上已经安装了 CUDA 和 cuDNN,且需要知道它们的安装位置。

使用 PaddlePaddle 训练结束后,得到推理模型,可以用于推理部署。 本文准备了 resnet50 推理模型,可以从链接下载,或者 wget 下载。

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

下面分别介绍在 Linux/Ubuntu 操作系统下和 Windows 操作系统下用 GPU 原生推理的 C++ 和 Python 示例。

1. C++ 示例

C++ 示例代码在链接,你可以将此仓库全部克隆下来留着使用。下面从先介绍 Paddle Inference C++ API 的使用流程,然后介绍在 Linux/Ubuntu 系统下和 Windows 系统下编译和执行此示例代码。

使用 Paddle Inference C++ API 的典型过程包含下面六个步骤。 完整代码在示例中的resnet50_test.cc

(1) 包含头文件

使用 Paddle Inference 推理库,只需包含 paddle_inference_api.h

#include "paddle/include/paddle_inference_api.h"

(2) 设置 Config

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

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

paddle_infer::Config config;
config.SetModel(FLAGS_model_file, FLAGS_params_file); // Load combined model
config.EnableUseGpu(500, 0);
config.SwitchIrOptim(true);
config.EnableMemoryOptim();
config.EnableTensorRtEngine(1 << 30, FLAGS_batch_size, 10, PrecisionType::kFloat32, false, false);

如果您不想使用 TensorRT 加速,仅想用 GPU 原生推理,请注释掉config.EnableTensorRtEngine(); 这行。

(3) 创建 Predictor

std::shared_ptr<paddle_infer::Predictor> predictor = paddle_infer::CreatePredictor(config);

(4) 设置输入

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

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());

(5) 执行Predictor

predictor->Run();

(6) 获取输出

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());

Linux/Ubuntu 部署示例

请参考下载安装 Ubuntu 推理库下载 C++ 推理库,名称中带有 cuda 的为用于 GPU 的推理库。以 manylinux_cuda11.2_cudnn8.2_avx_mkl_trt8_gcc8.2为例,它要求您的系统上安装 CUDA 版本为11.2,cuDNN 版本为8.2, TensorRT 版本为8.x, gcc 版本为8.2,当然,如果您的上述版本不能完全对应,那么或许也是可以的。注意,如果您的机器上没有安装 TensorRT,您仍然可以下载这个库,只不过模型就只能用 GPU 原生推理,而不能使用 TensorRT 加速。

下面介绍示例代码在 Linux/Ubuntu 下的编译和执行。 您需要关心下面四个文件即可,首先进入 C++ 示例代码的目录。 文件resnet50_test.cc 为推理的样例程序(程序中的输入为固定值,如果您有opencv或其他方式进行数据读取的需求,需要对程序进行一定修改)。文件Paddle-Inference-Demo/c++/lib 为编译构建文件, 脚本 compile.sh 为编译脚本,它将复制Paddle-Inference-Demo/c++/lib到当前目录,并编译生成可执行文件。 脚本run.sh 下载模型,并运行可执行程序。

先要把您下载好的 Paddle Inference 推理库放到Paddle-Inference-Demo/c++/lib中,然后在 compile.sh 里面进行如下设置。 如果您不想使用 TensorRT 或机器上没有安装 TensorRT,那您要记得把USE_TENSORRT置为OFFLIB_DIR就是您的 Paddle Inference 推理库的放置路径,CUDNN_LIBCUDA_LIBTENSORRT_ROOT分别为您的 cuDNN 的库路径,CUDA 的库路径,以及 TensorRT 的根目录。

WITH_MKL=ON
WITH_GPU=ON
USE_TENSORRT=OFF

LIB_DIR=${work_path}/../../lib/paddle_inference
CUDNN_LIB=/usr/lib/x86_64-linux-gnu/
CUDA_LIB=/usr/local/cuda/lib64
TENSORRT_ROOT=/usr/local/TensorRT-7.1.3.4

最后只需两个命令即可完成编译和执行。

bash compile.sh
bash run.sh

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

Windows 部署示例

请参考下载安装 Windows 推理库下载 C++ 推理库。

在 Windows上 部署示例的话,您需要下面几个图形界面的操作,此时您只需要关注两个文件即可。

文件resnet50_test.cc 为推理的样例程序(程序中的输入为固定值,如果您有opencv或其他方式进行数据读取的需求,需要对程序进行一定的修改)。文件Paddle-Inference-Demo/c++/lib/CMakeLists.txt 为编译构建文件,请把它手动复制到和resnet50_test.cc相同目录。

打开 cmake-gui 程序生成vs工程:

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

_images/win_x86_cpu_cmake_11.pngwin_x86_cpu_cmake_1

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

_images/win_x86_cpu_cmake_21.pngwin_x86_cpu_cmake_2

  • 设置 CMake Options,点击 Add Entry,新增 PADDLE_LIB、CMAKE_BUILD_TYPE、DEMO_NAME等选项。具体配置项如下图所示,其中 PADDLE_LIB 为您下载的推理库路径,DEMO_NAME 设置为.cc文件的文件名。

_images/win_x86_cpu_cmake_31.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_11.pngwin_x86_cpu_vs_1

运行示例

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

_images/win_x86_cpu_vs_21.pngwin_x86_cpu_vs_2

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

_images/win_x86_cpu_vs_31.pngwin_x86_cpu_vs_3

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

_images/win_x86_cpu_vs_41.pngwin_x86_cpu_vs_4

2. Python 示例

请从下载安装 Ubuntu 推理库下载 Python whl 包并安装,名称中带有 cuda 的为用于 GPU 的推理库。

此示例需要您在 Python 里安装 opencv,命令为python -m pip install opencv-python

Python 示例代码在链接,下面从先介绍 Paddle Inference Python API 的使用流程,然后介绍在 Linux/Ubuntu 系统下和 Windows 系统下编译和执行此示例代码。

使用 Paddle Inference Python API 的典型过程包含下面六个步骤。 完整代码在示例中的infer_resnet.py

(1) Python 导入

from paddle.inference import Config
from paddle.inference import create_predictor
from paddle.inference import PrecisionType

(2)设置 Config

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

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

# args 是解析的输入参数
# Init config
config = Config(args.model_file, args.params_file)
config.enable_use_gpu(500, 0)
config.switch_ir_optim()
config.enable_memory_optim()
config.enable_tensorrt_engine(workspace_size=1 << 30, precision_mode=PrecisionType.Float32,max_batch_size=1, min_subgraph_size=5, use_static=True, use_calib_mode=False)

如果您不想使用 TensorRT 加速,仅想使用 GPU 原生推理,请注释掉config.enable_tensorrt_engine(); 这行。

(3)创建Predictor

# Create predictor
predictor = create_predictor(config)

(4) 设置输入

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

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())

(5) 执行 Predictor

predictor.run()

(6) 获取输出

output_names = predictor.get_output_names()
output_tensor = predictor.get_output_handle(output_names[0])
output_data = output_tensor.copy_to_cpu()

下面介绍请参考示例代码的编译和执行。 您需要关心如下三个文件,首先进入 Python 示例代码的目录。 文件img_preprocess.py是对图像进行预处理。 文件infer_resnet.py是示例程序。 脚本 run.sh负责下载模型和执行示例程序。

在 Linux/Ubuntu 下您只需要执行bash run.sh,就可以看到程序被执行。运行结束后,程序会将模型结果打印到屏幕,说明运行成功。 Windows 下,您需要手动下载模型,然后执行run.sh 里面的 Python 命令即可。

GPU TensorRT 加速推理(NV-GPU/Jetson)

1. 概要

TensorRT 是一个针对 NVIDIA GPU 及 Jetson 系列硬件的高性能机器学习推理 SDK,可以使得深度学习模型在这些硬件上的部署获得更好的性能。Paddle Inference 以子图方式集成了 TensorRT,将可用 TensorRT 加速的算子组成子图供给 TensorRT,以获取 TensorRT 加速的同时,保留 PaddlePaddle 即训即推的能力。在这篇文章中,我们会介绍如何使用 TensorRT 加速推理。

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

目前 Paddle Inference 支持 TensorRT 的静态 shape、动态 shape 两种运行方式。静态 shape 用于模型输入 shape 除 batch 维外,其他维度大小不变的情况,静态 shape 模式下支持图像分类,分割,检测模型;动态 shape 可用于输入 size 任意变化的模型, 如动态 shape 的图像模型(FCN, Faster rcnn)、 NLP 的 Bert/Ernie 等模型,当然也包括静态 shape 支持的模型。 静态 shape 和动态 shape 都支持fp32、fp16、int8 等多种计算精度。TensorRT 支持服务器端GPU,如T4、A10, 也支持边缘端硬件,如 Jetson NX、 Jetson Nano、 Jetson TX2 等。 在边缘硬件上,除支持常规的 GPU 外,还可用 DLA 进行推理,也支持 RTX2080,3090 等游戏显卡。

用 TensorRT 首次推理时,TensorRT 需要进行各 Op 融合、显存复用、以及 Op 的 Kernel 选择等,导致首帧耗时过长。Paddle Inference 开放了 TensorRT 序列化接口,用于将 TensorRT 分析的信息进行存储,在后续推理直接载入相关序列化信息,从而减少启动耗时。

2. 环境准备

在 GPU 下使用 TensorRT 加速推理,需要安装 CUDA、cuDNN、TensorRT 和对应版本的 Paddle Inference 预编译包。 关于这几个软件的安装版本,请参考如下建议(原因:CUDA、cuDNN、TensorRT 版本众多,且有严格的版本对应关系):

  • 电脑上 CUDA、cuDNN、TensorRT 都还没安装的开发者,建议参考 Paddle Inference 提供的预编译包信息,去安装对应版本的CUDA、cuDNN、TensorRT。

  • 电脑上已安装 CUDA、cuDNN,但没有安装TensorRT,建议参考Paddle Inference提供的cuda、cudnn的对应版本的TensorRT版本去安装TensorRT。

  • 电脑上已安装 CUDA、cuDNN、TensorRT的开发者,去下载对应版本的 Paddle Inference 预编译包。

    • 如果 Paddle Inference 预编译包没有对应版本的,一种方式是按照 Paddle Inference 提供的预编译包信息重新安装CUDA、cuDNN、TensorRT,一种是自己源码编译对对应电脑上 CUDA、cuDNN、TensorRT 版本的 Paddle Inference 预编译包。从工程难易程度,建议选择第一种方案。

如果您需要安装 TensorRT,请参考 TensorRT 文档

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

Note:

  1. 从源码编译支持 TensorRT 加速的 Paddle Infenrence 推理库时,你需要设置编译选项 TENSORRT_ROOT 为 TensorRT SDK 的根目录。

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

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

3, API 使用介绍

上一节中,我们了解到 Paddle Inference 推理流程包含了以下六步:

  • 导入包

  • 设置 Config

  • 创建 Predictor

  • 准备输入

  • 执行 Predictor

  • 获取输出

Paddle Inference 中启用 TensorRT 也是遵照这样的流程。我们先用一个简单的例子来介绍这一流程(我们假设您已经对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 Inference 是以以子图的形式接入 TensorRT 的,为了避免性能损失,当子图内部节点个数大于 min_subgraph_size 的时候,才会将此子图接入 TensorRT 运行。

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

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

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

对于 Jetson 系列硬件上,除了可将模型运行在 GPU 上,还可将模型运行在 DLA 上,DLA 是一款针对深度学习操作的固定功能加速器引擎,旨在对卷积神经网络进行全硬件加速。Paddle Inference 开放了启用指定的 DLA 进行模型推理的接口,默认启动第 0 个 DLA:

// python API
config.enable_tensorrt_dla(0)
// C++ API
config.EnableTensorRtDLA(0);

DLA 上对运行的模型有一定要求,详情请可参考链接。当 DLA 遇到模型中的某些不支持的层时,会回退到 GPU 进行推理,目前 DLA 仅支持fp16 和 int8 精度。

4. 运行 Dynamic shape

当模型的输入 shape 不固定的话(如 OCR,NLP 的相关模型),需要推理框架提供动态 shape 的支持。从1.8 版本开始, Paddle Inference 对 TensorRT 子图进行了 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 的接口。“image” 对应模型文件中输入的名称。

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

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

此接口的相关示例请参考下面链接。

Paddle Inference 还提供了另外一份使用动态 shape 方法,此接口不用明确指出输入 shape 范围,但需要准备一些数据来运行模型,以便于收集模型中 Tensor 的大小,使用接口如下:

    if args.tune:
        config.collect_shape_range_info(shape_file)
    if args.use_gpu:
        config.enable_use_gpu(1000, 0)
        if args.use_trt:
            # using dynamic shpae mode, the max_batch_size will be ignored.
            config.enable_tensorrt_engine(
                workspace_size=1 << 30,
                max_batch_size=1,
                min_subgraph_size=5,
                precision_mode=PrecisionType.Float32,
                use_static=False,
                use_calib_mode=False)
            if args.tuned_dynamic_shape:
                config.enable_tuned_tensorrt_dynamic_shape(shape_file, True)

首次运行时,需设置 args.tuneTrue,此时运行模型将会不做任何优化,collect_shape_range_info 将模型中间 Tensor 的所有 shape 保存到文件 shape_file 中。

第二次运行时,需设置 args.tuneFalse,同时设置args.tuned_dynamic_shape 为 True,此时运行模型将会根据生成的 shape_file 进行模型优化。

此接口的相关示例请参考下面链接。

  • Python 样例请访问此处链接

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

5. Paddle Inference 适配 TensorRT 原理介绍

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

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

原始网络

转换的网络

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

GPU TensorRT 低精度或量化推理

深度学习模型训练好之后,其权重参数在一定程度上是冗余的,在很多任务上,我们可以采用低精度或量化进行模型推理而不影响模型精度。这一方面可以减少访存、提升计算效率,另一方面,可以降低显存占用。Paddle Inference 的 GPU 原生推理仅支持 Fp32,Fp16 精度目前处于实验阶段;采用 TensorRT 加速推理的方式可支持 Fp32、Fp16 以及 Int8 量化推理。使用前,请参考链接确保您的 GPU 硬件支持您使用的精度。

1. Fp16 推理

为使用 Fp16 带来的性能提升,只需在指定 TensorRT 配置时,将 precision_mode 设为 paddle_infer.PrecisionType.Half即可,示例如下:

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

2. Int8 量化推理

使用 Int8 量化推理的流程可以分为两步:(1)产出量化模型。(2)加载量化模型进行推理。下面我们对使用Paddle Inference 进行 Int8 量化推理的完整流程进行详细介绍。

1. 产出量化模型

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

a. 使用 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 张左右的真实输入数据,在上述配置下,运行模型。( TensorRT 会统计模型中每个 tensor 值的范围信息,并将其记录到校准表中,运行结束后,会将校准表写入模型目录下的 _opt_cache 目录中)。

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

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

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

2. 加载量化模型进行Int8推理

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

若使用的量化模型为 TensorRT 离线量化校准产出的,需要将 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)

若使用的量化模型为 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)

Int8 量化推理的完整 demo 请参考链接

附录:硬件支持列表

Paddle Inference 对 NVIDIA GPU 的支持能力请参考下表:

GPU架构

Compute Capability

对应GPU硬件型号

请下载以下CUDA版本的飞桨安装包

Fermi

sm_20

GeForce 400、 500、 600、 GT-630

不支持

Kepler

sm_30

GeForce 700、 GT-730

不支持

Kepler

sm_35

Tesla K40

CUDA10

Kepler

sm_37

Tesla K80

CUDA10

Maxwell

sm_50

Tesla/Quadro M series

CUDA10、CUDA11

Maxwell

sm_52

Quadro M6000 、 GeForce 900、 GTX-970、 GTX-980、 GTX Titan X

CUDA10、CUDA11

Pascal

sm_60

Quadro GP100、Tesla P100

CUDA10、CUDA11

Pascal

sm_61

GTX 1080、 GTX 1070、 GTX 1060、 GTX 1050、 GTX 1030 (GP108)、 GT 1010 (GP108) Titan Xp、 Tesla P40、 Tesla P4

CUDA10、CUDA11

Volta

sm_70

DGX-1 with Volta、 Tesla V100、 GTX 1180 (GV104)、 Titan V、 Quadro GV100

CUDA10、CUDA11

Turing

sm_75

GTX/RTX Turing _ GTX 1660 Ti、 RTX 2060、 RTX 2070、 RTX 2080、 Titan RTX、 Quadro RTX 4000、 Quadro RTX 5000、 Quadro RTX 6000、 Quadro RTX 8000、 Quadro T1000/T2000、 Tesla T4

CUDA10、CUDA11

Ampere

sm_80

NVIDIA A100、 GA100、 NVIDIA DGX-A100

CUDA11

Ampere

sm_86

Tesla GA10x cards、 RTX Ampere _ RTX 3080、 GA102 _ RTX 3090、 RTX A2000、 A3000、 RTX A4000、 A5000、 A6000、 NVIDIA A40、 GA106 _ RTX 3060、 GA104 _ RTX 3070、 GA107 _ RTX 3050、 RTX A10、 RTX A16、 RTX A40、 A2 Tensor Core GPU

CUDA11、CUDA11.2(推荐)

Paddle Inference 对 NVIDIA Jetson平台 的支持能力请参考下表:

GPU架构

Compute Capability

对应Jetson硬件型号

Maxwell

SM53

CTegra (Jetson) TX1 / Tegra X1C /Jetson Nano

Pascal

SM62

Tegra (Jetson) TX2

Volta

SM72

Jetson AGX Xavier

其他硬件部署

Paddle Inference支持在非常多的硬件平台上进行推理,如果您使用的是x86 CPU或NVIDIA GPU以外的其他硬件平台,您可以在下表中查询您使用的硬件平台是否被Paddle Inference支持;

点击 安装指引与示例 一列中的对应链接即可跳转到对应硬件平台的说明文档,在文档中我们提供了详细的环境配置流程和开发引导供您参考。

Note

此处列举的硬件型号为经过验证测试的型号,在未列举的其他型号上安装Paddle Inference可能遇到兼容性问题

分类

架构

公司

型号

安装指引与示例

服务端CPU

ARM

飞腾

FT-2000+/64、S2500/64

在飞腾 CPU 芯片上安装和使用

服务端CPU

ARM

华为

鲲鹏 920 2426SK

在鲲鹏 CPU 芯片上安装和使用

服务端CPU

MIPS

龙芯

龙芯 3A4000、3A5000、3C5000L

在龙芯 CPU 芯片上安装和使用

服务端CPU

x86

兆芯

全系列 CPU

在兆芯 CPU 芯片上安装和使用

服务端CPU

SW

申威

申威 SW6A、SW6B

在申威 CPU 芯片上安装和使用

服务端GPU

AMD

AMD GPU MI100

在 AMD GPU 芯片上安装和使用

AI加速芯片

海光

海光 DCU Z100

在海光 DCU 芯片上安装和使用

AI加速芯片

XPU

昆仑

昆仑 K200、R200等

在昆仑 XPU 芯片上安装和使用

AI加速芯片

达芬奇

华为

昇腾910

在昇腾 NPU 芯片上安装和使用

AI加速芯片

Graphcore

Colossus MK2 GC200 IPU

在Graphcore IPU 芯片上安装和使用

飞腾/鲲鹏 CPU 安装说明

Paddle Inference 支持基于飞腾/鲲鹏 CPU 的推理部署, 当前仅支持通过源码编译的方式安装。

系统要求

当前 Paddle Inference 支持飞腾/鲲鹏 CPU 在如下环境下的源码编译和安装部署:

处理器 操作系统
FT-2000+/64、S2500/64 麒麟 V10,统信 UOS
鲲鹏 920 2426SK 麒麟 V10,统信 UOS
源码编译

环境准备: 请根据编译依赖表准备符合版本要求的依赖库,推荐使用飞桨官方镜像,否则请参考麒麟技术文档统信生态社区

第一步: 从飞桨镜像库拉取编译镜像,该镜像基于麒麟 V10 操作系统构建

# 拉取镜像
docker pull registry.baidubce.com/device/paddle-cpu:kylinv10-aarch64-gcc73

# 启动容器
docker run -it --name paddle-dev -v `pwd`:/workspace \
     --network=host --shm-size=128G --workdir=/workspace \
     --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
     registry.baidubce.com/device/paddle-cpu:kylinv10-aarch64-gcc73 /bin/bash

第二步: 下载 Paddle 源码并编译,CMAKE 编译选项含义请参见编译选项表

# 下载源码,默认 develop 分支
git clone https://github.com/PaddlePaddle/Paddle.git
cd Paddle

# 创建编译目录
mkdir build && cd build

# 执行cmake
cmake .. -DPY_VERSION=3 -DPYTHON_EXECUTABLE=`which python3` -DWITH_ARM=ON \
         -DWITH_TESTING=OFF -DON_INFER=ON -DWITH_XBYAK=OFF \
         -DCMAKE_CXX_FLAGS="-Wno-error -w"

# 使用以下命令来编译
make TARGET=ARMV8 -j$(nproc)

第三步: 编译完成之后,请检查编译目录下的 Python whl 包 和 C++ 预测库是否正确生成

# 检查编译目录下的 Python whl 包
Paddle/build/python/dist/
└── paddlepaddle-0.0.0-cp37-cp37m-linux_aarch64.whl

# 检查编译目录下的 C++ 预测库,目录结构如下
Paddle/build/paddle_inference_install_dir
├── CMakeCache.txt
├── paddle
│   ├── include                                    # C++ 预测库头文件目录
│   │   ├── crypto
│   │   ├── experimental
│   │   ├── internal
│   │   ├── paddle_analysis_config.h
│   │   ├── paddle_api.h
│   │   ├── paddle_infer_contrib.h
│   │   ├── paddle_infer_declare.h
│   │   ├── paddle_inference_api.h                 # C++ 预测库头文件
│   │   ├── paddle_mkldnn_quantizer_config.h
│   │   ├── paddle_pass_builder.h
│   │   └── paddle_tensor.h
│   └── lib
│       ├── libpaddle_inference.a                  # C++ 静态预测库文件
│       └── libpaddle_inference.so                 # C++ 动态态预测库文件
├── third_party
│   ├── install                                    # 第三方链接库和头文件
│   │   ├── cryptopp
│   │   ├── gflags
│   │   ├── glog
│   │   ├── openblas
│   │   ├── protobuf
│   │   ├── utf8proc
│   │   └── xxhash
│   └── threadpool
│       └── ThreadPool.h
└── version.txt                                    # 预测库版本信息
安装部署
Python 安装部署

请参考以下步骤执行 Python 安装部署示例程序:

# 1) 安装源码编译生成的 Python whl 包
python3 -m pip install -U paddlepaddle-0.0.0-cp37-cp37m-linux_aarch64.whl

# 2) 进行简单功能的健康检查
python3 -c "import paddle; paddle.utils.run_check()"
# 预期得到如下输出结果
# Running verify PaddlePaddle program ...
# PaddlePaddle works well on 1 CPU.
# PaddlePaddle works well on 2 CPUs.
# PaddlePaddle is installed successfully! Let's start deep learning with PaddlePaddle now.

# 3) 下载 Paddle-Inference-Demo 示例代码,并进入 Python 代码目录
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git
cd Paddle-Inference-Demo/python/cpu/resnet50

# 4) 下载推理模型
wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar xzf resnet50.tgz

# 5) 准备预测图片
wget https://paddle-inference-dist.bj.bcebos.com/inference_demo/python/resnet50/ILSVRC2012_val_00000247.jpeg

# 6) 运行 Python 预测程序
python3 infer_resnet.py --model_file=./resnet50/inference.pdmodel --params_file=./resnet50/inference.pdiparams
# 预期得到如下输出结果
# class index:  13
C++ 安装部署

请参考以下步骤执行 C++ 安装部署示例程序:

# 1) 下载 Paddle-Inference-Demo 代码
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git

# 2) 拷贝源码编译生成的 C++ 预测库到 Paddle-Inference-Demo/c++/lib 目录下
cp -r Paddle/build/paddle_inference_install_dir Paddle-Inference-Demo/c++/lib/paddle_inference
# 拷贝完成之后 Paddle-Inference-Demo/c++/lib 目录结构如下
Paddle-Inference-Demo/c++/lib/
├── CMakeLists.txt
└── paddle_inference
    ├── CMakeCache.txt
    ├── paddle
    ├── third_party
    └── version.txt

# 3) 进入 C++ 示例代码目录,下载推理模型
cd Paddle-Inference-Demo/c++/cpu/resnet50/
wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar xzf resnet50.tgz

# 4) 修改 compile.sh 编译文件,需根据 C++ 预测库的 version.txt 信息对以下的几处内容进行修改
WITH_MKL=OFF
WITH_GPU=OFF
WITH_ARM=ON

# 5) 执行编译,编译完成之后在 build 下生成 resnet50_test 可执行文件
./compile.sh

# 6) 运行 C++ 预测程序
./build/resnet50_test --model_file resnet50/inference.pdmodel --params_file resnet50/inference.pdiparams
# 预期得到如下输出结果
# I0525 11:07:28.354579 40116 resnet50_test.cc:76] run avg time is 713.049 ms
# I0525 11:07:28.354732 40116 resnet50_test.cc:113] 0 : 8.76171e-29
# I0525 11:07:28.354772 40116 resnet50_test.cc:113] 100 : 8.76171e-29
# ... ...
# I0525 11:07:28.354880 40116 resnet50_test.cc:113] 800 : 3.85244e-25
# I0525 11:07:28.354895 40116 resnet50_test.cc:113] 900 : 8.76171e-29
如何卸载

C++ 预测库无需卸载,Python whl 包请使用以下命令卸载:

python3 -m pip uninstall paddlepaddle

申威 CPU 安装说明

Paddle Inference 支持基于申威 CPU 的推理部署, 当前仅支持通过源码编译的方式安装。

系统要求

当前 Paddle Inference 支持申威 CPU 在如下环境下的源码编译和安装部署:

处理器 操作系统
申威 SW6A、SW6B 麒麟 V10,普华 iSoft Linux 5
源码编译

环境准备: 请根据编译依赖表准备符合版本要求的依赖库,推荐使用飞桨官方镜像,否则请参考麒麟技术文档普华操作系统

第一步: 从飞桨镜像库拉取编译镜像,该镜像基于麒麟 V10 操作系统构建

# 拉取镜像
docker pull registry.baidubce.com/device/paddle-cpu:kylinv10-sw64-gcc83

# 启动容器
docker run -it --name paddle-dev -v `pwd`:/workspace \
     --network=host --shm-size=128G --workdir=/workspace \
     --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
     registry.baidubce.com/device/paddle-cpu:kylinv10-sw64-gcc83 /bin/bash

第二步: 下载 Paddle 源码并编译,CMAKE 编译选项含义请参见编译选项表

# 下载源码,默认 develop 分支
git clone https://github.com/PaddlePaddle/Paddle.git
cd Paddle

# 创建编译目录
mkdir build && cd build

# 执行cmake
cmake .. -DPY_VERSION=3 -DPYTHON_EXECUTABLE=`which python3` -DWITH_SW=ON \
         -DWITH_TESTING=OFF -DON_INFER=ON -DWITH_XBYAK=OFF \
         -DCMAKE_CXX_FLAGS="-Wno-error -w"

# 使用以下命令来编译
make -j$(nproc)

第三步: 编译完成之后,请检查编译目录下的 Python whl 包 和 C++ 预测库是否正确生成

# 检查编译目录下的 Python whl 包
Paddle/build/python/dist/
└── paddlepaddle-0.0.0-cp37-cp37m-linux_sw_64.whl

# 检查编译目录下的 C++ 预测库,目录结构如下
Paddle/build/paddle_inference_install_dir
├── CMakeCache.txt
├── paddle
│   ├── include                                    # C++ 预测库头文件目录
│   │   ├── crypto
│   │   ├── experimental
│   │   ├── internal
│   │   ├── paddle_analysis_config.h
│   │   ├── paddle_api.h
│   │   ├── paddle_infer_contrib.h
│   │   ├── paddle_infer_declare.h
│   │   ├── paddle_inference_api.h                 # C++ 预测库头文件
│   │   ├── paddle_mkldnn_quantizer_config.h
│   │   ├── paddle_pass_builder.h
│   │   └── paddle_tensor.h
│   └── lib
│       ├── libpaddle_inference.a                  # C++ 静态预测库文件
│       └── libpaddle_inference.so                 # C++ 动态态预测库文件
├── third_party
│   ├── install                                    # 第三方链接库和头文件
│   │   ├── cryptopp
│   │   ├── gflags
│   │   ├── glog
│   │   ├── protobuf
│   │   ├── utf8proc
│   │   └── xxhash
│   └── threadpool
│       └── ThreadPool.h
└── version.txt                                    # 预测库版本信息
安装部署
Python 安装部署

请参考以下步骤执行 Python 安装部署示例程序:

# 1) 安装源码编译生成的 Python whl 包
python3 -m pip install -U paddlepaddle-0.0.0-cp37-cp37m-linux_sw_64.whl

# 2) 进行简单功能的健康检查
python3 -c "import paddle; paddle.utils.run_check()"
# 预期得到如下输出结果
# Running verify PaddlePaddle program ...
# PaddlePaddle works well on 1 CPU.
# PaddlePaddle works well on 2 CPUs.
# PaddlePaddle is installed successfully! Let's start deep learning with PaddlePaddle now.

# 3) 下载 Paddle-Inference-Demo 示例代码,并进入 Python 代码目录
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git
cd Paddle-Inference-Demo/python/cpu/resnet50

# 4) 下载推理模型
wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar xzf resnet50.tgz

# 5) 准备预测图片
wget https://paddle-inference-dist.bj.bcebos.com/inference_demo/python/resnet50/ILSVRC2012_val_00000247.jpeg

# 6) 运行 Python 预测程序
python3 infer_resnet.py --model_file=./resnet50/inference.pdmodel --params_file=./resnet50/inference.pdiparams
# 预期得到如下输出结果
# class index:  13
C++ 安装部署

请参考以下步骤执行 C++ 安装部署示例程序:

# 1) 下载 Paddle-Inference-Demo 代码
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git

# 2) 拷贝源码编译生成的 C++ 预测库到 Paddle-Inference-Demo/c++/lib 目录下
cp -r Paddle/build/paddle_inference_install_dir Paddle-Inference-Demo/c++/lib/paddle_inference
# 拷贝完成之后 Paddle-Inference-Demo/c++/lib 目录结构如下
Paddle-Inference-Demo/c++/lib/
├── CMakeLists.txt
└── paddle_inference
    ├── CMakeCache.txt
    ├── paddle
    ├── third_party
    └── version.txt

# 3) 进入 C++ 示例代码目录,下载推理模型
cd Paddle-Inference-Demo/c++/cpu/resnet50/
wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar xzf resnet50.tgz

# 4) 修改 compile.sh 编译文件,需根据 C++ 预测库的 version.txt 信息对以下的几处内容进行修改
WITH_MKL=OFF
WITH_GPU=OFF
WITH_SW=ON

# 5) 执行编译,编译完成之后在 build 下生成 resnet50_test 可执行文件
./compile.sh

# 6) 运行 C++ 预测程序
./build/resnet50_test --model_file resnet50/inference.pdmodel --params_file resnet50/inference.pdiparams
# 预期得到如下输出结果
# I0529 08:34:34.277042 37515 resnet50_test.cc:79] run avg time is 3669.86 ms
# I0529 08:34:34.277190 37515 resnet50_test.cc:116] 0 : 8.76168e-29
# ... ...
# I0529 08:34:34.277348 37515 resnet50_test.cc:116] 800 : 3.85252e-25
# I0529 08:34:34.277367 37515 resnet50_test.cc:116] 900 : 8.76168e-29
如何卸载

C++ 预测库无需卸载,Python whl 包请使用以下命令卸载:

python3 -m pip uninstall paddlepaddle

兆芯 CPU 安装说明

Paddle Inference 支持基于兆芯 CPU 的推理部署, 当前仅支持通过源码编译的方式安装。

系统要求

当前 Paddle Inference 支持兆芯 CPU 在如下环境下的源码编译和安装部署:

处理器 操作系统
全系列CPU Linux操作系统 (Ubuntu、CentOS)
源码编译

环境准备: 请根据编译依赖表准备符合版本要求的依赖库,推荐使用飞桨官方镜像。

第一步: 从飞桨镜像库拉取编译镜像并启动容器,该镜像基于 Ubuntu 18.04 操作系统构建

# 拉取镜像
docker pull registry.baidubce.com/device/paddle-cpu:ubuntu18-x86_64-gcc82

# 启动容器
docker run -it --name paddle-dev -v `pwd`:/workspace \
     --network=host --shm-size=128G --workdir=/workspace \
     --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
     registry.baidubce.com/device/paddle-cpu:ubuntu18-x86_64-gcc82 /bin/bash

第二步: 下载 Paddle 源码并编译,CMAKE 编译选项含义请参见编译选项表

# 下载源码,默认 develop 分支
git clone https://github.com/PaddlePaddle/Paddle.git
cd Paddle

# 创建编译目录
mkdir build && cd build

# 执行cmake
cmake .. -DPY_VERSION=3 -DPYTHON_EXECUTABLE=`which python3` \
         -DWITH_TESTING=OFF -DON_INFER=ON -DWITH_XBYAK=OFF

# 使用以下命令来编译
make -j$(nproc)

第三步: 编译完成之后,请检查编译目录下的 Python whl 包 和 C++ 预测库是否正确生成

# 检查编译目录下的 Python whl 包
Paddle/build/python/dist/
└── paddlepaddle-0.0.0-cp37-cp37m-linux_x86_64.whl

# 检查编译目录下的 C++ 预测库,目录结构如下
Paddle/build/paddle_inference_install_dir
├── CMakeCache.txt
├── paddle
│   ├── include                                    # C++ 预测库头文件目录
│   │   ├── crypto
│   │   ├── experimental
│   │   ├── internal
│   │   ├── paddle_analysis_config.h
│   │   ├── paddle_api.h
│   │   ├── paddle_infer_contrib.h
│   │   ├── paddle_infer_declare.h
│   │   ├── paddle_inference_api.h                 # C++ 预测库头文件
│   │   ├── paddle_mkldnn_quantizer_config.h
│   │   ├── paddle_pass_builder.h
│   │   └── paddle_tensor.h
│   └── lib
│       ├── libpaddle_inference.a                  # C++ 静态预测库文件
│       └── libpaddle_inference.so                 # C++ 动态态预测库文件
├── third_party
│   ├── install                                    # 第三方链接库和头文件
│   │   ├── cryptopp
│   │   ├── gflags
│   │   ├── glog
│   │   ├── mkldnn
│   │   ├── mklml
│   │   ├── protobuf
│   │   ├── utf8proc
│   │   └── xxhash
│   └── threadpool
│       └── ThreadPool.h
└── version.txt                                    # 预测库版本信息
安装部署
Python 安装部署

请参考以下步骤执行 Python 安装部署示例程序:

# 1) 安装源码编译生成的 Python whl 包
python3 -m pip install -U paddlepaddle-0.0.0-cp37-cp37m-linux_x86_64.whl

# 2) 进行简单功能的健康检查
python3 -c "import paddle; paddle.utils.run_check()"
# 预期得到如下输出结果
# Running verify PaddlePaddle program ...
# PaddlePaddle works well on 1 CPU.
# PaddlePaddle works well on 2 CPUs.
# PaddlePaddle is installed successfully! Let's start deep learning with PaddlePaddle now.

# 3) 下载 Paddle-Inference-Demo 示例代码,并进入 Python 代码目录
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git
cd Paddle-Inference-Demo/python/cpu/resnet50

# 4) 下载推理模型
wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar xzf resnet50.tgz

# 5) 准备预测图片
wget https://paddle-inference-dist.bj.bcebos.com/inference_demo/python/resnet50/ILSVRC2012_val_00000247.jpeg

# 6) 运行 Python 预测程序
python3 infer_resnet.py --model_file=./resnet50/inference.pdmodel --params_file=./resnet50/inference.pdiparams
# 预期得到如下输出结果
# class index:  13
C++ 安装部署

请参考以下步骤执行 C++ 安装部署示例程序:

# 1) 下载 Paddle-Inference-Demo 代码
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git

# 2) 拷贝源码编译生成的 C++ 预测库到 Paddle-Inference-Demo/c++/lib 目录下
cp -r Paddle/build/paddle_inference_install_dir Paddle-Inference-Demo/c++/lib/paddle_inference
# 拷贝完成之后 Paddle-Inference-Demo/c++/lib 目录结构如下
Paddle-Inference-Demo/c++/lib/
├── CMakeLists.txt
└── paddle_inference
    ├── CMakeCache.txt
    ├── paddle
    ├── third_party
    └── version.txt

# 3) 进入 C++ 示例代码目录,下载推理模型
cd Paddle-Inference-Demo/c++/cpu/resnet50/
wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar xzf resnet50.tgz

# 4) 修改 compile.sh 编译文件,需根据 C++ 预测库的 version.txt 信息对以下的几处内容进行修改
WITH_GPU=OFF

# 5) 执行编译,编译完成之后在 build 下生成 resnet50_test 可执行文件
./compile.sh

# 6) 运行 C++ 预测程序
./build/resnet50_test --model_file resnet50/inference.pdmodel --params_file resnet50/inference.pdiparams
# 预期得到如下输出结果
# I0526 20:23:05.371094   452 resnet50_test.cc:79] run avg time is 807.533 ms
# I0526 20:23:05.371285   452 resnet50_test.cc:116] 0 : 0
# I0526 20:23:05.371311   452 resnet50_test.cc:116] 100 : 2.04168e-37
# ... ...
# I0526 20:23:05.371394   452 resnet50_test.cc:116] 800 : 3.85262e-25
# I0526 20:23:05.371405   452 resnet50_test.cc:116] 900 : 1.52393e-30
如何卸载

C++ 预测库无需卸载,Python whl 包请使用以下命令卸载:

python3 -m pip uninstall paddlepaddle

龙芯 CPU 安装说明

Paddle Inference 支持基于龙芯 CPU 的推理部署, 当前仅支持通过源码编译的方式安装。

系统要求

当前 Paddle Inference 支持龙芯 CPU 在如下环境下的源码编译和安装部署:

处理器 操作系统
龙芯 3A4000、3A5000、3C5000L 麒麟 V10,Loongnix release 1.0
源码编译

环境准备: 请根据编译依赖表准备符合版本要求的依赖库。麒麟操作系统请参考麒麟技术文档Loongnix请参考龙芯开源社区

第一步: 下载 Paddle 源码并编译,CMAKE 编译选项含义请参见编译选项表

# 下载源码,默认 develop 分支
git clone https://github.com/PaddlePaddle/Paddle.git
cd Paddle

# 创建编译目录
mkdir build && cd build

# 执行cmake
cmake .. -DPY_VERSION=3 -DPYTHON_EXECUTABLE=`which python3` -DWITH_MIPS=ON \
         -DWITH_TESTING=OFF -DON_INFER=ON -DWITH_XBYAK=OFF

# 使用以下命令来编译
make -j$(nproc)

第二步: 编译完成之后,请检查编译目录下的 Python whl 包 和 C++ 预测库是否正确生成

# 检查编译目录下的 Python whl 包
Paddle/build/python/dist/
└── paddlepaddle-0.0.0-cp37-cp37m-linux_loongarch64.whl

# 检查编译目录下的 C++ 预测库,目录结构如下
Paddle/build/paddle_inference_install_dir
├── CMakeCache.txt
├── paddle
│   ├── include                                    # C++ 预测库头文件目录
│   │   ├── crypto
│   │   ├── experimental
│   │   ├── internal
│   │   ├── paddle_analysis_config.h
│   │   ├── paddle_api.h
│   │   ├── paddle_infer_contrib.h
│   │   ├── paddle_infer_declare.h
│   │   ├── paddle_inference_api.h                 # C++ 预测库头文件
│   │   ├── paddle_mkldnn_quantizer_config.h
│   │   ├── paddle_pass_builder.h
│   │   └── paddle_tensor.h
│   └── lib
│       ├── libpaddle_inference.a                  # C++ 静态预测库文件
│       └── libpaddle_inference.so                 # C++ 动态态预测库文件
├── third_party
│   ├── install                                    # 第三方链接库和头文件
│   │   ├── cryptopp
│   │   ├── gflags
│   │   ├── glog
│   │   ├── protobuf
│   │   ├── utf8proc
│   │   └── xxhash
│   └── threadpool
│       └── ThreadPool.h
└── version.txt                                    # 预测库版本信息
安装部署
Python 安装部署

请参考以下步骤执行 Python 安装部署示例程序:

# 1) 安装源码编译生成的 Python whl 包
python3 -m pip install -U paddlepaddle-0.0.0-cp37-cp37m-linux_loongarch64.whl

# 2) 进行简单功能的健康检查
python3 -c "import paddle; paddle.utils.run_check()"
# 预期得到如下输出结果
# Running verify PaddlePaddle program ...
# PaddlePaddle works well on 1 CPU.
# PaddlePaddle works well on 2 CPUs.
# PaddlePaddle is installed successfully! Let's start deep learning with PaddlePaddle now.

# 3) 下载 Paddle-Inference-Demo 示例代码,并进入 Python 代码目录
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git
cd Paddle-Inference-Demo/python/cpu/resnet50

# 4) 下载推理模型
wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar xzf resnet50.tgz

# 5) 准备预测图片
wget https://paddle-inference-dist.bj.bcebos.com/inference_demo/python/resnet50/ILSVRC2012_val_00000247.jpeg

# 6) 运行 Python 预测程序
python3 infer_resnet.py --model_file=./resnet50/inference.pdmodel --params_file=./resnet50/inference.pdiparams
# 预期得到如下输出结果
# class index:  13
C++ 安装部署

请参考以下步骤执行 C++ 安装部署示例程序:

# 1) 下载 Paddle-Inference-Demo 代码
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git

# 2) 拷贝源码编译生成的 C++ 预测库到 Paddle-Inference-Demo/c++/lib 目录下
cp -r Paddle/build/paddle_inference_install_dir Paddle-Inference-Demo/c++/lib/paddle_inference
# 拷贝完成之后 Paddle-Inference-Demo/c++/lib 目录结构如下
Paddle-Inference-Demo/c++/lib/
├── CMakeLists.txt
└── paddle_inference
    ├── CMakeCache.txt
    ├── paddle
    ├── third_party
    └── version.txt

# 3) 进入 C++ 示例代码目录,下载推理模型
cd Paddle-Inference-Demo/c++/cpu/resnet50/
wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar xzf resnet50.tgz

# 4) 修改 compile.sh 编译文件,需根据 C++ 预测库的 version.txt 信息对以下的几处内容进行修改
WITH_MKL=OFF
WITH_GPU=OFF
WITH_ARM=OFF
WITH_MIPS=ON

# 5) 执行编译,编译完成之后在 build 下生成 resnet50_test 可执行文件
./compile.sh

# 6) 运行 C++ 预测程序
./build/resnet50_test --model_file resnet50/inference.pdmodel --params_file resnet50/inference.pdiparams
# 预期得到如下输出结果
# I0524 20:35:45.904501 1737530 resnet50_test.cc:76] run avg time is 1558.38 ms
# I0524 20:35:45.904872 1737530 resnet50_test.cc:113] 0 : 8.76159e-29
# I0524 20:35:45.904923 1737530 resnet50_test.cc:113] 100 : 8.76159e-29
# ... ...
# I0524 20:35:45.904990 1737530 resnet50_test.cc:113] 800 : 3.85252e-25
# I0524 20:35:45.904997 1737530 resnet50_test.cc:113] 900 : 8.76159e-29
如何卸载

C++ 预测库无需卸载,Python whl 包请使用以下命令卸载:

python3 -m pip uninstall paddlepaddle

昆仑 XPU 安装说明

Paddle Inference 支持基于昆仑 XPU 的推理部署, 当前仅支持通过源码编译的方式安装。

系统要求

当前 Paddle Inference 支持昆仑 XPU 在如下环境下的源码编译和安装部署:

芯片型号 操作系统
昆仑1代芯片(K100、K200) Linux操作系统 (Ubuntu、CentOS), 麒麟 V10
昆仑2代芯片 (R200、R300) Linux操作系统 (Ubuntu、CentOS), 麒麟 V10
源码编译

环境准备: 请根据编译依赖表准备符合版本要求的依赖库,推荐使用飞桨官方镜像,否则请参考操作系统使用文档如麒麟技术文档

X86_64 编译安装

第一步: 从飞桨镜像库拉取编译镜像并启动容器,该镜像基于 Ubuntu 18.04 操作系统构建

# 拉取镜像
docker pull registry.baidubce.com/device/paddle-xpu:ubuntu18-x86_64-gcc82

# 启动容器,注意这里需要添加参数 --privileged,否则无法在容器内查看设备
docker run -it --name paddle-dev -v `pwd`:/workspace \
           --shm-size=128G --network=host --privileged \
           --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
           registry.baidubce.com/device/paddle-xpu:ubuntu18-x86_64-gcc82 /bin/bash

# 容器内检查设备情况
xpu_smi
# 预期获得如下输出结果
Runtime Version: 4.0
Driver Version: 4.0
  DEVICES
-------------------------------------------------------------------------------------------------------
| DevID |   PCI Addr   | Model |        SN        |    INODE   | UseRate |     L3     |    Memory     |
-------------------------------------------------------------------------------------------------------
|     0 | 0000:06:00.0 | K200  | 0200210302000998 | /dev/xpu0  |     0 % | 14 / 16 MB | 504 / 8064 MB |
|     1 | 0000:06:00.0 | K200  | 0200210302000998 | /dev/xpu1  |     0 % | 14 / 16 MB | 504 / 8064 MB |
|     2 | 0001:03:00.0 | K200  | 0200210202001104 | /dev/xpu2  |     0 % |  0 / 16 MB |   0 / 8064 MB |
|     3 | 0001:03:00.0 | K200  | 0200210202001104 | /dev/xpu3  |     0 % |  0 / 16 MB |   0 / 8064 MB |
-------------------------------------------------------------------------------------------------------
  PROCESSES
-------------------------------------------------
| DevID | PID | Streams | L3 | Memory | Command |
-------------------------------------------------
-------------------------------------------------

第二步: 下载 Paddle 源码并编译,CMAKE 编译选项含义请参见编译选项表

# 下载源码,默认 develop 分支
git clone https://github.com/PaddlePaddle/Paddle.git
cd Paddle

# 创建编译目录
mkdir build && cd build

# 执行cmake
cmake .. -DPY_VERSION=3 -DPYTHON_EXECUTABLE=`which python3` -DWITH_XPU=ON \
         -DON_INFER=ON -DWITH_TESTING=OFF -DWITH_XBYAK=OFF

# 使用以下命令来编译
make -j$(nproc)
Aarch64 编译安装

第一步: 从飞桨镜像库拉取编译镜像并启动容器,该镜像基于麒麟 V10 操作系统构建

# 拉取镜像
docker pull registry.baidubce.com/device/paddle-xpu:kylinv10-aarch64-gcc73

# 启动容器,注意这里需要添加参数 --privileged,否则无法在容器内查看设备
docker run -it --name paddle-dev -v `pwd`:/workspace \
       --shm-size=128G --network=host --privileged \
       --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
       registry.baidubce.com/device/paddle-xpu:kylinv10-aarch64-gcc73 /bin/bash

# 容器内检查设备情况
xpu_smi
# 预期获得如下输出结果
Runtime Version: 4.0
Driver Version: 4.0
  DEVICES
---------------------------------------------------------------------------------------------------------
| DevID |   PCI Addr   | Model |        SN        |    INODE   | UseRate |     L3     |     Memory      |
---------------------------------------------------------------------------------------------------------
|     0 | 0000:03:00.0 | R200  | 02K00Y6219V00013 | /dev/xpu0  |    12 % | 63 / 63 MB | 4146 / 13568 MB |
|     1 | 0001:03:00.0 | R200  | 02K00Y621AV0001Y | /dev/xpu1  |     0 % |  0 / 63 MB |    0 / 13568 MB |
---------------------------------------------------------------------------------------------------------
  PROCESSES
-------------------------------------------------
| DevID | PID | Streams | L3 | Memory | Command |
-------------------------------------------------
-------------------------------------------------

第二步: 下载 Paddle 源码并编译,CMAKE 编译选项含义请参见编译选项表

# 下载源码,默认 develop 分支
git clone https://github.com/PaddlePaddle/Paddle.git
cd Paddle

# 创建编译目录
mkdir build && cd build

# 执行cmake
cmake .. -DPY_VERSION=3 -DPYTHON_EXECUTABLE=`which python3` -DWITH_XPU=ON \
         -DON_INFER=ON -DWITH_TESTING=OFF -DWITH_XBYAK=OFF -DWITH_ARM=ON \
         -DWITH_AARCH64=ON -DCMAKE_CXX_FLAGS="-Wno-error -w"

# 使用以下命令来编译
make TARGET=ARMV8 -j$(nproc)
编译后检查

编译完成之后,请检查编译目录下的 Python whl 包 和 C++ 预测库是否正确生成。以 Aarch64 环境为例,生成的的目录结构如下所示:

# 检查编译目录下的 Python whl 包
Paddle/build/python/dist/
└── paddlepaddle_xpu-0.0.0-cp37-cp37m-linux_aarch64.whl

# 检查编译目录下的 C++ 预测库,目录结构如下
Paddle/build/paddle_inference_install_dir
├── CMakeCache.txt
├── paddle
│   ├── include                                    # C++ 预测库头文件目录
│   │   ├── crypto
│   │   ├── experimental
│   │   ├── internal
│   │   ├── paddle_analysis_config.h
│   │   ├── paddle_api.h
│   │   ├── paddle_infer_contrib.h
│   │   ├── paddle_infer_declare.h
│   │   ├── paddle_inference_api.h                 # C++ 预测库头文件
│   │   ├── paddle_mkldnn_quantizer_config.h
│   │   ├── paddle_pass_builder.h
│   │   └── paddle_tensor.h
│   └── lib
│       ├── libpaddle_inference.a                  # C++ 静态预测库文件
│       └── libpaddle_inference.so                 # C++ 动态态预测库文件
├── third_party
│   ├── install
│   │   ├── cryptopp
│   │   ├── gflags
│   │   ├── glog
│   │   ├── openblas
│   │   ├── protobuf
│   │   ├── utf8proc
│   │   ├── xpu
│   │   └── xxhash
│   └── threadpool
│       └── ThreadPool.h
└── version.txt                                    # 预测库版本信息
安装部署

本章节以 Aarch64 环境为例说明 Paddle Inference Demo 的安装部署示例:

Python 安装部署

请参考以下步骤执行 Python 安装部署示例程序:

# 1) 安装源码编译生成的 Python whl 包
python3 -m pip install -U paddlepaddle_xpu-0.0.0-cp37-cp37m-linux_aarch64.whl

# 2) 进行简单功能的健康检查
python3 -c "import paddle; paddle.utils.run_check()"
# 预期得到如下输出结果
# Running verify PaddlePaddle program ...
# PaddlePaddle works well on 1 XPU.
# PaddlePaddle works well on 4 XPUs.
# PaddlePaddle is installed successfully! Let's start deep learning with PaddlePaddle now.

# 3) 下载 Paddle-Inference-Demo 示例代码,并进入 Python 代码目录
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git
cd Paddle-Inference-Demo/python/xpu/resnet50

# 4) 下载推理模型
wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar xzf resnet50.tgz

# 5) 准备预测图片
wget https://paddle-inference-dist.bj.bcebos.com/inference_demo/python/resnet50/ILSVRC2012_val_00000247.jpeg

# 6) 运行 Python 预测程序
python3 infer_resnet.py --model_file=./resnet50/inference.pdmodel --params_file=./resnet50/inference.pdiparams
# 预期得到如下输出结果
# class index:  13
C++ 安装部署

请参考以下步骤执行 C++ 安装部署示例程序:

# 1) 下载 Paddle-Inference-Demo 代码
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git

# 2) 拷贝源码编译生成的 C++ 预测库到 Paddle-Inference-Demo/c++/lib 目录下
cp -r Paddle/build/paddle_inference_install_dir Paddle-Inference-Demo/c++/lib/paddle_inference
# 拷贝完成之后 Paddle-Inference-Demo/c++/lib 目录结构如下
Paddle-Inference-Demo/c++/lib/
├── CMakeLists.txt
└── paddle_inference
    ├── CMakeCache.txt
    ├── paddle
    ├── third_party
    └── version.txt

# 3) 进入 C++ 示例代码目录,下载推理模型
cd Paddle-Inference-Demo/c++/xpu/resnet50/
wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar xzf resnet50.tgz

# 4) 修改 compile.sh 编译文件,需根据 C++ 预测库的 version.txt 信息对以下的几处内容进行修改
WITH_MKL=ON  # 这里如果是 Aarch64 环境,则改为 OFF
WITH_ARM=OFF # 这里如果是 Aarch64 环境,则改为 ON
WITH_XPU=ON

# 5) 执行编译,编译完成之后在 build 下生成 resnet50_test 可执行文件
./compile.sh

# 6) 运行 C++ 预测程序
./build/resnet50_test --model_file resnet50/inference.pdmodel --params_file resnet50/inference.pdiparams
# 预期得到如下输出结果
# W0525 20:56:43.035851 95178 xpu_context.cc:89] Please NOTE: xpu device: 0
# W0525 20:56:43.035950 95178 device_context.cc:310] Please NOTE: xpu device: 0
# I0525 20:56:43.083045 95178 resnet50_test.cc:79] run avg time is 46.773 ms
# I0525 20:56:43.083097 95178 resnet50_test.cc:116] 0 : 6.93194e-15
# I0525 20:56:43.083169 95178 resnet50_test.cc:116] 100 : 6.93194e-15
# ... ...
# I0525 20:56:43.083432 95178 resnet50_test.cc:116] 800 : 6.93194e-15
# I0525 20:56:43.083436 95178 resnet50_test.cc:116] 900 : 6.93194e-15
如何卸载

C++ 预测库无需卸载,Python whl 包请使用以下命令卸载:

python3 -m pip uninstall paddlepaddle-xpu

AMD GPU / 海光 DCU 安装说明

Paddle Inference 支持基于 AMD GPU / 海光 DCU 的推理部署, 当前仅支持通过源码编译的方式安装。

系统要求

当前 Paddle Inference 支持 AMD GPU / 海光 DCU 在如下环境下的源码编译和安装部署:

芯片型号 操作系统 SDK 版本
AMD GPU MI100 CentOS 7.9 ROCm 4.5.2
Hygon DCU Z100 CentOS 7.8 ROCm 4.0.1
源码编译

环境准备: 请根据编译依赖表准备符合版本要求的依赖库,推荐使用飞桨官方镜像,或者根据 ROCm安装文档 来准备相应的运行环境。

第一步: 从飞桨镜像库拉取编译镜像,启动容器并在容器内检查设备情况

# 拉取镜像 - ROCm 4.0.1 对应 Hygon DCU Z100 芯片
docker pull registry.baidubce.com/device/paddle-dcu:rocm4.0.1
# 拉取镜像 - ROCm 4.5.2 对应 AMD GPU MI100 芯片
docker pull registry.baidubce.com/device/paddle-dcu:rocm4.5.2

# 启动容器,注意这里的参数,如shm-size, device等均需配置,请根据芯片型号修改这里的镜像名称
docker run -it --name paddle-dev -v `pwd`:/workspace \
           --shm-size=128G --network=host --workdir=/workspace \
           --device=/dev/kfd --device=/dev/dri --group-add video \
           --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
           registry.baidubce.com/device/paddle-dcu:rocm4.0.1 /bin/bash

# 容器内检查设备情况
rocm-smi
# 预期获得如下输出结果
======================= ROCm System Management Interface =======================
================================= Concise Info =================================
GPU  Temp   AvgPwr  SCLK    MCLK     Fan     Perf  PwrCap  VRAM%  GPU%
0    42.0c  39.0W   300Mhz  1200Mhz  20.78%  auto  290.0W    0%   0%
================================================================================
============================= End of ROCm SMI Log ==============================

第二步:下载Paddle源码并编译,CMAKE编译选项含义请参见编译选项表

# 下载源码,默认 develop 分支
git clone https://github.com/PaddlePaddle/Paddle.git
cd Paddle

# 创建编译目录
mkdir build && cd build

# 执行cmake
cmake .. -DPY_VERSION=3 -DPYTHON_EXECUTABLE=`which python3` -DWITH_ROCM=ON \
         -DON_INFER=ON -DWITH_TESTING=OFF -DWITH_XBYAK=OFF

# 使用以下命令来编译
make -j$(nproc)

第三步: 编译完成之后,请检查编译目录下的 Python whl 包 和 C++ 预测库是否正确生成

# 检查编译目录下的 Python whl 包
Paddle/build/python/dist/
└── paddlepaddle_rocm-0.0.0-cp37-cp37m-linux_x86_64.whl

# 检查编译目录下的 C++ 预测库,目录结构如下
Paddle/build/paddle_inference_install_dir
├── CMakeCache.txt
├── paddle
│   ├── include                                    # C++ 预测库头文件目录
│   │   ├── crypto
│   │   ├── experimental
│   │   ├── internal
│   │   ├── paddle_analysis_config.h
│   │   ├── paddle_api.h
│   │   ├── paddle_infer_contrib.h
│   │   ├── paddle_infer_declare.h
│   │   ├── paddle_inference_api.h                 # C++ 预测库头文件
│   │   ├── paddle_mkldnn_quantizer_config.h
│   │   ├── paddle_pass_builder.h
│   │   └── paddle_tensor.h
│   └── lib
│       ├── libpaddle_inference.a                  # C++ 静态预测库文件
│       └── libpaddle_inference.so                 # C++ 动态态预测库文件
├── third_party
│   ├── install                                    # 第三方链接库和头文件
│   │   ├── cryptopp
│   │   ├── gflags
│   │   ├── glog
│   │   ├── mkldnn
│   │   ├── mklml
│   │   ├── protobuf
│   │   ├── utf8proc
│   │   └── xxhash
│   └── threadpool
│       └── ThreadPool.h
└── version.txt                                    # 预测库版本信息
安装部署
Python 安装部署

请参考以下步骤执行 Python 安装部署示例程序:

# 1) 安装源码编译生成的 Python whl 包
python3 -m pip install -U paddlepaddle_rocm-0.0.0-cp37-cp37m-linux_x86_64.whl

# 2) 进行简单功能的健康检查
python3 -c "import paddle; paddle.utils.run_check()"
# 预期得到如下输出结果
# Running verify PaddlePaddle program ...
# PaddlePaddle works well on 1 GPU.
# PaddlePaddle works well on 4 GPUs.
# PaddlePaddle is installed successfully! Let's start deep learning with PaddlePaddle now.

# 3) 下载 Paddle-Inference-Demo 示例代码,并进入 Python 代码目录
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git
cd Paddle-Inference-Demo/python/dcu/resnet50

# 4) 下载推理模型
wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar xzf resnet50.tgz

# 5) 准备预测图片
wget https://paddle-inference-dist.bj.bcebos.com/inference_demo/python/resnet50/ILSVRC2012_val_00000247.jpeg

# 6) 运行 Python 预测程序
python3 infer_resnet.py --model_file=./resnet50/inference.pdmodel --params_file=./resnet50/inference.pdiparams
# 预期得到如下输出结果
# class index:  13
C++ 安装部署

请参考以下步骤执行 C++ 安装部署示例程序:

# 1) 下载 Paddle-Inference-Demo 代码
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git

# 2) 拷贝源码编译生成的 C++ 预测库到 Paddle-Inference-Demo/c++/lib 目录下
cp -r Paddle/build/paddle_inference_install_dir Paddle-Inference-Demo/c++/lib/paddle_inference
# 拷贝完成之后 Paddle-Inference-Demo/c++/lib 目录结构如下
Paddle-Inference-Demo/c++/lib/
├── CMakeLists.txt
└── paddle_inference
    ├── CMakeCache.txt
    ├── paddle
    ├── third_party
    └── version.txt

# 3) 进入 C++ 示例代码目录,下载推理模型
cd Paddle-Inference-Demo/c++/dcu/resnet50/
wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar xzf resnet50.tgz

# 4) 修改 compile.sh 编译文件,需根据 C++ 预测库的 version.txt 信息对以下的几处内容进行修改
WITH_MKL=ON  # 这里如果是 Aarch64 环境,则改为 OFF
WITH_ARM=OFF # 这里如果是 Aarch64 环境,则改为 ON
WITH_ROCM=ON
ROCM_LIB=/opt/rocm/lib # 这里请根据实际情况修改,目录下需存在 libamdhip64.so 库

# 5) 执行编译,编译完成之后在 build 下生成 resnet50_test 可执行文件
./compile.sh

# 6) 运行 C++ 预测程序
./build/resnet50_test --model_file resnet50/inference.pdmodel --params_file resnet50/inference.pdiparams
# 预期得到如下输出结果
# I0525 18:17:23.383029 22394 resnet50_test.cc:76] run avg time is 151.992 ms
# I0525 18:17:23.383116 22394 resnet50_test.cc:113] 0 : 0
# I0525 18:17:23.383133 22394 resnet50_test.cc:113] 100 : 2.04164e-37
# ... ...
# I0525 18:17:23.383301 22394 resnet50_test.cc:113] 800 : 3.85254e-25
# I0525 18:17:23.383309 22394 resnet50_test.cc:113] 900 : 1.52393e-30
如何卸载

C++ 预测库无需卸载,Python whl 包请使用以下命令卸载:

python3 -m pip uninstall paddlepaddle-rocm

昇腾 NPU 安装说明

Paddle Inference 支持基于 华为昇腾 NPU 的推理部署, 当前仅支持通过源码编译的方式安装。

系统要求

当前 Paddle Inference 支持 华为昇腾 NPU 在如下环境下的源码编译和安装部署:

芯片型号 操作系统 SDK 版本
Ascend 910 Ubuntu 18.04 CANN 5.0.4.alpha005
源码编译

环境准备: 请根据编译依赖表准备符合版本要求的依赖库,推荐使用飞桨官方镜像,或者根据 CANN 文档 来准备相应的运行环境。

第一步: 从飞桨镜像库拉取编译镜像,启动容器并在容器内检查设备情况

# 拉取镜像
docker pull registry.baidubce.com/device/paddle-npu:cann504-x86_64-gcc75

# 启动容器,注意这里的参数 --device,容器仅映射设备ID为2到3的2张NPU卡,如需映射其他卡相应增改设备ID号即可
docker run -it --name paddle-dev -v `pwd`:/workspace  \
       --workdir=/workspace --pids-limit 409600 \
       --privileged --network=host --shm-size=128G \
       -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \
       -v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \
       -v /usr/local/dcmi:/usr/local/dcmi \
       registry.baidubce.com/device/paddle-npu:cann504-x86_64-gcc75 /bin/bash

# 容器内检查设备情况
npu-smi info
# 预期获得如下输出结果
+-------------------------------------------------------------------------------------------+
| npu-smi 21.0.4                   Version: 21.0.4                                          |
+----------------------+---------------+----------------------------------------------------+
| NPU   Name           | Health        | Power(W)    Temp(C)           Hugepages-Usage(page)|
| Chip                 | Bus-Id        | AICore(%)   Memory-Usage(MB)  HBM-Usage(MB)        |
+======================+===============+====================================================+
| 0     910A           | OK            | 70.9        42                15   / 15            |
| 0                    | 0000:C1:00.0  | 0           839  / 15170      1    / 32768         |
+======================+===============+====================================================+
| 1     910A           | OK            | 67.2        36                15   / 15            |
| 0                    | 0000:81:00.0  | 0           1274 / 15171      1    / 32768         |
+======================+===============+====================================================+

第二步:下载Paddle源码并编译,CMAKE编译选项含义请参见编译选项表

# 下载源码,默认 develop 分支
git clone https://github.com/PaddlePaddle/Paddle.git
cd Paddle

# 创建编译目录
mkdir build && cd build

# 执行cmake
cmake .. -DPY_VERSION=3.7 -DPYTHON_EXECUTABLE=`which python3` -DON_INFER=ON \
         -DWITH_ASCEND=OFF -DWITH_ASCEND_CL=ON -DWITH_ASCEND_INT64=ON  \
         -DWITH_ASCEND_CXX11=ON -DWITH_TESTING=OFF \
         -DCMAKE_CXX_FLAGS="-Wno-error -w"

# 使用以下命令来编译
make -j$(nproc)

第三步: 编译完成之后,请检查编译目录下的 Python whl 包 和 C++ 预测库是否正确生成

# 检查编译目录下的 Python whl 包
Paddle/build/python/dist/
└── paddlepaddle_npu-0.0.0-cp37-cp37m-linux_x86_64.whl

# 检查编译目录下的 C++ 预测库,目录结构如下
Paddle/build/paddle_inference_install_dir
├── CMakeCache.txt
├── paddle
│   ├── include                                    # C++ 预测库头文件目录
│   │   ├── crypto
│   │   ├── experimental
│   │   ├── internal
│   │   ├── paddle_analysis_config.h
│   │   ├── paddle_api.h
│   │   ├── paddle_infer_contrib.h
│   │   ├── paddle_infer_declare.h
│   │   ├── paddle_inference_api.h                 # C++ 预测库头文件
│   │   ├── paddle_mkldnn_quantizer_config.h
│   │   ├── paddle_pass_builder.h
│   │   └── paddle_tensor.h
│   └── lib
│       ├── libpaddle_inference.a                  # C++ 静态预测库文件
│       └── libpaddle_inference.so                 # C++ 动态态预测库文件
├── third_party
│   ├── install                                    # 第三方链接库和头文件
│   │   ├── cryptopp
│   │   ├── gflags
│   │   ├── glog
│   │   ├── mkldnn
│   │   ├── mklml
│   │   ├── protobuf
│   │   ├── utf8proc
│   │   └── xxhash
│   └── threadpool
│       └── ThreadPool.h
└── version.txt                                    # 预测库版本信息
安装部署
Python 安装部署

请参考以下步骤执行 Python 安装部署示例程序:

# 1) 安装源码编译生成的 Python whl 包
python3 -m pip install -U paddlepaddle_npu-0.0.0-cp37-cp37m-linux_x86_64.whl

# 2) 进行简单功能的健康检查
python3 -c "import paddle; paddle.utils.run_check()"
# 预期得到如下输出结果
# Running verify PaddlePaddle program ...
# PaddlePaddle works well on 1 NPU.
# PaddlePaddle works well on 4 NPUs.
# PaddlePaddle is installed successfully! Let's start deep learning with PaddlePaddle now.

# 3) 下载 Paddle-Inference-Demo 示例代码,并进入 Python 代码目录
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git
cd Paddle-Inference-Demo/python/npu/resnet50

# 4) 下载推理模型
wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar xzf resnet50.tgz

# 5) 准备预测图片
wget https://paddle-inference-dist.bj.bcebos.com/inference_demo/python/resnet50/ILSVRC2012_val_00000247.jpeg

# 6) 运行 Python 预测程序
python3 infer_resnet.py --model_file=./resnet50/inference.pdmodel --params_file=./resnet50/inference.pdiparams
# 预期得到如下输出结果
# class index:  13
C++ 安装部署

请参考以下步骤执行 C++ 安装部署示例程序:

# 1) 下载 Paddle-Inference-Demo 代码
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git

# 2) 拷贝源码编译生成的 C++ 预测库到 Paddle-Inference-Demo/c++/lib 目录下
cp -r Paddle/build/paddle_inference_install_dir Paddle-Inference-Demo/c++/lib/paddle_inference
# 拷贝完成之后 Paddle-Inference-Demo/c++/lib 目录结构如下
Paddle-Inference-Demo/c++/lib/
├── CMakeLists.txt
└── paddle_inference
    ├── CMakeCache.txt
    ├── paddle
    ├── third_party
    └── version.txt

# 3) 进入 C++ 示例代码目录,下载推理模型
cd Paddle-Inference-Demo/c++/npu/resnet50/
wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar xzf resnet50.tgz

# 4) 修改 compile.sh 编译文件,需根据 C++ 预测库的 version.txt 信息对以下内容进行修改
WITH_MKL=ON  # 这里如果是 Aarch64 环境,则改为 OFF
WITH_ARM=OFF # 这里如果是 Aarch64 环境,则改为 ON
WITH_NPU=ON
ASCEND_LIB=/usr/local/Ascend # 这里请根据实际 CANN 安装路径修改

# 5) 执行编译,编译完成之后在 build 下生成 resnet50_test 可执行文件
./compile.sh

# 6) 运行 C++ 预测程序
./build/resnet50_test --model_file resnet50/inference.pdmodel --params_file resnet50/inference.pdiparams
# 预期得到如下输出结果
# I0531 15:14:32.535790 23336 resnet50_test.cc:85] run avg time is 99605.8 ms
# I0531 15:14:32.535897 23336 resnet50_test.cc:122] 0 : 2.67648e-43
# I0531 15:14:32.535917 23336 resnet50_test.cc:122] 100 : 1.98485e-37
# ... ...
# I0531 15:14:32.536034 23336 resnet50_test.cc:122] 800 : 3.80368e-25
# I0531 15:14:32.536043 23336 resnet50_test.cc:122] 900 : 1.46269e-30
如何卸载

C++ 预测库无需卸载,Python whl 包请使用以下命令卸载:

python3 -m pip uninstall paddlepaddle-npu

Graphcore IPU 安装说明

Paddle Inference 支持基于 Graphcore IPU 的推理部署, 当前仅支持通过源码编译的方式安装。

系统要求

当前 Paddle Inference 支持 Graphcore IPU 在如下环境下的源码编译和安装部署:

芯片型号 操作系统 SDK 版本
Colossus MK2 GC200 IPU Ubuntu 18.04 Poplar 2.5.1
源码编译

环境准备: 请根据编译依赖表准备符合版本要求的依赖库,推荐使用飞桨官方镜像,或者根据 Poplar SDK 文档 来准备相应的运行环境。

第一步: 从飞桨镜像库拉取编译镜像,启动容器并在容器内检查设备情况

注意:容器启动命令需将主机端的 IPUoF 配置文件映射到容器中,可通过设置 IPUOF_CONFIG_PATH 环境变量指向 IPUoF 配置文件传入,更多关于 IPUoF 配置的信息请访问 Graphcore: IPUoF configuration file

# 拉取镜像
docker pull registry.baidubce.com/device/paddle-ipu:poplar251

# 启动容器,注意这里的参数,如shm-size, device等均需配置
export IPUOF_CONFIG_PATH=/opt/ipuof.conf
docker run -it --name paddle-dev -v `pwd`:/workspace \
     --shm-size=128G --network=host --ulimit memlock=-1:-1 \
     --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
     --cap-add=IPC_LOCK --device=/dev/infiniband/ --ipc=host \
     -v ${IPUOF_CONFIG_PATH}:/ipuof.conf -e IPUOF_CONFIG_PATH=/ipuof.conf \
     registry.baidubce.com/device/paddle-ipu:poplar251 /bin/bash

# 容器内检查设备情况
gc-monitor
# 预期获得如下输出结果
+---------------+--------------------------------------------------------------------------------+
|  gc-monitor   |              Partition: ipuof [active] has 4 reconfigurable IPUs               |
+-------------+--------------------+--------+--------------+----------+------+----+------+-------+
|    IPU-M    |       Serial       |IPU-M SW|Server version|  ICU FW  | Type | ID | IPU# |Routing|
+-------------+--------------------+--------+--------------+----------+------+----+------+-------+
|...31.100.130| 0134.0002.8210321  |        |    1.8.1     |  2.3.5   |M2000 | 0  |  3   |  DNC  |
|...31.100.130| 0134.0002.8210321  |        |    1.8.1     |  2.3.5   |M2000 | 1  |  2   |  DNC  |
|...31.100.130| 0134.0001.8210321  |        |    1.8.1     |  2.3.5   |M2000 | 2  |  1   |  DNC  |
|...31.100.130| 0134.0001.8210321  |        |    1.8.1     |  2.3.5   |M2000 | 3  |  0   |  DNC  |
+-------------+--------------------+--------+--------------+----------+------+----+------+-------+
+--------------------------------------------------------------------------------------------------+
|                             No attached processes in partition ipuof                             |
+--------------------------------------------------------------------------------------------------+

第二步:下载Paddle源码并编译,CMAKE编译选项含义请参见编译选项表

# 下载源码,默认 develop 分支
git clone https://github.com/PaddlePaddle/Paddle.git
cd Paddle

# 创建编译目录
mkdir build && cd build

# 执行cmake
cmake .. -DPY_VERSION=3 -DPYTHON_EXECUTABLE=`which python3` -DWITH_IPU=ON \
         -DPOPLAR_DIR=/opt/poplar -DPOPART_DIR=/opt/popart \
         -DON_INFER=ON -DWITH_TESTING=OFF -DWITH_XBYAK=OFF

# 使用以下命令来编译
make -j$(nproc)

第三步: 编译完成之后,请检查编译目录下的 Python whl 包 和 C++ 预测库是否正确生成

# 检查编译目录下的 Python whl 包
Paddle/build/python/dist/
└── paddlepaddle_ipu-0.0.0-cp37-cp37m-linux_x86_64.whl

# 检查编译目录下的 C++ 预测库,目录结构如下
Paddle/build/paddle_inference_install_dir
├── CMakeCache.txt
├── paddle
│   ├── include                                    # C++ 预测库头文件目录
│   │   ├── crypto
│   │   ├── experimental
│   │   ├── internal
│   │   ├── paddle_analysis_config.h
│   │   ├── paddle_api.h
│   │   ├── paddle_infer_contrib.h
│   │   ├── paddle_infer_declare.h
│   │   ├── paddle_inference_api.h                 # C++ 预测库头文件
│   │   ├── paddle_mkldnn_quantizer_config.h
│   │   ├── paddle_pass_builder.h
│   │   └── paddle_tensor.h
│   └── lib
│       ├── libpaddle_inference.a                  # C++ 静态预测库文件
│       └── libpaddle_inference.so                 # C++ 动态态预测库文件
├── third_party
│   ├── install                                    # 第三方链接库和头文件
│   │   ├── cryptopp
│   │   ├── gflags
│   │   ├── glog
│   │   ├── mkldnn
│   │   ├── mklml
│   │   ├── protobuf
│   │   ├── utf8proc
│   │   └── xxhash
│   └── threadpool
│       └── ThreadPool.h
└── version.txt                                    # 预测库版本信息
安装部署
Python 安装部署

请参考以下步骤执行 Python 安装部署示例程序:

# 1) 安装源码编译生成的 Python whl 包
python3 -m pip install -U paddlepaddle_ipu-0.0.0-cp37-cp37m-linux_x86_64.whl

# 2) 进行简单功能的健康检查
python3 -c "import paddle; paddle.utils.run_check()"
# 预期得到如下输出结果
# Running verify PaddlePaddle program ...
# PaddlePaddle works well on 1 CPU.
# PaddlePaddle works well on 2 CPUs.
# PaddlePaddle is installed successfully! Let's start deep learning with PaddlePaddle now.

# 3) 下载 Paddle-Inference-Demo 示例代码,并进入 Python 代码目录
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git
cd Paddle-Inference-Demo/python/ipu/resnet50

# 4) 下载推理模型
wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar xzf resnet50.tgz

# 5) 准备预测图片
wget https://paddle-inference-dist.bj.bcebos.com/inference_demo/python/resnet50/ILSVRC2012_val_00000247.jpeg

# 6) 运行 Python 预测程序
python3 infer_resnet.py --model_file=./resnet50/inference.pdmodel --params_file=./resnet50/inference.pdiparams
# 预期得到如下输出结果
# class index:  13
C++ 安装部署

请参考以下步骤执行 C++ 安装部署示例程序:

# 1) 下载 Paddle-Inference-Demo 代码
git clone https://github.com/PaddlePaddle/Paddle-Inference-Demo.git

# 2) 拷贝源码编译生成的 C++ 预测库到 Paddle-Inference-Demo/c++/lib 目录下
cp -r Paddle/build/paddle_inference_install_dir Paddle-Inference-Demo/c++/lib/paddle_inference
# 拷贝完成之后 Paddle-Inference-Demo/c++/lib 目录结构如下
Paddle-Inference-Demo/c++/lib/
├── CMakeLists.txt
└── paddle_inference
    ├── CMakeCache.txt
    ├── paddle
    ├── third_party
    └── version.txt

# 3) 进入 C++ 示例代码目录,下载推理模型
cd Paddle-Inference-Demo/c++/ipu/resnet50/
wget https://paddle-inference-dist.bj.bcebos.com/Paddle-Inference-Demo/resnet50.tgz
tar xzf resnet50.tgz

# 4) 修改 compile.sh 编译文件,需根据 C++ 预测库的 version.txt 信息对以下内容进行修改
WITH_MKL=ON  # 这里如果是 Aarch64 环境,则改为 OFF
WITH_ARM=OFF # 这里如果是 Aarch64 环境,则改为 ON

# 5) 执行编译,编译完成之后在 build 下生成 resnet50_test 可执行文件
./compile.sh

# 6) 运行 C++ 预测程序
./build/resnet50_test --model_file resnet50/inference.pdmodel --params_file resnet50/inference.pdiparams
# 预期得到如下输出结果
# I0530 18:15:45.519501 47607 resnet50_test.cc:82] run avg time is 2.204 ms
# I0530 18:15:45.519557 47607 resnet50_test.cc:119] 0 : 0
# I0530 18:15:45.519572 47607 resnet50_test.cc:119] 100 : 2.04165e-37
# ... ...
# I0530 18:15:45.519615 47607 resnet50_test.cc:119] 800 : 3.85256e-25
# I0530 18:15:45.519620 47607 resnet50_test.cc:119] 900 : 1.52396e-30
如何卸载

C++ 预测库无需卸载,Python whl 包请使用以下命令卸载:

python3 -m pip uninstall paddlepaddle-ipu

Paddle Inference 部署示例

Paddle-Inference-Demo 中,提供了 C++、Python、Go 三种语言在不同平台下进行推理的示例。

C++ 部署示例速查列表

示例名称 功能概述
ascend310 晟腾310 预测样例
IPU IPU 预测样例
cpu/resnet50 单输入模型 oneDnn/ONNXRuntime 预测样例
cpu/yolov3 多输入模型 oneDnn/ONNXRuntime 预测样例
gpu/resnet50 单输入模型 原生GPU/TensorRT_fp32/TensorRT_fp16/TensorRT_int8/TensorRT_dynamic_shape 预测样例
gpu/yolov3 多输入模型 原生GPU/TensorRT_fp32/TensorRT_fp16/TensorRT_int8/TensorRT_dynamic_shape 预测样例
gpu/tuned_dynamic_shape TensorRT动态shape自动推导 预测样例
gpu/ernie_varlen ernie 变长预测样例
gpu/gpu_fp16 GPU 混合精度推理 预测样例
gpu/multi_stream GPU 多流 预测样例
advanced/custom_operator 自定义算子 样例
advanced/share_external_data share_external_data 预测样例
advanced/multi_thread 多线程预测样例
advanced/x86_gru_int8 slim_int8 预测样例
mixed/LIC2020 LIC2020比赛 预测样例

Python 部署示例速查列表

示例名称 功能概述
cpu/resnet50 单输入模型 oneDnn/ONNXRuntime 预测样例
cpu/yolov3 多输入模型 oneDnn/ONNXRuntime 预测样例
gpu/resnet50 单输入模型 原生GPU/GPU混合精度推理/TensorRT_fp32/TensorRT_fp16/TensorRT_int8/TensorRT_dynamic_shape 预测样例
gpu/yolov3 多输入模型 原生GPU/GPU混合精度推理/TensorRT_fp32/TensorRT_fp16/TensorRT_int8/TensorRT_dynamic_shape 预测样例
gpu/tuned_dynamic_shape TensorRT动态shape自动推导 预测样例
advanced/custom_operator 自定义算子 样例
advanced/share_external_data share_external_data 预测样例
advanced/multi_thread 多线程预测样例
mixed/ELMo ELMo 预测样例
mixed/mask_detection 口罩检测预测样例
mixed/x86_lstm_demo Lstm 预测样例

Go 部署示例速查列表

示例名称 功能概述
resnet50 Go 预测样例

调试与优化

本章节分为以下几个小节,介绍了分析精度/性能问题的标准流程,以及进行性能优化的常用方法,供开发者参考。

精度核验与问题追查

本文档将向您介绍在推理部署过程中可以完成推理,但是结果不一致,出现影响到最终指标的 diff 的情况下,如何进行精度问题的追查。

1 追查准备工作

在追查出现精度问题原因前需要对齐所有的推理配置项,控制其他变量一致,其中包括:

(1) paddle版本

(2) 硬件环境

(3) 模型

(4) 预处理和模型输入

如果是 C++Python 结果不一致,请使用同一硬件环境;如果是不同硬件结果不一致,请使用同样的测试代码。

2 追查具体步骤

以正确结果为基准(训练前向,或不开任何优化的推理结果),不断按如下步骤调整错误结果的配置从而复现问题。

2.1 预处理和模型输入对齐

打印模型输入数据,确定预处理对齐(比对两种情况下的全部模型输入是否完全一致)。

2.2 关闭所有优化

关闭所有优化(对应 API 如下),不开启 TensorRT,排查结果是否对齐,此种情况下约等于使用训练前向进行推理。

API 内存显存优化 IR TensorRT
C++ //config.EnableMemoryOptim() 不开启 config.SwitchIrOptim(false) No
Python #config.enable_memory_optim() 不开启 config.switch_ir_optim(False) No

结果分析

(1) 如果此步骤发现结果不对齐或报错,可以在同样环境下用训练前向的 paddle.static.Executor 接口加载模型,跑训练前向验证一下结果是否一致,结果仍不一致则为原生 OP 实现问题,结果一致但是推理接口出错则是推理问题。

(2) 定位引起不一致的具体 OP : 可以通过裁剪模型的方式,尝试进一步定位引发结果出错的具体 OP。裁剪网络方式可以使用二分法,或者针对网络结构设计容易快速定位 OP 的方式(如果裁剪重复结构的分界线,backbone 等)。模型裁剪可以通过组网代码,或者使用我们研发使用的模型裁剪工具

2.3 开启内存显存优化

配置选项:

API 内存显存优化 IR TensorRT
C++ config.EnableMemoryOptim() 开启 config.SwitchIrOptim(false) No
Python config.enable_memory_optim() 开启 config.switch_ir_optim(False) No

模型推理在是否开启内存显存优化的情况下均可正常使用,不会影响性能,只可能影响显存的大小,在使用 TensorRT 的情况下,对显存影响也不大。若开启内存显存优化情况下,结果出现不一致,请您能够提交相关的样例至 issue,协助我们解决框架问题。

2.4 开启IR优化

配置选项:

API 内存显存优化 IR TensorRT
C++ config.EnableMemoryOptim() 开启 config.SwitchIrOptim(true) No
Python config.enable_memory_optim() 开启 config.switch_ir_optim(True) No

IR 优化会涉及到具体的 Pass 优化,如果开启 IR 优化后出现结果不一致的情况,下一步需要定位引发问题的具体 Pass。

(1) C++ API

config.pass_builder()->DeletePass("xxx_fuse_pass")

(2) Python API

config.delete_pass("xxxx_fuse_pass")

为了快速定位出问题的 Pass,有两种思路:

(1) 二分法。一次注释一半的 Pass,二分法查找。Pass 全集见运行日志中的 ir_analysis_pass 部分。

(2) 逐个查找。delete 命中的 Pass(有命中日志的 Pass ),如下图。

2.5 开启 TensorRT

开启 TensorRT,一般不会出现精度问题,会出现推理出错的情况。

2.5.1 动态 shape 输入

如果开启 TensorRT 后有如下报错,请参考日志设置正确动态 shape 输入变量。

(1) c++ API

std::map<std::string, std::vector<int>> min_input_shape = {
  {"data", {1, 3, 224, 224}}};
std::map<std::string, std::vector<int>> max_input_shape = {
  {"data", {1, 3, 224, 224}}};
std::map<std::string, std::vector<int>> opt_input_shape = {
  {"data", {1, 3, 224, 224}}};

config.SetTRTDynamicShapeInfo(min_input_shape, max_input_shape,
                            opt_input_shape);

(2) Python API

min_input_shape = {"data":[1, 3, 224, 224]}
max_input_shape = {"data":[1, 3, 224, 224]}
opt_input_shape = {"data":[1, 3, 224, 224]}

config.set_trt_dynamic_shape_info(min_input_shape, max_input_shape, opt_input_shape)

或者使用推理自动生成动态 shape 的方式进行操作,对应 API 如下,先调用 CollectShapeRangeInfo 接口生成动态 shape 文件,在推理时将 CollectShapeRangeInfo 接口删掉,使用 EnableTunedTensorRtDynamicShape 接口调用动态 shape 输入即可。

(1) c++ API

//config.CollectShapeRangeInfo("shape_range_info.pbtxt");
config.EnableTunedTensorRtDynamicShape("shape_range_info.pbtxt", 1);

(2) Python API

#config.collect_shape_range_info("shape_range_info.pbtxt")
config.enable_tuned_tensorrt_dynamic_shape("shape_range_info.pbtxt", True)
2.5.2 禁止 OP 进入 TensorRT

若推理报错结果显示某个具体 OP 推理出现问题,可将此 OP 移出进入 TensorRT 的列表,使用原生 GPU 进行推理。

(1) c++ API

config.Exp_DisableTensorRtOPs({"concat"});

(2) Python API

config.exp_disable_tensorrt_ops(["concat"])
2.6 其他

如果通过以上步骤仍未解决您的问题,请再仔细检查各步骤是否完全对齐或者可提交 issue,将您的具体问题、单测以及模型等提供给 Paddle Inference 框架同学,感谢。

性能分析

本文档将向您介绍如何使用性能分析工具定位推理部署过程中的性能瓶颈,以方便进行深度优化。

1 框架 Profile 工具

通过使用 Paddle Inference 提供的 Profile 工具可以查看 timeline,用于定位 CPU 和 GPU 的瓶颈,开启 Profile,需要在 config 中设置对应的指令:

(1) c++ API

config.EnableProfile() 

(2) python API

config.enable_profile()

推理完成后会得到完整的 Profile 数据报告,其中包括整体计算和框架耗时占比、详细的 OP 耗时占比以及内存使用情况。 应该重点分析的是每个 OP 的耗时占比,详细输出信息如下图,其中:

(1) Calls,调用次数

(2) Total,CPU 时间 + GPU 时间

(3) CPU 时间,准确

(4) GPU 时间,准确

(5) Min、Max、Ave,最大、最小、平均时间

(6) Ratio,Total 时间占比

CPU 和 GPU 耗时较为准确,可作为性能分析的参考。我们可以通过分析各部分耗时来判断性能瓶颈是由 CPU 还是 GPU 导致,得到最大耗时对应的 OP,精确统计指定 C++ 代码的开销。

2 NVIDIA Nsight Systems 性能分析工具

NVIDIA Nsight Systems 是一种系统级别的调优工具,能够对程序使用到的 GPU、DLA、CPU、显存、线程等数据进行可视化,以帮助用户调查瓶颈,优化性能。 NVIDIA 官方提供了 Nsight Systems 数据手册

2.1 Nsight Systems 使用

(1) Linux 服务器/ Jetson 设备

首先,需要在 Nsight Systems 官网 下载对应 Linux 版本的安装包,执行安装命令即可完成安装。

 //.run 安装包
 sh NsightSystems-linux-public-xxx.run --accept --quiet
 //.deb 安装包
 dpkg -i NsightSystems-linux-public-xxx.deb 

然后,在 Linux 终端使用 nsys 命令进行 Profile 产生基础数据,以运行 resnet 为例说明 nsys 命令使用方法。

 nsys profile -o output ./resnet

之后,会生成 output.qdrep 文件,最后拷贝到 Host 机器(Mac、Linux、Windows)上,使用 Nsight Host 进行图形化展示。

(2) Windows 服务器

在 Windows 服务器上使用 Nsight Systems 工具,无需在终端生成 .qdrep 文件,点击左上角 File –> New Project,连接好服务器、添加好可执行文件以及可视化内容选项设置好,点击 Start 即可完成 Profile 过程。

2.2 Nsight Systems 结果分析

在 Mac / Windows 上安装好图形化查看界面 Nsight Host,打开生成的 .qdrep 文件,就可以查看运行的 timeline,可重点分析 CUDA HW、TensorRT 以及 CUDA API 这三部分。

(1) CUDA HW 包含了所有运行的 GPU Kernel 计算单元。在界面中放大可以查看所有时间段,选中某次运行的时间段,Nsight Systems 会自动显示该时间段运行 kernel 的详细数据信息,包括执行时间、内存申请以及所在流等。

(2) TensorRT 以及 CUDA API 包含了 所有 GPU Kernel Launch 的过程。

(3) 将左侧菜单栏展开,还会得到更加详细的 Kernel 计算占比以及多 Stream 使用情况。

2.3 Nsight Systems 分析技巧

(1) Kernel 名字包含特定信息。例如 TensorRT 的 Kernel 会以 trt 开头;TensorRT 使用 fp16 的 Kernel 会包含 884 或 1688 字样,int8 会包含 int8。

(2) 循环运行多次,方便定位运行周期。因为初始化阶段和 TensorRT 的 build 阶段默认也会被 Profile,可以通过循环运行多次 run(例如 500、1000),方便在 timeline 中准确定位一个完整的运行周期。

(3) 重点关注 Kernel 相对时长。由于 timeline 上的时间一般会比实际运行时间长一些,重点关注 Kernel 之间的时长对比即可。

混合精度推理

混合精度推理是通过混合使用单精度(FP32)和半精度(FP16)来加速神经网络推理过程。相较于使用单精度(FP32)进行推理,既能能减少内存/显存占用,推理更大的网络,又能降低显存访问和计算耗时开销,在保证模型推理精度持平的情形下,提升推理效率。

一、半精度浮点类型 FP16

首先介绍半精度(FP16)。如图1所示,半精度(FP16)是一种相对较新的浮点类型,在计算机中使用2字节(16位)存储。在IEEE 754-2008标准中,它亦被称作binary16。与计算中常用的单精度(FP32)和双精度(FP64)类型相比,FP16更适于在精度要求不高的场景中使用。

missing
图 1. 半精度和单精度数据示意图
二、NVIDIA GPU的FP16算力

混合精度推理使用半精度浮点(FP16)和单精度(FP32)浮点即可达到与使用纯单精度推理相同的准确率,并可加速模型的推理速度,这主要得益于英伟达从Volta架构开始推出的Tensor Core技术。在使用FP16计算时具有如下特点:

  • FP16可降低一半的内存带宽和存储需求,这使得在相同的硬件条件下研究人员可使用更大更复杂的模型以及更大的batch size大小。

  • FP16可以充分利用英伟达Volta、Turing、Ampere架构GPU提供的Tensor Cores技术。在相同的GPU硬件上,Tensor Cores的FP16计算吞吐量是FP32的8倍。

三、使用 Paddle Inference 进行混合精度推理

使用 Paddle Inference 提供的 API,能够开启自动混合精度推理选项,在相关 OP 的计算过程中,根据内置的优化规则,自动选择 FP32 或者 FP16 计算。

3.1 如何开启混合精度推理选项
3.1.1 GPU 原生推理使用混合精度
  • C++ Config 选项

    Exp_EnableUseGpuFp16(std::unordered_set<std::string> gpu_fp16_disabled_op_types_)
    
  • Python Config 选项

    exp_enable_use_gpu_fp16()
    

可以在上述 API 接口中传入 OP 名称参数列表,来排除不支持 FP16 计算的 OP 使用混合精度推理。

详细API介绍,分别参考 C++ API 文档 - Config 或者 Python API 文档 - Config

3.1.2 TensorRT 推理使用混合精度

为了使用TensorRT 利用半精度进行混合精度推理,需将制定精度类型参数设定为半精度。

  • C++ Config 选项

    将以下接口中精度类型参数precision,设定为Precision::kFloat32

    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);
    
  • Python Config 选项

    将以下接口中精度类型参数precision_mode,设定为paddle_infer.PrecisionType.Half

    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)
    

详细API介绍,分别参考 C++ API 文档 - Config 或者 Python API 文档 - Config

3.2 混合精度推理使用示例

以下分别介绍 GPU 原生、TensorRT 混合精度推理示例,完整示例可参考。

3.2.1 GPU 原生混合精度推理示例
  • C++ 示例如下

    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(1000, 0);
    config.Exp_EnableUseGpuFp16();
    config.SwitchIrOptim(true);
    
    auto predictor = paddle_infer::CreatePredictor(config);
    

    完整示例见Paddle-Inference-Demo/c++/gpu/gpu_fp16

  • Python 示例如下

    if args.model_dir == "":
      config = Config(args.model_file, args.params_file)
    else:
      config = Config(args.model_dir)
    config.enable_use_gpu(1000, 0)
    config.exp_enable_use_gpu_fp16()
    config.switch_ir_optim(True)
    
    predictor = create_predictor(config)
    

    完整示例见Paddle-Inference-Demo/python/gpu/resnet50

3.2.2 TensorRT 混合精度推理示例
  • C++ 示例如下

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(1000, 0);
config.EnableTensorRtEngine(1 << 30, FLAGS_batch_size, 5,
                              PrecisionType::kHalf, false, false);
config.SwitchIrOptim(true);

auto predictor = paddle_infer::CreatePredictor(config);
  • Python 示例如下

    if args.model_dir == "":
      config = Config(args.model_file, args.params_file)
    else:
      config = Config(args.model_dir)
    config.enable_use_gpu(1000, 0)
    config.enable_tensorrt_engine(
      	workspace_size = 1<<30,
      	max_batch_size=1, min_subgraph_size=5,
      	precision_mode=paddle_infer.PrecisionType.Half,
      	use_static=False, use_calib_mode=False)
    config.switch_ir_optim(True)
    
    predictor = create_predictor(config)
    
  • 四、混合精度推理性能优化

    Paddle Inference 混合精度推理性能的根本原因是:利用 Tensor Core 来加速 FP16 下的matmulconv运算,为了获得最佳的加速效果,Tensor Core 对矩阵乘和卷积运算有一定的使用约束,约束如下:

    4.1 矩阵乘使用建议

    通用矩阵乘 (GEMM) 定义为:C = A * B + C,其中:

    • A 维度为:M x K

    • B 维度为:K x N

    • C 维度为:M x N

    矩阵乘使用建议如下:

    • 根据Tensor Core使用建议,当矩阵维数 M、N、K 是8(A100架构GPU为16)的倍数时(FP16数据下),性能最优。

    4.2 卷积计算使用建议

    卷积计算定义为:NKPQ = NCHW * KCRS,其中:

    • N 代表:batch size

    • K 代表:输出数据的通道数

    • P 代表:输出数据的高度

    • Q 代表:输出数据的宽度

    • C 代表:输入数据的通道数

    • H 代表:输入数据的高度

    • W 代表:输入数据的宽度

    • R 代表:滤波器的高度

    • S 代表:滤波器的宽度

    卷积计算使用建议如下:

    • 输入/输出数据的通道数(C/K)可以被8整除(FP16),(cudnn7.6.3及以上的版本,如果不是8的倍数将会被自动填充)

    • 对于网络第一层,通道数设置为4可以获得最佳的运算性能(NVIDIA为网络的第一层卷积提供了特殊实现,使用4通道性能更优)

    • 设置内存中的张量布局为NHWC格式(如果输入NCHW格式,Tesor Core会自动转换为NHWC,当输入输出数值较大的时候,这种转置的开销往往更大)

    多线程并发推理

    单线程的推理服务,往往由于无法实现较高QPS,而导致GPU利用率过低。利用多线程实现并发推理,能提高推理服务的吞吐量,实现推理服务的优化。

    利用多线程来实现并发推理
    使用示例

    下面的示例以C++为例。

    多线程实现
    • 1、创建Predictor

      auto main_predictor = paddle_infer::CreatePredictor(config);
    
    • 2、创建多个推理线程并执行

      std::vector<decltype(main_predictor)> predictors;
    
      for (int i = 0; i < FLAGS_thread_num - 1; ++i) {
        predictors.emplace_back(std::move(main_predictor->Clone()));
      }
      predictors.emplace_back(std::move(main_predictor));
    
      std::vector<std::thread> threads;
      auto begin = time();
      for (int i = 0; i < FLAGS_thread_num; ++i) {
        threads.emplace_back(Run, predictors[i], i);
      }
    

    Run() 为线程执行函数,以下代码片段供参考。

      void Run(std::shared_ptr<Predictor> predictor, int thread_id) {
    
      auto run_one_loop = [&](int batch_size) {
        // input
        int channels = 3;
        int height = 224;
        int width = 224;
        int input_num = channels * height * width * batch_size;
        std::vector<float> in_data(input_num, 0);
        for (int i = 0; i < input_num; ++i) {
          in_data[i] = i % 255 * 0.1;
        }
        auto in_names = predictor->GetInputNames();
        auto in_handle = predictor->GetInputHandle(in_names[0]);
        in_handle->Reshape({batch_size, channels, height, width});
        in_handle->CopyFromCpu(in_data.data());
    
        CHECK(predictor->Run());
    
        // output
        auto out_names = predictor->GetOutputNames();
        auto out_handle = predictor->GetOutputHandle(out_names[0]);
        std::vector<float> out_data;
        std::vector<int> out_shape = out_handle->shape();
        int output_num = std::accumulate(out_shape.begin(), out_shape.end(), 1,
                                         std::multiplies<int>());
        out_data.resize(output_num);
        out_handle->CopyToCpu(out_data.data());
        float mean_val = std::accumulate(out_data.begin(), out_data.end(),
                                         decltype(out_data)::value_type(0));
        // std::cout << "mean: " << mean_val << std::endl;
      };
    
      for (int i = 0; i < FLAGS_loop_times; ++i) {
        run_one_loop(FLAGS_batch_size);
      }
    }
    
    使用PredictorPool的多线程实现

    Paddle Inference提供了Predictor线程池的封装。

    • 1、创建Predictor Pool

      paddle_infer::services::PredictorPool pred_pool(config, thread_num);
      
    • 2、创建多个推理线程并执行

      std::vector<std::thread> threads;
      auto begin = time();
      for (int i = 0; i < FLAGS_thread_num; ++i) {
        threads.emplace_back(Run, pred_pool.Retrive(i), i);
      }
      

      Run() 为线程执行函数,同上。

    多线程并发推理测试
    • 测试环境

      • NVIDIA® T4 GPU

      • CUDA 11.2.2

      • cuDNN 8.2.1

      • PaddlePaddle 版本:v2.3

    • 测试结果 _images/multi_thread.pngimage 此处使用MobileNetV1进行测试,batch_size设定为1,线程数从1递增至16。可以看出QPS与线程数呈线性关系,同时请求latency变化不大。

    Benchmark

    本章节展示了使用Paddle Inference在主流CPU和GPU平台上执行部分常见模型推理的性能数据。

    CPU 性能数据

    测试条件
    • 测试模型

      • MobileNetV1

      • MobileNetV2

      • ResNet50

      • bert

      • ViT_base_patch32_384

      • ssdlite_mobilenet_v1_300_coco

    • 测试机器信息

      • Intel(R) Xeon(R) Gold 6271C CPU @ 2.60GHz

      • 内存 250G

    • 测试说明

      • 测试 PaddlePaddle 版本:v2.3

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

      • cpu_math_library_num_threads=1,num_samples=1000。

    数据
    model_name batch_size enable_mkldnn enable_ort precision avg_latency
    bert 1 False False fp32 106.6469
    bert 1 True False fp32 106.6411
    bert 2 False False fp32 149.6218
    bert 2 True False fp32 136.8391
    bert 4 False False fp32 276.0263
    bert 4 True False fp32 243.8251
    MobileNetV1 1 False False fp32 37.7486
    MobileNetV1 1 True False fp32 15.4455
    MobileNetV1 2 False False fp32 78.1411
    MobileNetV1 2 True False fp32 31.802
    MobileNetV1 4 False False fp32 150.2198
    MobileNetV1 4 True False fp32 57.1735
    MobileNetV1 1 True True fp32 10.3915
    MobileNetV1 2 True True fp32 22.8195
    MobileNetV1 4 True True fp32 42.5765
    MobileNetV2 1 False False fp32 43.6175
    MobileNetV2 1 True False fp32 14.8715
    MobileNetV2 2 False False fp32 85.8639
    MobileNetV2 2 True False fp32 25.7693
    MobileNetV2 4 False False fp32 175.4801
    MobileNetV2 4 True False fp32 49.5933
    MobileNetV2 1 True True fp32 9.2888
    MobileNetV2 2 True True fp32 19.4435
    MobileNetV2 4 True True fp32 41.0745
    ResNet50 1 False False fp32 123.8814
    ResNet50 1 True False fp32 73.7028
    ResNet50 2 False False fp32 232.9871
    ResNet50 2 True False fp32 159.2116
    ResNet50 4 False False fp32 494.5854
    ResNet50 4 True False fp32 289.705
    ResNet50 1 True True fp32 75.0158
    ResNet50 2 True True fp32 150.8254
    ResNet50 4 True True fp32 292.688
    ssdlite_mobilenet_v1_300_coco 1 False False fp32 88.7488
    ssdlite_mobilenet_v1_300_coco 1 True False fp32 36.2734
    ssdlite_mobilenet_v1_300_coco 2 False False fp32 164.834
    ssdlite_mobilenet_v1_300_coco 2 True False fp32 66.3129
    ssdlite_mobilenet_v1_300_coco 4 False False fp32 343.1162
    ssdlite_mobilenet_v1_300_coco 4 True False fp32 132.0374
    ssdlite_mobilenet_v1_300_coco 1 True True fp32 35.4703
    ssdlite_mobilenet_v1_300_coco 2 True True fp32 70.9778
    ssdlite_mobilenet_v1_300_coco 4 True True fp32 129.5277
    ViT_base_patch32_384 1 False False fp32 365.7941
    ViT_base_patch32_384 1 True False fp32 326.9727
    ViT_base_patch32_384 2 False False fp32 646.3851
    ViT_base_patch32_384 2 True False fp32 1126.7091
    ViT_base_patch32_384 4 False False fp32 1218.0988
    ViT_base_patch32_384 4 True False fp32 2187.3777
    ViT_base_patch32_384 1 True True fp32 274.8126
    ViT_base_patch32_384 2 True True fp32 536.2707
    ViT_base_patch32_384 4 True True fp32 1071.5036

    注:enable_ort字段为True,表示使用Paddle Inference集成的ONNXRuntime CPU后端。

    GPU 性能数据

    测试条件
    • 测试模型

      • MobileNetV1

      • MobileNetV2

      • ResNet50

      • mask_rcnn_r50_vd_fpn_1x_coco

      • ssdlite_mobilenet_v1_300_coco

      • yolov3_darknet53_270e_coco

      • deeplabv3p_resnet50

      • bert

      • ViT_base_patch32_384

      • SwinTransformer_base_patch4_window12_384

    • 测试机器信息

      • NVIDIA® T4 GPU

      • Intel(R) Xeon(R) Gold 6271C CPU @ 2.60GHz

      • CUDA 11.2.2

      • cuDNN 8.2.1

      • TensorRT 8.0.3.4

    • 测试说明

      • 测试 PaddlePaddle 版本:v2.3

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

      • cpu_math_library_num_threads=1,num_samples=1000。

    数据
    model_name precision batch_size avg_latency
    MobileNetV1 fp16 1 0.4925
    MobileNetV1 fp16 2 0.7485
    MobileNetV1 fp16 4 1.2914
    MobileNetV1 fp32 1 0.8737
    MobileNetV1 fp32 2 1.4106
    MobileNetV1 fp32 4 2.5238
    MobileNetV2 fp16 1 0.5926
    MobileNetV2 fp16 2 0.9131
    MobileNetV2 fp16 4 1.4491
    MobileNetV2 fp32 1 1.1125
    MobileNetV2 fp32 2 1.6682
    MobileNetV2 fp32 4 2.819
    ResNet50 fp16 1 1.3045
    ResNet50 fp16 2 1.8964
    ResNet50 fp16 4 3.1821
    ResNet50 fp32 1 3.5244
    ResNet50 fp32 2 5.2147
    ResNet50 fp32 4 9.3702
    SwinTransformer_base_patch4_window12_384 fp16 1 23.0886
    SwinTransformer_base_patch4_window12_384 fp16 2 42.2748
    SwinTransformer_base_patch4_window12_384 fp16 4 87.3252
    SwinTransformer_base_patch4_window12_384 fp32 1 43.5075
    SwinTransformer_base_patch4_window12_384 fp32 2 87.5455
    SwinTransformer_base_patch4_window12_384 fp32 4 173.796
    ViT_base_patch32_384 fp16 1 4.923
    ViT_base_patch32_384 fp16 2 7.5347
    ViT_base_patch32_384 fp16 4 12.899
    ViT_base_patch32_384 fp32 1 10.8246
    ViT_base_patch32_384 fp32 2 18.5213
    ViT_base_patch32_384 fp32 4 34.7381
    deeplabv3p_resnet50 fp16 1 26.1575
    deeplabv3p_resnet50 fp16 2 47.9256
    deeplabv3p_resnet50 fp16 4 95.9487
    deeplabv3p_resnet50 fp32 1 66.8809
    deeplabv3p_resnet50 fp32 2 133.6688
    deeplabv3p_resnet50 fp32 4 266.9613
    mask_rcnn_r50_vd_fpn_1x_coco fp16 1 40.6577
    mask_rcnn_r50_vd_fpn_1x_coco fp32 1 101.93
    yolov3_darknet53_270e_coco fp16 1 20.6326
    yolov3_darknet53_270e_coco fp16 2 41.5202
    yolov3_darknet53_270e_coco fp16 4 80.3059
    yolov3_darknet53_270e_coco fp32 1 44.1216
    yolov3_darknet53_270e_coco fp32 2 85.4666
    yolov3_darknet53_270e_coco fp32 4 183.9448

    API

    您可以通过以下四种官方支持的API来使用Paddle Inference开发您的应用

    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.pdmodel", "./mobilenet_v1.pdiparams")
    
    # 根据 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 类定义,输入分别为模型文件路径和参数文件路径
    class paddle.inference.Config(prog_file: str, params_file: str)
    

    代码示例:

    # 引用 paddle inference 预测库
    import paddle.inference as paddle_infer
    
    # 创建 config
    config = paddle_infer.Config("./mobilenet.pdmodel", "./mobilenet.pdiparams")
    
    # 根据 config 创建 predictor
    predictor = paddle_infer.create_predictor(config)
    
    设置预测模型
    从文件中加载预测模型

    API定义如下:

    # 设置模型文件路径,当需要从磁盘加载模型时使用
    # 参数:prog_file_path - 模型文件路径
    #      params_file_path - 参数文件路径
    # 返回:None
    paddle.inference.Config.set_model(prog_file_path: str, params_file_path: str)
    
    # 设置模型文件路径
    # 参数:x - 模型文件路径
    # 返回:None
    paddle.inference.Config.set_prog_file(x: str)
    
    # 设置参数文件路径
    # 参数:x - 参数文件路径
    # 返回:None
    paddle.inference.Config.set_params_file(x: str)
    
    # 获取模型文件路径
    # 参数:None
    # 返回:str - 模型文件路径
    paddle.inference.Config.prog_file()
    
    # 获取参数文件路径
    # 参数: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.pdmodel")
    config.set_params_file("./mobilenet_v2.pdiparams")
    
    # 通过 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.pdmodel', 'rb') as prog_file:
        prog_data=prog_file.read()
        
    with open('./mobilenet_v2.pdiparams', '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 加速库计算线程数
    # 参数:cpu_math_library_num_threads - CPU 加速库计算线程数
    # 返回:None
    paddle.inference.Config.set_cpu_math_library_num_threads(cpu_math_library_num_threads: int)
    
    # 获取 CPU 加速库计算线程数
    # 参数:None
    # 返回:int - CPU 加速库计算线程数
    paddle.inference.Config.cpu_math_library_num_threads()
    

    代码示例:

    # 引用 paddle inference 预测库
    import paddle.inference as paddle_infer
    
    # 创建 config
    config = paddle_infer.Config()
    
    # 设置 CPU 加速库线程数为 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])
    
    # 启用 MKLDNN INT8
    # 参数:使用 MKLDNN INT8 加速的 OP 集合
    # 返回:None
    paddle.inference.Config.enable_mkldnn_int8(op_list: Set[str])
    

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

    # 引用 paddle inference 预测库
    import paddle.inference as paddle_infer
    
    # 创建 config
    config = paddle_infer.Config("./mobilenet.pdmodel", "./mobilenet.pdiparams")
    
    # 启用 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.pdmodel", "./mobilenet.pdiparams")
    
    # 启用 MKLDNN 进行预测
    config.enable_mkldnn()
    
    # 启用 MKLDNN BFLOAT16 进行预测
    config.enable_mkldnn_bfloat16()
    
    # 设置启用 MKLDNN BFLOAT16 的 OP 列表
    config.set_bfloat16_op({"conv2d"})
    

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

    # 引用 paddle inference 预测库
    import paddle.inference as paddle_infer
    
    # 创建 config
    config = paddle_infer.Config("./mobilenet.pdmodel", "./mobilenet.pdiparams")
    
    # 启用 MKLDNN 进行预测
    config.enable_mkldnn()
    
    # 启用 MKLDNN INT8 进行预测
    config.enable_mkldnn_int8()
    
    使用 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 FP16 计算精度进行预测
    # 参数:op_list - 保持 FP32 计算精度算子名单
    # 返回:None
    paddle.inference.Config.exp_enable_use_gpu_fp16(op_list: Set[str])
    

    GPU设置代码示例:

    # 引用 paddle inference 预测库
    import paddle.inference as paddle_infer
    
    # 创建 config
    config = paddle_infer.Config("./mobilenet.pdmodel", "./mobilenet.pdiparams")
    
    # 启用 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
    
    # 启用 GPU FP16 计算精度进行预测
    config.enable_use_gpu(100, 0);
    config.exp_enable_use_gpu_fp16();
    
    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 在网络编译阶段进行kernel选择时使用的工作空间大小,不影响运
    #                           行时显存占用。该值设置过小可能会导致选不到最佳kernel,设置过大时会增加初始
    #                           化阶段的显存使用,请根据实际情况调整,建议值256MB
    #      max_batch_size     - 设置最大的 batch 大小,运行时 batch 大小不得超过此限定值
    #      min_subgraph_size  - Paddle 内 TensorRT 是以子图的形式运行,为了避免性能损失,当 TensorRT 
    #                           子图内部节点个数大于 min_subgraph_size 的时候,才会使用 TensorRT 运行
    #      precision          - 指定使用 TensorRT 的精度,支持 FP32(kFloat32),FP16(kHalf),Int8(kInt8)
    #      use_static         - 若指定为 true,在初次运行程序退出Predictor析构的时候会将 TensorRT 的优
    #                           化信息进行序列化到磁盘上。下次运行时直接加载优化的序列化信息而不需要重新生
    #                           成,以加速启动时间(需要在同样的硬件和相同 TensorRT 版本的情况下)
    #      use_calib_mode     - 若要运行 TensorRT 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,推理时输入 shape 的任何
    #                                 维度均不能小于该项配置
    #      max_input_shape          - TensorRT 子图支持动态 shape 的最大 shape,推理是输入 shape 的任何
    #                                 维度均不能大于该项配置
    #      optim_input_shape        - TensorRT 子图支持动态 shape 的最优 shape,TensorRT 在初始化选
    #                                 kernel 阶段以此项配置的 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 动态 shape 的自动推导
    # 参数: shape_range_info_path  - 统计生成的 shape 信息存储文件路径
    #       allow_build_at_runtime - 是否开启运行时重建 TensorRT 引擎功能,当设置为 true 时,输入 shape 
    #                                超过 tune 范围时会触发 TensorRT 重建。当设置为 false 时,输入 shape
    #                                超过 tune 范围时会引起推理出错
    # 返回:None
    paddle.inference.Config.enable_tuned_tensorrt_dynamic_shape(
                                         shape_range_info_path: str,
                                         allow_build_at_runtime: bool=True)
    
    # 启用 TensorRT OSS 进行 ERNIE / BERT 预测加速(原理介绍 https://github.com/PaddlePaddle/Paddle-Inference-Demo/tree/master/c%2B%2B/ernie-varlen )
    # 参数: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.pdmodel", "./mobilenet.pdiparams")
    
    # 启用 GPU 进行预测 - 初始化 GPU 显存 100M, Deivce_ID 为 0
    config.enable_use_gpu(100, 0)
    
    # 启用 TensorRT 进行预测加速 - FP32
    config.enable_tensorrt_engine(workspace_size = 1 << 28, 
                                  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 << 28, 
                                  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 << 28, 
                                  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.pdmodel", "./mobilenet.pdiparams")
    
    # 启用 GPU 进行预测 - 初始化 GPU 显存 100 M, Deivce_ID 为 0
    config.enable_use_gpu(100, 0)
    
    # 启用 TensorRT 进行预测加速 - Int8
    config.enable_tensorrt_engine(workspace_size = 1 << 29, 
                                  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("./ernie.pdmodel", "./ernie.pdiparams")
    
    # 启用 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 分配的显存大小,最大为 16 MB
    # 参数: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.pdmodel", "./mobilenet_v1.pdiparams")
    
    # 启用 XPU,并设置 L3 cache 大小为 10 MB
    config.enable_xpu(10 * 1024 * 1024)
    
    使用 IPU 进行预测

    API定义如下:

    # 启用 IPU 进行预测
    # 参数:ipu_device_num - 所需要的 IPU 个数.
    # 参数:ipu_micro_batch_size - 计算图输入的 batch size,用于根据输入 batch size 进行全图 Tensor shape 推导,仅在动态输入 batch size 的情况生效
    # 参数:ipu_enable_pipelining - 使能 IPU 间数据流水
    # 参数:ipu_batches_per_step - 在使能数据流水的条件下,指定每次跑多少 batch 的数据,如果关闭数据流水,该值应设置为 1
    # 返回:None
    paddle.inference.Config.enable_ipu(ipu_device_num = 1, 
                                       ipu_micro_batch_size = 1,
                                       ipu_enable_pipelining = False,
                                       ipu_batches_per_step = 1)
    
    
    
    # 配置 IPU 构图参数
    # 参数:ipu_enable_fp16 - 使能 float16 模式,将 float32 计算图转换为 float16 计算图
    # 参数:ipu_replica_num - 设置实例个数,举例 ipu_device_num = 2,表示单个实例需要 2 个 IPU 运行,设置ipu_replica_num = 8,表示总共有 8 个相同实例,所以总共需要 16 个 IPU
    # 参数:ipu_available_memory_proportion - 设置 matmul / conv OP 可使用的内存比例,取值 (0.0, 1.0],比例越高,计算性能越好
    # 参数:ipu_enable_half_partial - matmul OP 中间结果以 float16 存储于片上
    # 返回:None
    paddle.inference.Config.set_ipu_config(ipu_enable_fp16 = False,
                                           ipu_replica_num = 1,
                                           ipu_available_memory_proportion = 1.0,
                                           ipu_enable_half_partial = False)
    

    代码示例:

    # 引用 paddle inference 预测库
    import paddle.inference as paddle_infer
    
    # 创建 config
    config = paddle_infer.Config("./mobilenet_v1.pdmodel", "./mobilenet_v1.pdiparams")
    
    # 启用 IPU,并设置单个实例所需要的 IPU 个数为 1
    config.enable_ipu(1)
    
    # 使能 float16 模式
    config.set_ipu_config(True)
    
    使用 ONNXRuntime 进行预测

    API定义如下:

    # 启用 ONNXRuntime 进行预测
    # 参数:None
    # 返回:None
    paddle.inference.Config.enable_onnxruntime()
    
    # 禁用 ONNXRuntime 进行预测
    # 参数:None
    # 返回:None
    paddle.inference.Config.disable_onnxruntime()
    
    # 判断是否启用 ONNXRuntime 
    # 参数:None
    # 返回:bool - 是否启用 ONNXRuntime 
    paddle.inference.Config.onnxruntime_enabled()
    
    # 启用 ONNXRuntime 预测时开启优化
    # 参数:None
    # 返回:None
    paddle.inference.Config.enable_ort_optimization()
    

    ONNXRuntime设置代码示例:

    # 引用 paddle inference 预测库
    import paddle.inference as paddle_infer
    
    # 创建 config
    config = paddle_infer.Config("./model.pdmodel", "./model.pdiparams")
    
    # 启用 ONNXRuntime 进行预测
    config.enable_onnxruntime()
    # 通过 API 获取 ONNXRuntime 信息
    print("Use ONNXRuntime is: {}".format(config.onnxruntime_enabled())) # True
    
    # 开启 ONNXRuntime 优化
    config.enable_ort_optimization()
    
    # 设置 ONNXRuntime 算子计算线程数为 10
    config.set_cpu_math_library_num_threads(10)
    
    # 禁用 ONNXRuntime 进行预测
    config.disable_onnxruntime()
    
    # 通过 API 获取 ONNXRuntime 信息
    print("Use ONNXRuntime is: {}".format(config.onnxruntime_enabled())) # False
    
    设置模型优化方法
    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.pdmodel", "./mobilenet_v1.pdiparams")
    
    # 开启 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.pdmodel", "./mobilenet_v1.pdiparams")
    
    # 启用 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.pdmodel", "./mobilenet_v1.pdiparams")
    
    # 开启 CPU 显存优化
    config.enable_memory_optim()
    
    # 启用 GPU 进行预测
    config.enable_use_gpu(100, 0)
    
    # 开启 GPU 显存优化
    config.enable_memory_optim()
    
    设置缓存路径

    注意: 如果当前使用的为 TensorRT INT8 且设置从内存中加载模型,则必须通过 set_optim_cache_dir 来设置缓存路径。

    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.pdmodel", "./mobilenet_v1.pdiparams")
    
    # 设置缓存路径
    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.pdmodel", "./mobilenet_v1.pdiparams")
    
    # 打开 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.pdmodel", "./mobilenet_v1.pdiparams")
    
    # 去除 Paddle Inference 运行中的 LOG
    config.disable_glog_info()
    
    # 判断是否禁用 LOG - true
    print("GLOG INFO is: {}".format(config.glog_info_disabled()))
    
    查看config配置

    API定义如下:

    # 返回 config 的配置信息
    # 参数:None
    # 返回:string - config 配置信息
    paddle.inference.Config.summary()
    

    调用summary()的输出如下所示:

    +-------------------------------+----------------------------------+
    | Option                        | Value                            |
    +-------------------------------+----------------------------------+
    | model_dir                     | ./inference_pass/TRTFlattenTest/ |
    +-------------------------------+----------------------------------+
    | cpu_math_thread               | 1                                |
    | enable_mkdlnn                 | false                            |
    | mkldnn_cache_capacity         | 10                               |
    +-------------------------------+----------------------------------+
    | use_gpu                       | true                             |
    | gpu_device_id                 | 0                                |
    | memory_pool_init_size         | 100MB                            |
    | thread_local_stream           | false                            |
    | use_tensorrt                  | true                             |
    | tensorrt_precision_mode       | fp32                             |
    | tensorrt_workspace_size       | 1073741824                       |
    | tensorrt_max_batch_size       | 32                               |
    | tensorrt_min_subgraph_size    | 0                                |
    | tensorrt_use_static_engine    | false                            |
    | tensorrt_use_calib_mode       | false                            |
    | tensorrt_enable_dynamic_shape | false                            |
    | tensorrt_use_oss              | true                             |
    | tensorrt_use_dla              | false                            |
    +-------------------------------+----------------------------------+
    | use_xpu                       | false                            |
    +-------------------------------+----------------------------------+
    | ir_optim                      | true                             |
    | ir_debug                      | false                            |
    | memory_optim                  | false                            |
    | enable_profile                | false                            |
    | enable_log                    | true                             |
    +-------------------------------+----------------------------------+
    

    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.pdmodel", "./mobilenet_v1.pdiparams")
    
    # 根据 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.pdmodel", "./mobilenet_v1.pdiparams")
    
    # 创建 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,该接口内含同步等待 GPU 运行结束,当 Predictor 
    #    运行在 GPU 硬件时,在 CPU 线程下对该 API 调用进行计时是不准确的
    # 参数: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.pdmodel", "./mobilenet_v1.pdiparams")
    
    # 根据 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.pdmodel", "./mobilenet_v1.pdiparams")
    
    # 启用 GPU, 初始化 100 MB 显存,使用 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 文档

    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);
    

    注意事项: 一个 Config 对象只能用于调用一次 CreatePredictor 生成一个 Predictor,需要通过 CreatePredictor 创建多个 Predictor 时请分别创建 Config 对象。

    GetVersion 方法

    API定义如下:

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

    代码示例:

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

    返回值实例:

    version: 2.3.0
    commit: b207edf916
    branch: release/2.3
    

    Config 类

    Config 构造函数

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

    构造函数定义如下:

    // 创建 Config 对象,默认构造函数
    Config();
    
    // 创建 Config 对象,输入为其他 Config 对象
    Config(const Config& other);
    
    // 创建 Config 对象,输入分别为模型文件路径和参数文件路径
    Config(const std::string& prog_file, const std::string& params_file);
    

    代码示例:

    // 字符串 prog_file 为 Combine 模型文件所在路径
    std::string prog_file = "../assets/models/mobilenet_v1.pdmodel";
    // 字符串 params_file 为 Combine 模型参数文件所在路径
    std::string params_file = "../assets/models/mobilenet_v1.pdiparams";
    
    // 根据模型文件和参数文件构造 Config 对象
    paddle_infer::Config config(prog_file, params_file);
    
    // 根据 Config 对象创建预测器对象
    auto predictor = paddle_infer::CreatePredictor(config);
    

    注意事项: 一个 Config 对象只能用于调用一次 CreatePredictor 生成一个 Predictor,需要通过 CreatePredictor 创建多个 Predictor 时请分别创建 Config 对象。

    设置预测模型
    从文件中加载预测模型

    API定义如下:

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

    代码示例:

    // 字符串 prog_file 为模型文件所在路径
    std::string prog_file = "../assets/models/mobilenet_v1.pdmodel";
    // 字符串 params_file 为参数文件所在路径
    std::string params_file = "../assets/models/mobilenet_v1.pdiparams";
    
    // 创建默认 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.pdmodel";
    std::string params_file = "../assets/models/mobilenet_v1.pdiparams";
    
    // 加载模型文件到内存
    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 加速库计算线程数
    // 参数:cpu_math_library_num_threads - CPU 加速库计算线程数
    // 返回:None
    void SetCpuMathLibraryNumThreads(int cpu_math_library_num_threads);
    
    // 获取 CPU 加速库计算线程数
    // 参数:None
    // 返回:int - CPU 加速库计算线程数
    int cpu_math_library_num_threads() const;
    

    代码示例:

    // 创建默认 Config 对象
    paddle_infer::Config config();
    
    // 设置 CPU 加速库线程数为 10
    config.SetCpuMathLibraryNumThreads(10);
    
    // 通过 API 获取 CPU 信息
    int num_thread = config.cpu_math_library_num_threads();
    std::cout << "CPU 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 INT8
    // 参数:op_list - 使用 MKLDNN INT8 加速的 OP 列表
    // 返回:None
    void EnableMkldnnInt8(const std::unordered_set<std::string>& op_list);
    
    // 判断是否启用 MKLDNN INT8
    // 参数:None
    // 返回:bool - 是否启用 MKLDNN INT8
    bool mkldnn_int8_enabled() const;
    
    // 判断是否启用 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.pdmodel",
                                FLAGS_infer_model + "/mobilenet.pdiparams");
    
    // 启用 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.pdmodel",
                                FLAGS_infer_model + "/mobilenet.pdiparams");
    
    // 启用 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;
    

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

    // 创建 Config 对象
    paddle_infer::Config config(FLAGS_infer_model + "/mobilenet.pdmodel",
                                FLAGS_infer_model + "/mobilenet.pdiparams");
    
    // 启用 MKLDNN 进行预测
    config.EnableMKLDNN();
    
    // 启用 MKLDNN INT8 进行预测
    config.EnableMkldnnInt8();
    
    // 通过 API 获取 MKLDNN INT8 启用结果 - true
    std::cout << "Enable MKLDNN INT8 is: " << config.mkldnn_int8_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 FP16 计算精度进行预测
    // 参数:op_list - 保持 FP32 计算精度算子名单
    // 返回:None
    void Exp_EnableUseGpuFp16(std::unordered_set<std::string> op_list);
    
    // 判断是否启用 GPU FP16 计算精度 
    // 参数:None
    // 返回:bool - 是否启用 GPU FP16 计算精度
    bool gpu_fp16_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
    
    // 启用 GPU FP16 计算精度进行预测
    config.EnableUseGpu(100, 0);
    config.Exp_EnableUseGpuFp16();
    // 通过 API 获取是否启用了 GPU FP16 计算精度
    std::cout << "Use GPU FP16 is: " << config.gpu_fp16_enabled() << std::endl; // true
    

    开启多线程流代码示例:

    // 自定义 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("./model/resnet.pdmodel", "./model/resnet.pdiparams");
          config.EnableGpuMultiStream();
          test_main(config, &barrier);
        });
      }
      for (auto& th : threads) {
        th.join();
      }
    }
    
    TensorRT 设置

    注意:

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

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

    3. 启用 TensorRT OSS 可以支持更多 plugin,详细参考 TensorRT OSS。当前开始OSS只对ERNIE/BERT模型加速效果(示例代码)。

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

    API定义如下:

    // 启用 TensorRT 进行预测加速
    // 参数:workspace_size     - 指定 TensorRT 在网络编译阶段进行kernel选择时使用的工作空间大小,不影响运
    //                           行时显存占用。该值设置过小可能会导致选不到最佳kernel,设置过大时会增加初始
    //                           化阶段的显存使用,请根据实际情况调整,建议值256MB
    //      max_batch_size     - 设置最大的 batch 大小,运行时 batch 大小不得超过此限定值
    //      min_subgraph_size  - Paddle 内 TensorRT 是以子图的形式运行,为了避免性能损失,当 TensorRT 
    //                           子图内部节点个数大于 min_subgraph_size 的时候,才会使用 TensorRT 运行
    //      precision          - 指定使用 TensorRT 的精度,支持 FP32(kFloat32),FP16(kHalf),
    //                           Int8(kInt8)
    //      use_static         - 若指定为 true,在初次运行程序退出Predictor析构的时候会将 TensorRT 的优
    //                           化信息进行序列化到磁盘上。下次运行时直接加载优化的序列化信息而不需要重新生
    //                           成,以加速启动时间(需要在同样的硬件和相同 TensorRT 版本的情况下)
    //      use_calib_mode     - 若要运行 TensorRT 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,推理时输入 shape 的任何
    //                                 维度均不能小于该项配置
    //      max_input_shape          - TensorRT 子图支持动态 shape 的最大 shape,推理是输入 shape 的任何
    //                                 维度均不能大于该项配置
    //      optim_input_shape        - TensorRT 子图支持动态 shape 的最优 shape,TensorRT 在初始化选
    //                                 kernel 阶段以此项配置的 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 动态 shape 的自动推导,使用示例参考 https://github.com/PaddlePaddle/Paddle-Inference-Demo/blob/d6c1aac35fa8a02271c9433b0565ff0054a5a82b/c++/paddle-trt/tuned_dynamic_shape 
    // 参数: shape_range_info_path  - 统计生成的 shape 信息存储文件路径
    //       allow_build_at_runtime - 是否开启运行时重建 TensorRT 引擎功能,当设置为 true 时,输入 shape 
    //                                超过 tune 范围时会触发 TensorRT 重建。当设置为 false 时,输入 shape
    //                                超过 tune 范围时会引起推理出错
    // 返回:None
    void EnableTunedTensorRtDynamicShape(const std::string& shape_range_info_path,
                                         bool allow_build_at_runtime = true);
    
    
    // 启用 TensorRT OSS 进行 ERNIE / BERT 预测加速(示例代码 https://github.com/PaddlePaddle/Paddle-Inference-Demo/tree/master/c%2B%2B/ernie-varlen )
    // 参数: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("./model/mobilenet.pdmodel", "./model/mobilenet.pdiparams");
    
    // 启用 GPU 进行预测
    config.EnableUseGpu(100, 0);
    
    // 启用 TensorRT 进行预测加速 - FP32
    config.EnableTensorRtEngine(1 << 28, 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 << 28, 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 << 28, 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("./model/mobilenet.pdmodel", "./model/mobilenet.pdiparams");
    
    // 启用 GPU 进行预测
    config.EnableUseGpu(100, 0);
    
    // 启用 TensorRT 进行预测加速 - Int8
    config.EnableTensorRtEngine(1 << 29, 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("./model/ernie.pdmodel", "./model/ernie.pdiparams");
    
    // 启用 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);
    
    // 开启 Lite 子图引擎
    config.EnableLiteEngine();
    
    // 启用 XPU,并设置 l3 cache 大小为 10M
    config.EnableXpu(10*1024*1024);
    

    注意事项: Xpu 推理依赖 Lite 子图引擎,配置需开启 EnableLiteEngine,API 文档参考设置模型优化方法

    使用 ONNXRuntime 进行预测

    API定义如下:

    // 启用 ONNXRuntime 进行预测
    // 参数:None
    // 返回:None
    void EnableONNXRuntime();
    
    // 禁用 ONNXRuntime 进行预测
    // 参数:None
    // 返回:None
    void DisableONNXRuntime();
    
    // 判断是否启用 ONNXRuntime 
    // 参数:None
    // 返回:bool - 是否启用 ONNXRuntime 
    bool use_onnxruntime() const;
    
    // 启用 ONNXRuntime 预测时开启优化
    // 参数:None
    // 返回:None
    void EnableORTOptimization();
    

    ONNXRuntime设置代码示例:

    // 创建 Config 对象
    paddle_infer::Config config(FLAGS_model_file, FLAGS_params_file);
    
    // 启用 ONNXRuntime
    config.EnableONNXRuntime();
    // 通过 API 获取 ONNXRuntime 信息
    std::cout << "Use ONNXRuntime is: " << config.use_onnxruntime() << std::endl; // true
    
    // 开启 ONNXRuntime 优化
    config.EnableORTOptimization();
    
    // 设置 ONNXRuntime 算子计算线程数为 10
    config.SetCpuMathLibraryNumThreads(10);
    
    // 禁用 ONNXRuntime 进行预测
    config.DisableONNXRuntime();
    // 通过 API 获取 ONNXRuntime 信息
    std::cout << "Use ONNXRuntime is: " << config.use_onnxruntime() << std::endl; // false
    
    使用 IPU 进行预测

    API定义如下:

    // 启用 IPU 进行预测
    // 参数:ipu_device_num - 所需要的 IPU 个数.
    // 参数:ipu_micro_batch_size - 计算图输入的 batch size,用于根据输入 batch size 进行全图 Tensor shape 推导,仅在动态输入 batch size 的情况生效
    // 参数:ipu_enable_pipelining - 使能 IPU 间数据流水
    // 参数:ipu_batches_per_step - 在使能数据流水的条件下,指定每次跑多少 batch 的数据,如果关闭数据流水,该值应设置为 1
    // 返回:None
    void EnableIpu(int ipu_device_num = 1, int ipu_micro_batch_size = 1,
                   bool ipu_enable_pipelining = false,
                   int ipu_batches_per_step = 1);
    
    // 配置 IPU 构图参数
    // 参数:ipu_enable_fp16 - 使能 float16 模式,将 float32 计算图转换为 float16 计算图
    // 参数:ipu_replica_num - 设置实例个数,举例 ipu_device_num = 2,表示单个实例需要 2 个 IPU 运行,设置 ipu_replica_num = 8,表示总共有 8 个相同实例,所以总共需要 16 个IPU
    // 参数:ipu_available_memory_proportion - 设置 matmul / conv Op 可使用的内存比例,取值 (0.0, 1.0], 比例越高,计算性能越好
    // 参数:ipu_enable_half_partial - matmul Op 中间结果以 float16 存储于片上
    // 返回:None
    void SetIpuConfig(bool ipu_enable_fp16 = false, int ipu_replica_num = 1,
                      float ipu_available_memory_proportion = 1.0,
                      bool ipu_enable_half_partial = false);
    

    代码示例:

    // 创建 Config 对象
    paddle_infer::Config config(FLAGS_model_dir);
    
    // 启用 IPU,并设置单个实例所需要的 IPU 个数为 1
    config.EnableIpu(1);
    // 启动 float16 模式
    config.SetIpuConfig(true);
    
    设置模型优化方法
    IR 优化

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

    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("./model/resnet50.pdmodel", "./model/resnet50.pdiparams");
    
    // 开启 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 子图,以使用 XPU / NPU 推理加速
    // 参数: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("./model/resnet50.pdmodel", "./model/resnet50.pdiparams");
    
    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("./mobilenet.pdmodel", "./mobilenet.pdiparams");
    
    // 开启 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();
    
    // 设置缓存路径
    config.SetOptimCacheDir("./model/OptimCacheDir");
    
    FC Padding

    在使用MKL时,启动此配置项可能会对模型推理性能有提升(参考PR描述)。

    API定义如下:

    // 禁用 FC Padding
    // 参数:None
    // 返回:None
    void DisableFCPadding();
    
    // 判断是否启用 FC Padding
    // 参数:None
    // 返回:bool - 是否启用 FC Padding
    bool use_fc_padding() const;
    

    代码示例:

    // 创建 Config 对象
    paddle_infer::Config config("./mobilenet.pdmodel", "./mobilenet.iparams");
    
    // 禁用 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("./mobilenet.pdmodel", "./mobilenet.iparams");
    
    // 打开 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("./mobilenet.pdmodel", "./mobilenet.iparams");
    
    // 去除 Paddle Inference 运行中的 LOG
    config.DisableGlogInfo();
    
    // 判断是否禁用 LOG - true
    std::cout << "GLOG INFO is: " << config.glog_info_disabled() << std::endl;
    
    查看config配置

    API定义如下:

    // 返回 config 的配置信息
    // 参数:None
    // 返回:string - config 配置信息
    std::string Summary();
    

    调用 Summary() 的输出如下所示:

    +-------------------------------+----------------------------------+
    | Option                        | Value                            |
    +-------------------------------+----------------------------------+
    | model_dir                     | ./inference_pass/TRTFlattenTest/ |
    +-------------------------------+----------------------------------+
    | cpu_math_thread               | 1                                |
    | enable_mkdlnn                 | false                            |
    | mkldnn_cache_capacity         | 10                               |
    +-------------------------------+----------------------------------+
    | use_gpu                       | true                             |
    | gpu_device_id                 | 0                                |
    | memory_pool_init_size         | 100MB                            |
    | thread_local_stream           | false                            |
    | use_tensorrt                  | true                             |
    | tensorrt_precision_mode       | fp32                             |
    | tensorrt_workspace_size       | 1073741824                       |
    | tensorrt_max_batch_size       | 32                               |
    | tensorrt_min_subgraph_size    | 0                                |
    | tensorrt_use_static_engine    | false                            |
    | tensorrt_use_calib_mode       | false                            |
    | tensorrt_enable_dynamic_shape | false                            |
    | tensorrt_use_oss              | true                             |
    | tensorrt_use_dla              | false                            |
    +-------------------------------+----------------------------------+
    | use_xpu                       | false                            |
    +-------------------------------+----------------------------------+
    | ir_optim                      | true                             |
    | ir_debug                      | false                            |
    | memory_optim                  | false                            |
    | enable_profile                | false                            |
    | enable_log                    | true                             |
    +-------------------------------+----------------------------------+
    

    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("./mobilenet.pdmodel", "./mobilenet.pdiparams");
    
    // 开启 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("./mobilenet.pdmodel", "./mobilenet.pdiparams");
    
    // 开启 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提供的接口设置输入数据、执行模型预测、获取输出等。

    注意事项: 一个 Config 对象只能用于调用一次 CreatePredictor 生成一个 Predictor,需要通过 CreatePredictor 创建多个 Predictor 时请分别创建 Config 对象。

    获取输入输出

    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("./resnet.pdmodel", "./resnet.pdiparams");
    
    // 创建 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("./resnet.pdmodel", "./resnet.pdiparams");
    // 启用 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,该接口内含同步等待 GPU 运行结束,当 Predictor 
    // 运行在 GPU 硬件时,在 CPU 线程下对该 API 调用进行计时是不准确的
    //
    // 参数:data - CPU 数据指针
    // 返回:None
    template <typename T>
    void CopyToCpu(T* data);
    
    // 使用用户的数据指针创建输入/输出 Tensor
    // 创建输入 Tensor 时,用户保证输入指针数据预测过程中有效
    // 创建输出 Tensor 时,用户保证输出指针的数据长度大于等于模型的输出数据大小
    // 参数:data - CPU/GPU 数据指针
    // 参数:shape - 数据 shape
    // 参数:place - 数据的存放位置
    // 参数:layout - 数据格式,默认为 NCHW,当前仅支持 NCHW
    // 返回:None
    template <typename T>
    void ShareExternalData(const T* data, const std::vector<int>& shape,
                           PlaceType place, DataLayout layout = DataLayout::kNCHW);
    
    // 获取 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("./resnet.pdmodel", "./resnet.pdiparams");
    
    // 创建 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());
    
    //  方式3: 通过 ShareExternalData 设置输入数据
    input_tensor->ShareExternalData<float>(input, INPUT_SHAPE, PlaceType::kCPU);
    
    // 执行预测
    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,
      INT8,
      FLOAT16,
    };
    
    // 获取各个 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("./mobilenet.pdmodel", "./mobilenet.pdiparams");
    
    // 启用 GPU 进行预测
    config.EnableUseGpu(100, 0);
    
    // 启用 TensorRT 进行预测加速 - FP16
    config.EnableTensorRtEngine(1 << 28, 1, 3, 
                                paddle_infer::PrecisionType::kHalf, false, false);
    
    PlaceType

    PlaceType 为目标设备硬件类型,用户可以根据应用场景选择硬件平台类型。枚举变量定义如下:

    // PlaceType 枚举类型定义
    enum class PlaceType { kUNK = -1, kCPU, kGPU, kXPU, kNPU, kIPU, kCUSTOM };
    

    代码示例:

    // 创建 Config 对象
    paddle_infer::Config config("./mobilenet.pdmodel", "./mobilenet.pdiparams");
    
    // 启用 GPU 预测
    config.EnableUseGpu(100, 0);
    
    // 创建 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;
    

    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);
    
    设置预测模型
    从文件中加载预测模型

    API定义如下:

    // 设置模型文件路径
    // 参数:pd_config        - Config 对象指针
    //      prog_file_path   - 模型文件所在路径
    //      params_file_path - 模型参数文件所在路径
    // 返回:None
    void PD_ConfigSetModel(PD_Config* pd_config, const char* prog_file_path, const char* params_file_path);
    
    // 设置模型文件路径,当需要从磁盘加载模型时使用。
    // 参数:pd_config      - Config 对象指针
    //      prog_file_path - 模型文件路径
    // 返回:None
    void PD_ConfigSetProgFile(PD_Config* pd_config, const char* prog_file_path);
    
    
    // 设置参数文件路径,当需要从磁盘加载模型时使用
    // 参数: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();
    
      // 设置推理模型路径
      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 加速库计算线程数
    // 参数:config - Config 对象指针
    //      cpu_math_library_num_threads - CPU 加速库计算线程数
    // 返回:None
    void PD_ConfigSetCpuMathLibraryNumThreads(PD_Config* pd_config, int32_t cpu_math_library_num_threads);
    
    // 获取 CPU 加速库计算线程数
    // 参数:pd_config - Config 对象指针
    // 返回:int32_t - CPU 加速库计算线程数
    int32_t PD_ConfigGetCpuMathLibraryNumThreads(PD_Config* pd_config);
    

    代码示例:

    // 创建 Config 对象
    PD_Config* config = PD_ConfigCreate();
    
    // 设置 CPU 加速库线程数为 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. 可以尝试启用 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 显存 100 MB, 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);
    
    TensorRT 设置

    注意:

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

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

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

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

    API定义如下:

    // 启用 TensorRT 进行预测加速
    // 参数:pd_config          - Config 对象指针
    //      workspace_size     - 指定 TensorRT 在网络编译阶段进行kernel选择时使用的工作空间大小,不影响运
    //                           行时显存占用。该值设置过小可能会导致选不到最佳kernel,设置过大时会增加初始
    //                           化阶段的显存使用,请根据实际情况调整,建议值256MB
    //      max_batch_size     - 设置最大的 batch 大小,运行时 batch 大小不得超过此限定值
    //      min_subgraph_size  - Paddle 内 TensorRT 是以子图的形式运行,为了避免性能损失,当 TensorRT 
    //                           子图内部节点个数大于 min_subgraph_size 的时候,才会使用 TensorRT 运行
    //      precision          - 指定使用 TensorRT 的精度,支持 FP32(kFloat32),FP16(kHalf),
    //                           Int8(kInt8)
    //      use_static         - 若指定为 true,在初次运行程序退出Predictor析构的时候会将 TensorRT 的优
    //                           化信息进行序列化到磁盘上。下次运行时直接加载优化的序列化信息而不需要重新生
    //                           成,以加速启动时间(需要在同样的硬件和相同 TensorRT 版本的情况下)
    //      use_calib_mode     - 若要运行 TensorRT 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 显存 100MB, 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();
    
    // 设置预测模型路径
    const char* model_path  = "./model/inference.pdmodel";  
    const char* params_path = "./model/inference.pdiparams";
    PD_ConfigSetModel(config, model_path, params_path);
    
    // 启用 GPU 进行预测 - 初始化 GPU 显存 100MB, 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/ernie.pdmodel";  
    const char* params_path = "./model/ernie.pdiparams";
    PD_ConfigSetModel(config, model_path, params_path);
    
    // 启用 GPU 进行预测 - 初始化 GPU 显存 100MB, 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 大小为 100MB
    PD_ConfigEnableXpu(config, 100);
    
    // 判断是否开启 XPU - True
    printf("Use XPU is: %s\n", PD_ConfigUseXpu(config) ? "True" : "False");
    
    // 销毁 Config 对象
    PD_ConfigDestroy(config);
    
    使用 ONNXRuntime 进行推理

    API定义如下:

    // 启用 ONNXRuntime 进行推理
    // 参数:None
    // 返回:None
    void PD_ConfigEnableONNXRuntime(PD_Config* pd_config);
    
    // 禁用 ONNXRuntime 进行推理
    // 参数:None
    // 返回:None
    void PD_ConfigDisableONNXRuntime(PD_Config* pd_config);
    
    // 判断是否启用 ONNXRuntime 
    // 参数:None
    // 返回:bool - 是否启用 ONNXRuntime 
    PD_Bool PD_ConfigONNXRuntimeEnabled(PD_Config* pd_config);
    
    // 启用 ONNXRuntime 推理时开启优化
    // 参数:None
    // 返回:None
    void PD_ConfigEnableORTOptimization(PD_Config* pd_config);
    

    ONNXRuntime设置代码示例:

    // 创建 Config 对象
    PD_Config* config = PD_ConfigCreate();
    
    // 启用 ONNXRuntime
    PD_ConfigEnableONNXRuntime(config);
    // 通过 API 获取 ONNXRuntime 信息
    printf("Use ONNXRuntime is: %s\n", PD_ConfigONNXRuntimeEnabled(config) ? "True" : "False"); // True
    
    // 开启ONNXRuntime优化
    PD_ConfigEnableORTOptimization(config);
    
    // 设置 ONNXRuntime 算子计算线程数为 10
    PD_ConfigSetCpuMathLibraryNumThreads(config, 10);
    
    // 禁用 ONNXRuntime 进行推理
    PD_ConfigDisableONNXRuntime(config);
    // 通过 API 获取 ONNXRuntime 信息
    printf("Use ONNXRuntime is: %s\n", PD_ConfigONNXRuntimeEnabled(config) ? "True" : "False"); // False
    
    设置模型优化方法
    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);
    
    // 删除图分析阶段指定的 PASS
    // 参数:pd_config - Config 对象指针
    //      pass_name - 要删除的 PASS 名称
    // 返回:None
    void PD_DeletePass(PD_AnalysisConfig* config, char* pass_name);
    

    代码示例:

    // 创建 Config 对象
    PD_Config* config = PD_ConfigCreate();
    
    // 设置预测模型路径
    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);
    // 删除 PASS fc_fuse_pass
    PD_DeletePass(config, "fc_fuse_pass");
    
    // 通过 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 显存 100MB, 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 显存 100MB, 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();
    
    // 设置预测模型路径
    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

    在使用MKL时,启动此配置项可能会对模型推理性能有提升(参考PR描述)。

    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);
    
    查看config配置

    API定义如下:

    // 返回config的配置信息
    // 参数:pd_config - Config 对象指针
    // 返回:const char* - Config 配置信息,注意用户需释放该指针。
    const char* PD_ConfigSummary(PD_Config* pd_config);
    

    代码示例:

    // 创建 Config 对象
    PD_Config* config = PD_ConfigCreate();
    
    PD_Cstr* summary = PD_ConfigSummary(config);
    
    printf("summary is %s\n", summary->data);
    
    // 销毁 summary 对象
    PD_CstrDestroy(summary);
    // 销毁 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();
    
    // 设置推理模型路径
    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();
    
    // 设置推理模型路径
    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();
    
    // 设置推理模型路径
    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();
    
    // 设置推理模型路径
    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);
    

    GO API 文档

    AnalysisConfig 方法

    创建 Config

    Config 对象相关方法用于创建预测相关配置,构建 Predictor 对象的配置信息,如模型路径、是否开启 GPU 等。

    相关方法定义如下:

    // 创建 Config 对象
    // 参数:None
    // 返回:*Config - Config 对象指针
    func Config() *Config
    
    // 判断当前 Config 是否有效
    // 参数:None
    // 返回:bool - 当前 Config 是否有效
    func (config *Config) IsValid() bool
    

    代码示例:

    package main
    
    // 引入 Paddle Golang Package
    import pd "github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi"
    import fmt
    
    func main() {
        // 创建 Config 对象
        config := pd.NewConfig()
    
        // 判断当前 Config 是否有效 - true
        fmt.Println("Config validation is: ", config.IsValid())
    }
    
    设置预测模型
    从文件中加载预测模型

    API定义如下:

    // 设置模型文件路径
    // 参数:model - 模型文件所在路径
    //      params - 模型参数文件所在路径
    // 返回:None
    func (config *Config) SetModel(model, params string)
    
    // 获取 Combined 模型的模型文件路径
    // 参数:无
    // 返回:string - 模型文件路径
    func (config *Config) ProgFile() string
    
    // 获取 Combined 模型的参数文件路径
    // 参数:无
    // 返回:string - 参数文件路径
    func (config *Config) ParamsFile() string
    

    代码示例:

    package main
    
    // 引入 Paddle Golang Package
    import pd "github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi"
    import fmt
    
    func main() {
        // 创建 Config 对象
        config := paddle.NewConfig()
    
        // 设置预测模型路径,这里为 Combined 模型
        config.SetModel("data/resnet.pdmodel", "data/resnet.pdiparams")
    
        // 输出模型路径
        fmt.Println("Combined model path is: ", config.ProgFile())
        fmt.Println("Combined param path is: ", config.ParamsFile())
    }
    
    使用 CPU 进行预测

    注意:

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

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

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

    CPU 设置

    API定义如下:

    // 设置 CPU 加速库计算线程数
    // 参数:mathThreadsNum - CPU 加速库计算线程数
    // 返回:None
    func (config *Config) SetCpuMathLibraryNumThreads(mathThreadsNum int32)
    
    // 获取 CPU 加速库计算线程数
    // 参数:无
    // 返回:int - CPU 加速库计算线程数
    func (config *Config) CpuMathLibraryNumThreads() int32
    

    代码示例:

    package main
    
    // 引入 Paddle Golang Package
    import pd "github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi"
    import fmt
    
    func main() {
        // 创建 Config 对象
        config := paddle.NewConfig()
    
        // 设置预测模型路径
        config.SetCpuMathLibraryNumThreads(10)
    
        // 输出模型路径
        fmt.Println("CPU Math Lib Thread Num is: ", config.CpuMathLibraryNumThreads())
    }
    
    MKLDNN 设置

    注意:

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

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

    API定义如下:

    // 启用 MKLDNN 进行预测加速
    // 参数:无
    // 返回:None
    func (config *Config) EnableMkldnn()
    
    // 判断是否启用 MKLDNN
    // 参数:无
    // 返回:bool - 是否启用 MKLDNN
    func (config *Config) MkldnnEnabled() bool
    
    // 启用 MKLDNN BFLOAT16
    // 参数:无
    // 返回:None
    func (config *Config) EnableMkldnnBfloat16()
    
    // 判断是否启用 MKLDNN BFLOAT16
    // 参数:无
    // 返回:bool - 是否启用 MKLDNN BFLOAT16
    func (config *Config) MkldnnBfloat16Enabled() bool
    

    代码示例:

    package main
    
    // 引入 Paddle Golang Package
    import pd "github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi"
    import fmt
    
    func main() {
        // 创建 Config 对象
        config := pd.NewConfig()
    
        // 启用 MKLDNN 进行预测
        config.EnableMkldnn()
    
        // 通过 API 获取 MKLDNN 启用结果 - true
        fmt.Println("Enable MKLDNN is: ", config.MkldnnEnabled())
    
        // 启用 MKLDNN BFLOAT16 进行预测
        config.EnableMkldnnBfloat16()
    
        // 通过 API 获取 MKLDNN BFLOAT16 启用结果
        // 如果当前CPU支持AVX512,则返回 true, 否则返回 false
        fmt.Println("Enable MKLDNN BF16 is: ", config.MkldnnBfloat16Enabled())
    }
    
    使用 GPU 进行预测

    注意:

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

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

    GPU 设置

    API定义如下:

    // 启用 GPU 进行预测
    // 参数:memorySize - 初始化分配的 GPU 显存,以 MB 为单位
    //      deviceId - 设备 id
    // 返回:None
    func (config *Config) EnableUseGpu(memorySize uint64, deviceId int32)
    
    // 禁用 GPU 进行预测
    // 参数:无
    // 返回:None
    func (config *Config) DisableGpu()
    
    // 判断是否启用 GPU 
    // 参数:无
    // 返回:bool - 是否启用 GPU 
    func (config *Config) UseGpu() bool
    
    // 获取 GPU 的 device id
    // 参数:无
    // 返回:int -  GPU 的 device id
    func (config *Config) GpuDeviceId() int32
    
    // 获取 GPU 的初始显存大小
    // 参数:无
    // 返回:int -  GPU 的初始的显存大小
    func (config *Config) MemoryPoolInitSizeMb() int32
    
    // 初始化显存占总显存的百分比
    // 参数:无
    // 返回:float32 - 初始的显存占总显存的百分比
    func (config *Config) FractionOfGpuMemoryForPool() float32
    

    GPU设置代码示例:

    package main
    
    // 引入 Paddle Golang Package
    import pd "github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi"
    import fmt
    
    func main() {
        // 创建 Config 对象
        config := pd.NewConfig()
      
        // 启用 GPU 进行预测 - 初始化 GPU 显存 100MB, DeivceID 为 0
        config.EnableUseGpu(100, 0)
      
        // 通过 API 获取 GPU 信息
        fmt.Println("Use GPU is: ", config.UseGpu()) // True
        fmt.Println("GPU deivce id is: ", config.GpuDeviceId())
        fmt.Println("GPU memory size is: ", config.MemoryPoolInitSizeMb())
        fmt.Println("GPU memory frac is: ", config.FractionOfGpuMemoryForPool())
      
        // 禁用 GPU 进行预测
        config.DisableGpu()
      
        // 通过 API 获取 GPU 信息 - False
        fmt.Println("Use GPU is: ", config.UseGpu())
    }
    
    TensorRT 设置

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

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

    API定义如下:

    // 启用 TensorRT 进行预测加速
    // 参数:workspaceSize     - 指定 TensorRT 使用的工作空间大小
    //      maxBatchSize      - 设置最大的 batch 大小,运行时 batch 大小不得超过此限定值
    //      minSubgraphSize   - Paddle-TRT 是以子图的形式运行,为了避免性能损失,当子图内部节点个数
    //                          大于 min_subgraph_size 的时候,才会使用 Paddle-TRT 运行
    //      precision         - 指定使用 TRT 的精度,支持 FP32(kFloat32),FP16(kHalf),Int8(kInt8)
    //      useStatic         - 若指定为 true,在初次运行程序的时候会将 TRT 的优化信息进行序列化到磁盘上,
    //                          下次运行时直接加载优化的序列化信息而不需要重新生成
    //      useCalibMode      - 若要运行 Paddle-TRT INT8 离线量化校准,需要将此选项设置为 true
    // 返回:None
    func (config *Config) EnableTensorRtEngine(workspaceSize int32, maxBatchSize int32, minSubgraphSize int32,
    	precision Precision, useStatic bool, useCalibMode bool)
    
    // 设置 TensorRT 的动态 Shape
    // 参数:minInputShape          - TensorRT 子图支持动态 shape 的最小 shape
    //      maxInputShape          - TensorRT 子图支持动态 shape 的最大 shape
    //      optimInputShape        - TensorRT 子图支持动态 shape 的最优 shape
    //      disableTrtPluginFp16   - 设置 TensorRT 的 plugin 不在 fp16 精度下运行
    // 返回:None
    func (config *Config) SetTRTDynamicShapeInfo(minInputShape map[string][]int32, 
                                                 maxInputShape map[string][]int32, 
                                                 optimInputShape map[string][]int32, 
                                                 disableTrtPluginFp16 bool)
    
    // 判断是否启用 TensorRT 
    // 参数:无
    // 返回:bool - 是否启用 TensorRT
    func (config *Config) TensorrtEngineEnabled() bool
    

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

    package main
    
    // 引入 Paddle Golang Package
    import pd "github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi"
    import fmt
    
    func main() {
        // 创建 Config 对象
        config := pd.NewConfig()
      
        // 启用 GPU 进行预测 - 初始化 GPU 显存 100MB, Deivce_ID 为 0
        config.EnableUseGpu(100, 0)
    
        // 启用 TensorRT 进行预测加速 - FP32
        config.EnableTensorRtEngine(1 << 20, 1, 3, pd.PrecisionFloat32, false, false)
    
        // 启用 TensorRT 进行预测加速 - FP16
        config.EnableTensorRtEngine(1 << 20, 1, 3, pd.PrecisionHalf, false, false)
    
        // 启用 TensorRT 进行预测加速 - Int8
        config.EnableTensorRtEngine(1 << 20, 1, 3, pd.PrecisionInt8, false, false)
      
        // 通过 API 获取 TensorRT 启用结果 - true
        fmt.Println("Enable TensorRT is: ", config.TensorrtEngineEnabled())
    }
    
    使用 ONNXRuntime 进行推理

    API定义如下:

    // 启用 ONNXRuntime 进行推理
    // 参数:None
    // 返回:None
    func (config *Config) EnableONNXRuntime()
    
    // 禁用 ONNXRuntime 进行推理
    // 参数:None
    // 返回:None
    func (config *Config) DisableONNXRuntime();
    
    // 判断是否启用 ONNXRuntime 
    // 参数:None
    // 返回:bool - 是否启用 ONNXRuntime 
    func (config *Config) ONNXRuntimeEnabled() bool;
    
    // 启用 ONNXRuntime 推理时开启优化
    // 参数:None
    // 返回:None
    func (config *Config) EnableORTOptimization();
    

    ONNXRuntime设置代码示例:

    package main
    
    // 引入 Paddle Golang Package
    import pd "github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi"
    import fmt
    
    func main() {
        // 创建 Config 对象
        config := pd.NewConfig()
    
        // 启用 ONNXRuntime 进行推理
        config.EnableONNXRuntime()
      
        // 通过 API 获取 ONNXRuntime 信息
        fmt.Println("Use ONNXRuntime is: ", config.ONNXRuntimeEnabled()) // True
      
        // 开启ONNXRuntime优化
        config.EnableORTOptimization();
    
        // 禁用 ONNXRuntime 进行推理
        config.DisableONNXRuntime()
      
        // 通过 API 获取 ONNXRuntime 信息
        fmt.Println("Use ONNXRuntime is: ", config.ONNXRuntimeEnabled()) // False
    }
    
    设置模型优化方法

    API定义如下:

    // 启用 IR 优化
    // 参数:x - 是否开启 IR 优化,默认打开
    // 返回:None
    func (config *Config) SwitchIrOptim(x bool)
    
    // 判断是否开启 IR 优化 
    // 参数:无
    // 返回:bool - 是否开启 IR 优化
    func (config *Config) IrOptim() bool
    
    // 设置是否在图分析阶段打印 IR,启用后会在每一个 PASS 后生成 dot 文件
    // 参数:x - 是否打印 IR,默认关闭
    // 返回:None
    func (config *Config) SwitchIrDebug(x bool)
    
    // // 返回 pass_builder,用来自定义图分析阶段选择的 IR
    // // 参数:pass - 需要删除的 pass 名称
    // // 返回:None
    // func (config *Config) DeletePass(pass string)
    

    代码示例:

    package main
    
    // 引入 Paddle Golang Package
    import pd "github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi"
    import fmt
    
    func main() {
        // 创建 Config 对象
        config := pd.NewConfig()
    
        // 设置预测模型路径
        config.SetModel("./model/resnet.pdmodel", "./model/resnet.pdiparams")
    
        // 开启 IR 优化
        config.SwitchIrOptim(true);
        // 开启 IR 打印
        config.SwitchIrDebug(true);
    
        // 通过 API 获取 IR 优化是否开启 - true
        fmt.Println("IR Optim is: ", config.IrOptim())
    
        // 根据 Config 创建 Predictor
        predictor := paddle.NewPredictor(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
    
    启用内存优化

    API定义如下:

    // 开启内存/显存复用,具体降低内存效果取决于模型结构
    // 参数:无
    // 返回:None
    func (config *Config) EnableMemoryOptim()
    
    // 判断是否开启内存/显存复用
    // 参数:无
    // 返回:bool - 是否开启内/显存复用
    func (config *Config) MemoryOptimEnabled() bool
    

    代码示例:

    package main
    
    // 引入 Paddle Golang Package
    import pd "github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi"
    import fmt
    
    func main() {
        // 创建 Config 对象
        config := pd.NewConfig()
    
        // 开启 CPU 内存优化
        config.EnableMemoryOptim();
        // 通过 API 获取 CPU 是否已经开启显存优化 - true
        fmt.Println("CPU Mem Optim is: ", config.MemoryOptimEnabled())
    
        // 启用 GPU 进行预测
        config.EnableUseGpu(100, 0)
        // 开启 GPU 显存优化
        config.EnableMemoryOptim();
        // 通过 API 获取 GPU 是否已经开启显存优化 - true
        fmt.Println("GPU Mem Optim is: ", config.MemoryOptimEnabled())
    }
    
    Profile 设置

    API定义如下:

    // 打开 Profile,运行结束后会打印所有 OP 的耗时占比。
    // 参数:无
    // 返回:None
    func (config *Config) EnableProfile()
    
    // 判断是否开启 Profile
    // 参数:无
    // 返回:bool - 是否开启 Profile
    func (config *Config) ProfileEnabled() bool
    

    代码示例:

    package main
    
    // 引入 Paddle Golang Package
    import pd "github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi"
    import fmt
    
    func main() {
        // 创建 Config 对象
        config := paddle.NewConfig()
    
        // 打开 Profile
        config.EnableProfile();
        // 判断是否开启 Profile - true
        fmt.Println("Profile is: ", config.ProfileEnabled())
    }
    

    执行预测之后输出的 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
    func (config *Config) DisableGlogInfo()
    

    代码示例:

    package main
    
    // 引入 Paddle Golang Package
    import pd "github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi"
    
    func main() {
        // 创建 Config 对象
        config := paddle.NewConfig()
    
        // 去除 Paddle Inference 运行中的 LOG
        config.DisableGlogInfo();
    }
    
    查看config配置

    API定义如下:

    // 返回 Config 的配置信息
    // 参数:None
    // 返回:string - Config 的配置信息
    func (config *Config) Summary() string
    

    调用Summary()的输出如下所示:

    +-------------------------------+----------------------------------+
    | Option                        | Value                            |
    +-------------------------------+----------------------------------+
    | model_dir                     | ./inference_pass/TRTFlattenTest/ |
    +-------------------------------+----------------------------------+
    | cpu_math_thread               | 1                                |
    | enable_mkdlnn                 | false                            |
    | mkldnn_cache_capacity         | 10                               |
    +-------------------------------+----------------------------------+
    | use_gpu                       | true                             |
    | gpu_device_id                 | 0                                |
    | memory_pool_init_size         | 100MB                            |
    | thread_local_stream           | false                            |
    | use_tensorrt                  | true                             |
    | tensorrt_precision_mode       | fp32                             |
    | tensorrt_workspace_size       | 1073741824                       |
    | tensorrt_max_batch_size       | 32                               |
    | tensorrt_min_subgraph_size    | 0                                |
    | tensorrt_use_static_engine    | false                            |
    | tensorrt_use_calib_mode       | false                            |
    | tensorrt_enable_dynamic_shape | false                            |
    | tensorrt_use_oss              | true                             |
    | tensorrt_use_dla              | false                            |
    +-------------------------------+----------------------------------+
    | use_xpu                       | false                            |
    +-------------------------------+----------------------------------+
    | ir_optim                      | true                             |
    | ir_debug                      | false                            |
    | memory_optim                  | false                            |
    | enable_profile                | false                            |
    | enable_log                    | true                             |
    +-------------------------------+----------------------------------+
    

    Predictor 方法

    Paddle Inference 的推理器,由 NewPredictor 根据 Config 进行创建。用户可以根据 Predictor 提供的接口设置输入数据、执行模型推理、获取输出等。

    创建 Predictor

    API定义如下:

    // 根据 Config 构建推理执行对象 Predictor
    // 参数: config - 用于构建 Predictor 的配置信息
    // 返回: *Predictor - 推理对象指针
    func NewPredictor(config *Config) *Predictor
    

    代码示例:

    package main
    
    // 引入 Paddle Golang Package
    import pd "github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi"
    import fmt
    
    func main() {
        // 创建 Config 对象
        config := pd.NewConfig()
    
        // 设置推理模型路径
        config.SetModel("./model/resnet.pdmodel", "./model/resnet.pdiparams")
    
        // 根据 Config 构建推理执行对象 Predictor
        predictor := pd.NewPredictor(config)
    
        fmt.Printf("%T\n", predictor)
    }
    
    输入输出与执行推理

    API 定义如下:

    // 获取模型输入 Tensor 的数量
    // 参数:无
    // 返回:uint - 模型输入 Tensor 的数量
    func (p *Predictor) GetInputNum() uint
    
    // 获取模型输出 Tensor 的数量
    // 参数:无
    // 返回:uint - 模型输出 Tensor 的数量
    func (p *Predictor) GetOutputNum() uint
    
    // 获取输入 Tensor 名称
    // 参数:无
    // 返回:[]string - 输入 Tensor 名称
    func (p *Predictor) GetInputNames() []string
    
    // 获取输出 Tensor 名称
    // 参数:无
    // 返回:[]string - 输出 Tensor 名称
    func (p *Predictor) GetOutputNames() []string
    
    // 获取输入 handle
    // 参数:name - 输入handle名称
    // 返回:*Tensor - 输入 handle
    func (p *Predictor) GetInputHandle(name string) *Tensor
    
    // 获取输出 handle
    // 参数:name - 输出handle名称
    // 返回:*Tensor - 输出 handle
    func (p *Predictor) GetOutputHandle(name string) *Tensor
    
    // 执行推理
    // 参数:无
    // 返回:None
    func (p *Predictor) Run()
    
    // 释放中间Tensor
    // 参数:None
    // 返回:None
    func (p *Predictor) ClearIntermediateTensor()
    
    // 释放内存池中的所有临时 Tensor
    // 参数:None
    // 返回:None
    func (p *Predictor) TryShrinkMemory()
    

    代码示例:

    package main
    
    // 引入 Paddle Golang Package
    import pd "github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi"
    import fmt
    
    func main() {
        // 创建 Config 对象
        config := pd.NewConfig()
    
        // 设置推理模型路径
        config.SetModel("./model/resnet.pdmodel", "./model/resnet.pdiparams")
    
        // 根据 Config 构建推理执行对象 Predictor
        predictor := pd.NewPredictor(config)
    
        // 获取输入输出 Tensor 信息
        println("input num: ", predictor.GetInputNum())
        println("input name: ", predictor.GetInputNames()[0])
        println("output num: ", predictor.GetOutputNum())
        println("output name: ", predictor.GetOutputNames()[0])
    
        // 获取输入输出 Tensor 指针
        input := predictor.GetInputHandle(predictor.GetInputNames()[0])
        output := predictor.GetOutputTensors(predictor.GetOutputNames()[0])
    
        inputData := make([]float32, 1 * 3 * 224 * 224)
        for i := 0; i < 1 * 3 * 224 * 224; i++ {
          inputData[i] = 1.0
        }
        input.Reshape([]int32{1, 3, 224, 224})
        input.CopyFromCpu(inputData)
    
        // 执行推理
        predictor.Run()
    
        // 获取输出 Tensor
        outData := make([]float32, numElements(output.Shape()))
    		output.CopyToCpu(outData)
    }
    
    func numElements(shape []int32) int32 {
    	n := int32(1)
    	for _, v := range shape {
    		n *= v
    	}
    	return n
    }
    

    Tensor 方法

    Tensor 是 Paddle Inference 的数据组织形式,用于对底层数据进行封装并提供接口对数据进行操作,包括设置 Shape、数据、LoD 信息等。

    注意: 应使用 PredictorGetInputHandleGetOutputHandle 接口获取输入输出 Tensor

    Tensor 的API定义如下:

    // 获取 Tensor 维度信息
    // 参数:无
    // 返回:[]int32 - 包含 Tensor 维度信息的 int 数组
    func (tensor *Tensor) Shape() []int32
    
    // 设置 Tensor 维度信息
    // 参数:shape - 包含维度信息的 int 数组
    // 返回:None
    func (tensor *Tensor) Reshape(shape []int32)
    
    // 获取 Tensor 名称
    // 参数:无
    // 返回:string - Tensor 名称
    func (tensor *Tensor) Name() string
    
    // 获取 Tensor 数据类型
    // 参数:无
    // 返回:DataType - Tensor 数据类型
    func (tensor *Tensor) Type() DataType
    
    // 设置 Tensor 数据
    // 参数:value - Tensor 数据
    // 返回:None
    func (tensor *Tensor) CopyFromCpu(value interface{})
    
    // 获取 Tensor 数据
    // 参数:value - 用来存储 Tensor 数据
    // 返回:None
    func (t *Tensor) CopyToCpu(value interface{})
    

    代码示例:

    package main
    
    // 引入 Paddle Golang Package
    import pd "github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi"
    import fmt
    
    func main() {
        // 创建 AnalysisConfig 对象
        config := pd.NewConfig()
    
        // 设置推理模型路径
        config.SetModel("./model/resnet.pdmodel", "./model/resnet.pdiparams")
    
        // 根据 Config 构建推理执行对象 Predictor
        predictor := pd.NewPredictor(config)
    
        // 获取输入输出 Tensor 信息
        println("input num: ", predictor.GetInputNum())
        println("input name: ", predictor.GetInputNames()[0])
        println("output num: ", predictor.GetOutputNum())
        println("output name: ", predictor.GetOutputNames()[0])
    
        // 获取输入输出 Tensor 指针
        input := predictor.GetInputHandle(predictor.GetInputNames()[0])
        output := predictor.GetOutputTensors(predictor.GetOutputNames()[0])
    
        inputData := make([]float32, 1 * 3 * 224 * 224)
        for i := 0; i < 1 * 3 * 224 * 224; i++ {
          inputData[i] = 1.0
        }
    
        // 设置输入 Tensor
        input.Reshape([]int32{1, 3, 224, 224})
        input.CopyFromCpu(inputData)
    
        // 执行推理
        predictor.Run()
    
        // 获取输出 Tensor
        outData := make([]float32, numElements(output.Shape()))
    		output.CopyToCpu(outData)
    }
    
    func numElements(shape []int32) int32 {
    	n := int32(1)
    	for _, v := range shape {
    		n *= v
    	}
    	return n
    }
    

    枚举类型

    DataType

    DataType 为模型中 Tensor 的数据精度。变量定义如下:

    type DataType C.PD_DataType
    
    const (
    	Unk     DataType = C.PD_DATA_UNK
    	Float32 DataType = C.PD_DATA_FLOAT32
    	Int32   DataType = C.PD_DATA_INT32
    	Int64   DataType = C.PD_DATA_INT64
    	Uint8   DataType = C.PD_DATA_UINT8
    	Int8    DataType = C.PD_DATA_INT8
    )
    
    Precision

    Precision 设置模型的运行精度。变量定义如下:

    type Precision C.PD_PrecisionType
    
    const (
    	PrecisionFloat32 Precision = C.PD_PRECISION_FLOAT32
    	PrecisionInt8    Precision = C.PD_PRECISION_INT8
    	PrecisionHalf    Precision = C.PD_PRECISION_HALF
    )
    

    代码示例:

    package main
    
    // 引入 Paddle Golang Package
    import pd "github.com/paddlepaddle/paddle/paddle/fluid/inference/goapi"
    import fmt
    
    func main() {
        // 创建 AnalysisConfig 对象
        config := pd.NewAnalysisConfig()
      
        // 启用 GPU 进行预测 - 初始化 GPU 显存 100M, Deivce_ID 为 0
        config.EnableUseGpu(100, 0)
    
        // 启用 TensorRT 进行预测加速 - FP32
        config.EnableTensorRtEngine(1 << 20, 1, 3, pd.PrecisionFloat32, false, false)
    
        // 启用 TensorRT 进行预测加速 - FP16
        config.EnableTensorRtEngine(1 << 20, 1, 3, pd.PrecisionHalf, false, false)
    
        // 启用 TensorRT 进行预测加速 - Int8
        config.EnableTensorRtEngine(1 << 20, 1, 3, pd.PrecisionInt8, false, false)
      
        // 通过 API 获取 TensorRT 启用结果 - true
        fmt.Println("Enable TensorRT is: ", config.TensorrtEngineEnabled())
    }
    

    常见问题与解答

    收录了用户开发过程中遇到的高频咨询类问题,并按照问题类型分为如下几类:

    环境与编译问题

    如果您在配置环境或是编译过程中遇到错误,可以参考以下常见问题并尝试解决。

    1. 编译报错, 且出错语句没有明显语法错误。答: 请检查使用的GCC版本,目前PaddlePaddle支持的编译器版本为 GCC 5.4 和 GCC 8.2.0。

    2. 编译时报错No CMAKE_CUDA_COMPILER could be found答: 编译时未找到 nvcc。设置编译选项 -DCMAKE_CUDA_COMPILER=nvcc 所在路径,注意需要与 CUDA 版本保持一致。

    运行报错

    如果您的推理应用程序在运行中出现报错提示或异常退出,可以参考以下常见问题并尝试解决。

    1. 运行时报错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 架构编译。

    2. 运行时报错PaddleCheckError: Expected id < GetCUDADeviceCount(), but received id:0 >= GetCUDADeviceCount():0答: 一般原因是找不到驱动文件。请检查环境设置,需要在 LD_LIBRARY_PATH 中加入 /usr/lib64(驱动程序libcuda.so所在的实际路径)。

    3. 运行时报错Error: Cannot load cudnn shared library.答: 请在 LD_LIBRARY_PATH 中加入 cuDNN 库的路径。

    4. 运行时报错 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 中。

    5. 运行时报错CUDNN_STATUS_NOT_INITIALIZED at ...答: 请检查运行时链接的cuDNN版本和编译预测库使用的 cuDNN 版本是否一致。

    6. 运行时报错OMP: Error #100 Fatal system error detected答: OMP 的问题,可参考链接 https://unix.stackexchange.com/questions/302683/omp-error-bash-on-ubuntu-on-windows

    7. 初始化阶段报错Program terminated with signal SIGILL, Illegal instruction答: 请检查下是否在不支持 AVX 的机器上使用了 AVX 的预测库。

    8. 在 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。

    9. 运行时报错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 的设计,无需手动设置。

    10. 运行时报错PaddleCheckError: an illegal memory access was encounted at xxx答: 请检查输入 Tensor 是否存在指针越界。

    精度与性能

    如果您的推理程序输出结果存在精度异常,或者您的程序性能和内存消耗不符合您的预期,可以参考以下常见问题并尝试解决。

    1. Predictor 是否有 Profile 工具。答: config.EnableProfile()可以打印op耗时,请参考API文档-Profile设置

    2. 同一个模型的推理耗时不稳定。答: 请按以下方向排查: 1)硬件资源(CPU、GPU等)是否没有他人抢占。 2)输入是否一致,某些模型推理时间跟输入有关,比如检测模型的候选框数量。 3)使用 TensorRT 时,初始的优化阶段比较耗时,可以通过少量数据 warm up 的方式解决。

    3. 如何开启 CPU 预测的多线程加速。答: 请使用config.EnableMKLDNN()config.SetCpuMathLibraryNumThreads(),请参考x86 CPU预测

    除此之外,你也可以查看 Paddle Inference 文档历史Issues飞桨论坛 来寻求解答。

    同时,你也可以在 Github Issues 中进行提问,飞桨会有专门的技术人员解答。