最近在部署一个分割模型(DeepLabV3+,backbone是ResNet50),PyTorch里跑测试集mIOU有0.78,转成ONNX后拿onnxruntime验证精度几乎没掉,但用trtexec转成TensorRT FP16后,输出mask明显有噪点,边界也不对。我试过调整dynamic shape范围和batch size,也关了FP16用FP32,问题还是存在。有人说可能是某些算子在TRT里实现有差异,也有人让我检查一下预处理里的归一化是不是被融合掉了。有没有大佬踩过类似的坑?是不是必须得用onnx-simplifier或者手动改网络结构才行?求个排查思路,谢谢!
PyTorch转ONNX再转TensorRT,为啥推理结果和原模型对不上?
全部回复
共 5 条大概率是BN折叠和算子的显式对齐问题,先试下onnx-simplifier再逐层对比输出定位。另外查下预处理归一化是否被TRT强制融合了。
大概率是预处理被TRT优化时改了数值分布,试试把归一化层留在模型外面或用INT8校准下。
我之前也遇到过类似的,分割模型对数值敏感,TRT里头FP16的精度损失经常不是全局的,而是集中在某些层,特别是BN和反卷积附近。建议你先用trtexec导出每层输出对比下,定位到具体哪个节点开始漂移,别急着改网络结构。预处理归一化确实容易被融合掉,你检查下TRT的层里是不是多了一个不合理的scale操作,手动把输入改成归一化后的数据再试。另外onnx-simplifier能去掉些冗余转换节点,但解决不了算子实现差异,关键还是得看是不是某个特定算子(比如Resize的坐标变换模式)在TRT里有bug。
(换个角度补充)我猜你可能漏了TensorRT的算法选择问题,它默认会用一些激进优化,像卷积的winograd或者tactic搜索,对精度影响不小。试试在构建engine时设置builder_optimization_level为1,或者直接对输出mask做方差统计,看看是不是恒定偏移——如果是,那就是某个层的量化scale算错了。我之前用DeepLabV3+也出过类似噪点,后来发现是aspp里的空洞卷积被TRT重排了顺序,手动加个identity层就稳定了。你可以先跑个单张图,把中间特征dump出来跟onnxruntime对,差个1e-2以内基本就正常,别信trtexec的默认验证。
我之前做检测模型转TRT也碰到过类似情况,ONNX Runtime精度没问题但TRT就是输出怪。后来查了半天发现是BatchNorm层在TRT里被折叠的方式跟PyTorch不完全一致,数值上会有微小偏差,但分割任务对边界特别敏感,这点偏差就被放大了。你试试用polygraphy对比一下中间层的输出,定位到具体是哪一层开始差的,别上来就改网络结构。另外你说FP32也有问题,那大概率不是精度模式的事,更像是算子映射或者图优化导致的语义变化,比如Resize的坐标变换模式,TRT默认可能跟PyTorch的align_corners行为不同,这个坑特别隐蔽。预处理归一化被融合这事确实存在,但onnxruntime没掉精度说明ONNX本身没问题,问题更可能出在TRT的优化上。我建议你先固定输入尺寸,排除dynamic shape的干扰,再用onnx-simplifier过一遍,同时把TRT的layer可视化出来检查有没有奇怪的算子替换。如果还不行,可以试试把backbone里某些敏感操作(比如上采样)拆开写,强制TRT用特定实现。别急着手动改整个网络,先做逐层对比排查,通常能找到根因。
FP16下分割模型边界出噪点挺常见的,尤其是DeepLabV3+里的空洞卷积和上采样,TRT对某些padding方式处理跟PyTorch不完全一致。你可以先用polygraphy逐层比对ONNX和TRT的中间输出,定位到具体是哪个节点开始飘的,比盲目调shape有用。另外预处理那块也要确认下,TRT不会自动帮你做mean/std归一化,如果之前是靠ONNX图里的Sub/Div,转TRT时容易被优化掉。实在不行试试trtexec加--precisionConstraints=obey,强制关键层走FP32。