
1. 为什么要把 YOLOv4-tiny 转到 TensorRT1.1 这个项目到底在做什么YOLOv4-tiny 是 YOLO 系列里一个非常特殊的版本。它把骨干网络砍到只剩两个残差块组检测头也从三个尺度缩减为两个参数量大概在 600 万左右模型权重文件通常只有 20 多 MB。这个体量意味着它在普通 CPU 上就能跑到接近实时的水平但如果你手上有 NVIDIA 的显卡或者边缘设备比如 Jetson 系列那把它转成 TensorRT 引擎之后推理速度还能再上一个台阶。我自己的实测数据是这样的同一段 1080p 视频同一台带 RTX 3060 的机器用 Darknet 原始框架跑 YOLOv4-tiny单帧推理大概在 12 到 15 毫秒换成 ONNX Runtime 的 CUDA 执行提供者能压到 8 毫秒左右而转成 TensorRT 的 FP16 引擎之后单帧直接掉到 3 毫秒上下。这个差距在单张图片上可能感觉不明显但如果你要做多路视频流分析或者部署在算力有限的边缘盒子上那就是能不能落地的区别。这个项目适合谁呢如果你已经训练好了 YOLOv4-tiny 的权重文件手里有 NVIDIA 的硬件想让推理速度再快一点那这篇内容就是写给你的。如果你还没训练模型只是想了解一下部署流程也可以先看看整体思路心里有个底。1.2 转换链路的核心思路YOLOv4-tiny 的原始权重是 Darknet 格式的.weights文件TensorRT 并不直接吃这个格式。所以中间需要一个桥梁业界最通用的做法是走 ONNX。整条链路是这样的Darknet 权重先转成 ONNX 模型然后 ONNX 再转成 TensorRT 引擎。有些工具链也支持从 Darknet 直接转 TensorRT但那种方式灵活性差遇到自定义层或者特殊算子的时候很容易卡住所以我个人更推荐走 ONNX 这条中间路线。为什么选 ONNX 做中间格式因为 ONNX 是一个开放的模型交换标准它把计算图、算子、权重都描述得很清楚TensorRT 的解析器对 ONNX 的支持也最成熟。而且 ONNX 本身可以用 Netron 这样的工具可视化出了问题你能直接看到是哪一层不对排查起来方便很多。整个流程里最容易出问题的环节其实是第一步也就是 Darknet 转 ONNX。YOLOv4-tiny 的网络结构里有一些 Darknet 特有的层比如route层、upsample层、yolo层这些在标准 ONNX 里没有直接对应的算子需要转换脚本做映射。不同的转换工具对这些层的处理方式不一样有的会把yolo层拆成几个基础算子有的会保留成自定义层。这一步处理得好不好直接决定了后面 TensorRT 能不能顺利解析。1.3 转换之后能带来什么实际收益除了速度提升转 TensorRT 还有几个实际的好处。第一是显存占用更可控TensorRT 在构建引擎的时候会做层融合和内存复用同样的模型跑起来显存峰值通常比原生框架低。第二是支持 FP16 和 INT8 量化FP16 基本上不会掉精度INT8 需要做校准但如果校准集选得好精度损失可以控制在 1 个百分点以内速度还能再翻一倍。第三是部署简单TensorRT 引擎是一个序列化文件加载的时候不需要原始的网络定义代码推理的时候只需要一个反序列化加一个执行上下文集成到 C 或者 Python 服务里都很干净。不过也要说清楚TensorRT 引擎是跟硬件绑定的。你在 RTX 3060 上构建的引擎拿到 Jetson Nano 上是跑不起来的甚至同型号显卡但驱动版本不一样引擎也可能不兼容。所以实际部署的时候引擎构建这一步通常是在目标设备上完成的或者至少是在同型号同环境的机器上构建好再拷贝过去。2. 环境准备与工具选型2.1 软件栈的版本搭配这个项目对版本比较敏感我踩过的坑里有一半是因为版本不匹配。下面这套组合是我实测下来比较稳的组件推荐版本说明CUDA11.4 或 11.8太老的版本不支持新显卡太新的版本 TensorRT 可能还没跟上cuDNN对应 CUDA 版本必须和 CUDA 匹配否则 TensorRT 构建时会报错TensorRT8.4 或 8.58.x 系列对 ONNX 的解析器比较成熟ONNX1.12 左右太新的版本导出的算子集 TensorRT 可能不认识PyTorch1.12 或 1.13如果你用 PyTorch 做中间转换的话Python3.8 或 3.93.10 以上有些库的 wheel 包还不全我特别想强调一下 TensorRT 的版本。TensorRT 8.x 和 7.x 在 API 上有不小的差异网上很多教程还是基于 7.x 写的你照着做会发现接口对不上。另外 TensorRT 8.5 之后对 ONNX 的 opset 支持到了 17但 YOLOv4-tiny 的转换脚本通常导出的是 opset 11 或 12这个一般没问题TensorRT 会做向下兼容。2.2 转换工具的选择Darknet 转 ONNX 这一步市面上有几个常用的工具。一个是darknet2onnx这个工具比较老对 YOLOv4-tiny 的支持还行但遇到自定义的yolo层处理得不够干净。另一个是onnx2trt系列脚本通常跟 TensorRT 的示例代码一起发布。还有一个是torch2trt但那个是从 PyTorch 转的不适用于 Darknet 权重。我实际用的是基于darknet2onnx修改过的一个脚本主要改动是在yolo层的处理上。原始的yolo层在 Darknet 里包含了锚框解码、sigmoid 激活、类别预测等操作转换的时候需要把这些拆成 ONNX 的基础算子。有些脚本会把整个yolo层保留成一个自定义算子然后指望 TensorRT 的插件去处理但 TensorRT 自带的插件里并没有现成的 YOLO 插件所以这种方案在构建引擎的时候会失败。正确的做法是在转 ONNX 的时候就把yolo层展开。具体来说yolo层的输出是三个张量边界框坐标、目标置信度、类别概率。在 ONNX 里可以用Reshape、Transpose、Sigmoid、Concat这些基础算子拼出来。这样转出来的 ONNX 模型是纯标准算子TensorRT 解析起来没有任何障碍。2.3 硬件环境的注意事项如果你是在 Jetson 设备上做转换有一点要特别注意Jetson 上的 TensorRT 是随 JetPack 一起安装的版本跟 JetPack 绑定你不能随便升级。比如 JetPack 4.6 自带的是 TensorRT 8.0JetPack 5.0 自带的是 TensorRT 8.4。所以在 Jetson 上做转换之前先确认一下你的 JetPack 版本然后找对应版本的转换脚本。另外Jetson 设备的内存通常比较紧张构建 TensorRT 引擎的时候会占用大量内存。如果你在 Nano 上构建建议先把不必要的过程关掉或者在工作站上构建好引擎再拷贝过去。但前面说了引擎跟硬件绑定工作站上构建的引擎在 Nano 上不一定能用所以最稳妥的办法还是在目标设备上构建只是构建过程可能要跑个十几分钟。3. Darknet 权重转 ONNX 的完整实操3.1 准备工作权重文件和配置文件你手上应该有两个文件一个是.weights权重文件一个是.cfg配置文件。这两个文件必须配套cfg 里定义了网络结构weights 里是训练好的参数。如果你只有 weights 没有 cfg那转换没法做因为解析器需要 cfg 来重建计算图。我建议在转换之前先检查一下 cfg 文件里的yolo层配置。打开 cfg 文件找到最后两个[yolo]段看看mask、anchors、classes这几个参数。classes就是你的类别数这个值后面在 TensorRT 里做后处理的时候要用到。anchors是锚框的尺寸YOLOv4-tiny 用的是 6 个锚框分两组每组 3 个。这些参数在转换过程中会被写入 ONNX 模型所以一定要确认是对的。还有一个细节cfg 文件里的width和height是训练时的输入尺寸通常是 416x416。但 YOLOv4-tiny 是全卷积网络理论上支持任意尺寸的输入。不过 TensorRT 在构建引擎的时候需要指定一个优化尺寸这个尺寸可以跟训练尺寸不一样但最好是 32 的倍数因为网络里有 5 次下采样每次下采样是 2 倍所以输入尺寸必须是 32 的倍数。我一般用 416x416 或者 608x608前者速度快后者精度高一点。3.2 转换脚本的核心逻辑转换脚本的核心工作是把 Darknet 的每一层映射成 ONNX 的算子。我拿一个简化版的脚本来讲一下关键部分。首先是解析 cfg 文件把每一层的类型、参数读出来然后按顺序构建 ONNX 的计算图。对于卷积层Darknet 的[convolutional]层对应 ONNX 的Conv算子如果有batch_normalize1那后面还要接一个BatchNormalization然后是激活函数。YOLOv4-tiny 用的是leaky激活对应 ONNX 的LeakyRelu斜率是 0.1。对于[route]层这个层的作用是把前面某几层的输出拼接起来。在 ONNX 里对应Concat算子layers参数指定了要拼接的层号axis通常是 1也就是通道维度。对于[upsample]层Darknet 用的是最近邻插值stride是 2。在 ONNX 里对应Resize算子mode设为nearestscales设为[1,1,2,2]。对于[yolo]层这是最复杂的部分。前面说了需要把锚框解码、sigmoid、类别预测都展开成基础算子。具体来说yolo层的输入是一个特征图输出需要经过一系列变换才能得到最终的检测框。在转换脚本里我通常会把这个过程拆成几个步骤先做Reshape把特征图变成[batch, num_anchors, 5classes, grid_h, grid_w]的形状然后做Transpose调整维度顺序接着对坐标和置信度做Sigmoid最后把两个yolo层的输出Concat到一起。这里有一个容易出错的地方Darknet 的yolo层在计算边界框坐标的时候用的是相对于网格单元的偏移量而且有一个scale_x_y参数默认是 1。这个参数在 YOLOv4-tiny 里通常保持默认但如果你训练的时候改过转换的时候也要对应改。3.3 转换命令与参数说明假设你已经把转换脚本准备好了文件名是darknet2onnx.py那转换命令大概是这样的python darknet2onnx.py \ --cfg yolov4-tiny.cfg \ --weights yolov4-tiny.weights \ --output yolov4-tiny.onnx \ --opset 11 \ --input_shape 1 3 416 416这里几个参数我解释一下。--opset 11是指定 ONNX 的算子集版本11 是比较稳的选择TensorRT 8.x 对它的支持很好。--input_shape是[batch, channels, height, width]batch 通常设为 1因为 TensorRT 引擎在构建的时候会针对 batch size 做优化如果你后面要跑多 batch可以在构建引擎的时候再改。转换完成之后你会得到一个.onnx文件。我强烈建议用 Netron 打开看一下确认一下输入输出的名字和形状。输入通常叫input或者data输出通常叫output或者output0、output1。这些名字在后面的 TensorRT 构建脚本里要用到如果对不上构建的时候会报错。还有一个检查点用onnxruntime跑一下这个 ONNX 模型看看输出是不是正常的。你可以随便找一张图片做一下预处理然后喂给 ONNX 模型看看输出的形状和数值范围。如果输出全是 NaN 或者形状不对那说明转换过程中有问题需要回去检查脚本。4. ONNX 转 TensorRT 引擎的关键步骤4.1 构建脚本的编写要点ONNX 转 TensorRT 有两种方式一种是用trtexec命令行工具另一种是用 TensorRT 的 Python API 写脚本。trtexec适合快速验证但灵活性差比如你想自定义插件或者做 INT8 校准trtexec就不太方便。所以我一般用 Python API 来构建。构建脚本的核心流程是这样的先创建一个Builder然后创建一个Network接着用OnnxParser把 ONNX 模型解析到Network里然后设置BuilderConfig最后调用build_engine生成引擎。下面是一段核心代码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(yolov4-tiny.onnx, rb) as f: parser.parse(f.read()) config builder.create_builder_config() config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 1 30) config.set_flag(trt.BuilderFlag.FP16) engine builder.build_engine(network, config) with open(yolov4-tiny.engine, wb) as f: f.write(engine.serialize())这里有几个关键点。EXPLICIT_BATCH标志是必须的因为 ONNX 模型通常有显式的 batch 维度。set_memory_pool_limit是设置工作空间大小1GB 通常够用如果你的模型更大或者输入尺寸更大可以适当增加。FP16标志是开启半精度这个能显著提升速度而且 YOLOv4-tiny 这种小模型对精度不敏感FP16 基本不会掉点。4.2 动态 batch 与动态尺寸的处理如果你的应用场景需要动态 batch 或者动态输入尺寸那在构建引擎的时候需要设置优化配置文件。TensorRT 8.x 里用OptimizationProfile来管理这个。比如你想支持 batch size 从 1 到 4输入尺寸从 320x320 到 640x640可以这样设置profile builder.create_optimization_profile() profile.set_shape(input, (1, 3, 320, 320), (1, 3, 416, 416), (4, 3, 640, 640)) config.add_optimization_profile(profile)这里set_shape的三个参数分别是最小形状、最优形状、最大形状。TensorRT 会针对最优形状做优化但在最小和最大之间都能正常运行。不过要注意动态尺寸会带来额外的显存开销和性能损失如果你的场景不需要动态那就固定尺寸性能会更好。还有一个坑YOLOv4-tiny 的网络里有Resize算子这个算子在动态尺寸下可能会出问题。因为Resize的缩放比例是固定的如果输入尺寸变了输出尺寸也会跟着变但后面的Concat算子要求两个输入的特征图尺寸一致。所以如果你要用动态尺寸需要确保两个yolo层的输出在Concat之前尺寸是对齐的。这个在固定尺寸下不是问题因为 416x416 的输入经过下采样之后两个特征图的尺寸是 13x13 和 26x26Concat的时候会做对齐。但动态尺寸下这个对齐关系可能会被打破。4.3 INT8 量化的校准流程如果你追求极致的速度可以试试 INT8 量化。INT8 的核心是校准也就是用一批有代表性的图片跑一遍 FP32 或者 FP16 的模型统计每一层激活值的分布然后确定量化的缩放因子。TensorRT 提供了IInt8EntropyCalibrator2接口你需要自己实现一个校准器。校准器的实现大概是这样的继承trt.IInt8EntropyCalibrator2实现get_batch、get_batch_size、read_calibration_cache、write_calibration_cache这几个方法。get_batch返回一批预处理好的图片数据get_batch_size返回 batch size后面两个方法是用来缓存校准结果的避免每次构建都重新校准。校准集的选择很关键。我一般从训练集里随机抽 500 到 1000 张图片要覆盖各种场景和光照条件。如果校准集太单一量化后的模型在某些场景下精度会掉得很厉害。另外校准的时候用的预处理方式要和推理时一致包括归一化、通道顺序这些。INT8 量化之后速度通常能比 FP16 再快 1.5 到 2 倍但精度可能会掉 1 到 3 个百分点。如果你的应用对精度要求不高比如只是做粗略的目标检测那 INT8 是很划算的。但如果你的场景里小目标很多或者类别之间很相似那 INT8 可能会让漏检和误检变多这时候还是老老实实用 FP16。5. 推理后处理与性能调优5.1 输出解码的正确姿势TensorRT 引擎的输出是原始的张量不是最终的检测框。你需要自己做后处理把输出解码成边界框坐标、置信度和类别。YOLOv4-tiny 有两个输出层每个输出层的形状是[batch, num_anchors, 5classes, grid_h, grid_w]。这里的num_anchors是 35classes是 4 个坐标加 1 个置信度加类别数。解码的过程是这样的对于每个网格单元和每个锚框模型预测的是相对于网格左上角的偏移量以及相对于锚框宽高的缩放因子。你需要用 sigmoid 把偏移量归一化到 0 到 1 之间然后加上网格坐标再乘以步长得到绝对坐标。宽高则是用 exp 函数处理缩放因子再乘以锚框的宽高。这里有一个细节YOLOv4-tiny 的坐标预测用的是sigmoid激活但有些版本的实现里用的是scale_x_y参数来调整 sigmoid 的斜率。如果你在训练的时候改过这个参数解码的时候也要对应改。我一般保持默认也就是scale_x_y1这样 sigmoid 就是标准的。解码之后你会得到一大堆候选框然后需要用非极大值抑制来去掉重叠的框。NMS 的阈值通常设 0.45 左右置信度阈值设 0.25 左右。这两个值可以根据你的场景调如果漏检多就降低置信度阈值如果误检多就提高。5.2 性能瓶颈的定位方法转完 TensorRT 之后你可能会发现速度没有想象中那么快。这时候需要定位一下瓶颈在哪里。我常用的方法是先用trtexec跑一下纯推理的耗时命令是这样的trtexec --loadEngineyolov4-tiny.engine --batch1 --iterations100 --avgRuns10这个命令会输出平均推理时间、吞吐量等指标。如果纯推理时间很短比如 2 到 3 毫秒但你实际跑起来感觉慢那瓶颈很可能在后处理或者数据拷贝上。数据拷贝是一个容易被忽略的瓶颈。如果你是在 GPU 上做推理但后处理是在 CPU 上做的那需要把输出从 GPU 显存拷贝到 CPU 内存这个拷贝操作在输出张量很大的时候会很耗时。解决办法是把后处理也放到 GPU 上做用 CUDA kernel 或者 TensorRT 的自定义插件来实现。不过这样开发成本比较高如果你的输出张量不大拷贝耗时可以接受那就没必要折腾。另一个瓶颈可能是预处理。图片从 CPU 内存拷贝到 GPU 显存然后做归一化、resize 这些操作如果实现得不好也会拖慢整体速度。我一般用 CUDA 的cudaMemcpy做异步拷贝然后用一个简单的 kernel 做归一化和 resize这样能跟推理重叠起来减少等待时间。5.3 精度与速度的平衡技巧在实际部署中精度和速度往往需要权衡。我总结了几条经验。第一输入尺寸不要盲目加大。416x416 和 608x608 的精度差距可能在 2 到 3 个百分点但速度差距接近一倍。如果你的场景里目标比较大416 就够用了。第二FP16 基本上可以无脑开精度损失很小速度提升明显。第三INT8 要谨慎一定要用有代表性的校准集而且要在验证集上确认精度损失在可接受范围内。还有一个技巧是层融合。TensorRT 在构建引擎的时候会自动做层融合比如把卷积、批归一化、激活函数融合成一个算子。但有些融合需要你手动开启比如BuilderFlag.FP16和BuilderFlag.INT8。另外如果你发现某些层没有被融合可以检查一下 ONNX 模型里这些层是不是连续的中间有没有插入不必要的算子。最后说一个我踩过的坑TensorRT 引擎在第一次运行的时候会有一个预热过程耗时比后续运行要长。所以如果你做性能测试一定要先跑几次预热然后再统计平均耗时。我一开始不知道这个测出来的数据忽高忽低后来才发现是预热的问题。6. 常见问题与排查实录6.1 转换阶段的典型报错报错一Unsupported ONNX data type: UINT8这个通常是因为 ONNX 模型里某些张量的数据类型是 UINT8而 TensorRT 不支持。解决办法是在转 ONNX 的时候确保所有张量都是 FLOAT 或者 INT32。如果你用的是现成的转换脚本检查一下有没有地方用了uint8类型。报错二Attribute not found: perm这个一般是Transpose算子的属性没写对。ONNX 的Transpose需要指定perm属性也就是维度排列顺序。如果转换脚本里漏了这个属性TensorRT 解析的时候就会报错。解决办法是检查脚本里所有Transpose的地方确保perm属性存在且正确。报错三Concat输入维度不匹配这个通常出现在两个yolo层的输出拼接的时候。如果两个特征图的尺寸不一致Concat就会失败。检查一下输入尺寸是不是 32 的倍数以及两个yolo层的步长设置是不是正确。YOLOv4-tiny 的两个yolo层分别对应 32 倍下采样和 16 倍下采样输出尺寸应该是输入尺寸除以 32 和除以 16。6.2 推理阶段的异常表现现象一检测框全部偏移这个一般是解码的时候坐标计算错了。检查一下网格坐标的计算方式以及步长是不是用对了。YOLOv4-tiny 的两个输出层对应的步长分别是 32 和 16如果你搞反了框就会偏得很厉害。现象二置信度全部接近 0 或 1这个可能是 sigmoid 激活没加上或者加错了地方。检查一下 ONNX 模型里置信度的输出有没有经过 sigmoid。有些转换脚本会把 sigmoid 放在后处理里做而不是在模型里做这样 TensorRT 的输出就是 logits你需要自己加 sigmoid。现象三速度比预期慢很多先确认一下引擎是不是用 FP16 构建的。如果构建的时候没加FP16标志那引擎就是 FP32 的速度会慢不少。另外检查一下输入尺寸是不是太大了以及有没有不必要的内存拷贝。6.3 常见问题速查表问题现象可能原因排查方法解决方案构建引擎时报错Unsupported ONNX data typeONNX 里有 UINT8 张量用 Netron 查看张量类型转 ONNX 时统一用 FLOAT构建引擎时报错Attribute not found算子属性缺失检查报错算子的属性补全属性或换转换脚本推理结果全是 NaN权重没加载对检查 ONNX 输出数值重新转 ONNX确认权重路径检测框偏移严重解码坐标算错对比原始 Darknet 输出检查步长和网格坐标计算速度不达预期没用 FP16 或输入太大用 trtexec 测纯推理耗时开启 FP16减小输入尺寸引擎加载失败引擎与硬件不匹配确认构建和运行的设备一致在目标设备上重新构建6.4 我踩过的几个坑第一个坑是 ONNX 的 opset 版本。我一开始用了 opset 13结果 TensorRT 8.4 解析的时候报了一堆算子不支持的错。后来降到 opset 11 就顺利通过了。所以如果你遇到算子不支持的问题先试试降 opset。第二个坑是输入张量的名字。我在构建脚本里硬编码了输入名字叫input但转换脚本导出的 ONNX 里输入名字叫data结果解析的时候找不到输入报了个很模糊的错。后来用 Netron 看了一下才发现名字对不上。所以一定要用 Netron 确认输入输出的名字。第三个坑是 Jetson 上的内存限制。我在 Jetson Nano 上构建引擎的时候因为工作空间设得太大直接把系统搞崩了。后来把工作空间降到 512MB才顺利构建完成。所以如果你在边缘设备上构建工作空间不要设太大够用就行。第四个坑是 INT8 校准集的选取。我第一次做 INT8 量化的时候随便选了 100 张图片做校准结果量化后的模型在夜间场景下几乎检测不到目标。后来把校准集扩充到 800 张覆盖了白天、夜间、雨天等各种场景精度才恢复过来。所以校准集一定要有代表性不能随便选。7. 部署上线的几点经验7.1 引擎的序列化与加载TensorRT 引擎构建好之后用engine.serialize()可以序列化成二进制文件这个文件就是最终部署时要用的。加载的时候用trt.Runtime反序列化with open(yolov4-tiny.engine, rb) as f: engine_data f.read() runtime trt.Runtime(logger) engine runtime.deserialize_cuda_engine(engine_data) context engine.create_execution_context()这里有一个细节context是执行上下文它保存了推理时的中间状态。如果你要多线程推理每个线程需要独立的context但engine可以共享。不过engine本身不是线程安全的所以多线程的时候要注意加锁或者每个线程用自己的engine实例。7.2 多路视频流的处理策略如果你要做多路视频流分析比如同时处理 4 路摄像头那有几种策略。第一种是串行处理每路轮流跑这种最简单但延迟高。第二种是用多个context并行跑但 GPU 的算力是有限的并行太多反而会互相抢资源。第三种是用 batch 推理把多路视频的帧拼成一个 batch 一起送进去这样能充分利用 GPU 的并行能力。我一般用 batch 推理的方式。比如 4 路视频每路取一帧拼成 batch size 为 4 的输入一次推理得到 4 帧的结果。这样比串行处理快很多而且延迟也可控。不过要注意batch 推理的时候输入尺寸必须一致如果不同路的视频分辨率不一样需要先统一 resize。7.3 长期运行的稳定性保障部署上线之后稳定性比速度更重要。我遇到过几个稳定性问题。一个是显存泄漏原因是context没有及时释放跑久了显存就满了。解决办法是在每次推理之后调用context.reset()或者定期重建context。另一个是引擎加载失败原因是引擎文件损坏或者跟当前环境不匹配。解决办法是在加载的时候加异常处理如果加载失败就重新构建引擎。还有一个是温度问题。GPU 长时间高负载运行会发热温度过高会触发降频推理速度就会掉。如果你是在边缘设备上部署散热条件通常不太好建议加个风扇或者降低推理频率。我一般会在代码里加一个温度监控如果温度超过阈值就暂停推理等温度降下来再继续。7.4 版本升级的注意事项TensorRT 的版本升级有时候会带来兼容性问题。比如从 8.4 升到 8.5有些 API 的名字变了或者行为变了。如果你要升级 TensorRT建议先在测试环境里验证一遍确认引擎能正常构建和加载推理结果跟之前一致。另外升级之后需要重新构建引擎因为旧版本的引擎在新版本的 TensorRT 上可能加载不了。CUDA 和 cuDNN 的升级也要谨慎。CUDA 的大版本升级通常意味着驱动也要升级而驱动升级可能会影响其他依赖 CUDA 的应用。所以如果没必要不要轻易升级 CUDA 版本。我一般是在新装机器的时候选一个稳定的版本组合然后一直用下去除非有明确的需求要升级。最后分享一个小技巧如果你不确定某个版本组合能不能用可以先在一个隔离的环境里试一下比如用 Docker 起一个容器装好对应的 CUDA、cuDNN、TensorRT然后跑一遍完整的转换和推理流程。这样即使出了问题也不会影响主环境。我在尝试新版本的时候都是这么做的省去了很多重装系统的麻烦。