ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ONNX转TensorRT:三种方式与动态Shape、INT8量化实践

ONNX转TensorRT:三种方式与动态Shape、INT8量化实践 先说一句可能得罪人的话很多同学对 ONNX 转 TensorRT 这件事有误解以为和把 PNG 转成 JPG 一样拿一条命令或者一个 API 处理完就万事大吉。实际上这更像是把一份做菜的菜谱交给一位精通本地食材的大厨让他提前把火候、调料、切配方案全部定死最后你只负责把菜端上桌。放在工程里就是先用 ONNX 描述神经网络结构再用 TensorRT 针对特定 GPU 编译成专属的 plan 引擎最后在 Python 里加载运行。网上关于这个主题的资料很散有人给你贴trtexec命令有人给你甩一段tensorrt的 Python 转换代码还有人告诉你直接装一个 ONNX Runtime 加 TensorRT provider 就行。三种说法都对但它们背后是三条完全不同的落地路径性能和可维护性也不一样。这篇文章我会把这三种方式全部拆开按照“转换过程、Python 加载、踩坑点”的顺序写清楚尽量可以照着代码直接复现。这次我会以 YOLO 类的目标检测模型为例因为这类 ONNX 模型最常见动态 batch、FP16、INT8 的需求也最典型。1. 先搞清楚ONNX 到 TensorRT 这步到底是在“翻译”什么1.1 TensorRT 真正做的事情不是格式转换ONNX 是一个开放的计算图格式它把一个模型的结构、权重、算子类型都记录下来。TensorRT 能做的不只是读图它在拿到 ONNX 之后会做几件很关键的事算子融合比如把 Conv、BN、ReLU 这类连续算子合并成一个 kernelKernel 自动选择根据你的 GPU 架构挑选最优实现精度重排比如把 FP32 计算改成 FP16 或 INT8显存复用把中间张量分配得尽量省层间内存规划减少推理时的 host/device 数据搬运。所以 ONNX 转 TensorRT与其说是“转换”不如说是“编译”。它编译出的 plan 文件不是跨平台中间格式而是和 GPU 架构、TensorRT 版本强绑定的二进制结果。同一份 ONNX在 RTX 4090 上编译出来的引擎拿到 RTX 5070 上大概率加载不了TensorRT 8.6 生成的引擎也不能保证在 TensorRT 10.x 环境里反序列化成功。1.2 既然是编译为什么大家还是先从 ONNX 入手因为 ONNX 是“最大公约数”。PyTorch 训练完可以导出 ONNX某些模型仓库也会直接提供 ONNX 权重。只要 tensorrt 的 ONNX Parser 能读进去后面不管你用 trtexec、Python API还是 ONNX Runtime 的 TensorRT provider本质上都共享同一套 TensorRT 优化底座。但这也带来一个常见误区很多人以为转换失败是命令没用对其实大概率是 ONNX 图本身有问题比如算子版本太老、动态维度没有声明、出现了 TensorRT 不支持的 plugin。所以我的习惯是拿到 ONNX 后先用onnx.checker、onnxruntime各跑一遍确认图本身没问题再开始折腾 TensorRT。1.3 三种方式的本质区别这里先把三条路列清楚后面每一章再展开。方式一用 NVIDIA 自带的trtexec命令行工具把 ONNX 变成 plan 引擎然后在 Python 里加载 plan 推理。方式二直接用 TensorRT 的 Python API在脚本里创建 Builder、解析 ONNX、构建引擎然后同一份代码里完成推理。方式三不手动构建 plan而是让 ONNX Runtime 使用 TensorRT Execution Provider在初始化 session 时由 ONNX Runtime 内部调用 TensorRT 完成子图转换和执行。方式一适合快速验证方式二适合做自动化部署工具方式三适合不想维护引擎生命周期、又想吃到 TensorRT 加速的人。下面我逐个讲。2. 方式一trtexec 命令行转引擎Python 只负责加载和推理2.1 环境准备TENSORRT 装好TRTEXEC 也要能直接用不管选哪种方式第一步都是确认环境。用pip install tensorrt会安装 Python 包但 C 工具链和trtexec不一定进 PATH。常见的 TensorRT deb 包安装后trtexec在/usr/src/tensorrt/bin/trtexec容器镜像里则一般在/usr/src/tensorrt/bin/下。可以先执行trtexec --help能输出一堆参数就说明命令行工具没问题。再用 Python 确认一下import tensorrt as trt print(trt.__version__)我遇到过一种情况Python 里 import tensorrt 出来的版本是 10.0但trtexec还是旧版 8.6。这种版本错位会带来很隐蔽的问题比如 Python API 能解析的 ONNX 算子trtexec却报错。所以建议统一用同一种方式安装 TensorRT或者直接进 NVIDIA NGC 容器省心很多。2.2 trtexec 的核心参数拆解FP16、动态 Shape、显存上限假设你的模型是 YOLO输入名称为input形状是动态的[N, 3, 640, 640]要转成支持 FP16 的引擎命令可以这样写trtexec \ --onnxyolov12.onnx \ --saveEngineyolov12.engine \ --fp16 \ --minShapesinput:1x3x640x640 \ --optShapesinput:1x3x640x640 \ --maxShapesinput:8x3x640x640 \ --memPoolSizeworkspace:2G几个参数逐个说明。--fp16是启用 FP16 计算不加的话默认 FP32--int8也可以加但 INT8 后面单独讲不是加个参数就完事的。--minShapes、--optShapes、--maxShapes这三个组合起来定义了一个优化 profile。TensorRT 需要知道你推理时 shape 的最小值、最优值和最大值。如果模型是固定尺寸这三个值写一样的就行如果 batch 可能从 1 到 8那就按上面的写法。需要特别注意的是optShapes不是摆设TensorRT 在做 kernel autotune 时会优先考虑这个 shape所以它应该接近你线上最常用的尺寸而不是每次都随手写个 1。--memPoolSizeworkspace:2G是限制 TensorRT 构建引擎时最多能用的显存/内存。这不是最终运行时占用的实际显存而是编译期规划时允许使用的上限。旧版本参数叫--workspace新版本改成--memPoolSize如果你用的 TensorRT 版本比较老注意看一下 help 输出。转换完成后会生成yolov12.engine文件这就是 TensorRT 针对当前显卡编译出来的可执行计划。想测试这个引擎本身能不能跑可以直接用 trtexec 加载并指定推理 shapetrtexec --loadEngineyolov12.engine --shapesinput:4x3x640x640它会输出平均延迟、吞吐量这些指标。这个命令很实用调试阶段先确认引擎没问题再写 Python 加载代码能把问题切开避免什么都混在一起。2.3 Python 加载 engine 文件并完成推理现在进入最核心的部分在 Python 里把.engine文件加载起来跑一次前向。加载 plan 不需要重新解析 ONNX只需要 TensorRT Runtime 反序列化。下面的代码是我常用的模板假设模型只有一个输入和一个输出。如果你的模型是多输入多输出需要把代码扩展成列表但逻辑类似。import time import numpy as np import tensorrt as trt class TRTEngine: def __init__(self, engine_path, input_name, output_name): logger trt.Logger(trt.Logger.WARNING) runtime trt.Runtime(logger) with open(engine_path, rb) as f: self.engine runtime.deserialize_cuda_engine(f.read()) self.context self.engine.create_execution_context() self.input_name input_name self.output_name output_name def infer(self, input_data, input_shape, output_shape): # 如果是动态 shape必须告诉 context 本次推理的实际 shape self.context.set_input_shape(self.input_name, input_shape) output_data np.zeros(output_shape, dtypenp.float32) # 分配 device 端输入输出 d_input cuda.mem_alloc(input_data.nbytes) d_output cuda.mem_alloc(output_data.nbytes) # 拷贝输入到 GPU cuda.memcpy_htod(d_input, input_data) # 拿到绑定地址 bindings [int(d_input), int(d_output)] # 执行推理 self.context.execute_v2(bindings) # 拷贝输出回 CPU cuda.memcpy_dtoh(output_data, d_output) return output_data注意上面代码依赖 PyCUDA所以还要pip install pycuda并在文件开头加一句import pycuda.autoinit它会帮你完成 CUDA context 初始化。如果你不想引入 PyCUDA也可以用 TensorRT 新版本里的tensorrt.CUDAContext或者cuda-python但工程上 PyCUDA 仍然是最省事的方案。实际使用时如果 ONNX 模型的输入是 NCHW 格式你要把 numpy 数组转成(batch, 3, height, width)并且 dtype 是float32。YOLO 预处理通常已经把图片归一化到 0~1 或 0~255只要和训练时一致就行。2.4 这个方案的坑序列化限制、动态 batch、显存问题我先说最坑的一件事trtexec --saveEngine生成的引擎是绑定当前 GPU 的换一张不同架构的卡就会加载失败。错误信息往往是反序列化时报Invalid engine或者直接段错误。所以团队里如果有人在一张卡上转了 engine 发给你别直接拿去用先问清楚是不是同一型号显卡。其次是动态 batch 的问题。我见过有人把minShapes设为1maxShapes设为8但推理时传了batch7按理说没问题。可如果代码里忘了调用context.set_input_shape()TensorRT 会沿用上一次或者默认的 shape轻则结果不对重则直接报错。因此动态引擎的 Python 推理代码必须显式设置每个输入的实际 shape。还有显存问题。构建引擎时--memPoolSizeworkspace:2G给得越大TensorRT 选择的 kernel 越有可能偏向低延迟但运行时占用的显存也可能更高。如果你的显存紧张别一味加大 workspace先默认值跑起来再看 profiling 结果决定要不要调整。3. 方式二用 TensorRT Python API 一条龙转换适合做自动化工具3.1 为什么还要用代码再实现一遍转换很多同学会问trtexec 已经能转 engine为什么还要写 Python 代码理由是自动化。trtexec 只能通过命令行参数控制转换你没法方便地在转换前后加自定义逻辑比如从训练配置里读取动态 shape、把校准数据集传进去做 INT8 量化、批量处理几十个 ONNX 文件、把转换失败的信息格式化后通知监控系统。这时候用 TensorRT Python API 会更顺手。另一个原因是排查问题。Python API 可以逐步查看网络结构、检查每个输入输出张量、在构建前修改网络。trtexec 是一个黑盒它失败了只会告诉你哪个节点有问题但你想“打断”转换过程去看中间状态非常困难。3.2 核心流程Logger、Builder、Network、OnnxParserPython API 转换 ONNX 的标准流程如下每一块我拆开讲。import tensorrt as trt logger trt.Logger(trt.Logger.WARNING) builder trt.Builder(logger) network builder.create_network( 1 int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH) ) parser trt.OnnxParser(network, logger) with open(yolov12.onnx, rb) as f: if not parser.parse(f.read()): for i in range(parser.num_errors): print(parser.get_error(i)) raise RuntimeError(ONNX parse failed)trt.Logger是 TensorRT 的日志输出口WARNING 级别够用如果调试可以改成INFO或VERBOSE。Builder负责构建引擎。Network是网络定义他会在这张计算图上做优化。EXPLICIT_BATCH这个标志很关键它告诉 TensorRT 网络输入 shape 里的 batch 维度是显式的否则默认还走旧版隐式 batch 模式很多 ONNX 模型会解析失败。OnnxParser就是负责读 ONNX 的解析器。如果 parse 失败遍历parser.get_error(i)能找到具体节点信息。我通常先把 error 打出来再决定要不要上onnx-simplifier。3.3 动态 Shape 与 Optimization Profile 的处理解析成功后网络里已经有一个输入名字通常是input。接下来要配置优化 profile否则动态 shape 的模型会在 build 时报错。config builder.create_builder_config() config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 2 30) profile builder.create_optimization_profile() profile.set_shape(input, (1, 3, 640, 640), (1, 3, 640, 640), (8, 3, 640, 640)) config.add_optimization_profile(profile)set_shape的三个参数分别是 min、opt、max。如果你的模型有多个输入每个输入都要设置否则 build 的时候 TensorRT 不知道如何处理那个输入维度的变化范围。这里还要注意一点config.set_memory_pool_limit的第二个参数单位是字节2 30是 2GB。旧版本里可能是config.max_workspace_size 2 30到 TensorRT 9/10 之后被set_memory_pool_limit取代写代码前先确认你的 API 版本。如果同一个模型需要在多个 shape 范围下部署比如一个场景 batch 常用 1另一个场景 batch 常用 16你可以 add 多个 optimization profile。推理时由context.set_input_shape选择匹配的 profile。多个 profile 会增加构建时间和内存消耗所以不要贪多够用就好。3.4 构建引擎、保存文件、加载推理有了 network 和 config就可以构建引擎了。TensorRT 10 开始推荐使用build_serialized_network直接得到序列化后的字节流engine_bytes builder.build_serialized_network(network, config) with open(yolov12_py.engine, wb) as f: f.write(engine_bytes)如果你用的是 TensorRT 8.6 或更老版本可能会看到builder.build_engine(network, config)返回一个ICudaEngine然后通过engine.serialize()得到字节流。这两种方式没有本质区别只是一个新一个旧。构建过程中TensorRT 会进行 layer fusion 和 kernel 选择慢的话可能几分钟日志里能看到进度。后面加载推理和方式一完全一样都是把.engine文件读进来用runtime.deserialize_cuda_engine反序列化然后create_execution_context执行。完整推理代码可以参考第一章里的TRTEngine类也可以把engine_path换成刚才生成的yolov12_py.engine。3.5 和 trtexec 的差异对比我用一个表格总结下因为很多人会在方式一和方式二之间纠结。维度trtexecPython API上手难度低改参数就行中等要理解几个核心对象可编程性差只能通过命令行选项高可在构建前后做任意逻辑动态 shape 配置支持 min/opt/max支持多 profile更灵活INT8 量化校准只支持命令行指定校准文件可以自定义校准器类接入自己的数据集批量转换需要 shell 循环可以直接写进 Python 调度系统排错能力弱日志输出有限强可以逐层检查网络我的建议是如果只是临时验证优先用 trtexec如果这个转换动作会成为平台或工具链的一部分直接用 Python API 重写一遍并不亏。4. 方式三用 ONNX Runtime 加 TensorRT Provider不转引擎也能吃 TRT 红利4.1 这个方案解决什么问题很多公司模型部署已经标准化到 ONNX Runtime 上了接一个模型只需要准备 ONNX 文件然后用InferenceSession跑推理。如果这时候为了上 TensorRT 还要额外维护 plan 文件、写 TensorRT 推理代码改动成本很高。ONNX Runtime 的 TensorRT Execution Provider 就是为了这个场景设计的。你只需要在创建 session 时把 provider 列表改成包含 TensorRTONNX Runtime 在初始化时就会把支持的计算图子图交给 TensorRT 执行。对你来说接口仍然是session.run不需要自己写 bindings、不需要管理 CUDA 内存。4.2 配置 providers 和会话选项先安装带 GPU 支持且包含 TensorRT 的 ONNX Runtimepip install onnxruntime-gpu不同版本的 onnxruntime-gpu 对 TensorRT 版本有对应关系强烈建议先去官网查 compatibility 页面。比如 onnxruntime 1.17.x 搭配 TensorRT 8.6 / CUDA 11.8 这种组合如果版本差距太大provider 可能直接初始化失败。下面是一个配置示例import onnxruntime as ort sess_options ort.SessionOptions() sess_options.graph_optimization_level ort.GraphOptimizationLevel.ORT_ENABLE_ALL sess_options.enable_profiling True providers [ ( TensorrtExecutionProvider, { device_id: 0, trt_fp16_enable: True, trt_engine_cache_enable: True, trt_engine_cache_path: ./trt_cache, }, ), CUDAExecutionProvider, ] session ort.InferenceSession( yolov12.onnx, sess_optionssess_options, providersproviders, ) print(session.get_providers())这里 provider 列表里第一个是 TensorRT第二个是 CUDA fallback。TensorRT 不支持的算子会落到 CUDA EP 上保证模型能跑起来避免初始化直接失败。trt_engine_cache_enable和trt_engine_cache_path会让 ORT 把 TensorRT 内部生成的引擎缓存到本地。第一次跑的时候会慢一些因为要做转换和 kernel 选择第二次再加载同一个 ONNX、同一个 shape 范围就会直接用缓存省掉大量时间。我建议正式环境里一定开这个缓存。4.3 怎么确认是真的在用 TensorRT不验证的话没人知道 provider 到底生效没有。最简单的办法是打印print(session.get_providers())正常输出里能看到TensorrtExecutionProvider在前面。如果里面只有CPUExecutionProvider说明 GPU 相关的动态库没装好得回去查 onnxruntime-gpu 和 CUDA/cuDNN 的版本。更细的验证方式是开 profiling。sess_options.enable_profiling True跑一次后当前目录会生成一个onnxruntime_profile_*.json打开后能看到每个节点由哪个 EP 执行。这一步能直观地看出 TensorRT 接管了哪些节点。还有一种方式设置trt_dump_subgraphs: True它会输出 TensorRT EP 的子图划分结果。如果你发现模型基本都落在 TensorRT EP 上性能通常不会差如果大量算子落到 CUDA EP那就要考虑是不是模型里有不支持的算子。4.4 这个方式什么时候不该用ONNX Runtime 的 TensorRT provider 虽然方便但也有它的边界。第一它不完全等于手写 TensorRT 引擎。ORT 为了兼容性和通用性会做出一部分优化取舍子图划分也可能不是最优。如果你已经明确要用 TensorRT 做核心推理引擎并且对延迟有极致要求还是应该走前两种方式拿到独立 plan 文件。第二缓存和模型更新容易出问题。模型文件本身没变化但 ONNX 里某个节点被改了计算图属性ORT 的缓存 key 可能没感知到导致用了旧缓存。每次更新模型后最好清一次缓存目录。第三动态 shape 的控制粒度更粗。TRT EP 支持动态 shape但 profile 的配置不如手写 TensorRT 灵活。你没法像 Python API 那样精细地配置多个 profile 做不同 batch 的切换。5. 动态 Shape 和 INT8 量化高频热词背后的真实门槛5.1 INT8 量化不是“加个--int8就行”搜索热词里有一堆和.onnx 量化 int8相关的内容我单独说下这个。很多人看完命令文档以为转 INT8 就是把--fp16换成--int8。这是错的。TensorRT 做 INT8 推理时需要知道每一层激活值的动态范围这个范围通常需要你对一小批有代表性的真实数据做校准否则精度可能掉得没法看。在 Python API 里需要继承trt.IInt8Calibrator实现get_batch_size、get_batch、read_calibration_cache、write_calibration_cache这几个方法然后在 builder config 里设置config.set_flag(trt.BuilderFlag.INT8) config.int8_calibrator calibrator校准数据集不需要很大通常几百张图足够但一定要覆盖你的真实部署场景。训练集和测试集不是一个分布校准结果也会失真。具体到 YOLO 这类目标检测模型INT8 在 mAP 上通常有 1~3 个点的损失。如果模型是用于小目标检测INT8 的影响会更明显。我建议流程上先跑 FP16把功能验证通过再单独开一个分支做 INT8 量化不要一开始就用 INT8否则后面排查精度问题时你不知道是预处理问题还是量化问题。5.2 动态 Shape 的三个值min、opt、max 该怎么填动态 shape 是另一高频难点。绝大多数 ONNX 模型输入是固定的比如[1, 3, 640, 640]。如果只有 batch 会变就关心 batch 维度的区间。如果是语义分割或者超分模型可能高和宽也会变那就是完整的三组 shape。min 是你线上可能出现的最小输入一般取 1max 是最大输入要同时考虑显存容量opt 是你最常见的输入TensorRT 会优先优化这个尺寸上的性能。比如视频流推理大部分时候 batch 是 1偶尔需要并发处理 4 张图那 opt 可以填 1max 填 4这样单帧延迟最稳不会因为经常出现 batch4 而性能下降太多。还有一个容易犯的错set_shape里的元组必须和 ONNX 输入维度一一对应YOLO 的输入是[N, C, H, W]所以写(1, 3, 640, 640)而不是(1, 640, 640, 3)。ONNX 默认是 NCHW除非你在导出时换过布局否则别弄反。5.3 三种方式在这个场景下的选型建议从热词和实际项目经验看不同人适合的路径差别很大。我按需求列个表你的情况推荐方式理由只想知道自己的模型在 TensorRT 上能跑多快方式一trtexec最快参数改一改就能拿到延迟数据要把转换流程集成到部署平台经常批量转模型方式二Python API可编程、可监控、可配量化校准项目已经用 ONNX Runtime不想改推理代码方式三ORT TRT EP接入成本低代码改动最小需要精细控制多 profile 动态 shape方式二Python APITRT EP 动态 shape 控制粒度不够细模型包含大量自定义算子方式二 自定义 plugin最可控能直接嵌入 plugin 注册逻辑这个表没有标准答案同一个团队里也可能三种方式并存。我见过不少团队先用 trtexec 做可行性验证确认收益后再用 Python API 写正式转换脚本线上服务则加载独立 plan 推理。这样既保证了灵活度又回避了 ONNX Runtime 的额外开销。6. 我踩过的坑和三种方式的最终选型6.1 版本匹配是头号杀手显卡驱动、CUDA、cuDNN、TensorRT、ONNX Runtime跑 TensorRT 最痛苦的从来不是 API 不会写而是版本装错。常见错误是显卡驱动太老不支持新版 CUDACUDA 版本对了cuDNN 不对TensorRT 装的是 deb 包但 Python 包来自 pip两套版本不一致onnxruntime-gpu 和 TensorRT 版本不匹配provider 直接报NotImplementedError或初始化失败。我的经验是先去 NVIDIA 官网查清楚以下对应关系组件必须匹配的对象GPU 驱动CUDA 最低版本要求CUDAcuDNN、TensorRT、onnxruntime-gpucuDNNTensorRT 构建时的版本TensorRTplan 引擎序列化格式onnxruntime-gpuCUDA / cuDNN / TensorRT 版本如果是在自有服务器上部署最省心的做法是用 NGC PyTorch 容器镜像里已经把 CUDA、cuDNN、TensorRT 版本配好了你只需要在里面安装自己的 Python 依赖。但生产环境如果不能用容器就一定要写一个版本清单文件记录每次部署用到的具体版本号否则换台机器大概率复现不了结果。6.2 常见错误和排查思路我把自己踩过的高频错误整理一下遇到类似问题可以按顺序排查。第一个是AttributeError: module tensorrt has no attribute XXX。出现这种错误一般是 TensorRT Python API 版本和你的代码不匹配。TensorRT 9 和 10 之间 API 有变化比如InferShape、BuilderFlag这些对象在不同版本里名称几乎一样但某些方法被重命名或移动位置。遇到后不要怀疑是自己代码写错了先看当前版本里正确的调用方式。第二个是Could not parse ONNX。这种错误信息很多常见原因有两个ONNX opset 版本太高TensorRT Parser 没跟上或者模型里有自定义算子。先用onnxsim化简太大胆的算子再查 TensorRT 支持的 layers 列表。如果确实有自定义算子通常需要写 plugin。第三个是加载引擎时崩溃。重点检查 GPU 是否一致、TensorRT 版本是否一致。还有一个小众问题.engine文件没有以二进制方式完整读取或者文件在传输过程中被截断。可以对比一下文件大小和生成时打印的序列化大小。第四个是 FP16 下精度异常。先用 FP32 跑一遍确认输出是对的再切 FP16。之前我遇到过某个正常转换的模型FP16 输出在最后一个检测头出现 NaN排查了很久最后发现是某个算子精度敏感需要在转换时强制保留 FP32。这类问题很考验耐心但可以通过在 Python API 里逐层设置精度策略来解决这也是方式二比 trtexec 更灵活的地方。6.3 我个人在实际项目里是怎么选的如果今天让我给一个团队做技术方案我会先根据“最终运行环境”倒推如果最终运行代码是 Python而且团队里没人想维护 TensorRT C 那套工具链我会用方式一生成 plan再写一个轻量 Python loader把所有输入输出封装成类对外只暴露infer()方法。这样可以最大程度保证性能同时代码量最少。如果团队有自动化产线每天要处理几十个模型或版本迭代我会把方式二做成服务接收 ONNX 路径、返回 engine 路径转换日志和错误信息都统一结构化。这个服务里顺便把 INT8 校准器也实现掉。如果项目只是快速验证或者模型迭代得非常频繁ONNX 文件一天换好几个我会用方式三先跑起来等模型稳定了再切换前两种。我个人更偏方式二因为它把“编译引擎”和“跑推理”这两件事统一在了一套 Python 代码里调试时可以用 jupyter 分段执行看网络结构、看 profile、看每层输出非常顺手。方式一适合做 benchmark方式三适合做胶水层但真正要把 TensorRT 的性能吃透还是得理解 Builder、Network、BuilderConfig 这一套 API。最后再分享一个小技巧无论用哪种方式都把trtexec的 benchmark 输出留一份。因为后面做 FP16、INT8、动态 shape 调整时你需要一个基线来判断改动到底变快还是变慢。我通常会把 FP32、FP16、INT8 三份延迟数据记在同一个表格里模型一更新就重跑一遍这样部署调优才有据可依。
返回列表