最近在搞一个分割模型的部署,PyTorch 1.13转ONNX(opset 11)再转TensorRT 8.5,fp16推理。输入是1024x1024的遥感影像,模型结构是UNet++加SE注意力。转完以后发现Dice系数从0.93掉到0.87,尤其小目标(比如道路细线)几乎全断。试过直接TensorRT的onnx-parser和polygraphy,也试过设置dynamic shape和固定shape,还是掉。最诡异的是,如果用opset 12转出来的ONNX,TensorRT能跑但会报一个“Assertion failed: (nbDims == 4)”的错。有没有老哥遇到过类似情况?是精度校准选错了还是某些算子(比如GroupNorm)在TRT里精度有问题?求指点,实在不想逐层排错,太痛苦了。
PyTorch转ONNX再转TensorRT,精度掉得离谱,有人遇到过吗?
全部回复
共 24 条FP16掉点其实挺常见的,尤其你这个模型里带SE注意力,sigmoid和后面的scale在fp16下动态范围容易出问题,小目标断裂基本就是这个原因。你可以先别急着怀疑转换链路,把TensorRT里的fp16关掉跑一遍fp32,如果Dice能回到0.92以上,那问题就锁定在精度上了。ONNX opset 11到12那个assertion,大概率是某个op在12里shape inference行为变了,导致parser拿到的维度信息对不上,可以拿polygraphy的surgeon去dump一下每层输出对比。另外UNet++的密集跳连加上1024输入,中间feature map数值分布差异很大,TensorRT默认的校准和kernel选择不一定照顾到小目标那部分。建议试试在TRT里对分割头前面那几层强制fp32,或者用explicit quantization加min/max校准,比盲目调opset靠谱。
试试关掉fp16,小目标断裂多半是精度截断导致的,先验证下是不是这个锅。
我前段时间搞遥感分割也踩过几乎一样的坑,Dice从0.92掉到0.85,细长目标断得跟虚线似的。fp16大概率是元凶,UNet++里SE那层做全局池化再sigmoid,值域压得很小,fp16尾数位不够直接把小响应抹掉了,你试试把SE和最后的sigmoid输出层强制keep fp32,其他层fp16,通常能救回一大半。opset 11转出来掉得比12狠,是因为11对某些插值和reshape的算子导出会插额外的Cast或Transpose,TensorRT解析时容易走错builder路径,那个nbDims==4的assert基本就是某个动态shape没锁死,polygraphy里加--trt-min-shapes把四维全固定死再看。还有个隐蔽点,PyTorch的interpolate在转ONNX时如果align_corners没显式写,默认行为和TensorRT的resize对不齐,边缘几个像素的偏移对细线就是灭顶之灾。建议你拿polygraphy逐层比对ONNX和TRT的余弦相似度,掉得最狠的那层就是真凶,别一上来就怀疑整个模型。
这个精度掉得有点狠,尤其小目标断裂,大概率不是单纯fp16的锅。UNet++加SE注意力里有些算子对fp16特别敏感,比如sigmoid前的累加、softmax附近的数值范围,TensorRT为了速度会做一些融合和截断,细线那点响应很容易被抹掉。opset 12报nbDims==4那个断言我也见过,通常是某个op的权重或输入被当成3维处理了,可能跟SE模块里的reshape或global pooling有关,你最好用polygraphy逐层比对ONNX和TRT的输出,看哪一层开始炸。固定shape和dynamic shape都试过还掉,那基本排除shape推导问题,更像是精度策略和层融合的锅。可以试着对SE那块或者上采样前的卷积强制走fp32,或者用trtexec加上--precisionConstraints来定位。另外Dice从0.93到0.87,如果验证集里小目标占比不高,这个跌幅其实已经说明主干特征图被破坏得挺严重了。