最近在部署一个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一直报错,求大佬指点
全部回复
共 118 条试试在转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吗,它能打印出具体是哪一层报的错,比直接跑推理好用很多。
试试把onnx的opset调到17以上,然后trt那边min/opt/max三个维度必须严格对应实际输入范围,我之前就是这么解决的。
我之前也卡在这块好久,后来发现多半是ONNX导出时dynamic_axes的命名跟TensorRT里--minShapes那些参数没对齐,建议你把导出的ONNX用网上的可视化工具打开,确认下输入层的名字和维度顺序。另外opset版本别用太新的,11或者12更稳,8.6的TensorRT对某些高版本opset支持有坑。还有个土办法,实在不行就固定一个常用分辨率先跑通,再慢慢调dynamic,至少能排除是不是模型本身的问题。
这问题我上个月也踩过,先别急着怀疑opset。你检查下ONNX导出的dynamic_axes是不是把batch和hw都设成-1了,YOLOv8-seg的mask分支有时会漏掉某个输出的动态维度设置,导致TensorRT优化时把某个维度锁死成固定值。另外8.6对动态shape的优化器选择有坑,建议显式加上--optimizationLevel=1试试,还有Jetson上记得用jetpack自带的TensorRT版本,别混装pip的。我之前是发现min/max范围设太窄,实际推理分辨率超出范围就崩,把min改成输入的一半再试试。
之前搞yolov5的时候也踩过这坑,八成不是opset的问题,是ONNX里dynamic_axes的name和TensorRT那边--minShapes的键没对上,比如batch和height/width的轴名不一致。还有注意下TensorRT 8.6对某些reshape或者gather操作在动态shape下支持很迷,建议先用固定shape把流程跑通再开dynamic,或者试试用onnxsim先简化下模型,YOLO-seg的输出层动态分支特别容易触发这个问题。
遇到过一模一样的情况,最后发现坑不在TensorRT的dynamicShape配置,而是ONNX导出的问题。你用的ultralytics官方脚本,它导出的dynamic_axes其实只对输入尺寸的H和W生效,但yolov8-seg的输出层里那个mask分支的维度是跟输入分辨率强绑定的,ONNX里如果不把中间某些reshape或concat的维度也标成dynamic,转TRT时就会把某些层固定成你导出时的尺寸。我建议你先用onnxsimplifier把模型过一遍,然后自己写个脚本检查一下输入输出节点的维度,特别是seg头的输出,看是不是有个维度写死了。
另外8.6版本对dynamic shape的支持其实有点迷,我后来换成了8.5.2反而稳了,你可以试试降版本。还有个细节是TRT的--minShapes和--maxShapes里给的H和W必须是32的倍数,而且min和max的宽高比最好跟实际推理保持一致,不然会触发重新优化然后崩。如果你只是测试不同分辨率,建议先把优化profile设成固定的几个档位,比如640、960、1280,别用特别宽的range,跑通之后再慢慢放宽。opset我试过11到17,感觉不是主因,但建议统一用16,因为ultralytics新版默认就是16。最后想问下,你日志里有没有提示是哪个具体layer报的错?我之前卡在了一个叫GridSample的节点上,如果是那个的话得手动改一下ONNX图结构。
大概率是ONNX里Resize的roi跟TRT dynamic shape八字不合,试下固定opset=17或导出时把batch也锁死。
遇到过一模一样的坑,最后发现是ONNX导出时dynamic_axes的axes没写全,光设了batch和height,width漏了或者顺序不对,TensorRT那边解析出来就乱套。你可以先不转TensorRT,直接用onnxruntime跑动态输入试试,能过再排查TRT这边。另外8.6版本对动态shape的优化有点迷,建议把min/opt/max的shape调成实际部署会用的范围,别拍脑袋写个1和9999,有些层会炸。我之前是换成固定batch但宽高动态才好的,如果你业务允许可以试试这个折中方案。
YOLOv8-seg的dynamic导出坑挺多,建议查下onnx里mask分支的shape是不是被写死了,TRT对这块很敏感。
YOLOv8-seg的dynamic shape确实挺容易踩坑,我之前也折腾过一阵。建议先检查ONNX导出时的min/opt/max三个shape有没有对齐,TensorRT建engine时用的profile必须跟实际推理输入落在同一个区间里,不然就会报你那个不兼容。另外ultralytics默认导出的opset有时候偏旧,可以试试手动指定opset=17再转,还有TensorRT 8.6对seg模型里的一些resize算子支持不太稳,必要时得用trtexec加--verbose看具体卡在哪一层。
YOLOv8-seg导出这块确实挺折腾的,你遇到的问题大概率不在TensorRT本身,而是ONNX那一步的dynamic axes没覆盖全。ultralytics官方脚本导出时默认只给输入开了dynamic,但seg模型多了一个原型mask分支,里面有些reshape或者interpolate的尺寸是写死的,转TRT之后这些节点仍然绑着固定shape,所以你一换分辨率就直接炸。建议你先把onnx用onnxsim跑一遍,再用polygraphy或者trtexec的--verbose看看是哪一层shape对不上,日志里通常会直接告诉你是哪个节点不兼容。另外TRT 8.6对min/opt/max这三个profile很敏感,opt shape最好设成你实际最常用的分辨率,max别随手写个特别大的值,不然显存和kernel选择都会出问题。还有个容易忽略的点是opset,YOLOv8一般用17没问题,但如果你手动改过导出代码就可能掉到16以下,某些动态op的支持会退化。实在不行可以试试直接用ultralytics自带的export engine=engine,它内部已经帮你处理了一部分dynamic的坑,比自己手撸ONNX稳一些。