最近在搞一个部署项目,把训练好的YOLOv5模型转成ONNX,用onnxruntime跑了一下,发现输出的检测框置信度整体偏低,有些原本能检出的目标直接丢了。对比了torch和onnx的输入输出,数据预处理都是一样的,也关掉了amp。查了日志,提示有些算子(比如Focus和SiLU)被拆成了多个小算子,不确定是不是这个原因。有没有大佬遇到过类似情况?是算子兼容性问题,还是转换时需要设置opset版本或者dynamic_axes?另外,用onnx-simplifier简化后会不会有改善?希望有经验的朋友指点一下,实在不想从头用TensorRT重写。
PyTorch转ONNX后推理结果和原模型差很多,是量化问题还是算子不支持?
全部回复
共 64 条遇到过类似情况,大概率不是量化问题,Focus和SiLU被拆算子确实容易导致精度抖动,尤其是SiLU在onnxruntime里某些版本实现有细微差异。建议先把opset设到12以上,dynamic_axes只对batch维度开,别动wh,然后试试onnx-simplifier,它能把Focus合并成slice+concat,有时候精度就回来了。另外你确认下onnxruntime的log里有没有算子fallback到CPU的警告,如果有那就是算子支持问题,得换onnxruntime-gpu或者改模型结构。
遇到过类似的,Focus和SiLU被拆小算子一般不影响精度,但onnxruntime对某些拆分后的子图优化可能引入数值误差,尤其是SiLU的近似实现。建议先试试opset设为11或12,同时把dynamic_axes配好,特别是batch维,避免固定shape导致预处理不一致。onnx-simplifier可以跑一下,但重点检查它有没有把一些融合算子替换成不精确的版本。另外,你对比下torch和onnx的输出张量差异,如果只是低置信度目标丢失,可能是NMS后处理在导出时被包含进去了,试着把后处理留在外面。
遇到过类似的,YOLOv5转ONNX后置信度飘了大概率不是量化问题,你关掉amp是对的。Focus和SiLU被拆成小算子本身不影响数值,但onnxruntime对某些拆分后的图优化可能引入精度损失,尤其是SiLU的近似实现。建议先把opset设到12以上,dynamic_axes只给batch和宽高,别全放开,然后跑一下onnx-simplifier,它能把冗余reshape和transpose理顺,我上次就是这么解决的。如果还不行,可以对比一下onnx和torch逐层的输出,定位到具体哪一层开始偏差,这样比盲猜快。
之前跑过类似流程,Focus和SiLU被拆算子通常不影响精度,但opset版本太低可能导致某些融合没生效,建议先试opset 12以上。置信度整体偏低更像数值精度问题,可以检查下onnxruntime的execution_mode设成sequential或开enable_optimize试试。simplifier对解决这种问题帮助不大,它主要合并节点,不改变计算逻辑。另外动态输入尺寸建议用dynamic_axes,静态输入下某些层可能会走不同的优化分支。
我试过简化后好不少,但置信度偏低八成是opset版本太低导致的,换成11以上试试。
之前做检测模型转换也踩过类似的坑,置信度偏低大概率不是量化的问题,因为onnx默认走fp32。你提到的Focus被拆成多个slice和concat,如果原始权重没跟着重写,很容易导致输出偏移,建议先检查转换后的图结构里有没有额外的reshape或transpose插入。opset版本尽量用12以上,dynamic_axes如果输入尺寸固定就先别开,简化器对SiLU这种激活函数会有帮助,但核心问题可能还是在Focus的权重映射上。我上次是手动改模型结构,把Focus换成普通卷积层才解决的,你可以试试在转onnx前先重构YOLOv5的backbone。
一般不是量化,opset版本低导致Focus展开后精度浮动很常见,试试opset=12加dynamic_axes。
遇到过类似的,当时是yolov5s转onnx,置信度掉得没那么夸张但确实有偏差。你先把opset拉到13以上试试,SiLU在低版本下拆分容易出数值问题,Focus拆成slice和concat通常不影响精度,但保险起见可以对照onnx的中间节点输出逐层排查。动态axes一般不影响精度,主要是shape变化时某些算子行为不同。onnx-simplifier对这种情况帮助有限,它只做图优化不改数值计算,建议先确认是不是后处理里解码坐标或anchor的步长没对齐。另外,你对比下torch和onnx的原始输出logits,如果logits接近那就是解码或nms阈值的问题,如果logits本身就有差,那大概率还是转换时某个算子的实现细节有差异。
排查下opset版本吧,还有转的时候把dynamic_axes设上,我之前也栽在Focus上。
遇到过类似的,多半是算子拆分精度丢了,试试onnx-simplifier,能去掉不少冗余节点。
之前跑过类似的坑,YOLOv5转ONNX置信度掉点大概率不是量化的问题,而是Focus和SiLU被拆解后某些版本onnxruntime对子图的精度处理有差异。建议先试opset=12以上,同时把dynamic_axes只对batch和宽高维度开启,别全给设成动态。onnx-simplifier可以跑一下,但别指望它修复数值偏差,更多是把冗余算子合并减少出错机会。另外可以对比一下onnx和torch每个中间层的输出,定位是哪个节点开始漂移的,我之前就是发现SiLU的拆分实现有精度损失,手动替换成自定义算子才解决。要是实在查不出来,试试用onnxruntime的CUDA EP跑,CPU和GPU的算子实现也可能导致微小差异被放大。
我之前也踩过类似的坑,YOLOv5转ONNX最容易出问题的不是量化,而是opset版本和模型里的动态尺寸。你试过把opset设到12以上吗?SiLU和Focus被拆解成小算子本身不会导致精度下降,但拆分后如果某些子算子的数值精度处理跟PyTorch不一致,累积误差就会让置信度整体漂移。
建议你先用onnxruntime的CUDAExecutionProvider跑一下,排除CPU和GPU实现差异。另外,dynamic_axes如果不设置,输入尺寸被固定后,YOLOv5的anchor生成逻辑可能会错位,这比算子问题更容易引起目标丢失。onnx-simplifier可以试,但它主要优化图结构,对数值精度影响有限,我建议先检查转换时是否把模型里的half权重转成了float32,有时候amp关掉但权重精度没对齐,也会有这种表现。
还有个很隐蔽的点,YOLOv5输出层的解码逻辑在ONNX里往往需要额外拼一个后处理节点,如果你直接在Python里用torch的decode,而ONNX模型输出的是原始预测,那对比肯定对不上。建议先导出时把decode层也一起包含进去,或者写个脚本把ONNX输出手动走一遍相同的decode,确认差异是来自模型还是后处理。实在不行,用onnxruntime的transformers优化工具跑一下,有时候能自动修复一些算子替换导致的问题。
我之前也踩过这坑,Focus被拆后输出对齐不了,试试opset设12或13,simplifier能解决一部分但别全指望它。
我之前也踩过类似的坑,YOLOv5转ONNX最容易出问题的其实不是量化,而是Focus算子的 slicing 操作在onnxruntime里对内存布局的假设和PyTorch不完全一致,导致特征图错位,置信度自然就崩了。你提到日志里算子被拆分,这很关键,很多时候不是不支持,而是拆出来的小算子组合在runtime里优化不到同一个kernel,数值精度就会漂移,尤其是SiLU这种激活函数,在FP32下理论上误差很小,但多步计算累积起来就明显了。建议你先别急着上simplifier,先试opset=12以上,然后给onnx模型跑一遍onnxruntime的 full profiling,看哪个节点的输出和torch对不上。dynamic_axes一般不影响数值,只影响shape,如果输入尺寸固定的话可以先不设。另外,如果你用的是官方YOLOv5的export脚本,可以试试把 --dynamic 关掉,并用 --opset 11 或 12 对比一下,我那次是opset=11反而比12准。onnx-simplifier对Focus的替换有时候能改善,但也可能引入新的图优化错误,建议在简化前后都跑同一张图对比输出,别只看mAP。实在不行,可以绕开Focus,用Conv+Slice手写替换,我之前就是这么解决的,效果立竿见影。
我之前也踩过这个坑,YOLOv5转ONNX后置信度掉一截大概率不是量化问题,而是Focus和SiLU被拆解后精度损失叠加了。建议先试试opset=12以上,配合dynamic_axes把batch和尺寸维度动态化,很多时候能保住原始计算图结构。onnx-simplifier对这类拆分有一定帮助,但别指望完全恢复,最好导出后逐层对比中间张量的数值差异定位偏差源头。另外检查下yolov5的export脚本里有没有自动做grid解码,有时候是后处理被重复或遗漏了。如果实在不行,可以考虑用onnxruntime的CUDA EP跑,有时CPU和GPU算子实现不同也会导致结果差异。
大概率是opset版本太低导致Focus展开后精度丢失,试试opset=12+并开dynamic_axes,simplifier基本没用。
我之前也踩过这个坑,YOLOv5转ONNX时SiLU被拆开确实会导致数值精度偏差,但一般不会让置信度掉这么多。你先试试把opset设到12以上,然后dynamic_axes记得给batch和hw都加上,不然onnxruntime会按固定shape优化。onnx-simplifier大概率能帮你合并掉那些多余的小算子,但对精度问题帮助有限。另外你确认下预处理里有没有做letterbox的padding,有时候是输入尺寸不一致导致特征图错位,不是模型本身的问题。如果还不行,建议直接对比某一层中间输出,看到底从哪个节点开始分叉的。
之前跑yolov5转onnx也踩过类似的坑,置信度掉得离谱,最后发现是opset版本太低导致Focus展开后某些节点精度不对。你可以试试把opset设到12以上,同时导出时加上dynamic_axes,让NMS那块别固化尺寸。onnx-simplifier可以跑一下,但别指望它解决精度问题,顶多帮你把冗余节点清掉,核心还得看算子映射是否一致。另外,SiLU在onnx里支持得还行,但如果你用的是老版本torch,建议手动把激活函数换成等价的表达式再试。实在不行就对比一下中间层输出,看到底哪一层开始偏差的,这样定位快很多。
我之前也踩过类似的坑,YOLOv5转ONNX后置信度掉一截大概率不是量化问题,而是Focus和SiLU被拆解后精度丢失,尤其是opset版本太低时。建议先把opset设到12以上,dynamic_axes按官方文档配好,再用onnx-simplifier试试,有时候能合并掉冗余节点。如果还不行,可以手动把Focus换成等效卷积,这招我试过挺管用。另外检查下onnxruntime的execution_mode,换成CPU或GPU的优化模式有时也会影响结果,实在不行再考虑TensorRT。
这问题我踩过坑,大概率不是opset版本的事,YOLOv5转ONNX时Focus和SiLU被拆开挺常见的,但一般不影响精度。你试试把模型导出时的merge=True选项打开,或者用onnx-simplifier过一遍,很多次我这么搞完精度就回来了。另外,如果还不行,检查下onnxruntime的execution_mode设成ORT_ENABLE_ALL,有时候默认的图优化反而会引入数值误差。
我遇到过类似的,大概率不是量化的问题,你amp关了就排除了这个。Focus和SiLU被拆算子很常见,但一般不影响精度,重点检查下opset版本,建议设到11以上,还有dynamic_axes最好明确指定一下,尤其是batch和宽高维度。onnx-simplifier可以试试,有时能解决一些图优化导致的数值偏差,但别指望它修复所有问题。另外,你对比下onnx中间层的输出和torch对应层,看到底是哪一层开始漂移的,这样定位更快。