1. 环境准备与版本匹配避开新版本部署的第一个大坑如果你和我一样最近在 Ubuntu 22.04 上折腾 TensorRT特别是想用上最新的 10.0 版本那第一步绝对不是急着敲安装命令。我踩过最大的坑就是版本不匹配。系统装好了CUDA 也装了结果 TensorRT 死活装不上或者装上了跑不起来十有八九就是版本“打架”了。这就像你买了一台最新型号的游戏主机却想插上一张五年前的老显卡接口都对不上更别提运行了。所以在动手之前我们必须把“全家桶”的版本对应关系理清楚。这个全家桶的核心就是NVIDIA 驱动、CUDA Toolkit、cuDNN 和 TensorRT。它们之间是层层依赖的。TensorRT 10.0 是2024年发布的新版本它支持的 CUDA 版本范围是 11.0 到 12.4。如果你的系统是 Ubuntu 22.04并且希望用上比较新的 CUDA 12.4那么 TensorRT 10.0 就是目前截至我写这篇文章时几乎唯一的选择。我一开始没注意系统里装的是 CUDA 12.2结果去官网找 TensorRT 10.0 的安装说明发现它明确要求 CUDA 12.4只好又重装了一遍 CUDA。怎么查看自己当前的版本呢打开终端依次输入以下命令# 查看NVIDIA驱动版本 nvidia-smi # 查看CUDA运行时版本通常由驱动决定 nvcc --versionnvidia-smi命令输出的右上角会显示你当前支持的最高CUDA 版本而nvcc --version显示的是你实际安装的 CUDA 编译工具版本。对于 TensorRT 来说我们更关心nvcc的版本。我建议直接安装 CUDA 12.4这样可以和 TensorRT 10.0 完美匹配。安装 CUDA 12.4 和对应的 cuDNN 8.9.x 版本是后续一切顺利的基础。cuDNN 的版本也需要对应TensorRT 10.0.0.6 官方测试用的是 cuDNN 8.9.7我们就用这个最稳。这里有个小技巧不要去死记硬背版本号。直接去 NVIDIA 官方的TensorRT Release Notes文档里查。在文档里搜索 “Support Matrix” 或 “Requirements”你会看到一个清晰的表格告诉你哪个版本的 TensorRT 需要搭配哪个版本的 CUDA 和 cuDNN。这是最权威、最不会出错的方法。我第二次安装时学乖了就是对着这个表格操作的一次成功。1.1 安装 CUDA 12.4 与 cuDNN 8.9.7确定了版本我们就开始安装。Ubuntu 22.04 的包管理器里可能没有最新的 CUDA 12.4所以我们需要从 NVIDIA 官网下载安装包。我推荐使用deb (network)安装方式这样管理起来比较方便。首先访问 NVIDIA CUDA 工具包下载页面选择 Linux - x86_64 - Ubuntu - 22.04 - deb (network)。页面上会给出安装命令通常长这样wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-ubuntu2204.pin sudo mv cuda-ubuntu2204.pin /etc/apt/preferences.d/cuda-repository-pin-600 wget https://developer.download.nvidia.com/compute/cuda/12.4.0/local_installers/cuda-repo-ubuntu2204-12-4-local_12.4.0-550.54.14_amd64.deb sudo dpkg -i cuda-repo-ubuntu2204-12-4-local_12.4.0-550.54.14_amd64.deb sudo cp /var/cuda-repo-ubuntu2204-12-4-local/cuda-*-keyring.gpg /usr/share/keyrings/ sudo apt-get update sudo apt-get -y install cuda-toolkit-12-4安装完成后别忘了把 CUDA 路径加到环境变量里。编辑你的~/.bashrc文件如果你用 zsh 就编辑~/.zshrcexport PATH/usr/local/cuda-12.4/bin${PATH::${PATH}} export LD_LIBRARY_PATH/usr/local/cuda-12.4/lib64${LD_LIBRARY_PATH::${LD_LIBRARY_PATH}}保存后执行source ~/.bashrc。然后运行nvcc --version确认版本是 12.4。接下来安装 cuDNN。你需要先注册一个 NVIDIA 开发者账号免费的然后去 cuDNN 下载页面。找到对应 CUDA 12.x 的版本选择 “Local Installer for Ubuntu 22.04 (Deb)” 格式的。下载后用以下命令安装# 假设下载的文件叫 cudnn-local-repo-ubuntu2204-8.9.7.29_1.0-1_amd64.deb sudo dpkg -i cudnn-local-repo-ubuntu2204-8.9.7.29_1.0-1_amd64.deb sudo cp /var/cudnn-local-repo-*/cudnn-*-keyring.gpg /usr/share/keyrings/ sudo apt-get update sudo apt-get install libcudnn88.9.7.29-1cuda12.2 sudo apt-get install libcudnn8-dev8.9.7.29-1cuda12.2 sudo apt-get install libcudnn8-samples8.9.7.29-1cuda12.2注意这里安装命令中的cuda12.2是包名的一部分不代表 CUDA 版本实际安装的 cuDNN 是兼容 CUDA 12.4 的。安装完成后可以跑一下自带的样例比如mnistCUDNN来测试不过这不是必须的。到这一步TensorRT 的底层依赖就全部搞定了。2. 安装 TensorRT 10.0两种方法总有一种适合你基础环境搭好主角 TensorRT 就可以登场了。官方提供了好几种安装方式我主要试过两种Python Wheels 直接安装和Tar 包安装。前者最简单适合快速上手和纯 Python 环境后者更“原始”包含了所有的头文件、库文件和工具适合需要 C 开发或者使用其命令行工具的场景。我两种都装过建议新手先从 Wheels 安装开始如果后续需要用到trtexec这样的强大工具再补上 Tar 包安装也不迟。2.1 使用 Python Wheels 安装最快最省心这是我最推荐给大部分人的方法特别是如果你主要用 Python 做模型转换和推理。它本质上就是通过 pip 安装几个 Python 包。在终端里依次执行下面几条命令# 确保 pip 是最新的 python3 -m pip install --upgrade pip python3 -m pip install wheel # 安装 TensorRT 的核心包 python3 -m pip install --pre --upgrade tensorrt # 安装 TensorRT Lean 和 Dispatch 包10.0 的新特性建议一起安装 python3 -m pip install --pre --upgrade tensorrt_lean python3 -m pip install --pre --upgrade tensorrt_dispatch这里的--pre参数是因为 TensorRT 10.0 在 pip 上可能还标记为预发布版本。命令执行完后安装就完成了。是不是很简单我们来验证一下。打开 Python 解释器 import tensorrt as trt print(trt.__version__) # 应该输出 10.0.0.6 或类似 logger trt.Logger(trt.Logger.WARNING) builder trt.Builder(logger) print(builder) # 如果没有报错打印出一个对象说明基础功能正常同样检查一下tensorrt_lean和tensorrt_dispatch import tensorrt_lean as trt_lean import tensorrt_dispatch as trt_dispatch print(trt_lean.__version__, trt_dispatch.__version__)如果所有这些导入和打印都没问题那么恭喜你TensorRT 的 Python 环境已经就绪了。这种方法安装的包其底层库文件会被放在 Python 的 site-packages 目录里系统会自动管理非常干净。但它的“缺点”是你没有拿到那些好用的命令行工具比如后面转换模型要用到的trtexec。2.2 使用 Tar 包安装功能最全如果你需要trtexec或者要进行 C 开发那么 Tar 包安装是必须的。首先去 NVIDIA 官网的 TensorRT 下载页面登录你的开发者账号。在列表中找到 “TensorRT 10.0.0.6 for Linux x86_64 and CUDA 12.4” 的TAR Package并下载。下载完成后我们把它解压到一个你喜欢的位置比如你的家目录下cd ~/Downloads # 假设包下载在这里 tar -zxvf TensorRT-10.0.0.6.Linux.x86_64-gnu.cuda-12.4.tar.gz解压后会得到一个TensorRT-10.0.0.6的文件夹。接下来的操作是关键我们需要把这个路径下的库文件告诉系统。最直接的方法是把它的lib目录添加到LD_LIBRARY_PATH环境变量中。同样是编辑~/.bashrc文件在末尾加上export LD_LIBRARY_PATH$LD_LIBRARY_PATH:/home/你的用户名/TensorRT-10.0.0.6/lib注意这里的路径一定要替换成你实际解压的绝对路径。保存并source ~/.bashrc。这一步非常重要否则系统在运行 TensorRT 程序时找不到对应的动态链接库。接下来安装 Tar 包里的 Python wheel 文件。这样你既拥有了完整的工具链Python 接口也能用了。进入解压目录的 python 子文件夹cd /home/你的用户名/TensorRT-10.0.0.6/python查看一下里面的.whl文件名字里会包含 Python 版本号如cp310表示 Python 3.10。用 pip 安装它们# 请将 cp3x 替换为实际的文件名后缀例如 cp310 python3 -m pip install tensorrt-*-cp3x-none-linux_x86_64.whl python3 -m pip install tensorrt_lean-*-cp3x-none-linux_x86_64.whl python3 -m pip install tensorrt_dispatch-*-cp3x-none-linux_x86_64.whl如果你后续可能会用到 TensorFlow 的模型转换UFF格式或者想对 ONNX 模型做更精细的图优化还可以安装附带的graphsurgeon和onnx_graphsurgeon包cd /home/你的用户名/TensorRT-10.0.0.6/graphsurgeon python3 -m pip install graphsurgeon-*.whl cd /home/你的用户名/TensorRT-10.0.0.6/onnx_graphsurgeon python3 -m pip install onnx_graphsurgeon-*.whl完成以上步骤后TensorRT 的安装就大功告成了。你可以再次进入 Python 验证导入同时现在你的系统里已经有了trtexec这个神器它位于TensorRT-10.0.0.6/bin/目录下。3. 模型转换第一步从 PyTorch 到 ONNX环境装好了我们得有个模型来试试刀。深度学习部署里ONNX 格式就像一个“世界语”它让不同框架训练的模型PyTorch, TensorFlow等都能被 TensorRT 理解。所以我们的第一步就是把训练好的 PyTorch 模型通常是.pth或.pt文件转换成.onnx文件。我以经典的 VGG16 为例假设我们有一个用 ImageNet 预训练权重微调过的二分类 VGG16 模型。转换脚本的核心是torch.onnx.export函数。这个函数有几个参数需要特别注意它们直接决定了转换的成功率和后续 TensorRT 优化的效果。import torch import torchvision.models as models # 1. 加载模型结构和权重 device torch.device(cuda if torch.cuda.is_available() else cpu) model models.vgg16(pretrainedFalse) # 我们不加载预训练分类头 model.classifier[6] torch.nn.Linear(4096, 2) # 修改最后一层为二分类 model.load_state_dict(torch.load(your_model.pth, map_locationdevice)) model.to(device) model.eval() # 切换到评估模式这很重要 # 2. 准备一个虚拟输入dummy input batch_size 1 # 初始批次大小动态轴可以后续调整 dummy_input torch.randn(batch_size, 3, 224, 224).to(device) # 3. 指定输入输出的名字以及动态维度 input_names [input] output_names [output] # 定义动态轴我们让 batch_size 维度是动态的这样以后可以处理任意批次的输入 dynamic_axes { input: {0: batch_size}, output: {0: batch_size} } # 4. 导出 ONNX 模型 onnx_path vgg16.onnx torch.onnx.export( model, dummy_input, onnx_path, export_paramsTrue, # 导出模型权重 opset_version13, # 我推荐用 opset 13 或更高兼容性更好 do_constant_foldingTrue, # 进行常量折叠优化 input_namesinput_names, output_namesoutput_names, dynamic_axesdynamic_axes, verboseFalse # 设为 True 可以看到导出详情 ) print(f模型已导出至: {onnx_path})这里我强烈建议使用opset_version13。ONNX 算子集版本越高支持的算子越新、越全。TensorRT 10.0 对 ONNX opset 13 的支持已经非常好了。另一个关键是dynamic_axes它声明了哪些维度可以是动态的。最常见的就是让 batch size 动态化这样生成的 TensorRT 引擎就能处理不同批大小的输入应用起来灵活得多。导出完成后我习惯用netron这个可视化工具打开生成的.onnx文件检查一下模型结构是否正确输入输出节点名称是否和脚本里设定的一致。这一步的仔细能避免后面很多莫名其妙的错误。4. 模型转换第二步用 trtexec 生成 TensorRT 引擎有了 ONNX 模型我们就可以请出 TensorRT 的“瑞士军刀”——trtexec命令行工具了。如果你是用 Tar 包方式安装的它就在bin目录下。这个工具功能强大可以完成模型解析、优化、精度校准INT8、引擎生成和性能测试等一系列工作。对于刚入门的朋友我们先用它完成最基本的功能将 ONNX 模型转换成 TensorRT 引擎文件.engine或.trt。打开终端切换到trtexec所在目录或者把它所在的路径TensorRT-10.0.0.6/bin加到系统的PATH环境变量里。最基本的转换命令如下./trtexec --onnxvgg16.onnx --saveEnginevgg16_fp32.engine --fp32这条命令做了三件事加载vgg16.onnx模型在 FP32单精度浮点数精度下进行优化和构建最后将构建好的引擎保存为vgg16_fp32.engine文件。这个过程被称为“构建期”Build TimeTensorRT 会在这里执行图层融合、内核自动调优、内存优化等魔法生成一个高度优化的推理引擎。但实际部署中我们更关心速度和效率。TensorRT 支持更低的精度来提升速度比如 FP16半精度甚至 INT8整型8位。生成 FP16 引擎的命令是./trtexec --onnxvgg16.onnx --saveEnginevgg16_fp16.engine --fp16如果你想同时支持多种精度可以加上--best参数让 TensorRT 自动选择最快的。对于支持动态形状的模型就是我们之前导出 ONNX 时设置了动态轴我们需要指定优化范围./trtexec --onnxvgg16.onnx --saveEnginevgg16_dynamic.engine \ --minShapesinput:1x3x224x224 \ --optShapesinput:8x3x224x224 \ --maxShapesinput:32x3x224x224 \ --fp16这里的--minShapes、--optShapes、--maxShapes分别指定了输入张量的最小、最优和最大形状。TensorRT 会为这个范围内的各种可能形状生成优化后的内核。--optShapes是你最常使用的形状TensorRT 会针对这个形状做特别优化。运行命令后你会看到终端输出大量的日志包括每一层的优化信息、使用的精度、最终引擎的序列化大小等。看到Engine built in ... ms和Engine serialized就表示成功了。我第一次用的时候被一个权限问题卡了半天。如果你在普通用户目录下操作没问题但如果trtexec或引擎文件路径涉及到系统目录可能会因为权限问题失败。一个简单的解决办法是在命令前加上sudo或者确保你对相关目录有读写权限。另外如果 ONNX 模型路径中有空格或特殊字符记得用引号包起来。5. Python 推理实战加载引擎并运行引擎文件.engine生成后它就是一个独立于训练框架的、高度优化的计算图。接下来我们要在 Python 中加载它并进行推理。TensorRT 的 Python API 提供了两种主要的运行时Runtime独立 Runtime和Builder Runtime 组合。对于我们已经构建好的引擎文件使用独立 Runtime 加载更简单直接。下面我写一个完整的、可运行的推理示例。这个例子包含了错误处理和一些最佳实践比如使用上下文Context池来管理推理任务import tensorrt as trt import pycuda.driver as cuda import pycuda.autoinit # 这行很重要用于初始化CUDA上下文 import numpy as np import time class TrtModel: def __init__(self, engine_path, max_batch_size1): 初始化TensorRT模型。 Args: engine_path: .engine 文件路径 max_batch_size: 最大批处理大小需与构建引擎时一致 self.logger trt.Logger(trt.Logger.WARNING) self.runtime trt.Runtime(self.logger) self.engine self._load_engine(engine_path) self.context self.engine.create_execution_context() self.max_batch_size max_batch_size # 分配输入输出内存Host和Device self.inputs, self.outputs, self.bindings, self.stream self._allocate_buffers() def _load_engine(self, engine_path): 从文件加载序列化的引擎 with open(engine_path, rb) as f: engine_data f.read() return self.runtime.deserialize_cuda_engine(engine_data) def _allocate_buffers(self): 为引擎的所有输入输出分配内存 inputs, outputs, bindings [], [], [] stream cuda.Stream() for binding in self.engine: # 获取绑定信息的形状和数据类型 shape self.engine.get_binding_shape(binding) dtype trt.nptype(self.engine.get_binding_dtype(binding)) size trt.volume(shape) * self.max_batch_size * np.dtype(dtype).itemsize # 分配设备内存 device_mem cuda.mem_alloc(size) bindings.append(int(device_mem)) # 根据是输入还是输出归类到不同列表 if self.engine.binding_is_input(binding): inputs.append({name: binding, device_mem: device_mem, shape: shape, dtype: dtype}) else: host_mem cuda.pagelocked_empty(size // np.dtype(dtype).itemsize, dtype) outputs.append({name: binding, device_mem: device_mem, host_mem: host_mem, shape: shape, dtype: dtype}) return inputs, outputs, bindings, stream def infer(self, input_data): 执行推理。 Args: input_data: numpy数组形状需与引擎输入匹配 Returns: 推理结果的numpy数组 # 1. 准备输入数据复制到设备 input_binding self.inputs[0] if input_data.dtype ! input_binding[dtype]: raise ValueError(f输入数据类型不匹配: 期望 {input_binding[dtype]}, 得到 {input_data.dtype}) if input_data.shape ! tuple(input_binding[shape]): # 如果构建时是动态形状这里需要设置形状 self.context.set_binding_shape(0, input_data.shape) # 将主机数据异步拷贝到设备 cuda.memcpy_htod_async(input_binding[device_mem], input_data.ravel(), self.stream) # 2. 执行推理 self.context.execute_async_v2(bindingsself.bindings, stream_handleself.stream.handle) # 3. 将结果从设备拷贝回主机 for output in self.outputs: cuda.memcpy_dtoh_async(output[host_mem], output[device_mem], self.stream) # 同步流等待所有操作完成 self.stream.synchronize() # 4. 整理输出结果 results [] for output in self.outputs: # 根据实际执行的形状重塑输出数据 shape self.context.get_binding_shape(self.engine.get_binding_index(output[name])) if -1 in shape: # 动态维度 shape list(shape) for i, s in enumerate(shape): if s -1: shape[i] input_data.shape[0] # 用输入批次大小替换-1 result output[host_mem].reshape(shape) results.append(result) return results[0] if len(results) 1 else results # 使用示例 if __name__ __main__: # 初始化模型 model TrtModel(vgg16_fp16.engine, max_batch_size8) # 准备一个批次的随机输入数据 (batch4, channel3, height224, width224) dummy_input np.random.randn(4, 3, 224, 224).astype(np.float32) # 预热第一次推理可能较慢 _ model.infer(dummy_input) # 正式计时推理 start time.time() for _ in range(100): output model.infer(dummy_input) end time.time() print(f推理结果形状: {output.shape}) print(f100次推理平均耗时: {(end - start) * 10:.2f} ms) # 平均每次耗时这段代码看起来有点长但结构很清晰。TrtModel类封装了加载引擎、分配内存和执行推理的核心流程。注意pycuda.autoinit这一行它负责初始化 CUDA 上下文没有它后续的 GPU 内存操作会失败。在infer方法中我们使用了execute_async_v2这个异步接口并配合 CUDA 流Stream来让数据拷贝和内核执行重叠这能最大化 GPU 的利用率提升吞吐量。实际跑起来后你可以对比一下同一个模型用原生 PyTorch在 GPU 上推理和用 TensorRT 引擎推理的速度差异。在我的测试里一个中等复杂的模型TensorRT 通常能带来 2 到 5 倍甚至更高的速度提升尤其是使用了 FP16 精度之后。这种性能提升在视频处理、实时检测等场景下是决定性的。6. 常见问题与深度优化技巧走完整个流程你可能已经成功跑通了。但真实项目总会遇到各种稀奇古怪的问题。我这里分享几个我踩过的坑和对应的解决办法以及一些让推理飞得更快的进阶技巧。问题一ONNX 导出失败提示“Unsupported operator XXX”这是最常见的问题。PyTorch 中的某些操作可能没有对应的、或者版本匹配的 ONNX 算子。解决办法降低 ONNX opset 版本比如从 13 降到 11。有些老算子在新 opset 中被弃用或修改了。修改模型代码将不支持的复杂操作如某些特殊的张量变形、自定义操作拆解成一系列基础操作。使用 PyTorch 的符号化导出对于动态控制流如 if-else可以使用torch.jit.script先转换成 TorchScript再尝试导出。但这需要更深入的调试。问题二trtexec 构建引擎时卡住或报错“Failed to parse the ONNX file”检查 ONNX 版本用onnxruntime加载一下你的 ONNX 文件如果能成功说明文件本身是好的。命令python -m onnxruntime.tools.check_onnx_model your_model.onnx。简化模型用一个极其简单的模型比如只有两层的线性网络导出 ONNX 并尝试转换如果简单模型可以说明问题出在原模型的某个复杂层。更新 TensorRT确保你用的是最新版本的 TensorRTNVIDIA 会持续增加对新算子的支持。问题三Python 推理时出现“CUDA error: out of memory”检查批次大小你推理时传入的批次大小是否超过了构建引擎时指定的maxShapes动态引擎虽然灵活但最大形状限制了内存分配的上限。释放资源确保你的TrtModel实例在不用时被正确销毁del model或者使用with语句管理上下文。长期运行的服务器程序要注意防止引擎上下文泄漏。使用内存池对于需要频繁创建和销毁上下文的场景可以考虑实现一个简单的上下文池避免反复分配设备内存的开销。深度优化技巧INT8 量化这是 TensorRT 的王牌功能之一能将模型精度从 FP32 降到 INT8带来巨大的速度提升和内存节省尤其适合部署在边缘设备。但 INT8 需要校准Calibration过程你需要准备一个代表性的校准数据集几百张图片即可让 TensorRT 统计每一层激活值的分布从而确定最佳的量化尺度。trtexec也支持 INT8 校准和构建参数是--int8和--calib。使用 TensorRT 的 Lean Runtime 和 DispatchTensorRT 10.0 引入了这两个新组件。Lean Runtime 是一个更轻量级的运行时适合对二进制大小敏感的场景Dispatch 则允许在运行时动态选择最优的内核实现。在 Python 中你可以尝试导入tensorrt_lean来创建一个更节省内存的运行时环境。Profile 和调试如果对性能还不满意可以使用 TensorRT 内置的分析器Profiler来找出推理过程中的瓶颈层。在构建引擎时可以通过IIBuilderConfig设置一个分析器它会输出每一层执行时间的详细报告。多流并行推理对于需要高吞吐量的应用可以创建多个 CUDA 流Stream和对应的引擎上下文同时处理多个推理请求。这需要更精细的编程来管理流之间的依赖和同步但能充分压榨 GPU 的并行能力。部署的学问很深从能跑到跑得快、跑得稳中间还有很多需要打磨的地方。我的经验是遇到问题多查 TensorRT 的官方文档和 GitHub Issues很多坑别人已经踩过了。最重要的是保持耐心一步步调试从最简单的模型和流程开始验证确保每一步都正确无误然后再去挑战复杂的模型和极致的性能优化。