最近在部署一个分割模型,训练时 mIoU 有 0.78,用 torch.onnx.export 转出来之后,用 onnxruntime 推理,mIoU 直接掉到 0.6 左右。一开始以为是动态 shape 的问题,固定了输入尺寸也不行。试过 opset_version 从 11 换到 17,也试过关闭一些优化 pass,结果还是差很多。更奇怪的是,单张图可视化发现,输出的 mask 大块区域是对的,但边缘细节全糊了,感觉像某些层被近似了。有没有大佬遇到过类似情况?是某些算子在 ONNX 里精度本来就有损失,还是我导出时的参数设置有问题?比如那个“keep_initializers_as_inputs”要不要设成 False?或者需要自己写个校准脚本来做量化感知训练?有点迷茫,求指点。
PyTorch 转 ONNX 后精度掉得离谱,是量化问题还是算子不支持?
全部回复
共 107 条遇到过,八成是ONNX某些算子精度实现跟PyTorch不一致,尤其是上采样和插值类,边缘糊很正常,建议先转成fp32并关掉所有优化试试。
你这情况更像导出时某些层被重写或融合了,建议单独对比一下中间特征图差异,定位到具体哪一层开始飘的。
大概率是某些算子在ONNX转换时被融合或近似了,试试把opset降到13以下,再关掉onnxruntime的graph optimization。
我之前遇到过类似情况,最后发现是导出时把upsample的mode设成nearest了,检查下你的插值参数。
边缘细节糊掉这个现象挺关键的,一般全图结构对但边界崩,多半是上采样或者插值那块在ONNX里行为不一致。你可以对比下PyTorch和onnxruntime中间层的输出,重点看双线性插值、align_corners这些参数有没有对齐。另外分割模型里常用的grid_sample在旧opset下支持很烂,换成opset16以上试试。keep_initializers_as_inputs这参数一般不影响精度,倒是有没有开constant folding值得查一下。
边缘细节糊掉这个现象挺关键的,一般不是量化问题,而是某些op在ONNX里被替换成了近似实现,比如grid_sample、interpolate或者自定义的归一化层,这些在导出时经常悄悄变味。你可以用onnxruntime的逐层输出对比一下,把PyTorch和ORT中间feature map拉出来算余弦相似度,很快能定位到是哪一层开始崩的。另外opset 11到17跨度太大,有些算子行为变了反而更糟,建议固定一个版本单独调。keep_initializers_as_inputs那个参数一般不影响精度,除非你后面还要改图。
边缘糊掉这个现象挺典型的,我之前跑分割也踩过,大概率不是量化问题,而是某些算子在 ONNX 里走了近似实现,比如 interpolate、grid_sample 这类。你可以先用 onnxruntime 逐层对比输出,把 torch 和 onnx 的中间特征拉出来比一下,掉点最狠的那层基本就是元凶。另外 keep_initializers_as_inputs 那个参数影响的是权重加载方式,一般不至于让 mIoU 掉这么多,先排除掉它。
边缘糊掉这个现象挺典型的,不一定是量化问题,因为默认导出不会自动量化。你可以先对比一下 PyTorch 和 ONNX 在同样输入下的中间层输出,重点看 BN 和插值相关的算子,比如 resize 的 coordinate_transformation_mode 设置不对就会让边缘明显变糊。另外分割头里如果有双线性插值或者 grid_sample,ONNX 的实现和 PyTorch 对不齐很常见,opset 高了也不一定修得好。建议拿单张图做逐层对齐,定位到具体是哪个节点开始偏的,比盲目调参数快得多。
边缘糊但大块区域对,这个现象基本可以排除量化问题——量化通常整张图都会有噪声,不会只糊边界。重点查一下上采样和插值算子,PyTorch 的 bilinear/nearest 在转 ONNX 时对齐方式经常有细微差异,还有 pad 的 mode 也可能被改。另外可以逐层对比 PyTorch 和 onnxruntime 的中间输出,定位到具体哪一层开始跑偏,比盲目调 opset 高效得多。