最近在把一个训练好的YOLOv8-seg模型转到TensorRT(FP16)部署,用torch2trt转换挺顺利的,但推理出来的mask精度差得离谱,边界框倒还行。对比了一下中间层输出,从第5个C2f模块开始数值就有偏差了,误差大概在1e-2量级。我已经关了TRT的层融合,也试过用onnx2trt,问题依旧。GPU是3090,CUDA和TensorRT版本都对齐了。查了一圈感觉可能和动态shape或者某些算子的实现有关,但实在没头绪。有没有折腾过类似问题的老哥?求指点一下排查思路,或者推荐一下靠谱的转换工具链,感谢!
PyTorch转TensorRT后推理结果和原模型对不上,有人遇到过吗?
全部回复
共 13 条FP16对分割mask影响确实大,试试强制指定某些层跑FP32,C2f那块最容易出问题。
FP16下mask分支崩但bbox正常,大概率是seg head里某些小数值算子被半精度截断了。可以先把TensorRT的FP16关掉跑一次FP32,看误差是不是直接消失,如果是就锁定精度问题。另外C2f那块有concat和sigmoid,TRT对这类融合处理有时会偷偷改精度,试试用polygraphy逐层dump对比,比盲猜快很多。工具链的话onnxsim加trtexec手动调精度标志比torch2trt可控。
FP16下mask分支精度崩掉挺常见的,C2f里那些小数值的激活很容易被半精度截断,bbox分支数值大所以扛得住。你可以先跑一遍FP32的TRT对比看看,如果FP32能对齐就基本锁定是精度问题,再针对seg head附近的层做混合精度或者干脆保留FP32。另外动态shape确实会让TRT选一些奇怪的kernel,试试固定输入尺寸跑一次排除掉这个变量。工具链的话可以看看TensorRT的polygraphy,逐层对比ONNX和TRT输出挺方便的。
FP16下mask分支比bbox更容易出问题,因为分割头对数值精度更敏感。你试试把整个网络保持FP32跑一遍TRT,如果mask就正常了,那基本能确认是FP16截断导致的,可以只对分割头强制FP32或者用敏感层列表。另外C2f里那些SiLU和concat在TRT里融合后行为可能和PyTorch有细微差别,建议用polygraphy逐层dump对比,比猜算子靠谱。
FP16下C2f这种带残差和concat的结构确实容易出精度问题,尤其YOLOv8-seg的mask分支对数值更敏感。建议先跑一遍FP32的TRT对比,如果FP32也偏那多半是算子映射问题,不是精度损失。可以试试trtexec加--layerPrecisions把C2f相关层强制FP32,或者换TensorRT 8.6以上版本,新版本对SiLU和split的融合处理改了不少。还有polygraphy这个工具挺适合逐层dump对比,比手动查中间层省事多了。
YOLOv8-seg 转 TRT 出这种精度塌方太常见了,尤其是分割头那部分,bbox 能扛住不代表 mask 也能扛住,因为 mask 对特征的微小漂移敏感得多。你说的第5个C2f开始有1e-2量级偏差,这个位置恰好是下采样之后通道数翻倍的地方,FP16 在这里的累积误差确实容易炸。我建议你先别急着换工具链,试试把整个网络强制跑 FP32 看看 mask 能不能对上,如果能对上那就是精度问题而不是算子语义问题。另外 YOLOv8 里那个 split 加 concat 的结构在 TRT 里有时候会被优化成奇怪的 shape,哪怕你关了层融合,parser 也可能偷偷做了重排。还有个坑是 torch2trt 对动态 shape 的支持其实挺拉的,你导出的时候最好固定 batch 和输入尺寸再对比一次。真想省心的话可以试试 TensorRT 自带的 trtexec 配合 polygraphy 做逐层对比,或者干脆用 ultralytics 官方那个 export 到 engine 的路径,他们对 seg 头的处理做过适配。
FP16下C2f这种带残差和SiLU的结构确实容易掉精度,尤其TensorRT对SiLU的FP16实现跟PyTorch不完全一致。建议先跑一遍FP32的TRT看看误差还在不在,如果FP32对得上就基本锁定是精度问题,可以试试对敏感层做FP32混合精度。另外动态shape如果没固定,TRT可能会选不同的kernel,也会引入偏差。torch2trt这项目好久没维护了,换polygraphy或者trtexec直接跑ONNX可能更省心。
FP16下seg分支出问题挺常见的,mask对精度本来就比bbox敏感得多。你试试把seg head那几个卷积层强制保持FP32,用trt的layerwise精度设置单独调一下,C2f那块的误差很可能被后面的上采样放大了。另外dynamic shape如果没固定死,TRT有时会选到很差的kernel,先把输入shape锁死再跑一遍对比看看。
YOLOv8-seg转TRT掉精度这事我也踩过,而且恰好也是分割头出问题、检测头正常,所以你这个现象我觉得挺典型的。第5个C2f开始偏1e-2,这个误差量级其实已经不小了,FP16理论上不该有这么大,我倾向于不是单纯的精度损失,而是某个算子被TRT换了实现。你可以重点看下C2f里的SiLU和Split/Concat那块,尤其是slice操作,TRT对非连续内存的处理有时候会偷偷插个reformat,数值就对不上了。另外你说关了层融合还是一样,那建议把FP16先关掉跑一遍FP32的TRT,如果FP32能对上,基本就锁定是半精度累积误差或者某个算子FP16 kernel的问题。还有个坑是YOLOv8的seg分支里有proto生成和矩阵相乘,动态shape下TRT可能选了不同的tactic,可以试着固定输入尺寸、把optimization profile写死再对比。工具链方面我后来换成了TensorRT自带的trtexec加polygraphy做逐层对比,polygraphy的run和compare真的比手动dump中间层省事太多,强烈建议试一下。如果实在搞不定,可以先用ONNX Runtime跑FP16看看对不对,能帮你把问题范围缩到TRT这一侧。
FP16精度掉点很常见,试试在C2f那块强制走FP32,或者换onnxruntime先验证下onnx输出对不对。
你这情况我太熟了,YOLOv8-seg转TRT翻车基本是家常便饭。误差从第5个C2f开始冒出来,八成是SiLU或者Split+Concat那块的FP16精度溢出,尤其seg分支里上采样和mask原型拼接对数值特别敏感。你可以先把整个网络强制用FP32跑一遍TRT,如果FP32对得上,那问题就锁定在FP16的累加精度上,试试给那几个C2f和检测头单独设成FP32层。另外动态shape确实是个坑,哪怕你推理时用固定尺寸,TRT内部如果按动态配置去优化也会选错kernel,建议导出ONNX时就把batch和hw全固定死。还有个别版本TensorRT对YOLOv8的anchor-free decode处理有bug,换个稍新或稍旧的版本也许就正常了。排查思路我一般是从第一个偏差层往前推,逐层dump出ONNX Runtime的输出来对,比盲猜算子靠谱多了。
我之前也遇到过类似问题,后来换了方案。
我之前也遇到过类似问题,后来换了方案。