ARTICLE DETAIL

资讯详情

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

YOLO26模型导出实战:从ONNX到TensorRT的完整部署指南

YOLO26模型导出实战:从ONNX到TensorRT的完整部署指南 1. 模型导出前的基础认知1.1 为什么训练好的模型不能直接上生产先把话说清楚YOLO26训练出来的.pt权重文件本质上是PyTorch的序列化对象里面不光有网络结构、权重参数还带着优化器状态、训练配置这些在推理阶段完全用不到的东西。很多人第一次部署时习惯直接拿着.pt文件去写推理脚本在开发机上跑没问题一旦挪到生产环境就翻车了。原因很简单PyTorch推理有几个绕不开的坎。第一是环境依赖太重目标机器上必须装对应版本的PyTorch、CUDA、各种Python包光是版本匹配就能折腾半天而且很多生产服务器为了安全根本不允许装完整的Python环境。第二是推理效率不够看PyTorch的Eager模式是逐算子解释执行虽然有torch.compile这样的优化手段但跟专门的推理引擎比还是有差距。第三是硬件适配差你总不能指望着嵌入式设备或者iPhone上装一个PyTorch吧。所以模型导出这件事本质上就是把训练框架里的模型“翻译”成推理框架能直接吃的格式。YOLO26这套模型本身是架构无关的导出成ONNX、TensorRT、OpenVINO、CoreML、TFLite之后就能脱离PyTorch独立运行还能利用目标设备上的专用加速单元把推理延迟压下来。在实际项目里模型导出不是“可选项”而是“必选项”只要有部署需求这一步就跑不掉。1.2 YOLO26模型家族选型导出前先想清楚用哪个YOLO26跟之前的YOLO系列一样发布了n、s、m、l、x五个规格的预训练模型。这五个规格的区别主要体现在网络宽度和深度上直接决定精度和速度的平衡点。导出的前提是你得先有一个训练好的模型所以选型这件事在导出之前就要定下来不然导出一个不符合现场算力要求的模型后面全是返工。拿实际场景举例如果跑在Jetson Orin Nano这类边缘设备上YOLO26n或者YOLO26s是主流选择参数量小、算力要求低配合TensorRT导出后能做到实时检测如果是服务器GPU推理YOLO26m或者YOLO26l更合适精度高出不少YOLO26x适合那些对精度要求极高、推理速度不敏感的场景比如离线批量分析。还有一个不得不提的选项是YOLO26的轻量化版本很多人自己改模型结构做剪枝、蒸馏折腾完照样能走导出流程只是导出时个别自定义算子可能需要特殊处理这个后面专门说。选型之后还要确认一下自己的训练结果。导出前最好在验证集上跑一遍mAP确认模型精度符合预期再导出不然导出来一个精度拉胯的模型排查了半天发现是源头训练就没收敛那就尴尬了。1.3 主流导出格式对比到底该选谁YOLO26支持的导出格式比较多但不是所有格式都适合你的场景。我在项目里做过一轮对比把常用的几种格式的核心差异整理成了表格方便你按设备和业务需求来选型。导出格式目标硬件/场景延迟表现依赖要求个人评价ONNX跨平台通用CPU/GPU均可中等onnxruntime万能中转格式推荐首选TensorRTNVIDIA GPU / Jetson极低TensorRT库英伟达设备上性能最优OpenVINOIntel CPU / 集成显卡 / 瑞芯微较低openvino runtime无独显设备上的救星CoreMLApple芯片设备iPhone/Mac极低coremltoolsiOS端部署唯一合理选择TFLiteAndroid / 嵌入式 / Edge TPU较低tflite-runtime移动端部署实用如果你暂时不确定设备或者想先在PC上验证效果我建议先导出ONNX。ONNX像一个“通用语言”几乎所有推理框架都能读它后续就算要转TensorRT或者OpenVINO也可以基于导出的ONNX再做转换。但要注意从.pt直接导出TensorRT和先导ONNX再转TensorRT是两条不同的路径后面章节我会解释哪条路更稳。2. 核心导出实操从环境准备到命令详解2.1 环境准备版本匹配是第一道生死关很多人导出失败不是因为代码写错而是Ultralytics版本、PyTorch版本、CUDA版本三方对不上。YOLO26要求ultralytics包至少在对应版本以上这个可以在官方仓库的release说明里查到。我建议直接用最新稳定版省去一堆兼容性麻烦。安装核心依赖的命令如下pip install ultralytics pip install onnx onnxruntime-gpu # 导出ONNX并做GPU验证 pip install tensorrt # 导出TensorRT用先装CUDA和cuDNN pip install openvino-dev # 导出OpenVINO用装完之后建议先跑一条命令确认环境没问题python -c from ultralytics import YOLO; print(YOLO.__module__)如果导入不报错说明ultralytics装好了。接着再确认PyTorch的CUDA可用性python -c import torch; print(torch.__version__, torch.cuda.is_available())如果你要用GPU导出TensorRT这里必须输出True否则后面转换会退化成CPU模式速度慢到怀疑人生。另外提醒一下TensorRT极其吃版本匹配TensorRT版本、CUDA版本、cuDNN版本三者必须与PyTorch编译时用的版本兼容不然大概率报Plugin相关的错。2.2 一条命令完成ONNX导出参数含义逐个拆解环境就绪之后导出操作本身非常简单Ultralytics封装得很彻底。我用的是官方YOLO26n预训练权重做演示from ultralytics import YOLO model YOLO(yolo26n.pt) model.export( formatonnx, imgsz640, dynamicFalse, simplifyTrue, opset12, halfFalse, )导出完成后会在当前目录下生成一个yolo26n.onnx文件。这段代码里的几个参数是真正影响导出结果的逐个说一下。imgsz640是训练时的输入尺寸默认640x640。如果你的训练数据是1280那这里要改成对应尺寸否则推理时resize会让精度下降。dynamicFalse表示静态尺寸导出后模型只能接受640x640的输入改成dynamicTrue则可以接受任意尺寸代价是推理速度略微下降而且部分推理引擎对动态shape支持不友好。simplifyTrue会调用onnx-simplifier对计算图做一轮优化去掉冗余算子这个建议开启能让模型体积变小、推理更快。opset12是ONNX算子集版本版本太低会缺算子太高则旧版推理引擎不支持。halfFalse表示不导出半精度权重如果确认推理设备支持FP16可以改成True模型体积减半、推理加速但精度有极小损失。同样的事情用命令行也能完成yolo export modelyolo26n.pt formatonnx imgsz640 simplifyTrue其实本质是同一个接口看个人习惯。2.3 TensorRT导出两条路径直接导还是中转导TensorRT的导出有两条路径一条是Ultralytics封装好的直接导出model.export(formatengine, imgsz640, halfTrue)另一条是先导出ONNX再用trtexec或者Python API转成enginetrtexec --onnxyolo26n.onnx --saveEngineyolo26n.engine --fp16两条路我都走过说下差异。第一条路省事一条命令全搞定但内部黑盒比较多出了问题不好排查。第二条路虽然多一步但你能明确看到TensorRT做了什么优化还能精细控制--workspace内存、--minShapes和--optShapes等参数。我个人的习惯是先导出ONNX确认计算图没问题再用trtexec转engine这样排查问题方便很多。尤其是有自定义算子的模型直接导出engine经常在算子翻译阶段就崩了这时候先出ONNX、再用trtexec可以看到具体是哪个节点不受支持定位起来快得多。还有一个坑必须提TensorRT导出的engine文件是跟GPU架构绑定的在RTX 3090上导出的engine拿到RTX 4090上跑不了在Jetson上导出的engine拿到服务器上也不行。所以engine文件一定要在目标设备上重新导出不能图省事拷贝着用。2.4 半精度与INT8量化速度翻倍的代价是什么很多人听说半精度和量化能让模型提速一大截就无脑开结果精度掉得妈都不认识。这里把原理和取舍讲清楚。FP16半精度导出就是刚才说的halfTrue权重从FP32变成FP16模型体积直接减半在支持FP16的GPU上推理速度能提升不少。代价是极少数数值敏感的层可能出现精度波动但目标检测任务里通常影响不大实测mAP下降一般在0.5%以内属于可接受范围。INT8量化就更激进了权重从FP32压到INT8模型体积只有原来的四分之一推理速度在支持INT8的硬件上非常可观。但INT8量化对权重分布敏感直接导出的INT8模型经常掉点严重。Ultralytics的int8True内部会走校准流程需要提供校准数据集。实际项目中我建议先跑通FP16如果速度不够再去尝试INT8并且一定要在验证集上对比精度。还有一点不是所有硬件都支持INT8加速TensorRT在部分GPU上支持OpenVINO在Intel CPU上支持较好TFLite在Edge TPU上支持。导出前先查清楚目标设备的INT8算力别白忙活。3. 导出后的验证与部署不能导完就撒手不管3.1 用Netron检查计算图结构对不对一眼看清导出完成后第一件事不是直接部署而是检查模型结构。我习惯用Netron打开导出的ONNX文件它是网页版或者桌面版都能用的可视化工具直接拖入文件就能看到完整的计算图。检查时重点看三个地方。第一输入节点的名字和shapeYOLO26默认输入名是imagesshape是[1, 3, 640, 640]如果你开了动态尺寸shape里相应维度是None或者dynamic这是正常的。第二输出节点的名字YOLO26的ONNX导出通常有两个输出一个是检测框的原始输出一个是NMS后的结果如果你开启了内置NMS。第三看整个图中是否有红色告警的算子如果有说明这个算子某些推理引擎不支持后面部署时大概率要出问题。Netron检查计算图真的花不了几分钟但能帮你提前拦住一大批部署阶段的低级错误。我在交付项目时连模型结构截图都作为交付物之一发给客户省去了大量扯皮时间。3.2 用ONNX Runtime验证导出精度对比与PyTorch的差异模型结构没问题下一步是验证推理结果是否跟PyTorch原模型一致。这一步很关键因为导出过程虽然保留了网络结构但算子实现可能有细微差异如果不去验证等到部署后发现检测框偏移又要穿回导出链路排查效率极低。验证逻辑很简单同一张测试图分别用PyTorch原模型和导出的ONNX模型推理对比输出。import onnxruntime as ort import numpy as np import cv2 from ultralytics import YOLO # PyTorch原模型推理 pt_model YOLO(yolo26n.pt) img cv2.imread(test.jpg) results_pt pt_model(img) # ONNX模型推理 onnx_model ort.InferenceSession(yolo26n.onnx, providers[CUDAExecutionProvider, CPUExecutionProvider]) input_name onnx_model.get_inputs()[0].name # 拿到输入图像做同样的预处理resize、归一化、CHW input_img cv2.resize(img, (640, 640)) input_img input_img[:, :, ::-1].transpose(2, 0, 1) # BGR转RGBHWC转CHW input_img np.ascontiguousarray(input_img, dtypenp.float32) input_img / 255.0 input_img np.expand_dims(input_img, axis0) outputs onnx_model.run(None, {input_name: input_img})对比两边输出的检测框、置信度、类别正常情况下差距应该非常小。如果你发现ONNX的输出形状跟PyTorch输出对不上大概率是后处理解析方式的问题YOLO26的原始输出是[1, 84, 8400]这种形状4个坐标 80个类别共84个通道8400是三个尺度特征图的Anchor点总数需要做解码后处理才能变成最终的检测结果。3.3 从ONNX到TensorRT的转换细节如果你选了TensorRT路线这里给出一份我在实际项目中用着最稳定的转换流程。trtexec --onnxyolo26n.onnx \ --saveEngineyolo26n_fp16.engine \ --fp16 \ --minShapesimages:1x3x640x640 \ --optShapesimages:1x3x640x640 \ --maxShapesimages:1x3x640x640 \ --workspace2048参数解释一下。--fp16开启半精度--minShapes/--optShapes/--maxShapes是给动态shape范围如果你的模型是静态导出这里可以不写。--workspace控制TensorRT构建时能用的显存上限设大一点能提高优化效果但别超过GPU显存。转换成功后得到的engine文件就能用TensorRT的Python API加载了。Jetson设备上的转换逻辑一样只是要注意Jetson的TensorRT版本通常比PC端低跨版本转换经常报错。我踩过的坑是PC上TensorRT 9.0导出的engine拿到Jetson上TensorRT 8.5加载直接崩后来统一在Jetson上重新转换才解决。3.4 导出后如何部署到边缘设备和移动端很多人的最终目标是边缘设备这里简单梳理一条完整链路。以在树莓派或RK3588这类Linux设备上部署为例OpenVINO是性价比很高的选择因为它的CPU推理优化做得很好不依赖专用加速器。from openvino.runtime import Core core Core() model core.read_model(yolo26n.xml) compiled_model core.compile_model(model, CPU)注意OpenVINO导出的文件是.xml和.bin两个文件配对使用一个描述结构一个保存权重。推理时用core.read_model读取xml即可。在Intel CPU上OpenVINO的推理速度比ONNX Runtime CPU版快很多实测YOLO26n在i5-1240P上能做到20ms以内。如果是iPhone端部署导出CoreML格式后用Core ML框架加载。Android端则是TFLite格式配合NNAPI或者GPU Delegate加速。这几个格式的导出命令在Ultralytics里都是一行代码的事model.export(formatopenvino, imgsz640) model.export(formatcoreml, imgsz640) model.export(formattflite, imgsz640)但导出只是第一步移动端部署还要处理前后处理的性能瓶颈经常出现模型推理只花20ms、但图像预处理却花了100ms的情况这时候需要用硬件加速和异步流水线把整个链路串起来不然总延迟根本压不下去。4. 常见问题与排查技巧实录4.1 导出失败高频原因速查表导出阶段报错是最让人头疼的因为报错信息千奇百怪。我把这几年遇到的高频问题和排查思路整理成了速查表直接照着查就行。报错现象根本原因排查与解决No module named onnxONNX依赖未安装执行pip install onnx onnxruntimeCUDA error: no kernel image availablePyTorch与CUDA版本不匹配重装匹配的PyTorch参考官方安装矩阵Export failure: Unsupported operator使用了自定义算子或torch版本过旧升级ultralytics和PyTorch反序列化重导出导出engine时显存不足workspace设置过大调低--workspace或关闭其他占用显存的程序动态batch与动态尺寸同时开启报错部分导出格式不支持双动态只保留其中一个动态维度OpenVINO导出后运行报图形错误模型输入shape不匹配检查xml文件的输入shape与实际输入是否一致4.2 导出后精度明显下降问题出在哪这个问题最常见而且原因往往不是导出本身而是预处理不一致。PyTorch训练时图像归一化通常是把像素值除以255然后再按ImageNet的mean和std做标准化。ONNX模型导出的计算图里已经包含了归一化操作但部署侧如果又做了一遍等于重复归一化精度肯定崩。正确的做法是先在验证脚本里把两侧的预处理对齐再谈精度对比。我自己验证时用的方法很笨但很有效拿PyTorch原模型跑一张固定图拿到推理结果存成npy文件再拿ONNX跑同一张图逐元素对比输出差异。如果出现较大差异再用二分法逐个模块对比基本能定位到具体是哪个算子在转换时出了问题。另外量化导致的精度下降是另一个高频原因。INT8量化对权重分布敏感尤其是YOLO26输出层那些数值范围大的张量量化误差容易被放大。如果量化后掉点超过2%建议改用FP16或者用更大的校准数据集重新量化。4.3 NMS到底该内嵌还是外置部署工程师永恒的争论YOLO26导出的ONNX默认可以不包含NMS后处理因为Ultralytics把NMS拆到了后处理脚本里。但很多部署框架需要模型输出直接的检测结果这时候你可以在导出时开启内置NMSmodel.export(formatonnx, nmsTrue, conf0.25, iou0.45)nmsTrue会把NMS算子编进ONNX计算图导出后模型直接输出最终的检测框、类别和置信度。听起来省事但有几个坑。内置NMS的ONNX在不同推理引擎上的兼容性参差不齐ONNX Runtime支持但版本要够新TensorRT对这种动态NMS节点支持比较差经常报算子不支持的错。而且内置NMS把置信度阈值和IoU阈值固化了后期想调参数还得重新导出模型非常不灵活。所以我的建议是除非框架强制要求否则一律用外置NMS。外置NMS其实就是几十行Python代码的事情而且调参非常方便后续想换NMS算法比如Soft-NMS也不用重新导出模型省时省力。4.4 推理速度反而变慢这事不怪模型导出成功、精度也验证过了结果一部署上线发现推理延迟比预想的高不少。这种情况我也遇到过多次排查思路分享给你。首先要检查是不是用了动态shape。动态shape意味着每次推理时要重新计算一些尺寸相关的优化推理引擎没法提前做内存布局优化速度自然慢。如果业务上输入尺寸固定务必导出静态shape模型。其次检查线程数设置。ONNX Runtime和OpenVINO默认线程数不一定是最优的尤其在多核CPU上。以ONNX Runtime为例把线程数设成物理核心数通常能榨出最好的性能sess_options ort.SessionOptions() sess_options.intra_op_num_threads 4 sess_options.inter_op_num_threads 1小模型本身并行度有限线程开太多反而会因为线程切换开销拖慢速度。YOLO26n这种小模型实测开4个线程往往比开8个线程更快建议实测对比一下。最后还有个容易被忽略的点内存拷贝。在PyTorch里GPU张量操作很自然但ONNX Runtime的GPU推理如果输入在CPU端每次都要做一次H2D拷贝这个过程如果没做流水线优化会占到总延迟的三分之一以上。部署时尽量把预处理也放到GPU上或者用CUDA Stream做异步拷贝延迟能明显降下来。4.5 自定义算子和轻量化改动后的导出注意事项很多人在YOLO26上做了改进比如替换了注意力模块、改了检测头、做了剪枝蒸馏。这些自定义结构的模型导出时容易踩坑因为导出的本质是走一遍PyTorch的torch.onnx.export流程遇到不支持的算子就会报错。如果是常见模块SE注意力、CBAM、ASFF这些新版torch和onnx-simplifier基本都能处理导出一般没问题。但如果用了非常新的算子比如某些自定义C扩展导出时大概率会报Unsupported operator。解决办法有几个升级PyTorch和onnx版本很多新算子在高版本里已经支持了其次是用torch.onnx.export的custom_ops参数注册自定义算子的导出函数实在不行就去改网络结构把那层自定义算子替换成等价的标准算子组合。轻量化模型导出还有一个特殊问题。剪枝后的模型权重里某些通道可能是空的虽然不影响推理但导出的模型体积减不下来。这种时候要用torch.nn.utils.prune彻底移除通道再导出不然体积和推理速度都达不到预期的“轻量化”效果。导出后一定要用Netron再看一遍确认模型确实瘦身了。最后说点实在的做模型导出这几年最大的感受是导出本身不是技术难题真正花时间的是排查兼容性问题和前后处理的一致性。我现在的标准流程是先跑通ONNX验证精度、再转目标格式、最后用Netron做结构确认这一套流程帮我避开了绝大部分的部署事故。还有一个小技巧分享给大家每次导出后在模型文件名上标注清楚导出参数和日期比如yolo26n_640_fp16_20250601.engine。时间一长、模型版本一多这个命名习惯能帮你少掉很多头发。至少我自己被“这到底是谁导出的模型”这件事坑过不止一次。
返回列表