首页 / 资讯中心 / 文章详情

YOLOv5模型导出实战:pt转ONNX与TensorRT engine

YOLOv5模型导出实战:pt转ONNX与TensorRT engine ★ FEATURED ARTICLE
手里攥着一个训练好的 YOLOv5 pt 文件想把它变成能在推理后端上跑的 ONNX 或者 TensorRT engine这事儿在很多人嘴里就是“一行命令的事”可真敲下去报错能连着蹦出七八条。我自己第一次用 export.py 转 engine从 CUDA 版本对不上、到显存不够、再到导出成功但推理结果全错前前后后折腾了大半天中间甚至一度怀疑是模型训练本身出了问题。后来把导出这条链路彻底拆开捋了一遍才发现坑基本都集中在那几个固定的地方版本对位、动态 shape 的 profile 设置、精度标志与硬件的匹配、以及导出后到底有没有做数值比对。这篇就围绕 YOLOv5 仓库里自带的 export.py把 pt 到 ONNX、pt 到 TensorRT engine 这两条路完整走一遍。不绕弯子从环境盘点、参数含义、内部逻辑、具体命令、到出错怎么查全按实操顺序铺开。适合刚接触模型导出、想把训练成果落到实际推理环境里的人看也适合那些导是导出来了、但不知道结果对不对、性能为什么没提升的人对照着排查。代码层面我不会抠到每一行 API但关键分支和参数取舍会讲透保证你照着改就能跑通。1. 从 pt 到 onnx/engine这条链路到底解决了什么1.1 训练态权重和推理态模型根本不是一回事很多人对 pt 文件的理解停留在“模型文件”这四个字上其实它内部装的东西比想象中杂。除了权重张量它还挂着整个模型结构的描述、训练时的超参、优化器状态、EMA 影子权重、以及一些只在训练阶段才成立的中间节点。PyTorch 加载它之后再 forward走的是完整的动态图计算流程每一步算子调用都有 Python 层的解释开销中间张量的生命周期由自动求导机制托管显存占用也比纯推理高出一大截。这就带来一个很现实的问题你在服务器上训练出来的 pt直接丢到推理侧去跑速度慢是小事更麻烦的是它依赖完整的 PyTorch 运行时环境体积动辄几个 G很多边缘设备或者精简容器里根本装不下。所以导出的本质是把训练态的计算图“裁剪并冻结”成一个只包含前向路径的静态图去掉所有与推理无关的分支再把算子映射成目标后端认识的表达形式。ONNX 是这种表达的一种通用中间格式TensorRT engine 则是针对特定加速卡做了深度优化的最终产物。理解了这一层你就明白为什么导出不是“格式转换”这么简单它实际上是一次计算图的重构。1.2 ONNX 与 TensorRT engine 各自的适用边界ONNX 的价值在于通用性。它是一个开放的中间表示几乎所有主流推理框架都支持加载跨平台、跨语言、跨硬件都没问题。你导出一次Windows 上能跑Linux 上能跑换成不同的推理后端也能跑调试的时候还能用可视化工具把图扒开看每一层。缺点也很明显它只是“中间表达”本身不带任何针对硬件的优化算子融合、内存复用、精度压缩这些活儿都得交给下游的推理引擎去做所以单看 ONNX 的推理速度通常不会比原生 PyTorch 快多少甚至因为多了一层解析还可能略慢。engine 就完全相反。它是在导出阶段就把优化做完了的产物算子融合、kernel 自动调优、精度选择、显存预分配全都在构建那一刻定死。跑起来的时候没有图解析、没有算子选择直接按预先编译好的执行计划走延迟能压到很低。代价是它绑死环境同一份 engine 换一张卡、换一个 TensorRT 版本、甚至换一个 CUDA 大版本都可能直接反序列化失败。所以工程上的常规做法是保留 ONNX 作为“源文件”engine 作为“部署产物”engine 丢了随时能从 ONNX 重新构建ONNX 才是那个需要长期归档的东西。选择哪条路取决于你的部署场景。做跨平台交付、或者推理侧环境不确定优先 ONNX做固定硬件的服务端推理、追求极致延迟直接上 engine。两个都要那就先出 ONNX再从 ONNX 构建 engine这条链路最稳因为中间那层 ONNX 给了你一次校验机会。2. 动手前的环境盘点与版本对齐2.1 export.py 认的到底是哪些依赖export.py 本身写得很薄它靠的是仓库里的 models/export.py 或者等价模块再往上调 torch.onnx.export 和 TensorRT 的 Python 接口。所以真正决定成败的不是这个脚本而是它背后那套依赖。核心就四样PyTorch、ONNX以及可选的 onnxsim、TensorRT含 Python binding、以及 CUDA/cuDNN。前两个用来出 ONNX后两个用来出 engine。这里有个很容易踩的坑export.py 里的 --include 参数支持一长串目标格式包括 torchscript、onnx、openvino、engine、coreml、saved_model、pb、tflite、edgetpu、tfjs、paddle 等等但它的默认值只有 torchscript。也就是说你不显式写 --include onnx 或者 --include engine脚本跑完只会吐出一个 .torchscript 文件然后你会盯着目录纳闷“我的 onnx 呢”。这不是 bug是设计如此。第一次用的人百分之百会在这里愣一下。另外一个隐性依赖是 pycuda。它不参与导出但参与导出之后的验证。你想用 Python 加载 engine 跑一次推理确认没问题就得靠 pycuda 做显存分配和数据搬运。很多人导出成功了验证时卡在 import pycuda 报错回头再去装又是一轮折腾。建议在开始之前就把这四样加 pycuda 一起装齐别边做边补。2.2 CUDA、TensorRT、PyTorch 三者的对位关系这是整篇文章里最需要强调的一段。TensorRT 的 Python binding 是跟特定 CUDA 版本编译在一起的PyTorch 的预编译包也是跟特定 CUDA 版本绑定的三个东西必须形成一条自洽的链。常见的错误组合是系统里装了新版 CUDA 驱动pip 装了一个高版本 TensorRT结果 PyTorch 是低 CUDA 版本编译的导出 ONNX 没问题一转 engine 就在解析阶段抛出一堆看不懂的符号错误。我的经验是先在环境里执行一遍版本自检把三个版本号打印出来对照 TensorRT 官方给出的兼容矩阵确认。判断标准不是“版本越新越好”而是“TensorRT 支持的 CUDA 版本”和“PyTorch 编译所用 CUDA 版本”必须一致或向下兼容。如果环境里同时存在多个 CUDA用环境变量明确指定别指望它自己选对。装依赖时尽量用官方提供的 wheel 包源码编译看起来可控实际上坑更多尤其是在容器里。还有一点容易被忽略TensorRT 的 Python binding 版本必须和 C 库版本严格一致。pip 装了一个系统里 apt 又装了一个运行时到底加载哪个取决于路径顺序这种“双份存在”的环境最容易出玄学问题。查的时候用 python -c 打印 tensorrt.version再和实际链接的库路径对一下不一致就先清理干净。2.3 export.py 参数速查与推荐组合参数不多但每个都有讲究。不同版本的仓库里参数名还改过早期叫 --img-size、--batch-size新一些的版本简写成 --imgsz、--batch写错了脚本直接报 unrecognized arguments。下面这张表按用途整理建议对照自己仓库里的 --help 输出再确认一遍。参数作用常用取值注意事项--weights指定 pt 路径yolov5s.pt必须是训练产出的 pt不能是 engine--include指定导出格式onnx / engine / 两者逗号分隔默认 torchscript必须显式写--imgsz输入分辨率640 或 640 640必须是 32 的整数倍--batch构建时的 batch1与动态 shape 配合时它只是 opt 值--device指定设备0 或 cpuengine 必须是 cuda 设备--half半精度空 / 加该 flag出 engine 时是 fp16出 onnx 时慎用--dynamic动态维度空 / 加该 flag只影响 batch 和 h/w 维度--simplify图简化空 / 加该 flag依赖 onnxsim动态图可能失败--opsetONNX 算子集版本12太低会缺算子太高兼容性差--workspace构建工作区4单位 GiB显存紧张时调小推荐的组合是这样先跑一发纯 ONNX、静态 shape、fp32、带 simplify确认图能出、数值对得上再跑一发动态 shape 的 ONNX最后从 ONNX 出发构建 engine先 fp32 再 fp16。分步走的好处是出问题时能立刻定位到是哪一步引入的偏差一把梭全开的话报错信息会混在一起排查成本翻倍。3. export.py 内部做了什么把黑盒拆开看3.1 加载与预热为什么导出前要先跑一次前向export.py 的流程开头永远是 attempt_load 加载权重然后构造一个全零的输入张量对这个模型跑一次 forward。这一步很多人以为是“测试模型能不能用”其实作用远不止于此。PyTorch 的模型在加载后处于一种“结构已知但执行计划未定”的状态很多算子只有在真正执行过一次之后才会完成初始化比如某些需要根据输入 shape 推导参数的层、以及内部缓存的分支判断。更关键的是torch.onnx.export 在追踪模式下是靠“跑一遍”来记录计算图的。它把输入喂进去观察实际发生的算子调用把这些调用序列翻译成 ONNX 节点。如果你的模型里有任何依赖运行时数据的控制流比如根据输入内容决定走哪个分支那么这次追踪就只会记录下当前这条路径其他分支直接丢失。YOLOv5 的检测头在推理模式下是静态的所以不存在这个问题但换成带后处理融合的导出模式就要小心了。预热还有一个副作用它会把模型切换到 eval 模式。训练态的 BatchNorm 用的是当前 batch 的统计量推理态用的是滑动平均两者数值差得很远。如果导出时忘了切 eval导出来的模型推理结果会明显偏低或者抖动。export.py 内部已经处理了这件事但如果你自己写脚本调 export_onnx务必手动 model.eval()这是个非常经典的翻车点。3.2 ONNX 导出路径的关键分支走 ONNX 这条路内部会依次做几件事。第一步是确定 opset 版本默认值随仓库版本变化12 是一个比较稳妥的选择既覆盖了 YOLOv5 用到的算子下游推理框架的支持度也够。调高到 17 以上能用到更新的算子定义但一些老的推理引擎可能解析不了。调低到 9 以下会缺少某些张量操作导出直接失败。第二步是构造 dynamic_axes。不开 --dynamic 的时候输入输出维度全部写死导出的图只认那一个 shape。开了之后会给输入的第一维batch和最后两维h、w打上动态标记输出维度也跟着联动。这里有个细节YOLOv5 的输出是一个三维张量形状大致是 [batch, 8400, 85]以 640 输入为例三个检测尺度加起来 80×8040×4020×20 共 8400 个先验点85 是四个坐标加一个置信度加 80 类。开动态之后这个 8400 也会随输入分辨率变化所以输出维度的动态标记不能漏。第三步是简化。开了 --simplify 会用 onnxsim 做一轮常量折叠和冗余节点消除通常能让图小一圈、推理快一点。但简化过程对动态图的支持并不完美尤其是 h/w 动态且不是 32 倍数的时候简化器推导 shape 会失败然后默默跳过。所以我的习惯是静态图必开简化动态图先不开、跑通了再试着加加失败就退回不加不影响功能。3.3 TensorRT 构建路径与 profile、workspace、精度转 engine 的时候export.py 干的事情比转 ONNX 复杂得多。它内部通常先偷偷导出一份 ONNX 作为中间产物然后拿这个 ONNX 去喂 TensorRT 的构建器。所以你只写 --include engine 的时候目录里可能会短暂出现一个 onnx 文件构建完又被删掉。知道这一点很重要因为构建失败的报错往往来自 ONNX 解析阶段而不是构建阶段你得能分辨。构建器要配三样东西。第一是 workspace也就是构建期间允许占用的显存上限单位 GiB。这个值给太小构建器没法为每层尝试最优 kernel会退化成保守策略甚至直接失败给太大也没用构建期峰值到不了那么高。4 GiB 对大多数检测模型够用显存特别紧张的环境可以降到 1 到 2。第二是 optimization profile。开动态的时候必须设给三个 shape最小、最优、最大。构建器会在这三个点附近各做一次调优运行时落在区间内就按插值策略走。常见的错误是把 max 设得太小导致实际推理 batch 超了直接报错或者 min 设成 0构建器无法推导。一般 min 取 1、opt 取常用 batch、max 取 opt 的两倍这个区间能覆盖绝大多数线上波动。第三是精度。--half 打开时走 fp16要求硬件支持快速 fp16 运算构建器会先自检这个能力不支持就回退或者直接报错。fp16 相对 fp32 通常能带来接近一倍的吞吐提升精度损失在检测任务上微乎其微一般 mAP 掉不到千分之一。如果你的场景对置信度阈值特别敏感可以先用 fp32 出一版当基准再出 fp16 对比看两者的检测框在边缘样本上有没有明显差异。至于 int8那需要额外的校准数据集export.py 这条命令本身不覆盖得另外走一套流程。4. 手把手pt 导出为 ONNX4.1 静态 shape 的最小可用命令先做最保守的一发。进到仓库根目录确认 export.py 在哪一层然后执行python export.py --weights yolov5s.pt --include onnx --imgsz 640 --batch 1 --device 0 --opset 12这条命令的意图很明确只要 onnx输入固定 640×640batch 固定 1用 GPU 跑导出GPU 上导出比 CPU 快一些而且能顺带验证 CUDA 环境算子集用 12。跑完之后目录里应该出现 yolov5s.onnx。这一步我建议你在任何自定义修改之前先跑一遍官方权重确认环境本身是通的。很多人上来就拿自己训的模型试报错了分不清是环境问题还是模型问题。用官方权重跑通等于给环境做了一次基线验证后面出问题就能排除掉一大半因素。如果你的仓库版本比较老参数名要换成 --img-size 和 --batch-size写错了会提示无法识别的参数照着报错改就行。判断版本的办法是看 export.py 里解析参数的段落或者直接 --help 看一眼。导出过程会打印一堆信息重点看两行一行是输入输出张量的形状另一行是导出的文件路径和大小。形状那行确认输入是 [1, 3, 640, 640]输出是 [1, 8400, 85]。如果输出维度对不上说明模型结构或者类别数跟你想的不一样先别急着往下走。4.2 动态 batch 与 onnxsim 简化静态图跑通之后再上动态。命令只多一个 flagpython export.py --weights yolov5s.pt --include onnx --imgsz 640 --batch 1 --device 0 --opset 12 --dynamic开动态之后导出的图会把 batch 和 h/w 都标记成动态维度。这时候输入形状变成类似 [batch, 3, height, width] 的符号形式输出第一维也跟着变成 batch。有个细节要留意动态 h/w 意味着 8400 这个数字也变成动态推导的结果某些推理框架对“输出维度依赖输入维度”这种情况支持得不好加载时会报维度推导失败。遇到这种情况要么退回去用静态图要么在推理侧手动指定 shape。简化这一步单独说因为它是失败率最高的环节。加上 --simplify 之后脚本会调 onnxsim 跑一轮。静态图上它一般很顺动态图上经常出现两类问题一类是 shape 推导冲突报某个节点输入维度不匹配另一类是简化后输出节点的名字被改掉导致下游按名字取输出的代码取不到东西。我的做法是简化前后各存一份文件用 onnx 自带的检查工具过一遍确认图结构合法、输入输出名没变再决定用哪一份。python -c import onnx; monnx.load(yolov5s.onnx); onnx.checker.check_model(m); print([i.name for i in m.graph.input], [o.name for o in m.graph.output])这行检查很朴素但能挡住大部分“文件生成了但其实不能用”的情况。它会做结构校验并把输入输出名字打出来你拿这个名字去和推理代码里写的名字对一下对不上就是这里的问题。4.3 输出正确性校验三方比对导出成功不等于导出正确。这一步最容易被跳过也最容易在后期埋雷。校验的思路很简单拿同一个输入分别喂给 PyTorch 原模型和 ONNX 模型比较两者的输出张量。import torch, onnxruntime as ort, numpy as np model torch.load(yolov5s.pt, map_locationcpu)[model].float().eval() x torch.randn(1, 3, 640, 640) with torch.no_grad(): y_torch model(x)[0].numpy() sess ort.InferenceSession(yolov5s.onnx, providers[CPUExecutionProvider]) y_onnx sess.run(None, {images: x.numpy()})[0] print(max abs diff:, np.abs(y_torch - y_onnx).max())这段代码有几个点要注意。加载 pt 的时候权重文件里通常是一个字典模型本体在 model 键下面直接 torch.load 拿到的是字典不是模型不对着结构取会报 attribute 错误。输入张量用随机数就行不需要真实图片因为我们要比的是数值一致性不是检测效果。输入名 images 是 YOLOv5 导出时固定写死的如果你改了导出代码这里要跟着改。判据怎么定fp32 情况下最大绝对误差在 1e-4 量级属于正常1e-3 也算能接受因为不同后端的算子实现细节不同浮点累加顺序也不一样。如果误差到 1e-1 甚至更大那基本可以确定是哪里错了常见原因是导出时模型没切 eval、或者输入归一化没对齐、或者输出取了不同的层。fp16 的话误差会明显放大1e-2 量级也不算异常这时候就别太苛刻重点看下游检测结果有没有实质变化。我一般还会做一步“真实图片 完整后处理”的比对用同一张图跑两遍把检测框画出来叠在一起看。数值比对查的是算子层面的一致性画框比对查的是整条链路的一致性两者都过才算真的没问题。5. 手把手pt 导出为 TensorRT engine5.1 静态 engine静态 engine 的构建命令和 ONNX 几乎一样就是把 include 换掉python export.py --weights yolov5s.pt --include engine --imgsz 640 --batch 1 --device 0 --workspace 4注意这里不需要写 opset因为内部走的是先出 ONNX 再构建的路径opset 用的是默认值。--device 必须是 cuda 设备写 cpu 会直接报错因为 TensorRT 的构建器本身就要在 GPU 上跑。workspace 给 4 GiB 是个安全值模型小的话给 2 也能过。跑的过程中会看到几个阶段先是 ONNX 导出然后解析然后构建最后序列化。构建阶段耗时最长小模型几十秒大模型几分钟都有可能中间会有进度输出别以为卡死了就 CtrlC。构建完成后目录里出现一个 .engine 文件大小通常比 pt 大一些因为里面除了权重还塞了执行计划和 kernel 的元信息。有个现象值得提一句同一份模型、同样的命令连续构建两次出来的 engine 大小可能完全一样但二进制的字节内容不一定相同。这是因为构建器内部有自动调优和不确定性的 kernel 选择。功能上没差别但如果你做文件哈希校验或者增量发布要注意这一点别把它当成异常。5.2 动态 batch engine动态 engine 是实际部署里更常用的形态因为你很难保证线上永远是 batch1。命令加 --dynamicpython export.py --weights yolov5s.pt --include engine --imgsz 640 --batch 4 --device 0 --workspace 4 --dynamic这里的 --batch 4 不只是构建参数它同时决定了 profile 里的最优 shape。内部会给输入设置一个区间最小值一般是 1最优值是 4最大值是 8不同版本的实现略有差异有的给两倍有的给固定上限。运行时你的实际 batch 落在这个区间里都能跑超出就报错。这个区间的设定是有代价的。构建器要在三个点上分别调优耗时大约是静态的三倍。而且动态区间越宽每层能选到的“最贴合当前 shape 的 kernel”就越少平均性能会略低于静态图。所以在延迟极其敏感的场景宁可多构建几个固定 batch 的 engine用哪个加载哪个也不要用一个大区间通吃。还有一种情况是 h/w 也动态。YOLOv5 支持把输入分辨率也放开但代价更大8400 这个输出维度会变成完全由输入推导很多下游推理框架的显存预分配逻辑会因此失效。除非你的业务真的需要多变分辨率否则建议 h/w 固定、只放开 batch这是工程上性价比最高的折中。5.3 engine 的加载与推理验证engine 文件不能像 pt 那样直接 load它需要先反序列化再创建执行上下文。YOLOv5 仓库里的推理脚本已经把这个封装好了直接用 engine 当权重路径就行python detect.py --weights yolov5s.engine --source data/images --device 0 --img-size 640这条命令能跑通说明 engine 从反序列化到推理到后处理整条链路都是通的。如果报错按错误信息分两类看如果卡在反序列化阶段基本是版本或硬件不匹配如果是推理阶段报显存或维度错误那多半是 profile 区间和你传入的 shape 对不上。自己写代码加载的话流程是先读文件拿到字节流交给运行时反序列化出 engine 对象再创建执行上下文然后分配输入输出显存用异步拷贝把数据送进去执行再把结果拷回来。这套流程用 pycuda 写起来不复杂但很啰嗦中间每一步的显存都要手动释放漏一个就是慢性泄漏。我在一个长期运行的服务里就因为漏了释放输出显存跑了两天把卡吃满了排查了很久才发现是这里的问题。验证这一步同样要做数值比对。拿同一个输入engine 的输出和 ONNX 的输出比一遍。fp32 的 engine 和 ONNX 差异应该在 1e-3 以内fp16 的 engine 差异会到 1e-2 量级甚至更大一点这都属于正常范围。如果差得离谱优先怀疑精度标志和输入预处理对不对而不是怀疑模型本身。6. 报错与排查几个高频坑的实录6.1 常见报错速查表导出这条链路上的报错信息普遍不友好很多是底层库直接抛出来的看着像天书。我把遇到过的、以及帮别人看过的典型情况整理成表方便对照。报错关键词大概率原因处理方式unrecognized arguments参数名与仓库版本不匹配用 --help 看实际参数名img-size/imgsz 二选一No module named onnx缺 onnx 依赖装 onnx出 engine 还需要 onnx 作为中间层找不到 onnxsim简化工具没装装 onnxsim或直接去掉 --simplifyCUDA out of memory构建期workspace 太大或显存被占调小 workspace先清掉其他进程serialization / deserialize 失败engine 与本机版本或硬件不匹配在目标机器上重新构建别跨机复制Illegal instruction 或符号缺失TensorRT 与 CUDA 版本错配对齐三者版本清理重复安装输出维度不匹配动态标记漏了输出维度检查 dynamic_axes 输出项推理结果全是 0 或很低导出时未切 eval或输入未归一化确认 eval 模式与预处理一致构建极慢或卡住动态区间过宽收窄 profile 区间或改静态这张表能覆盖大部分情况但排查的时候别只盯着报错文本要顺着“最后成功的是哪一步”往回推。能出 ONNX 但出不了 engine问题一定在 TensorRT 这一侧ONNX 都出不来问题在 PyTorch 和导出参数这一侧。把问题域先框小再往下挖。6.2 数值对不上的排查顺序比起直接报错更让人头疼的是“跑通了但结果不对”。这种情况我建议按固定顺序排查别东一榔头西一棒子。第一步查输入。把同一个张量同时喂给两边打印它的 min/max/mean确认送进去的东西真的完全一样。很多问题是预处理不一样比如一边做了归一化一边没做一边是 BGR 一边是 RGB一边 resize 用了不同的插值方式。这类问题在数值比对上表现为整体偏移不是个别点误差。第二步查输出形状。形状对不上就说明图结构有问题可能是类别数不一致可能是导出时用的模型和你想的不是同一个。YOLOv5 的输出维度是 [batch, 先验点数, 5类别数]先验点数跟输入分辨率强相关640 输入是 8400换成 512 就不是这个数了。第三步查精度设置。如果你拿 fp16 的 engine 去和 fp32 的 PyTorch 比误差大是必然的。这时候要先把两边都统一成 fp32 再比确认基础链路没问题再单独评估 fp16 带来的精度损失。第四步查后处理。检测模型导出的是原始输出置信度阈值、NMS 的 IoU 阈值、坐标解码方式这些都在后处理里。两边后处理参数不一致画出来的框自然不一样但这跟导出无关。我见过有人因为两边用了不同的置信度阈值硬是怀疑模型导出错了白折腾两天。6.3 部署侧的几条硬规矩第一条engine 绝不跨机复制。哪怕两张卡型号完全一样只要驱动版本、TensorRT 版本有任何差异反序列化都可能失败。正确的做法是把 ONNX 和构建脚本一起打包到目标机器上现场构建。构建花的那几分钟远比线上排查兼容性问题便宜。第二条ONNX 要长期归档而且要和对应的 pt 一起归档。模型迭代很快今天导出的图明天就找不到对应的权重了。我的习惯是每次导出都在文件名里带上日期和关键参数比如带上分辨率、精度、是否动态这些信息回头找的时候一目了然。第三条上线前一定要做端到端的精度回归。别只看单张图的数值差异要跑一遍完整的验证集看 mAP 掉了多少。fp32 的 engine 相对原模型基本无损fp16 通常在千分之一以内如果掉了百分之几那肯定是哪里配错了不是精度压缩的正常代价。第四条动态 shape 的区间要留余量但别留太多。留太多会让每层的 kernel 选择变保守性能下降留太少线上流量一波动就报错。我的经验是按历史峰值 batch 的一点五倍设上限同时在上层做队列削峰别让请求无限制堆积。7. 一些实际操作中的体会导出这件事技术门槛其实不高真正花时间的是那些“文档里不会写、但一定会遇到”的细节。比如参数名在版本之间改过、比如 include 默认值不是 onnx、比如转 engine 时内部会偷偷生成一个中间 onnx 文件这些都是踩过一次就记住、没踩过就干瞪眼的东西。我现在固定用一套流程先官方权重验证环境再自己的权重出静态 ONNX校验数值然后出动态 ONNX再校验最后从 ONNX 构建 enginefp32 和 fp16 各一份都跑端到端回归。整个流程走下来十几分钟但能省掉后面可能几天的排查。中间任何一步数值对不上立刻停下来查绝不带着疑点往下走。还有一个小心得把每次导出的命令、版本号、校验结果记在一个文本文件里跟着产物一起存。过两个月回头看你会感谢当时的自己。模型导出这条链路最容易出的问题不是“不会做”而是“做过一次但忘了当时怎么做的”尤其是在版本升级之后同样的命令结果可能完全不同有个记录能省掉大量重复劳动。
阅读完成 · 觉得有帮助?
咨询建站