最近在做一个部署项目,把训练好的YOLOv5模型转成ONNX,再用onnxruntime推理。结果发现输出的检测框置信度整体偏低,有些原本能检出来的目标直接漏检了。试了opset 11和12,也试过dynamic_axes,情况没改善。
PyTorch转ONNX后推理结果和原模型差很多,是量化问题还是算子支持问题?
全部回复
共 43 条YOLOv5转ONNX其实有个坑挺常见的,就是后处理里的anchor解码和NMS如果没对齐,置信度就会整体偏移。你先确认下导出时是不是用了官方那个export.py,还有输出层到底是三个feature map还是已经拼好的。另外别忘了检查预处理,letterbox的padding值和归一化方式在ORT里是不是跟训练时一致,这个不一致的话框和分数都会歪。量化我倒觉得可能性不大,你如果没主动做PTQ或QAT,ONNX默认还是FP32的。
你提到的这个现象我踩过坑,大概率不是量化问题,因为onnxruntime默认跑的是fp32。YOLOv5里有些算子像slice、resize在转ONNX时对opset版本挺敏感,特别是后处理那块,建议你把输出节点截到网络原始输出,别带后面的decode逻辑。另外可以拿onnxruntime和torch同样的输入跑一遍中间层,看从哪层开始数值对不上,一般比瞎试opset快。
这个坑我去年踩过,当时也是YOLOv5转ONNX后置信度掉了一截。你先别急着怀疑量化,因为如果你没主动做PTQ或QAT,PyTorch导出ONNX默认是FP32,量化这锅它不背。真正要查的是预处理和后处理有没有对齐:PyTorch里letterbox的padding值、归一化顺序、BGR还是RGB、NMS阈值这些,onnxruntime里如果自己重写了一遍,很容易出现微小偏差,累积到置信度上就明显了。另外YOLOv5的Focus层或者某些SiLU激活在旧opset里可能被拆成不同算子组合,数值精度会有细微差异,但通常不至于让目标直接漏检。建议你把同一个输入分别喂给原模型和ONNX,逐层对比中间feature map,先定位是从哪一层开始偏的。如果偏得早,那多半是算子映射问题;如果偏在后处理附近,那就是解码逻辑没对齐。还有dynamic_axes本身不影响数值,它只改shape推导,别在这上面浪费时间。