最近在部署一个分割模型(DeepLabV3+,backbone是ResNet101),PyTorch 1.12直接转TensorRT(8.5.3)精度基本没问题,但中间加一步ONNX(opset 12)再转TRT,mIoU直接从0.78掉到0.61,主要错在边缘细节。我确认过ONNX模型的输出和PyTorch原模型是一致的,所以问题应该出在TRT解析ONNX图的时候。试过FP32、TF32、FP16,FP32也掉。有人说是Resize算子或者BN层折叠的问题?但我不太想换backbone。有没有人遇到过类似情况?最后是怎么定位到具体层或者解决的?感谢!
PyTorch转ONNX再转TensorRT,精度掉得离谱,有人遇到过吗?
全部回复
共 7 条我之前也踩过一模一样的坑,PyTorch直接转TRT没问题,中间过一道ONNX就崩,而且崩得毫无规律。后来我把ONNX的opset从12提到13或者更高,Resize算子的坐标变换模式在TRT解析时可能选错了实现,尤其是DeepLabV3+里那个双线性插值的上采样,对边缘影响特别大。你可以在转TRT前用ONNX Runtime跑一下那个ONNX模型,看看输出是不是真的和PyTorch一致,如果一致,那就锁定是TRT的层融合或算子映射问题。我建议你用trtexec的--dumpProfile或者--layerInfo看看每一层的输出,把TRT模型里所有Resize和Conv的权重导出来对比,重点检查BN层是不是被错误折叠了,因为有些时候TRT会把BN的epsilon参数搞丢。还有一招,你试试在ONNX里显式把Resize的mode改成nearest或者改成三线性插值做对照,如果精度恢复,那就是插值实现的差异。另外,既然FP32也掉,大概率不是精度模式的问题,你可以试一下onnx-simplifier过一遍,去掉一些冗余的Shape和Gather节点,有时候TRT对动态shape的图特别敏感。最后实在不行就绕开ONNX,直接用PyTorch导出成TorchScript再转TRT,虽然麻烦点,但至少不会死磕在这个解析问题上。
试试onnx转trt时把opset拉到13以上,之前我遇到过resize对齐方式不一样导致边缘崩的情况。
也可能是TRT对某些算子的实现用了低精度近似,先开强一致性模式跑一遍排除下。
这个坑我踩过,DeepLab系列转TRT后边缘崩掉大概率是Resize插值方式的问题。ONNX默认导出的Resize可能是nearest或者half_pixel对齐方式和原PyTorch不一致,TRT解析时又做了自己的处理,小目标分割上特别明显。你可以用polygraphy或者trtexec逐层dump对比一下ONNX和TRT中间输出,重点看ASPP后面那几个上采样层。我当时的解法是手动把Resize换成固定scale的Upsample并锁死坐标变换模式,mIoU基本就回来了。
边缘细节掉这么厉害,大概率是Resize插值方式在TRT里被改了。PyTorch转ONNX时如果用的是opset 12,Resize节点默认可能写成nearest或者half_pixel语义,但TRT早期版本对coordinate_transformation_mode支持很差,经常直接当asymmetric处理,分割模型边缘一塌糊涂就很合理。你可以先把ONNX里所有Resize的mode和coordinate_transformation_mode打出来,跟TRT解析后的层对比一下。另一个常见坑是BN折叠后卷积权重精度变化,尤其ResNet101这种深backbone,FP32下误差累积到后面也不小,但一般不会掉17个点这么夸张。建议用polygraphy或者trtexec逐层dump,把TRT输出和ONNX Runtime输出做cosine相似度,定位到第一个差异超过阈值的层。如果真是Resize,可以试着在导出ONNX前把插值换成固定尺寸的bilinear,或者用自定义插件替代。实在不行就保留PyTorch直转TRT那条路,没必要非绕ONNX。
你这个现象还挺典型的,ONNX那步输出对得上不代表TRT解析后图结构没变。我之前搞OCR分割也遇到过,最后发现是Resize在TRT里被降级成最近邻了,边缘直接糊掉。建议你拿polygraphy或者trtexec把每层输出dump出来,跟ONNX Runtime逐层对比,重点看Resize和插值那块。另外BN折叠在FP32下一般不会这么夸张,先排除算子映射问题吧。
这个坑我去年踩过,当时也是分割模型,ONNX那边输出对得严丝合缝,一进TRT就崩,最后发现是Resize算子在搞鬼。PyTorch转ONNX的时候如果Resize的mode是nearest,ONNX里可能会被拆成一堆Cast+Floor+Resize的组合,TRT解析时对坐标变换的rounding处理跟PyTorch不一致,边缘就糊了。你可以先把ONNX里Resize节点单独拎出来,用polygraphy或者onnxruntime跑一遍中间层输出,跟TRT同层对比,基本能定位到是哪几个节点精度对不上。BN折叠一般不会导致这么大的掉点,除非你的模型里有GN跟BN混着用,TRT对GN的折叠有时候会出问题。实在不行试试在转ONNX时把opset降到11,或者手动把Resize替换成TRT自带的插件,DeepLabV3+的ASPP里也有几个对精度敏感的算子,可以顺带查查。
我之前也踩过类似的坑,DeepLabV3+转到TRT之后边缘崩得厉害,最后发现是Resize算子的问题。ONNX里插值模式如果是half_pixel,TRT默认可能按asymmetric或者align_corners处理,双线性插值一偏边缘就糊了。你可以导出ONNX后拿netron看一下Resize节点的coordinate_transformation_mode,试着在转ONNX时固定成align_corners或者显式指定half_pixel,再对比TRT输出。另外BN折叠一般不会单独导致边缘掉这么多,更像是插值和后续concat的shape对齐出了偏差。