news 2026/10/15 15:49:19

TensorRT 学习文档(一):从 PyTorch 训练到 TensorRT 部署的完整流程记录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TensorRT 学习文档(一):从 PyTorch 训练到 TensorRT 部署的完整流程记录

TensorRT 学习文档(一)

从 PyTorch 训练到 TensorRT 部署的完整流程记录


目录

  1. 整体流程
  2. 环境准备
  3. PyTorch 模型基础
  4. PyTorch → ONNX
  5. ONNX → TensorRT
  6. 推理运行时
  7. ONNX 图优化
  8. 精度验证
  9. 动态 Shape
  10. 常见问题

1. 整体流程

PyTorch 模型 ↓ torch.onnx.export ONNX 模型 ↓ onnxsim(可选) 简化 ONNX ↓ trtexec / TensorRT API TensorRT Engine ↓ 部署 推理服务

每个阶段的产物:

阶段产物特点
PyTorch.pth权重训练用,Python 运行
ONNX.onnx通用计算图,跨框架
TensorRT.engineGPU 专用优化,硬件相关

2. 环境准备

2.1 版本要求

组件版本说明
Ubuntu22.04系统
CUDA13.0驱动 580+
TensorRT11.4.0.106通过 deb 本地仓库安装
CMake3.31+编译 sample 需要
Python3.10conda 环境

2.2 安装 TensorRT(deb 本地仓库)

# 1. 安装本地仓库包sudodpkg-inv-tensorrt-local-repo-ubuntu2204-10.16.1-cuda-13.2_1.0-1_amd64.deb# 2. 复制密钥sudocp/var/nv-tensorrt-local-repo-ubuntu2204-10.16.1-cuda-13.2/nv-tensorrt-local-F07E5E88-keyring.gpg /usr/share/keyrings/# 3. 更新软件列表sudoapt-getupdate# 4. 安装 TensorRTsudoapt-getinstalltensorrt# 5. 安装 Python 包(可选)sudoapt-getinstallpython3-libnvinfer

2.3 验证

# 命令行工具trtexec--help# C++ 库dpkg-l|greplibnvinfer# Python 包python3-c"import tensorrt; print(tensorrt.__version__)"

2.4 conda 环境安装(推荐)

conda create-ntrt_envpython=3.10-yconda activate trt_env pipinstalltensorrt-cu13 pycuda numpy onnx onnxruntime onnxsim polygraphy

3. PyTorch 模型基础

3.1 卷积层参数

nn.Conv2d(输入通道,输出通道,核大小)

参数量公式:

输出通道 × (输入通道 × 核高 × 核宽 + 1)

示例:

nn.Conv2d(16,32,3)# 参数量 = 32 × (16×3×3 + 1) = 4640

3.2 全连接层参数

nn.Linear(输入数,输出数)

参数量公式:

输出数 × (输入数 + 1)

示例:

nn.Linear(1568,10)# 参数量 = 10 × (1568 + 1) = 15690

3.3 数据流示例

classSimpleCNN(nn.Module):def__init__(self):super().__init__()self.conv1=nn.Conv2d(1,16,3,padding=1)self.conv2=nn.Conv2d(16,32,3,padding=1)self.relu=nn.ReLU()self.pool=nn.MaxPool2d(2,2)self.fc=nn.Linear(32*7*7,10)defforward(self,x):x=self.pool(self.relu(self.conv1(x)))# 16 × 14 × 14x=self.pool(self.relu(self.conv2(x)))# 32 × 7 × 7x=torch.flatten(x,1)# 1568x=self.fc(x)# 10returnx

数据流:

输入 1×28×28 ↓ conv1(1→16 层) 16×28×28 ↓ pool 16×14×14 ↓ conv2(16→32 层) 32×14×14 ↓ pool 32×7×7 ↓ flatten 1568 ↓ fc 10

3.4 卷积的权值共享

维度是否共享
同一输出层,同一输入层,不同空间位置✅ 共享
同一输出层,不同输入层❌ 不共享
不同输出层,任意❌ 不共享

滑动 = 同一套参数在不同位置重复使用。


4. PyTorch → ONNX

4.1 导出脚本

importtorchfrommodel_v2importSimpleCNN# 1. 准备模型model=SimpleCNN()model.eval()# 必须!切换到推理模式# 2. 准备假输入dummy_input=torch.randn(1,1,28,28)# 3. 导出torch.onnx.export(model,dummy_input,"model_v2.onnx",input_names=["input"],output_names=["output"],opset_version=17)

4.2 关键参数

参数作用
model要导出的模型
dummy_input假输入,决定图结构
input_names给输入张量起名字
output_names给输出张量起名字
opset_versionONNX 操作集版本
dynamic_axes指定动态维度

4.3 trace vs script

tracescript
原理跑一遍,记录实际操作读代码,解析成图
if只记录走的分支两条都保留
for展开成具体次数保留循环结构
getattr能处理❌ 报错
动态列表能处理❌ 报错
Python 特性全部支持只支持子集

选择原则:

  • 纯顺序模型 → trace
  • 有控制流 → script
  • 有动态特性 → trace

trace 的风险:

ifx.sum()>0:x=self.fc1(x)else:x=self.fc2(x)

假输入走 fc1,导出后只有 fc1,fc2 完全丢失。静默错误,不报错。

4.4 打印 ONNX 图结构

importonnx model=onnx.load("model_v2.onnx")print("=== 输入 ===")forinpinmodel.graph.input:print(f"{inp.name}, shape={[d.dim_valuefordininp.type.tensor_type.shape.dim]}")print("\n=== 输出 ===")foroutinmodel.graph.output:print(f"{out.name}, shape={[d.dim_valuefordinout.type.tensor_type.shape.dim]}")print("\n=== 节点 ===")fori,nodeinenumerate(model.graph.node):print(f" [{i}]{node.op_type}输入:{list(node.input)}输出:{list(node.output)}")

5. ONNX → TensorRT

5.1 基本转换

trtexec--onnx=model_v2.onnx--saveEngine=model_v2.engine

5.2 转换过程做了什么

1. 解析 ONNX → 读出计算图 2. 构建网络 → 把 ONNX 节点转成 TensorRT 的层 3. 优化 → 选最快的算法,融合能融合的层 4. 生成 engine → 序列化保存

关键优化:

优化说明
算子融合Conv+BN+Relu 合成一个 kernel
算法选择同一卷积有多种实现,选最快的
精度校准FP32/FP16/INT8
内存优化复用显存

5.3 层融合

能融合的层:

  • Relu、Sigmoid、BatchNorm(推理时)
  • Scale、Add(相同形状)
  • 逐元素操作

不能融合的层:

  • Softmax(需要全局求和)
  • Reshape(改变数据布局)
  • Concat(需要等所有输入)

融合的条件:每个输出元素只依赖对应位置的输入,不需要等所有数据。

5.4 参数说明

参数作用
--onnx=输入 ONNX 文件
--saveEngine=输出 engine 文件
--minShapes/--optShapes/--maxShapes动态 shape 范围
--fp16开启 FP16
--int8开启 INT8 量化

5.5 动态 Shape 转换

trtexec--onnx=model_v2_dynamic.onnx\--saveEngine=model_v2_dynamic.engine\--minShapes=input:1x1x28x28\--optShapes=input:4x1x28x28\--maxShapes=input:16x1x28x28
  • minShapes:最小输入
  • optShapes:最常用输入(优化重点)
  • maxShapes:最大输入(显存按这个分配)

6. 推理运行时

6.1 核心概念

概念说明
Engine编译好的模型,只读,可序列化
Context运行实例,有独立中间激活值
Binding输入输出张量,需要自己分配内存
StreamCUDA 执行队列,异步

6.2 关系图

Engine ├── Context 1 → Binding 地址 1 → Stream 1 ├── Context 2 → Binding 地址 2 → Stream 2 └── ...

Binding 定义公用(名字、形状),地址每个 context 独立。

6.3 完整推理示例

importtensorrtastrtimportpycuda.driverascudaimportpycuda.autoinitimportnumpyasnp# 1. 加载 enginelogger=trt.Logger(trt.Logger.WARNING)runtime=trt.Runtime(logger)withopen("model.engine","rb")asf:engine=runtime.deserialize_cuda_engine(f.read())# 2. 创建 contextcontext=engine.create_execution_context()# 3. 查输入输出input_names=[]output_names=[]foriinrange(engine.num_io_tensors):name=engine.get_tensor_name(i)mode=engine.get_tensor_mode(name)ifmode==trt.TensorIOMode.INPUT:input_names.append(name)else:output_names.append(name)# 4. 分配内存bufs={}fornameininput_names+output_names:shape=tuple(engine.get_tensor_shape(name))dtype=trt.nptype(engine.get_tensor_dtype(name))host=np.random.randn(*shape).astype(dtype)ifnameininput_names \elsenp.empty(shape,dtype=dtype)device=cuda.mem_alloc(host.nbytes)context.set_tensor_address(name,int(device))bufs[name]=(host,device)# 5. 执行stream=cuda.Stream()fornameininput_names:host,device=bufs[name]cuda.memcpy_htod_async(device,host,stream)context.execute_async_v3(stream_handle=stream.handle)fornameinoutput_names:host,device=bufs[name]cuda.memcpy_dtoh_async(host,device,stream)stream.synchronize()

6.4 单 context vs 多 context

配置能否并行原因
1 context + 1 stream❌只有一个工作台
1 context + 多 stream❌ 不安全中间激活值只有一份
多 context + 多 stream✅每个 context 独立内存

7. ONNX 图优化

7.1 三类优化

类型说明
算子融合Conv+BN+Relu → 一个节点
常量折叠能在导出时算出来的,直接替换成常量
Shape 推理静态推导每个节点的输出形状

7.2 用 onnxsim 简化

pipinstallonnxsim onnxsim model_v2.onnx model_v2_sim.onnx

输出示例:

┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┓ ┃ ┃ Original Model ┃ Simplified Model ┃ ┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━┩ │ Concat │ 10 │ 5 │ │ Constant │ 91 │ 88 │ │ Conv │ 55 │ 55 │ └──────────────────┴─────────────────┴──────────────────┘

7.3 移除 initializer 中的输入

python3-monnxruntime.tools.remove_initializer_from_input\--inputvpd_d.onnx\--outputvpd_d_fixed.onnx

为什么需要:旧版 PyTorch 导出的模型,权重被同时声明为输入,影响优化。

7.4 什么时候需要 ONNX 优化

情况需要吗
模型简单,TensorRT 一次转成功❌ 可以跳过
模型复杂,TensorRT 报错✅ 必须
想减少编译时间建议
想减少运行时 kernel 数量建议

8. 精度验证

8.1 为什么需要

TensorRT 转换可能引入误差:

  • 层融合改变计算顺序
  • 算法选择和原始实现不同
  • FP16/INT8 量化有精度损失

8.2 用 polygraphy 验证

polygraphy run model_v2.onnx\--trt\--onnxrt\--input-shapes"input:[1,1,28,28]"

预期输出:

[I] Absolute Difference | Stats: mean=2.1e-08, max=5.9e-08 [I] Relative Difference | Stats: mean=2.0e-07, max=5.5e-07 [I] PASSED | Output: 'output' | Difference is within tolerance [I] PASSED | Runtime: 5.8s

8.3 误差标准

精度可接受误差
FP32< 1e-5
FP16< 1e-3
INT8< 1e-2

8.4 查看模型信息

polygraphy inspect model model_v2.onnx

输出:

---- 1 Graph Input(s) ---- {data [dtype=float32, shape=(1, 3, 192, 576)]} ---- 5 Graph Output(s) ---- {p3_conv_conf [dtype=float32, shape=(1, 8, 48, 144)], p4_conv_conf [dtype=float32, shape=(1, 8, 24, 72)], ...}

8.5 polygraphy 与 engine 的关系

polygraphy 用--trt时,会自动从 ONNX 构建 TensorRT engine,不需要提前用trtexec转出.engine文件。

你跑的命令:

polygraphy run model_v2.onnx--trt--onnxrt--input-shapes"input:[1,1,28,28]"

它内部做了:

model_v2.onnx ↓ TensorRT ONNX Parser ↓ TensorRT Builder 临时 engine(在内存里,不保存) ↓ 跑推理,和 ONNX Runtime 对比
两种运行模式

模式 1:从 ONNX 现场构建(默认)

polygraphy run model.onnx--trt--onnxrt
  • 不需要预先有 engine
  • 每次跑都会重新构建,较慢
  • 适合临时验证

模式 2:加载已有 engine

polygraphy run model.onnx--trt--onnxrt--load-engine=model.engine

或者直接对 engine 跑:

polygraphy run model.engine--trt
  • 不需要重新构建,快
  • 适合反复测试同一个 engine
是否自动保存 engine

默认不保存。临时构建的 engine 只存在内存里,跑完就释放,不会落盘。

想保存,加参数:

polygraphy run model.onnx\--trt\--onnxrt\--save-engine=model.engine

跑完当前目录会生成model.engine。下次可以:

polygraphy run model.onnx--trt--load-engine=model.engine

直接加载,不用重新构建,快很多。

为什么默认不保存

polygraphy 的定位是验证工具,不是转换工具。

它的主要任务是:

ONNX 输出 vs TensorRT 输出 → 对比 → PASSED / FAILED

engine 只是中间产物,验证完就丢。

要保存 engine,用trtexec --saveEngine=或加--save-engine。

对比
工具默认保存 engine 吗
trtexec --saveEngine=✅ 明确要求保存
polygraphy run --trt❌ 不保存
polygraphy run --trt --save-engine=✅ 保存

9. 动态 Shape

9.1 导出动态 ONNX

torch.onnx.export(model,dummy_input,"model_v2_dynamic.onnx",input_names=["input"],output_names=["output"],dynamic_axes={"input":{0:"batch_size"},"output":{0:"batch_size"}},opset_version=17)

9.2 转换时指定范围

trtexec--onnx=model_v2_dynamic.onnx\--saveEngine=model_v2_dynamic.engine\--minShapes=input:1x1x28x28\--optShapes=input:4x1x28x28\--maxShapes=input:16x1x28x28

9.3 验证不同 batch

trtexec--loadEngine=model_v2_dynamic.engine--shapes=input:1x1x28x28 trtexec--loadEngine=model_v2_dynamic.engine--shapes=input:8x1x28x28 trtexec--loadEngine=model_v2_dynamic.engine--shapes=input:16x1x28x28

9.4 动态 Shape 注意事项

  • maxShapes决定显存分配,设太大会浪费显存
  • optShapes是优化重点,设成最常用的 batch
  • 超出maxShapes的输入会报错

10. 常见问题

10.1 安装问题

问题原因解决
trtexec: command not found未安装或不在 PATHexport PATH=/usr/src/tensorrt/bin:$PATH
libnvinfer.so: cannot openLD_LIBRARY_PATH 未设置export LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH
Python 包找不到conda 环境隔离在 conda 环境里pip install tensorrt-cu13

10.2 转换问题

问题原因解决
Unknown option: --explicitBatchTensorRT 11.x 已移除去掉这个参数
Unsupported operator算子不支持用 onnxsim 简化,或自定义插件
CMake 3.31 or higher is requiredCMake 版本低升级 CMake

10.3 精度问题

问题原因解决
输出差异 > 1e-2转换有问题检查输入对齐、模型加载
某个输出差异大该分支有算子问题用 polygraphy 定位到具体层
Sigmoid 后差异大正常Sigmoid 会放大相对误差

10.4 性能问题

问题原因解决
GPU compute time unstable模型太小,测量噪声锁定 GPU 频率sudo nvidia-smi -lgc 1500
推理比预期慢没开 FP16加--fp16
显存不够maxShapes 设太大减小 maxShapes

附录:常用命令速查

# 导出 ONNXpython3 export_onnx.py# 简化 ONNXonnxsim model.onnx model_sim.onnx# 查看 ONNX 信息polygraphy inspect model model.onnx# 转换 TensorRTtrtexec--onnx=model.onnx--saveEngine=model.engine# 动态 Shape 转换trtexec--onnx=model.onnx--saveEngine=model.engine\--minShapes=input:1x1x28x28\--optShapes=input:4x1x28x28\--maxShapes=input:16x1x28x28# 精度验证(不保存 engine)polygraphy run model.onnx--trt--onnxrt--input-shapes"input:[1,1,28,28]"# 精度验证 + 保存 enginepolygraphy run model.onnx--trt--onnxrt--save-engine=model.engine --input-shapes"input:[1,1,28,28]"# 加载已有 engine 验证polygraphy run model.onnx--trt--load-engine=model.engine# 加载 engine 跑推理trtexec--loadEngine=model.engine# 指定 batch 跑推理trtexec--loadEngine=model.engine--shapes=input:8x1x28x28

附录:工具读法

工具读法音标
TensorRTTensor-R-T/ˈtensər ɑːr tiː/
ONNX欧尼克斯/ˈɒnɪks/
trtexecT-R-T-exec/tiː ɑːr tiː ˈeksək/
polygraphy波利格拉菲/pəˈlɪɡrəfi/
onnxsimONNX-sim/ˈɒnɪks sɪm/
PyCUDAPy-CUDA/paɪ ˈkuːdə/
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 15:48:30

用Flask+WebUploader实现大文件分段上传与断点续传

简介&#xff1a;面向大文件传输场景的Flask与WebUploader分段上传演示项目&#xff0c;提供分片上传与流式下载的可运行参考&#xff0c;适合Python后端工程师及需要并发上传、断点续传的Web开发人员。压缩包共26个文件、约427KB&#xff0c;含2个Python脚本、2个HTML模板、5个…

作者头像 李华
网站建设 2026/10/15 15:47:33

仿写静态页面:从结构拆解到像素级还原的前端实战

仿写一个静态页面&#xff0c;听起来像是初学者才干的活儿&#xff1f;我倒是觉得&#xff0c;这恰恰是前端基本功里最被低估的一环。我自己带过不少新人&#xff0c;聊起框架头头是道&#xff0c;可真给他一张设计稿&#xff0c;让他用HTML和CSS还原出来&#xff0c;页面却常常…

作者头像 李华
网站建设 2026/10/15 15:47:04

字符串解码算法详解:从栈到递归下降的工程实践

处理过嵌套日志、写过模板引擎&#xff0c;或者只是被朋友问过一道算法题的人&#xff0c;大概率都见过这种输入&#xff1a;3[a]2[bc]。把这种字符串还原成aaabcbc&#xff0c;就是字符串解码。我第一次接触这个题目时觉得规则很简单&#xff0c;真正动手实现才发现&#xff0…

作者头像 李华
网站建设 2026/10/15 15:47:02

SHINEtoolbox高光谱处理工具箱:MATLAB专用遥感数据全链路分析指南

简介&#xff1a;SHINEtoolbox是一款面向心理学、神经科学及视觉感知研究者的MATLAB专用工具箱&#xff0c;专为消除图像亮度、对比度、色彩饱和度、纹理与尺寸等低阶属性差异而设计&#xff0c;助力科研人员在实验中实现视觉刺激的标准化控制&#xff0c;提升认知与神经响应测…

作者头像 李华
网站建设 2026/10/15 15:46:54

Easy-OPM:面向中小团队的极简Java ORM框架

简介&#xff1a;Easy-OPM是一款面向Java中初级开发者、聚焦中小型项目的轻量级ORM框架&#xff0c;旨在替代Hibernate或MyBatis等重型方案&#xff0c;在不牺牲核心功能的前提下显著降低学习与集成成本&#xff0c;特别适用于教学实践、快速原型开发及资源受限的微服务模块。压…

作者头像 李华