最近在部署一个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 条我之前也被这个坑过,YOLOv8-seg加dynamic shape在TensorRT 8.6上确实特别容易出幺蛾子。你那个报错我太熟了,后来发现大概率不是opset的问题,而是ONNX里那几个动态维度的名字跟TensorRT解析时对不上,特别是segmentation分支的mask输出,它那个维度变化跟检测头还不一样,容易让engine以为你传的shape越界。我建议你先别急着改opset,用onnxruntime自己跑一下动态shape的输入,确认ONNX模型本身没问题,再回头查TensorRT那边。另外你--dynamicShapes后面是不是只指定了input?如果输出层也跟着变,得把输出名也写全,而且min/opt/max的数值得留够余量,比如把opt设成你实际部署时最常用的分辨率,别用训练时的尺寸去套。还有个小坑,TensorRT 8.6对某些动态维度需要显式加--preview,你可以试试加个--versionCompatible或者把builder的优化策略改成kPREFER_PRECISION_CONSTRAINTS,有时候能绕过去。我上次是干脆把segmentation的分支单独拆出来转了个固定shape的engine,检测头用动态,推理时再拼一下,虽然麻烦但稳定多了。你要是搞定了也回来说一声,我挺好奇你最后是卡在哪一步。
遇到过一模一样的坑,最后发现是ONNX导出时dynamic_axes的写法有问题,特别是batch和height/width的轴号得严格对应上,建议先打印一下导出的ONNX图确认下输入维度。另外TensorRT 8.6对动态shape的优化确实一般,试试把opset调到17以上,然后min/shape里高度宽度别设太小,比如64的倍数,有时候报错是因为内存对齐的问题。你检查下TRT的workspace设置,会不会是显存不够导致崩了?
大概率是opset版本和TensorRT的dynamic shape范围没对齐,试试把opset固定到17,min/opt/max三个维度都显式设成一样试试。
我之前也卡在这玩意上好久,后来发现大概率不是opset的锅,是min/max那个shape范围没对齐。你ONNX导出时dynamic_axes的维度名和TensorRT那边--minShapes里的参数名必须完全一致,大小写都别差,不然它直接不认。另外8.6对动态shape的优化有点拉胯,建议先用固定shape把流程跑通,再试动态,排查起来会快很多。还有你Jetson上是不是用的默认的GPU内存分配?有时候输入分辨率突变,显存碎片化也会莫名其妙崩,可以试试手动调一下workspace大小。
我之前也卡在这上面很久,最后发现是ONNX导出的opset版本和TensorRT的兼容性问题,建议你试试opset 17或18,别用最新的。另外dynamic shape这块,min/max/opt三个维度必须都得设,而且建议把opt设成你实际部署时最常用的分辨率,不然优化出来的kernel会很怪。还有个小坑,如果输入是3通道RGB,记得在导出时把layout固定成NCHW,有时候默认的NHWC会让TensorRT内部优化直接崩。你要是方便的话,可以把导出ONNX的完整参数贴出来,我帮你看看是不是漏了哪个细节。
这问题我太熟了,之前跑YOLOv8检测的时候也被dynamic shape折磨过好几天。你用的8.6版本其实对动态shape支持已经算不错了,但坑主要在ONNX导出那边,ultralytics官方脚本导出的opset默认好像是17,我之前试过改到16或者15反而更稳,你可以先排除下这个因素。另外TensorRT的--dynamicShapes参数只是告诉它你要用动态,但min/opt/max三个维度一定要跟你实际推理的范围匹配,比如你训练时输入是640,但部署想支持480到960,那opt最好设成实际用的最多的尺寸,别随便填。还有一个非常隐蔽的坑,就是ONNX里如果有一些Resize或者GridSample这类算子,它们的输出shape在动态模式下可能没法正确推导,导致TensorRT优化时直接崩,我之前是手动改ONNX图把某些层固定住才解决的。你报错说shape不兼容,基本就是min/max范围没覆盖到,或者某一层的维度被隐式固定了,建议先用onnxruntime动态跑一遍看看是不是ONNX本身就有问题,再回头查TensorRT的profile设置。
大概率是ONNX里dynamic axes没对齐,试试固定opset为17,然后TRT的min/max别设太激进。
我上个月刚踩完同一个坑,最后发现问题不在dynamic shape本身,而是ONNX导出时把batch维和宽高维都设成dynamic,TensorRT 8.6对这类多动态维度的支持其实有隐藏限制。你试下把batch固定成1,只让宽高动态,大概率能过。另外ultralytics官方脚本导出的onnx里有很多自定义算子(比如grid_sample),TensorRT解析器对它们的支持是按版本走的,8.6处理某些opset 17的节点就会静默失败,建议你用opset 12试一次,虽然老但兼容性反而稳。还有个小坑是min/shape要跟实际推理范围匹配,不能只设个1x3x640x640就完事,最好把你的最小输入比如320和最大输入比如1280都显式写进profile里,否则引擎内部优化会按默认值裁剪。我之前用torch.onnx.export时把dynamic_axes写成了{0:'batch',2:'height',3:'width'},结果转trt时profile的输入名跟onnx里对不上,也是报这种不兼容错误,检查下你的输入名是不是叫images。最后如果还崩,试试把TensorRT的preview features打开,8.6有些动态shape的bug要靠那个flag绕过。
我之前也卡在这坑里好久,后来发现TensorRT的dynamic shape必须显式设置optimization profile里的min/opt/max三个维度,光加--dynamicShapes不指定具体范围它根本不认。你试试用Python API写builder时明确给每个输入设好profile,别只依赖命令行参数。另外opset版本建议固定到17或18,ultralytics默认导出的opset有时候在TRT 8.6上会有兼容问题,尤其是seg模型多了个mask分支,很容易触发维度推断错误。如果还崩,就把输入分辨率先定成几个固定档位(比如640x640、1280x1280)分别转engine,运行时按实际输入选最近的档位resize,比硬刚dynamic省心多了。
我之前也卡在这坑里好久,后来发现TensorRT 8.6对dynamic shape的优化范围要求特别严格,min和opt的宽高比必须和实际输入保持一致,不然它内部会按固定比例分配显存,直接崩。你试试把min设成实际部署时可能用到的最小分辨率,opt设成最常见的那档,max设成最大,三个都要是32的倍数。另外检查下ONNX导出的opset,我换成17之后问题少很多,ultralytics默认的12在TRT里容易出些奇奇怪怪的兼容性问题。
大概率是onnx里dynamic axes和tensorrt的optimization profile没对齐,检查下min/max/opt设的维度是否一致。
我之前也卡在这块儿,后来发现多半是ONNX导出时dynamic_axes的命名和TensorRT的profile没对上,你检查下输入名是不是完全一致,大小写和空格都别忽略。另外8.6的TensorRT对某些opset版本支持有坑,建议把opset固定在17试试,我之前用19就莫名报错。还有个小技巧,先用固定shape生成一个engine跑通,再对比dynamic的profile范围,min和max别设成一样,否则容易触发内部优化bug。最好把报错的完整日志发出来,光看这句提示很难定位是解析层还是执行层的问题。
之前折腾yolov8检测也踩过这个坑,八成不是opset的锅,你检查下ONNX里dynamic_axes是不是真把所有维度都标了,尤其是batch和height/width都得绑上。另外TensorRT 8.6对动态shape的优化器策略很敏感,建议把min/opt/max的宽高都设成32的倍数,比如640/960/1280这样,不然优化器会给你生成一堆奇怪的profile。还有个隐藏坑是ultralytics导出的模型里可能带了非max的nms节点,那个东西在动态shape下经常炸,最好导出时把nms去掉自己写后处理。崩溃的话试试TRT的lean路径,或者换8.5.3版本,我这边8.6反而比老版本更容易崩。
我之前搞yolov5的时候也卡在这过,TensorRT 8.6对dynamic shape的优化其实挺挑的,尤其分割模型输出层有多个分支。建议你先把min/max的数值范围设小一点试试,比如高度宽度都限制在320到640之间,别直接给1到4096这种跨度。另外你检查下ONNX导出的opset是不是11以上,我之前用opset 12转出来的模型在TensorRT里就经常出这种兼容性报错,换成opset 13就好很多。还有个小坑是ultralytics官方脚本导出的模型有些节点是动态的,但TensorRT不认,可以试着手动用onnxsimplifier简化一遍再转。
试试把ONNX的opset升到17以上,之前我卡在动态shape也是这么解决的。
我之前也栽这过,八成是ONNX导出时opset版本和TensorRT的dynamic range没对齐,试试设成17。
很多坑其实在min/max那组参数上,设成1和最大分辨率再跑一次看看。
我之前也踩过这个坑,最后发现是ONNX导出时dynamic_axes的写法跟TensorRT的优化profile没对齐。你用的是ultralytics官方脚本吧?它默认导出的opset是12,但TensorRT 8.6对opset 17+的支持更稳,建议先试试手动把opset提到17,有时候就是版本间解析差异导致shape推断出问题。
另外你说加--dynamicShapes,但TensorRT里真正起作用的是min/opt/max三个profile的设定,尤其opt那个值很关键,如果你只设了min和max,opt用了默认值,而实际输入分辨率跟opt差太多,引擎优化时就会选错kernel,崩就是大概率事件。我之前在Orin上跑分割模型,把opt设成跟训练时最接近的尺寸(比如640x640),同时把max设成你需要的最大分辨率,就再没报过那个错。
还有个小坑,YOLOv8-seg输出有两个头,box和mask,如果ONNX里没把mask分支的dynamic shape单独处理好,TensorRT会认为它是固定维度。你可以用onnx-simplifier先过一遍图,再检查下输出节点的shape是不是带动态维度。要是还不行,干脆放弃纯TensorRT,用ONNX Runtime的CUDA EP做fallback,在Orin上速度差距其实没那么大,省心很多。
之前搞YOLOv8检测也踩过这个坑,问题大概率不在opset,而是TensorRT 8.6对dynamic shape的优化器要求输入范围严格匹配,min/opt/max三个维度必须都设对,尤其opt的HW值要选实际部署中最常见的分辨率,不然优化时选错kernel就会崩。另外ultralytics导出的ONNX里有些动态维度是写在grid_sample这类算子上的,TensorRT支持不好,建议先用polygraphy看看onnx的输入输出shape和算子支持情况。你试试把min和max设成相同值先跑通,再慢慢放宽,能排除很多干扰。
我当时搞yolov5动态尺寸也踩过这坑,后来发现TensorRT的min/mid/max三个维度必须跟实际输入完全对齐,而且ONNX导出的dynamic_axes里batch和height/width都得标上,光标一个batch没用。另外建议你试试把opset调到17以上,8.6的TRT对低版本opset的dynamic shape支持确实有点迷。还有个土办法,先固定一个尺寸生成engine能跑通,再用onnxruntime的transform工具把模型里的Resize算子简化一下,很多时候是这些细节在捣鬼。
我之前也踩过这个坑,最后发现问题往往不在TensorRT的--dynamicShapes参数,而是ONNX导出的dynamic_axes没把batch、height、width三个维度全都标对。YOLOv8-seg的输出里有个mask分支,那个输出的shape变化和检测头不一样,你要是只设了输入动态而输出没跟着动态,TensorRT就会在优化时固定死某些中间层的shape,推理时自然就崩了。建议你先用onnxruntime跑一下动态输入的ONNX,确认它本身没问题,再排查TensorRT。另外TensorRT 8.6对Dynamic Shape的优化器策略比较保守,min/max/opt三个档位的比例设置很关键,尤其min不能设太小,比如1x3x64x64,有些算子在这种极端尺寸下会产生非法配置。我当时的解决办法是直接把opset升到17,然后用onnx-simplifier处理一遍模型,再转TRT时把min设成实际部署中最常出现的最小分辨率,比如720p,opt设成1080p,max设成4K,就稳了。还有一点,ultralytics官方导出的脚本有时候会带一些自定义算子,转TRT前最好用trtexec单独测一下ONNX,看看具体是哪个节点报错,别一股脑全归咎于dynamic shape。