最近在部署一个YOLOv8-seg模型到Jetson Orin上,用PyTorch转ONNX的时候设了dynamic_axes,转TensorRT时也加了--dynamicShapes,但推理时只要输入分辨率不是固定尺寸就报 “The provided input shape is not compatible with the engine” 或者有时直接崩。我看网上教程都说设了dynamic就行,但实际跑起来各种坑。我用的TensorRT是8.6,ONNX是从ultralytics官方脚本导出的。有没有人遇到过类似问题?是ONNX导出时opset版本的问题,还是TensorRT这边min/opt/max的profile设置不对?另外,分割头的输出形状变化是不是也要单独处理?求有实际部署经验的大佬指点一下,卡了两天了,谢谢!
PyTorch转ONNX再转TensorRT,Dynamic Shape一直报错,求大佬指点
全部回复
共 108 条试试在转ONNX时把opset设成17,然后TensorRT的min/max别设太小,我之前卡了一周就是这么解决的。
之前也踩过这坑,多半是onnx里的动态轴没对齐,导出时加dynamic_axes后检查下每层输入输出shape,别光靠官方脚本。
我之前也被dynamic shape折磨过,最后发现是ONNX导出时把固定shape的op也带进去了,比如某些reshape或resize,转TRT时得用onnx-simplifier先清理一遍。另外8.6的dynamic shape对min/max的显存占用很敏感,你试试把opt shape设成实际部署最常见的分辨率,别跟max差太大。还有,YOLOv8-seg的mask分支输出shape是跟输入分辨率绑定的,建议把后处理拆出来,只让detect部分走dynamic。你确认下报错前有没有用trtexec单独测过生成的engine?有时候是profile没设对。
之前搞YOLOv8检测模型的时候也踩过这个坑,折腾了快一周才明白问题不在TensorRT的dynamicShapes参数上。我这边最后定位到是ONNX导出时dynamic_axes只给input和output的2、3维加了动态,但YOLOv8-seg的mask输出有个固定维度和输入分辨率挂钩,导致TensorRT优化时把这个维度也锁死了。你试试在导出时把opset调到17以上,然后手动检查一下ONNX里所有节点的输出维度是不是都标了动态,尤其是后处理那部分。另外TensorRT 8.6的dynamic shape对shape范围特别敏感,min/shape/opt三个值不能随便设,我建议min用320x320,opt和max用训练时的尺寸,差值太大会触发cudnn的启发式搜索失败直接崩。还有个偏方,先转一个固定shape的engine跑通,再对比动态导出时的网络结构差异,这样能快速定位是不是某个plugin不支持动态。你那边报错信息里有没有具体的assertion提示?比如是维度不匹配还是显存分配问题,这两个的解决方向完全不一样。
我之前搞YOLOv8检测也踩过这个坑,最后发现是ONNX导出时dynamic_axes的key没跟TensorRT的profile完全对应上,尤其是batch和height/width的命名要一致。另外TensorRT 8.6对opset 17以上的支持有点迷,建议先固定opset=16试试。还有个坑是min/max/opt的shape不能随便设,最好拿你实际要跑的分辨率范围去测,比如最小640x640,最大1920x1080,opt选个中间值,不然引擎内部优化会出问题。崩的话大概率是显存没对齐,把workspace调小点看看。
我上周刚踩完这坑,TensorRT 8.6对dynamic shape的兼容性确实有点迷。你先把ONNX的opset固定到17试试,我之前用ultralytics默认的18导出来,转TRT时profile设置没问题但推理就崩,降到17反而好了。另外排查下你导出时是不是只对输入设了dynamic,但输出层(比如segmentation的mask输出)shape也跟着变了,TRT这边要求所有dynamic tensor都得在优化profile里明确指定范围,缺一个就会报不兼容。还有个小细节,Jetson上跑的话,显存分配策略和PC端不一样,建议把工作空间大小调低点,用--workspace=1024试试。如果还不行,直接把min/opt/max三个维度设成完全相同的值,先跑通再慢慢放宽,别信网上那些说动态尺寸随便设的教程。
我之前也卡在这玩意儿上好久,后来发现多半不是opset的锅,而是你TensorRT这边min/mid/max的shape范围跟ONNX dynamic_axes没对齐。你推理时传入的尺寸如果不在你构建engine时指定的范围内,它照样报错,哪怕你设置了dynamicShapes。建议你先用trtexec把engine导出来,加--minShapes、--optShapes、--maxShapes都显式写一遍,别依赖脚本里的默认值。另外YOLOv8-seg的导出脚本里,dynamic_axes有时候只对图像输入生效,但mask分支的输出维度没跟着设动态,这也会导致TensorRT解析时直接崩。你可以先固定batch和高度宽度,只让通道数动态试试,看能不能跑通,再一步步扩大范围。还有个小坑,Jetson上用的是JetPack自带的TensorRT,版本跟PC上可能不完全一致,最好用板上配套的onnx-tensorrt解析器,别用PC上最新版,兼容性会好一些。如果还不行,可以考虑在ONNX里把Resize和Concat这类op用onnxsimplifier优化一下,有些op在动态shape下TensorRT支持得不好。
我之前搞yolov5的时候也踩过这坑,八成不是opset的问题,是min/max维度没对齐。你ONNX导出时dynamic_axes里写的是[1,3,H,W],但TensorRT那边--minShapes和--optShapes也得严格对应,尤其H、W要是32的倍数,不然直接崩。还有一招,试试先用固定shape导出ONNX再转,排除是不是dynamic本身在TRT 8.6上的兼容问题。另外,你Jetson上内存够不够?有时候shape太大分配不了也会报这个错。
我之前也被这个折磨过一阵,最后发现多半不是opset的锅,而是TensorRT的profile范围没设对。你--dynamicShapes只是开了开关,但min/opt/max三个维度必须跟实际输入严格匹配,尤其是batch和H、W的取值,比如你opt设了640,但实际推理传了1280,它可能就炸了。另外YOLOv8-seg导出的ONNX里有个nms或者后处理节点可能会把dynamic shape信息搞丢,建议先netron看一眼图结构,确认输入输出的shape是不是都标了dynamic。还有一个坑是TensorRT 8.6对某些op的dynamic支持不完整,比如Resize或者Gather,容易在build时成功但run时崩,你可以试着把opset降到17或者16再导出一次。我自己的经验是,先固定尺寸跑通,再逐渐放宽profile范围,别一上来就搞全动态,排查起来太痛苦。你试过用trtexec单独测试那个engine吗,它能打印出具体是哪一层报的错,比直接跑推理好用很多。