最近在把训练好的YOLOv5s模型转成ONNX,然后部署到手机端。转换过程没报错,但用onnxruntime推理时,输出的检测框置信度整体偏低,有些原本能检出来的目标直接漏掉了。对比了opset版本(用的12),也试过动态/静态batch,问题依旧。我怀疑是不是某些算子(比如Focus、SiLU)在转换时被替换成了近似实现?或者转换时精度损失是正常的?有没有人遇到过类似情况,一般怎么排查是哪个环节出了问题?另外,如果量化到INT8,是不是误差会更大?先谢谢各位了。
PyTorch模型转ONNX后推理结果和原模型对不上,是量化问题还是算子不支持?
全部回复
共 51 条Focus和SiLU这两个算子确实容易出问题,尤其是Focus里那几步slice和concat,有些版本导出时会拆得跟原逻辑不太一样,Silu在opset 12里虽然能映射到Sigmoid+Mul,但中间精度处理未必完全一致。我之前也碰到过类似情况,置信度整体掉了一截,后来发现是预处理里letterbox的padding值或者归一化方式跟原实现有细微差别,建议先拿同一张图把onnx和pytorch的输入张量dump出来对比一下,确认输入完全一致再往下查。排查的话可以逐层对比中间输出,onnxruntime有工具能跑per-node的output,定位到第一个偏差变大的节点,基本就能锁定是哪个算子的问题。另外opset 12对YOLOv5来说不算最优,可以试试升到16或17,有些算子融合策略会好很多。INT8量化误差肯定会更大,特别是检测头那部分,建议先用FP16跑通再考虑INT8,而且量化校准集要覆盖真实场景的分布,不然掉点会很夸张。
先确认下你对比的方式,onnxruntime跑的时候预处理是不是和原版完全一致?很多人栽在归一化或者letterbox的padding值上。Focus层转过去一般会被拆成slice+concat,这个通常没问题,但SiLU在某些老版本opset里确实可能被近似成sigmoid*x,建议把opset拉到13以上再试。排查的话可以逐层dump中间输出跟PyTorch对,看是从哪一层开始数值飘的。INT8量化误差肯定会更大,尤其检测头那块,最好用带校准集的QAT而不是直接PTQ。
先别急着怀疑量化,用onnxruntime和PyTorch同一张图跑一遍中间层输出,看是从哪层开始偏的,Focus和SiLU确实容易出问题。
先别急着怀疑量化,量化误差一般不会让置信度整体掉这么明显。建议你逐层对比ONNX和原模型中间输出,大概率是Focus或SiLU这类算子在转换时被拆成了近似结构,累积下来就偏了。我之前转YOLO也踩过这坑,把opset升到13以上、SiLU用原生支持会好很多。INT8量化确实会再放大误差,但前提是你得先把FP32的ONNX对齐,不然根本分不清是谁的锅。
Focus和SiLU这两个确实容易出问题,特别是SiLU在某些opset下会被拆成Sigmoid+Mul,数值上会有细微差异,YOLOv5里累积起来就明显了。你可以先试试用onnxruntime和PyTorch分别跑同一张图,把中间层输出dump出来逐层对比,很快能定位到是哪一层开始偏的。另外INT8量化误差肯定更大,尤其检测头那块对精度敏感,建议先解决FP32对齐问题再考虑量化。
先别急着量化,用onnxruntime逐层比对输出,多半是SiLU或Focus转换时精度掉了。
Focus和SiLU确实容易出问题,尤其SiLU在早期opset里可能被拆成多个算子组合,数值上会有细微偏差。我之前也遇到过类似情况,最后发现是预处理里的归一化参数没对齐,建议先用同一张图对比PyTorch和onnxruntime每层输出,定位到具体哪一层开始飘。INT8量化误差肯定更大,尤其检测头部分对置信度很敏感,最好先保证FP32下结果一致再考虑量化。
精度对不上多半不是量化背锅,因为你还没上INT8呢,先别急着怀疑量化。Focus层在转ONNX时经常被拆成Slice+Concat,如果opset或者导出脚本版本不匹配,确实可能出现数值偏差,但一般不会导致大面积掉点。更常见的坑是预处理没对齐,比如letterbox的padding值、归一化顺序、RGB/BGR,这些在PyTorch里你可能写死在某处,导出后onnxruntime那端没复现。建议先喂同一张固定输入图,把PyTorch中间层输出dump出来,和onnxruntime逐层对比,定位到第一个误差突然变大的节点。SiLU本身是标准算子,opset 12应该能直接映射,但如果你用了自定义的Hardswish或FReLU就难说了。INT8量化误差肯定更大,尤其YOLO这种对定位敏感的,建议先跑FP16看看,如果FP16都掉点,那问题就在导出而不是量化。
先别急着怀疑量化,FP32转ONNX本身就有精度损失,重点查下Focus层是不是被拆成了Slice+Concat,这块最容易出问题。
我之前也踩过这个坑,大概率不是量化问题,因为你是FP32转的。重点查一下Focus层,它那个切片操作在ONNX里经常被拆成Slice+Concat,有时顺序会出问题。建议用onnxruntime的per-node输出跟PyTorch逐层对比,定位到具体哪一层开始偏差变大。Silu倒还好,opset12里一般能正确映射成Sigmoid+Mul。至于INT8,误差肯定会放大,尤其检测头那块对置信度挺敏感的,最好先搞定FP32对齐再考虑量化。
先别急着怀疑量化,导出时把opset提到13以上试试,Focus和SiLU经常是版本坑。