目录
- 前言
- 0. 简述
- 1. 案例运行
- 2. 补充说明
- 3. engine分析
- 结语
- 下载链接
- 参考
前言
自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》,链接。记录下个人学习笔记,仅供自己参考
本次课程我们来学习课程第六章—部署分类器,一起来学习 trt-engine-explorer 工具的使用
课程大纲可以看下面的思维导图
0. 简述
本小节目标:学习如何使用 trt-engine-explorer 工具
这个小节的案例代码与 C++ 没有多大的关系,它更多的是对我们生成的 trt-engine 模型的分析,用 python 脚本把 engine 的模型架构进行可视化
下面我们开始本次课程的学习🤗
1. 案例运行
在正式开始课程之前,博主先带大家跑通 6.4-trt-engine-inspector 这个小节的案例🤗
源代码获取地址:https://github.com/kalfazed/tensorrt_starter
首先大家需要把 tensorrt_starter 这个项目给 clone 下来,指令如下:
git clone https://github.com/kalfazed/tensorrt_starter.git
也可手动点击下载,点击右上角的 Code
按键,将代码下载下来。至此整个项目就已经准备好了。也可以点击 here 下载博主准备好的源代码(注意代码下载于 2024/7/14 日,若有改动请参考最新)
整个项目后续需要使用的软件主要有 CUDA、cuDNN、TensorRT、OpenCV,大家可以参考 Ubuntu20.04软件安装大全 进行相应软件的安装,博主这里不再赘述
下面我们要创建一个虚拟环境来安装 trt-engine-explorer
工具,博主这里准备了一个可以运行本小节案例代码的虚拟环境,大家可以按照这个环境来,也可以参考 trt-engine-explorer 官方文档 进行安装,指令如下:
conda create -n env_trex python=3.9
conda activate env_trex
git clone https://github.com/NVIDIA/TensorRT.git
cd TensorRT/tools/experimental/trt-engine-explorer
python3 -m pip install -e.
pip install werkzeug==3.0.0 Flask==3.0.0
sudo apt-get --yes install graphviz
安装完成后大家可以通过 pip
指令查看安装好的 trex 工具,如下图所示:
假设你的项目、环境准备完成,下面我们一起来运行下 6.4-trt-engine-inspector 小节案例代码
首先我们需要准备一个 engine 模型文件供 trt-engine-explorer 工具分析,这里我们直接拿上个小节生成的 resnet50-int8.engine 过来
下面我们需要利用脚本文件生成分析文件,指令如下:
# 将已有的分析文件全部删除
cd tensorrt_starter/chapter6-deploy-classification-and-inference-design/6.4-trt-engine-inspector/
rm -rf result/resnet50/*
# 将上小节创建好的 engine 拷贝过来
cp ../6.3-in8-calibration/models/engine/resnet50-int8.engine ./result/resnet50/
cd src/python
conda env_trex
python process_engine.py ../../result/resnet50/resnet50-int8.engine ../../result/resnet50 --profile-engine
输出如下图所示:
同时在 results/resnet50 目录下生成了一系列的文件,如下图所示:
我们主要关注 resnet50-int8.engine.graph.json 文件,它将 engine 各个 layer 的详细信息都保存下来了,包括 layer 的 name、dimensions、format 以及 data type 等等,其他的文件例如 profile.json、timing.json 则是一些 layer 执行时间的信息,profile.log 则是 trtexec 执行输出的日志信息
我们生成 resnet50-int8.engine.graph.json 文件后可以绘制出其网络结构,执行指令如下:
python draw_engine.py ../../result/resnet50/resnet50-int8.engine.graph.json
你可能会遇到如下的问题:
这个问题主要是因为 TensorRT 前段时间 10.0 版本发布时相关代码进行了修改,所以这里韩军老师提供的 draw_engine.py 脚本文件的 API 发生了变化
我们直接从目前 TensorRT 官网下的 draw_engine.py 替换当前的内容,如下所示:
#!/usr/bin/env python3
#
# SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#"""
This script generates an SVG diagram of the input engine graph SVG file.Note: this script requires graphviz which can be installed manually:$ sudo apt-get --yes install graphviz$ python3 -m pip install graphviz
"""import warnings
import argparse
import shutil
import trex.graphing
import trex.engine_plandef draw_engine(engine_json_fname: str, profiling_json_fname: str=None, **kwargs):graphviz_is_installed = shutil.which("dot") is not Noneif not graphviz_is_installed:print("graphviz is required but it is not installed.\n")print("To install on Ubuntu:")print("sudo apt --yes install graphviz")exit()try:if kwargs["display_constants"] and not kwargs["display_regions"]:warnings.warn("Ignoring argument --display_constants because it requires --display_regions.")except KeyError:passplan = trex.engine_plan.EnginePlan(engine_json_fname, profiling_file=profiling_json_fname)layer_node_formatter = trex.graphing.layer_type_formattergraph = trex.graphing.to_dot(plan, layer_node_formatter, **kwargs)trex.graphing.render_dot(graph, engine_json_fname, "svg")def make_subcmd_parser(subparsers):draw = lambda args: draw_engine(engine_json_fname=args.input,profiling_json_fname=args.profiling_json,display_regions=args.display_regions,display_layer_names=not args.no_layer_names,display_constants=args.display_constant,)draw_parser = subparsers.add_parser("draw", help="Draw a TensorRT engine.")draw_parser.set_defaults(func=draw)_make_parser(draw_parser)def _make_parser(parser):parser.add_argument("input", help="name of engine JSON file to draw.")parser.add_argument("--profiling_json", "-pj",default=None, help="name of engine JSON file to draw")parser.add_argument("--display_regions", "-dr",action='store_true', help="render memory regions as graph nodes.")parser.add_argument("--display_constant", "-dc",action='store_true', help="render constant input tensors.")parser.add_argument("--no_layer_names", "-no_ln",action='store_true', help="render constants.")if __name__ == "__main__":parser = argparse.ArgumentParser()args = parser.parse_args(_make_parser(parser))draw_engine(engine_json_fname=args.input,profiling_json_fname=args.profiling_json,display_regions=True,expand_layer_details=False,display_latency=True,)
再次执行该脚本,输出如下:
执行成功后会生成一个 .svg 的图片格式文件,我们可以在浏览器中打开 .svg 的结构图,如下图所示:
如果大家能够看到上述输出结果,那就说明本小节案例已经跑通,下面我们就来看看具体的 engine 结构变化
2. 补充说明
在分析具体结构变化之前我们先来看下韩君老师在这小节中写的 README 文档
trt-engine-explorer
是 NVIDIA 官方提供的分析 TensorRT 优化后的推理引擎架构的工具包,可以在 TensorRT 官方的 git repository 中找打,具体细节大家可以参考:trt-engine-explorer
大家参考官方提供的 README.md
把环境搭建起来之后,我们可以尝试分析下我们的 engine 模型。为了方便,在 6.4 小节案例代码中的 src/python
目录下放置了一些官方提供的案例文件可以使用,比如:
通过 TensorRT 优化的推理引擎获取各个 layer 的信息,以 json 形式保存
python process_engine.py ../../result/resnet50/resnet50.engine ../../result/resnet50/resnet50.engine --profile-engine
通过保存得到的 json 信息,绘制出 TensorRT 优化后的网络模型架构,以 SVG 格式保存成图片
python draw_engine.py ../../result/resnet50/resnet50.engine.graph.json
通过 jupyter 在浏览器里打开 SVG 模式下的结构图
jupyter-notebook --ip=0.0.0.0 --no-browser
大家这里可以仔细比较一下各个模型的 .onnx
架构和 .engine
架构的不同,以及同一个模型的不同精度的 .engine
的不同。观察 TensorRT 优化后哪些层被融合了,哪些层是新添加的,大家感兴趣的还可以了解下 reformatter
节点是什么,其实 reformatter
非常重要,因为它涉及到了 TensorRT 的各个 layer 所支持的 data layout,如果想要把部署优化做到极致,理解 data layout 是必须要做的。
3. engine分析
我们对比着看下 resnet50.onnx 模型和 resnet50-int8.engine 引擎的结构变化
上图中左边是 onnx 右边是 engine,首先在 engine 中 input0 后面的 Reformat 是新添加的节点,接着有一个虚线框,这个代表着之前 onnx 中存在的节点但是现在经过 tensorRT 优化后不存在了。我们可以清晰的看到 onnx 模型中的 Conv、Relu 和 MaxPool 在 engine 中变成了一个 ConvActPool 节点,这个就是 TensorRT 做的层融合后的效果
Note:Engine 中的 Reformat 节点是什么呢?我们是不是应该尽量避免它的产生?
在 TensorRT 的引擎中,Reformat 节点用于在不同层之间处理数据格式的转换。这些节点通常出现在需要更改数据布局(例如从 NCHW 到 NHWC)或调整数据类型(如 FP32、FP16 和 INT8 之间的转换)时,这些转换是必要的,因为引擎中的不同层或操作可能偏好活需要不同的数据格式或精度,以实现性能优化。
通常来说,Reformat 节点越少越好,因为每次格式转换操作都会引入额外的开销,影响引擎的性能。过多的 Reformat 操作会减慢推理速度,因为这需要额外的内存操作
在进行 INT8 量化时,我们最好确保校准过程在各层之间是一致的,一致的量化可以减少在具有不同量化参数的层之间进行 Reformat 的需求
我们往后看可以发现 ONNX 中的 Relu 节点都不存在了,都变成了虚线框,resnet50 模型比较简单,所以整个 engine 架构还是比较好看的,没有添加什么冗余的东西
我们在生成 engine 的时候指定的精度是 INT8,那是不是意味着我们所有的节点都是 INT8 的呢,那当然不是,像 Convolution 这种密集型的计算大部分都是 INT8 精度的,但是像上图中的 SoftMax 节点的输入输出都是 FP32 精度
还有一个点大家需要注意,大家可以看到我们前面的数据都是 NCHW 这种格式,这个大家比较熟悉,但是经过 ConvActPool 之后就变成了 NC/32HW32 这种格式,那这个其实是 TensorRT 内部自定义的一种数据格式,更多细节大家可以看官方文档:data-format-desc
resnet 相对来说比较简单,这里大家可以再看看其他的模型例如 MobileNet 模型 engine 等等,像我们下一个章节要讲的 YOLO 模型,它的 INT8 engine 可能就没有这么好看,它的模型结构相对比较复杂,量化后会存在很多的 reformat 节点
在 tools 文件夹中有一个 build.sh 脚本文件,通过 trtexec
生成 engine,我们可以利用它来生成 mobilenet 的 int8 engine 模型,在此之前我们需要在该脚本文件中添加 --int8
参数,如下图所示:
接着执行如下指令:
bash tools/build.sh result/mobilnet/mobilenetV2.onnx int8
输出如下:
同时在当前目录会生成 .engine 和一系列日志文件,如下图所示:
那大家可能有所疑问为什么可以直接生成 INT8 的 engine 呢?难道不需要 calibration 吗?那其实 trtexec
提供了 --int8
参数运行我们生成 INT8 量化的 engine,如果你不提供 calibration_table,不提供各个 layer 的 dynamic range,那它量化出来的效果非常差,但是我们这里只是查看 mobilenet 的 engine 的结构,所以没有问题
下面我们就如法炮制,跟 resnet50 一样利用 trt-engine-explorer 去生成 engine 的 .svg 图片,这里博主就不再展示了,mobilenetV2-int8.engine 的结构如下图所示:
结语
本次课程我们学习了 trt-engine-explore 工具的使用,将 TensorRT 优化好的 engine 的结构绘制成 .svg 图片可视化出来。这个工具在做 QAT 量化时可能使用得更多,因为在 QAT 的过程中,模型的各个层之间经常会出现数据格式和精度不匹配的情况,导致 TensorRT 在构建引擎时插入 Reformat 节点。另外可以查看 QAT 模型中哪些层并没有按照我们期望的做 INT8,利用该工具可以有助我们优化模型结构,减少额外的转换操作,检查关键层的量化精度等等
OK,以上就是 6.4 小节案例的全部内容了,下节开始我们进入第七章节的学习,敬请期待😄
下载链接
- tensorrt_starter源码
- 6.4-trt-engine-inspector案例文件
参考
- Ubuntu20.04软件安装大全
- https://github.com/kalfazed/tensorrt_starter
- https://github.com/NVIDIA/TensorRT/tools/trt-engine-explorer
- https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#data-format-desc