diff --git a/doc/fluid/advanced_usage/deploy/inference/paddle_tensorrt_infer.md b/doc/fluid/advanced_usage/deploy/inference/paddle_tensorrt_infer.md index e05e0f3ef0540df2b03a3810b53602269a9dbb6e..da244f8791d8c7ea85cd13af1e12c1cccbd9895a 100644 --- a/doc/fluid/advanced_usage/deploy/inference/paddle_tensorrt_infer.md +++ b/doc/fluid/advanced_usage/deploy/inference/paddle_tensorrt_infer.md @@ -1,229 +1,151 @@ # 使用Paddle-TensorRT库预测 -NVIDIA TensorRT 是一个高性能的深度学习预测库,可为深度学习推理应用程序提供低延迟和高吞吐量。PaddlePaddle 采用了子图的形式对TensorRT进行了集成,即我们可以使用该模块来提升Paddle模型的预测性能。该模块依旧在持续开发中,目前已支持的模型有:AlexNet, MobileNetV1, ResNet50, VGG19, ResNext, Se-ReNext, GoogLeNet, DPN, ICNET, Deeplabv3, MobileNet-SSD等。在这篇文档中,我们将会对Paddle-TensorRT库的获取、使用和原理进行介绍。 +NVIDIA TensorRT 是一个高性能的深度学习预测库,可为深度学习推理应用程序提供低延迟和高吞吐量。PaddlePaddle 采用子图的形式对TensorRT进行了集成,即我们可以使用该模块来提升Paddle模型的预测性能。该模块依旧在持续开发中,目前支持的模型如下表所示: + +|分类模型|检测模型|分割模型| +|---|---|---| +|mobilenetv1|yolov3|ICNET| +|resnet50|SSD|| +|vgg16|mask-rcnn|| +|resnext|faster-rcnn|| +|AlexNet|cascade-rcnn|| +|Se-ResNext|retinanet|| +|GoogLeNet|mobilenet-SSD|| +|DPN||| + +在这篇文档中,我们将会对Paddle-TensorRT库的获取、使用和原理进行介绍。 + +**Note:** + +1. 从源码编译时,TensorRT预测库目前仅支持使用GPU编译,且需要设置编译选项TENSORRT_ROOT为TensorRT所在的路径。 +2. Windows支持需要TensorRT 版本5.0以上。 +3. Paddle-TRT目前仅支持固定输入shape。 +4. 若使用用户自行安装的TensorRT,需要手动在`NvInfer.h`文件中为`class IPluginFactory`和`class IGpuAllocator`分别添加虚析构函数: + ```c++ + virtual ~IPluginFactory() {}; + virtual ~IGpuAllocator() {}; + ``` ## 内容 -- [编译Paddle-TRT预测库](#编译Paddle-TRT预测库) -- [Paddle-TRT接口使用](#Paddle-TRT接口使用) -- [Paddle-TRT参数介绍](#Paddle-TRT参数介绍) +- [Paddle-TRT使用介绍](#Paddle-TRT使用介绍) - [Paddle-TRT样例编译测试](#Paddle-TRT样例编译测试) - [Paddle-TRT INT8使用](#Paddle-TRT_INT8使用) - [Paddle-TRT子图运行原理](#Paddle-TRT子图运行原理) +- [Paddle-TRT性能测试](#Paddle-TRT性能测试) +## Paddle-TRT使用介绍 -## 编译Paddle-TRT预测库 - -**使用Docker编译预测库** - -TensorRT预测库目前仅支持使用GPU编译。 - -1. 下载Paddle - - ``` - git clone https://github.com/PaddlePaddle/Paddle.git - ``` - -2. 获取docker镜像 - - ``` - nvidia-docker run --name paddle_trt -v $PWD/Paddle:/Paddle -it hub.baidubce.com/paddlepaddle/paddle:latest-dev /bin/bash - ``` - -3. 编译Paddle TensorRT - - ``` - # 在docker容器中执行以下操作 - cd /Paddle - mkdir build - cd build - # TENSORRT_ROOT为TRT的路径,默认为 /usr,根据自己需求进行改动 - # MKLDNN 可以根据自己的需求自行打开 - cmake .. \ - -DWITH_FLUID_ONLY=ON \ - -DWITH_MKL=ON \ - -DWITH_MKLDNN=OFF \ - -DCMAKE_BUILD_TYPE=Release \ - -DWITH_PYTHON=OFF \ - -DTENSORRT_ROOT=/usr \ - -DON_INFER=ON - - # 编译 - make -j - # 生成预测库 - make inference_lib_dist -j - ``` - - 编译后的库的目录如下: - - ``` - fluid_inference_install_dir - ├── paddle - │ - ├── CMakeCache.txt - ├── version.txt - ├── third_party - ├── boost - ├── install - └── engine3 - ``` - - `fluid_inference_install_dir`下, paddle目录包含了预测库的头文件和预测库的lib, version.txt 中包含了lib的版本和配置信息,third_party 中包含了预测库依赖的第三方库 - -## Paddle-TRT接口使用 - -Paddle-TRT预测使用总体上分为以下步骤: -1. 创建合适的配置AnalysisConfig. -2. 根据配置创建 `PaddlePredictor`. -3. 创建输入tensor. -4. 获取输出tensor,输出结果. - -以下的代码展示了完整的过程: - -```c++ -#include "paddle_inference_api.h" - -namespace paddle { -using paddle::AnalysisConfig; - -void RunTensorRT(int batch_size, std::string model_dirname) { - // 1. 创建AnalysisConfig - AnalysisConfig config(model_dirname); - // config->SetModel(model_dirname + "/model", - // model_dirname + "/params"); - config->EnableUseGpu(10, 0 /*gpu_id*/); - // 我们在这里使用了 ZeroCopyTensor, 因此需要将此设置成false - config->SwitchUseFeedFetchOps(false); - config->EnableTensorRtEngine(1 << 20 /*work_space_size*/, batch_size /*max_batch_size*/, AnalysisConfig::Precision::kFloat32, false /*use_static*/); - - // 2. 根据config 创建predictor - auto predictor = CreatePaddlePredictor(config); - // 3. 创建输入 tensor - int channels = 3; - int height = 224; - int width = 224; - - float *input = new float[input_num]; - memset(input, 0, input_num * sizeof(float)); - - auto input_names = predictor->GetInputNames(); - auto input_t = predictor->GetInputTensor(input_names[0]); - input_t->Reshape({batch_size, channels, height, width}); - input_t->copy_from_cpu(input); - - // 4. 运行 - predictor->ZeroCopyRun() - - // 5. 获取输出 - std::vector out_data; - auto output_names = predictor->GetOutputNames(); - auto output_t = predictor->GetOutputTensor(output_names[0]); - std::vector output_shape = output_t->shape(); - int out_num = std::accumulate(output_shape.begin(), output_shape.end(), 1, std::multiplies()); - - out_data.resize(out_num); - output_t->copy_to_cpu(out_data.data()); - } -} // namespace paddle - -int main() { - // 模型下载地址 http://paddle-inference-dist.cdn.bcebos.com/tensorrt_test/mobilenet.tar.gz - paddle::RunTensorRT(1, "./mobilenet"); - return 0; -} -``` - -## Paddle-TRT参数介绍 - -在使用AnalysisPredictor时,我们通过配置 +在使用AnalysisPredictor时,我们通过配置AnalysisConfig中的接口 ```c++ config->EnableTensorRtEngine(1 << 20 /* workspace_size*/, - batch_size /*max_batch_size*/, - 3 /*min_subgraph_size*/, - AnalysisConfig::Precision::kFloat32 /*precision*/, - false /*use_static*/, + batch_size /* max_batch_size*/, + 3 /* min_subgraph_size*/, + AnalysisConfig::Precision::kFloat32 /* precision*/, + false /* use_static*/, false /* use_calib_mode*/); ``` -的方式来指定使用Paddle-TRT子图方式来运行。以下我们将对此接口中的参数进行详细的介绍: - -- **`workspace_size`**,类型:int,默认值为`1 << 20`。 -- **`max_batch_size`**,类型:int,默认值1。需要提前设置最大的batch的大小,运行时batch数目不得超过此大小。 -- **`min_subgraph_size`**,类型:int,默认值3。Paddle-TRT是以子图的形式运行,为了避免性能损失,当子图内部节点个数大于`min_subgraph_size`的时候,才会使用Paddle-TRT运行。 -- **`precision`**,类型:`enum class Precision {kFloat32 = 0, kInt8,};`, 默认值为`AnalysisConfig::Precision::kFloat32`。如果需要使用Paddle-TRT calib int8的时候,需要指定precision为 `AnalysisConfig::Precision::kInt8`, 且`use_calib_mode` 为true -- **`use_static`**,类型:bool, 默认值为false。如果指定为true,在初次运行程序的时候会将TRT的优化信息进行序列化,下次运行的时候直接加载优化的序列化信息而不需要重新生成。 -- **`use_calib_mode`**,类型:bool, 默认值为false。如果需要运行Paddle-TRT calib int8的时候,需要将此设置为true。 +的方式来指定使用Paddle-TRT子图方式来运行。 +该接口中的参数的详细介绍如下: + +- **`workspace_size`**,类型:int,默认值为1 << 20。指定TensorRT使用的工作空间大小,TensorRT会在该大小限制下筛选合适的kernel执行预测运算。 +- **`max_batch_size`**,类型:int,默认值为1。需要提前设置最大的batch大小,运行时batch大小不得超过此限定值。 +- **`min_subgraph_size`**,类型:int,默认值为3。Paddle-TRT是以子图的形式运行,为了避免性能损失,当子图内部节点个数大于`min_subgraph_size`的时候,才会使用Paddle-TRT运行。 +- **`precision`**,类型:`enum class Precision {kFloat32 = 0, kHalf, kInt8,};`, 默认值为`AnalysisConfig::Precision::kFloat32`。指定使用TRT的精度,支持FP32(kFloat32),FP16(kHalf),Int8(kInt8)。若需要使用Paddle-TRT int8离线量化校准,需设定`precision`为 `AnalysisConfig::Precision::kInt8`, 且设置`use_calib_mode` 为true。 +- **`use_static`**,类型:bool, 默认值为false。如果指定为true,在初次运行程序的时候会将TRT的优化信息进行序列化到磁盘上,下次运行时直接加载优化的序列化信息而不需要重新生成。 +- **`use_calib_mode`**,类型:bool, 默认值为false。若要运行Paddle-TRT int8离线量化校准,需要将此选项设置为true。 **Note:** Paddle-TRT目前只支持固定shape的输入,不支持变化shape的输入。 ## Paddle-TRT样例编译测试 -1. 下载样例 - ``` - https://paddle-inference-dist.cdn.bcebos.com/tensorrt_test/paddle_trt_samples_v1.5.tar.gz - ``` +1. 下载或编译带有 TensorRT 的paddle预测库,参考[安装与编译C++预测库](./build_and_install_lib_cn.html)。 +2. 下载[预测样例](https://paddle-inference-dist.bj.bcebos.com/tensorrt_test/paddle_inference_sample_v1.6.tar.gz)并解压,进入`sample/paddle-TRT`目录下。 - 解压后的目录如下: + `paddle-TRT` 文件夹目录结构如下: ``` - sample + paddle-TRT ├── CMakeLists.txt ├── mobilenet_test.cc - ├── thread_mobilenet_test.cc + ├── fluid_generate_calib_test.cc + ├── fluid_int8_test.cc ├── mobilenetv1 │ ├── model │ └── params - └── run_impl.sh + ├── run.sh + └── run_impl.sh ``` + + - `mobilenet_test.cc` 为使用paddle-TRT预测的C++源文件 + - `fluid_generate_calib_test.cc` 为使用TRT int8离线量化校准的C++源文件 + - `fluid_int8_test.cc` 为使用TRT执行int8预测的C++源文件 + - `mobilenetv1` 为模型文件夹 + - `run.sh` 为预测运行脚本文件 - - `mobilenet_test.cc` 为单线程的程序文件 - - `thread_mobilenet_test.cc` 为多线程的程序文件 - - `mobilenetv1` 为模型文件 - - 在这里假设预测库的路径为 ``BASE_DIR/fluid_inference_install_dir/`` ,样例所在的目录为 ``SAMPLE_BASE_DIR/sample`` + 在这里假设样例所在的目录为 `SAMPLE_BASE_DIR/sample/paddle-TRT` -2. 编译样例 +3. 配置编译与运行脚本 + + 编译运行预测样例之前,需要根据运行环境配置编译与运行脚本`run.sh`。`run.sh`的选项与路径配置的部分如下: + + ```shell + # 设置是否开启MKL、GPU、TensorRT,如果要使用TensorRT,必须打开GPU + WITH_MKL=ON + WITH_GPU=ON + USE_TENSORRT=ON + + # 按照运行环境设置预测库路径、CUDA库路径、CUDNN库路径、模型路径 + LIB_DIR=YOUR_LIB_DIR + CUDA_LIB_DIR=YOUR_CUDA_LIB_DIR + CUDNN_LIB_DIR=YOUR_CUDNN_LIB_DIR + MODEL_DIR=YOUR_MODEL_DIR + ``` + + 按照实际运行环境配置`run.sh`中的选项开关和所需lib路径。 + +4. 编译与运行样例 ```shell - cd SAMPLE_BASE_DIR/sample - # sh run_impl.sh {预测库的地址} {测试脚本的名字} {模型目录} - sh run_impl.sh BASE_DIR/fluid_inference_install_dir/ mobilenet_test SAMPLE_BASE_DIR/sample/mobilenetv1 - ``` - -3. 编译多线程的样例 - - ```shell - cd SAMPLE_BASE_DIR/sample - # sh run_impl.sh {预测库的地址} {测试脚本的名字} {模型目录} - sh run_impl.sh BASE_DIR/fluid_inference_install_dir/ thread_mobilenet_test SAMPLE_BASE_DIR/sample/mobilenetv1 + sh run.sh ``` + ## Paddle-TRT INT8使用 1. Paddle-TRT INT8 简介 - 神经网络的参数在一定程度上是冗余的,在很多任务上,我们可以在保证模型精度的前提下,将Float32的模型转换成Int8的模型。目前,Paddle-TRT支持离线将预训练好的Float32模型转换成Int8的模型,具体的流程如下:1)**生成校准表**(Calibration table);我们准备500张左右的真实输入数据,并将数据输入到模型中去,Paddle-TRT会统计模型中每个op输入和输出值的范围信息,并将记录到校准表中,这些信息有效的减少了模型转换时的信息损失。2)生成校准表后,再次运行模型,**Paddle-TRT会自动加载校准表**,并进行INT8模式下的预测。 + 神经网络的参数在一定程度上是冗余的,在很多任务上,我们可以在保证模型精度的前提下,将Float32的模型转换成Int8的模型。目前,Paddle-TRT支持离线将预训练好的Float32模型转换成Int8的模型,具体的流程如下: + + 1) **生成校准表**(Calibration table):我们准备500张左右的真实输入数据,并将数据输入到模型中去,Paddle-TRT会统计模型中每个op输入和输出值的范围信息,并将其记录到校准表中,这些信息有效减少了模型转换时的信息损失。 + + 2) 生成校准表后,再次运行模型,**Paddle-TRT会自动加载校准表**,并进行INT8模式下的预测。 2. 编译测试INT8样例 - - ```shell - cd SAMPLE_BASE_DIR/sample - # sh run_impl.sh {预测库的地址} {测试脚本的名字} {模型目录} - # 我们随机生成了500个输入来模拟这一过程,建议大家用真实样例进行实验。 - sh run_impl.sh BASE_DIR/fluid_inference_install_dir/ fluid_generate_calib_test SAMPLE_BASE_DIR/sample/mobilenetv1 - - ``` - - 运行结束后,在 `SAMPLE_BASE_DIR/sample/build/mobilenetv1/_opt_cache` 模型目录下会多出一个名字为trt_calib_*的文件,即校准表。 - - ``` shell - # 执行INT8预测 - # 将带校准表的模型文件拷贝到特定地址 - cp -rf SAMPLE_BASE_DIR/sample/build/mobilenetv1 SAMPLE_BASE_DIR/sample/mobilenetv1_calib - sh run_impl.sh BASE_DIR/fluid_inference_install_dir/ fluid_int8_test SAMPLE_BASE_DIR/sample/mobilenetv1_calib - ``` + 将`run.sh`文件中的`mobilenet_test`改为`fluid_generate_calib_test`,运行 + + ```shell + sh run.sh + ``` + + 即可执行生成校准表样例,在该样例中,我们随机生成了500个输入来模拟这一过程,在实际业务中,建议大家使用真实样例。运行结束后,在 `SAMPLE_BASE_DIR/sample/paddle-TRT/build/mobilenetv1/_opt_cache` 模型目录下会多出一个名字为trt_calib_*的文件,即校准表。 + + 生成校准表后,将带校准表的模型文件拷贝到特定地址 + + ```shell + cp -rf SAMPLE_BASE_DIR/sample/paddle-TRT/build/mobilenetv1/ SAMPLE_BASE_DIR/sample/paddle-TRT/mobilenetv1_calib + ``` + + 将`run.sh`文件中的`fluid_generate_calib_test`改为`fluid_int8_test`,将模型路径改为`SAMPLE_BASE_DIR/sample/paddle-TRT/mobilenetv1_calib`,运行 + + ```shell + sh run.sh + ``` + + 即可执行int8预测样例。 ## Paddle-TRT子图运行原理 - PaddlePaddle采用子图的形式对TensorRT进行集成,当模型加载后,神经网络可以表示为由变量和运算节点组成的计算图。Paddle TensorRT实现的功能是能够对整个图进行扫描,发现图中可以使用TensorRT优化的子图,并使用TensorRT节点替换它们。在模型的推断期间,如果遇到TensorRT节点,Paddle会调用TensoRT库对该节点进行优化,其他的节点调用Paddle的原生实现。TensorRT在推断期间能够进行Op的横向和纵向融合,过滤掉冗余的Op,并对特定平台下的特定的Op选择合适的kenel等进行优化,能够加快模型的预测速度。 + PaddlePaddle采用子图的形式对TensorRT进行集成,当模型加载后,神经网络可以表示为由变量和运算节点组成的计算图。Paddle TensorRT实现的功能是对整个图进行扫描,发现图中可以使用TensorRT优化的子图,并使用TensorRT节点替换它们。在模型的推断期间,如果遇到TensorRT节点,Paddle会调用TensorRT库对该节点进行优化,其他的节点调用Paddle的原生实现。TensorRT在推断期间能够进行Op的横向和纵向融合,过滤掉冗余的Op,并对特定平台下的特定的Op选择合适的kernel等进行优化,能够加快模型的预测速度。 下图使用一个简单的模型展示了这个过程: @@ -238,4 +160,54 @@ config->EnableTensorRtEngine(1 << 20 /* workspace_size*/,

- 我们可以在原始模型网络中看到,绿色节点表示可以被TensorRT支持的节点,红色节点表示网络中的变量,黄色表示Paddle只能被Paddle原生实现执行的节点。那些在原始网络中的绿色节点被提取出来汇集成子图,并由一个TensorRT节点代替,成为转换网络中的`block-25` 节点。在网络运行过程中,如果遇到该节点,Paddle将调用TensorRT库来对其执行。 + 我们可以在原始模型网络中看到,绿色节点表示可以被TensorRT支持的节点,红色节点表示网络中的变量,黄色表示Paddle只能被Paddle原生实现执行的节点。那些在原始网络中的绿色节点被提取出来汇集成子图,并由一个TensorRT节点代替,成为转换后网络中的`block-25` 节点。在网络运行过程中,如果遇到该节点,Paddle将调用TensorRT库来对其执行。 + +## Paddle-TRT性能测试 + +### 测试环境 +- CPU:Intel(R) Xeon(R) Gold 5117 CPU @ 2.00GHz GPU:Tesla P4 +- TensorRT4.0, CUDA8.0, CUDNNV7 +- 测试模型 ResNet50,MobileNet,ResNet101, Inception V3. + +### 测试对象 +**PaddlePaddle, Pytorch, Tensorflow** + +- 在测试中,PaddlePaddle使用子图优化的方式集成了TensorRT, 模型[地址](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/image_classification/models)。 +- Pytorch使用了原生的实现, 模型[地址1](https://github.com/pytorch/vision/tree/master/torchvision/models)、[地址2](https://github.com/marvis/pytorch-mobilenet)。 +- 对TensorFlow测试包括了对TF的原生的测试,和对TF—TRT的测试,**对TF—TRT的测试并没有达到预期的效果,后期会对其进行补充**, 模型[地址](https://github.com/tensorflow/models)。 + + +#### ResNet50 + +|batch_size|PaddlePaddle(ms)|Pytorch(ms)|TensorFlow(ms)| +|---|---|---|---| +|1|4.64117 |16.3|10.878| +|5|6.90622| 22.9 |20.62| +|10|7.9758 |40.6|34.36| + +#### MobileNet + +|batch_size|PaddlePaddle(ms)|Pytorch(ms)|TensorFlow(ms)| +|---|---|---|---| +|1| 1.7541 | 7.8 |2.72| +|5| 3.04666 | 7.8 |3.19| +|10|4.19478 | 14.47 |4.25| + +#### ResNet101 + +|batch_size|PaddlePaddle(ms)|Pytorch(ms)|TensorFlow(ms)| +|---|---|---|---| +|1|8.95767| 22.48 |18.78| +|5|12.9811 | 33.88 |34.84| +|10|14.1463| 61.97 |57.94| + + +#### Inception v3 + +|batch_size|PaddlePaddle(ms)|Pytorch(ms)|TensorFlow(ms)| +|---|---|---|---| +|1|15.1613 | 24.2 |19.1| +|5|18.5373 | 34.8 |27.2| +|10|19.2781| 54.8 |36.7| + + diff --git a/doc/fluid/advanced_usage/deploy/inference/python_infer_cn.md b/doc/fluid/advanced_usage/deploy/inference/python_infer_cn.md index 23b0d0e2d558aa3231d7feb90962feee3fada349..507e8178e20d796bbbb333e99ca0bc7c2025646f 100644 --- a/doc/fluid/advanced_usage/deploy/inference/python_infer_cn.md +++ b/doc/fluid/advanced_usage/deploy/inference/python_infer_cn.md @@ -135,7 +135,8 @@ results = predictor.run([x_t, y_t]) * `enable_tensorrt_engine(workspace_size: int = 1 << 20, max_batch_size: int, min_subgraph_size: int, - precision: AnalysisConfig.precision, use_static: bool, + precision: AnalysisConfig.precision, + use_static: bool, use_calib_mode: bool) -> None` * `enable_mkldnn() -> None` * PaddlePredictor