最近在把一个语义分割模型(DeepLabV3+,backbone是ResNet50)从PyTorch转到TensorRT部署。流程是pt→onnx→trt,用trtexec转的FP16。本地测试单张图还行,但一跑完整验证集,mIoU直接掉了4个点,而且发现掉精度主要集中在暗部区域和小目标上。
PyTorch模型部署到生产环境,ONNX转TensorRT精度掉得离谱,求排查思路
全部回复
共 69 条FP16下BatchNorm和Resize的精度损失在暗部和小目标上会被放大,建议先转FP32验证流程,再对敏感层单独开FP16试试。
FP16对暗部和小目标确实容易炸,试试给敏感层保留FP32,或者看下onnx里就有没有精度损失。
碰到这种暗部区域掉点的情况,我第一反应是FP16的表示范围问题,ResNet50里的BN层在低光照下激活值分布会很集中,FP16的尾数精度不够容易把那些小梯度直接抹掉。你试试在ONNX里把BN层折叠掉再转,有时候能救回一点,不过根治还得靠校准。另外你说trtexec直接转,它默认用的校准数据集是随机抽的,跟你验证集的分布大概率对不上,建议自己写个校准器,专门采样暗部和小目标的图,让scale因子更贴合实际分布。我之前转过一个检测模型也这样,后来发现是Resize算子里的align_corners在TRT实现跟PyTorch不一致,暗部像素插值误差被放大了,你查查ONNX里有没有这个参数。还有个思路是混合精度,把前几层或者shortcut分支强制保留FP32,TRT支持per-layer精度设置的,代价是推理慢个10%左右,但mIoU基本能回来。最后提醒下,转完TRT别急着上生产,拿几十张典型坏case对比每层输出,看是哪个节点开始分叉的,比盲目调参快得多。
我之前也遇到过类似情况,后来发现是onnx里某些算子在转trt时被融合得过于激进,尤其是resize和bn层,建议先用onnx-simplifier过一遍再转。暗部和小目标掉点很可能是fp16的动态范围问题,试试给tensorrt加个calibration,或者对输入图像做分块推理,只让关键区域走fp16。还有个笨办法,拿同一张图把pt和trt的每层输出dump出来对比,看到底是哪个block开始漂移,我之前就是这么定位到deeplab的aspp模块上。
我之前也踩过类似的坑,建议先别急着怀疑TensorRT本身,把ONNX的中间结果用polygraphy对比一下每层输出,大概率能定位到是哪几个op在FP16下精度崩了。暗部和小目标掉点很可能是LayerNorm或Resize这类算子在低精度下的累积误差,试试给这些层单独设FP32精度,或者换成INT8加calibration,有时候反而比FP16稳。另外确认下onnx导出时opset版本和动态shape的设置,这两个地方也容易埋雷。
我之前也踩过类似的坑,暗部区域精度崩大概率是FP16的动态范围问题,可以先试试给TensorRT的每一层单独设动态范围,或者干脆对敏感层强制走FP32。另外小目标掉点建议检查一下ONNX导出的opset版本,有些算子在高版本下会被错误折叠,导致空间细节丢失。还有个笨办法,对比一下ONNX和PyTorch的输出,如果ONNX就已经有偏差,那就先修导出环节,别急着折腾TensorRT。你用的是trtexec默认的校准集吗?那个校准集如果和验证集分布差太多,也可能让量化scale跑偏。
FP16对暗部和小目标确实容易翻车,先试试只转backbone、head保持FP32,或者校准集里多塞点暗光样本。
FP16对暗部和小目标确实容易翻车,试试只对卷积层开FP16,敏感层保持FP32。
FP16掉点太正常了,尤其暗部和小目标,量化误差直接被放大。你先用trtexec加--fp16 --strict-types跑一遍,再单独dump中间层输出跟ONNX对比,看是哪一层开始偏的。另外ONNX导出时opset版本和dynamic shape设置也有坑,建议先固定shape验证。实在不行试试混合精度,把敏感层留FP32,一般能拉回来不少。