最近在部署一个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导出时dynamic_axes没把batch、height、width全绑对,尤其是YOLO的seg头有额外输出,容易漏掉某个维度。你试试用onnxsimplifier先优化一遍再转TRT,能省掉好多隐式reshape的麻烦。另外8.6对dynamic shape的优化确实一般,建议把min的shape设成你实际部署中最小的输入,比如640x640,别设太小,不然某些层会炸。还有个笨办法,直接固定几个常用分辨率分别转engine,运行时不走dynamic,稳定性高很多,Jetson上反正内存够。
我之前也被这个坑过,最后发现是ONNX导出的opset版本和TensorRT解析器不匹配,建议你试下opset 12到14之间切换看看。另外min/max/opt的shape范围要留够余量,尤其是batch和宽高别卡得太死,Jetson上显存有限也得注意。还有个容易忽略的点,ultralytics官方脚本导出时有些动态轴可能没真正放开,你检查下onnx里输入维度是不是全-1。我之前是直接在TensorRT的API里用setDimensions显式设置优化档位才解决。
我上周刚在Orin上踩完这个坑,最后发现问题出在ONNX导出的opset版本上。ultralytics默认可能用的是17,但TensorRT 8.6对opset 17的某些动态shape算子支持得并不好,尤其是涉及到Resize和Crop这类操作时,建议你强制用opset 16或者15重新导出试试。另外,你检查过ONNX里dynamic_axes是不是真的把batch和height、width都标对了吗?我之前就是只标了batch,结果height和width还是固定值,TensorRT那边虽然能构建引擎,但推理时一换分辨率就直接崩。还有个小细节,TensorRT的min/shape/opt三个维度不一定非要设成一样,但min和max之间的步长最好保持对齐,比如min是640,opt是960,max是1280,这样能减少一些奇怪的兼容性问题。如果你用的是ONNX GraphSurgeon,建议在转TensorRT前先用它把网络里的Resize层显式固定成静态shape,动态shape在TRT 8.6上对seg模型的支持真的不太成熟。最后可以试试在构建引擎时加上--preview=onnxFullDim,这个flag有时候能绕过形状推断的bug。
我之前也栽在这上面过,多半不是opset的锅,而是ONNX里dynamic_axes的写法跟TensorRT的profile没对齐。你导出时得确认一下axes参数是不是把batch、height、width全都标上了,TensorRT那边min/opt/max三个维度都得显式设好,尤其height和width的opt值别选极端尺寸。另外8.6对某些reshape操作很敏感,YOLOv8-seg的mask分支容易生成动态shape的中间节点,建议先用trtexec跑一遍看具体是哪个层报错,或者试下onnx-simplifier把冗余shape运算清掉。我上次是把输入改成固定h和w的倍数值(比如32的倍数)才稳下来,虽然麻烦但省心。
我之前也卡在这玩意儿上好久,最后发现多半是ONNX导出时把batch和hw都设成dynamic,但TensorRT的优化profile没给够范围,尤其是min和opt差距太大特别容易崩。你试试把min设成实际会用到的最小分辨率比如320,opt设成常见的640,max别超显存能扛的极限,然后确保ONNX里opset别低于17,我之前用16就各种莫名其妙。另外YOLOv8-seg的mask分支有个动态shape的算子,建议先单独导出不带mask的模型测试,排除是分割头的问题。
我上周刚踩完这个坑,最后发现是ONNX导出的opset版本太高(17以上)导致TensorRT解析dynamic shape时出了问题,降到16就好了。另外你检查下TensorRT的min/shape/opt这三个维度是不是都写对了,尤其是batch那维,有时候默认1会跟实际输入冲突。还有个容易忽略的点,就是onnx-simplifier跑一遍再转,很多冗余节点会导致dynamic shape信息丢失。
另外YOLOv8-seg的mask分支是动态输出的,TensorRT 8.6对多输出且shape联动的情况支持得并不好,建议你先把seg头砍掉单独验证检测部分,确认没问题再慢慢加回来。如果还崩,试试固定一个较小的max尺寸,比如1280,别设太大,Orin上显存限制也可能触发意外报错。
我上周刚好踩过这个坑,TensorRT 8.6对动态shape的优化范围卡得比较死,min/mid/max三个维度必须全部显式指定,而且ONNX导出时opset版本建议用17以上,ultralytics默认的12在转动态shape时容易丢维度信息。你可以先用onnx-simplifier把模型过一遍,再看下导出时是不是把batch维和空间维混在一起了,我之前就是没分开设导致引擎只认固定尺寸。另外Jetson上跑的话,建议把输入分辨率范围设小一点,比如640到1280,太大容易爆显存。
这问题我也踩过,min/max/opt shape得跟你实际输入对齐,别只设一组值跑不同分辨率。
我之前也卡在这块好久,最后发现多半是min/max/opt的shape没对齐,尤其batch和wh的上下限得跟实际输入范围匹配,不能光设个动态就完事。另外建议检查下ONNX导出的opset版本,用17以上会稳一点,TensorRT 8.6对某些op的dynamic支持确实有坑。你试试把opt尺寸设成实际推理最常用的分辨率,别跟min差太远,崩溃概率会小很多。还有个小技巧,导出时把end2end和nms都关了,排查起来更干净。
我之前也卡在这块好久,后来发现多半不是opset的问题,而是ONNX里dynamic_axes的命名和TensorRT的profile没对齐,尤其YOLOv8-seg输出层有多个head,容易漏绑。你试试在转TensorRT时显式指定minShapes和optShapes,用实际跑的最小心眼和常见分辨率,别只给一个范围。另外8.6对某些op的dynamic支持有bug,可以先固定batch,只让H、W动态,能省不少事。崩溃的话可以开TensorRT的verbose日志,看具体是哪个层不兼容,比瞎猜快。
我之前也卡在这块好久,后来发现问题多半出在ONNX导出时dynamic_axes没覆盖到所有维度,尤其是segmentation分支的输出,只给输入设了动态轴但输出还是固定shape的话,TensorRT优化时就容易炸。建议先用onnx-simplifier跑一遍,然后检查下每个输出的shape是不是都带动态维度,opset版本倒不是重点,11以上基本都行。另外TensorRT 8.6对dynamic shape的min/max范围很敏感,你设的profile范围别太宽,比如高度从320到1280这种跨度,实际部署时最好固定几个档位分别建engine,别指望一个万能engine能通吃所有分辨率。
试试把opset拉到17以上,之前我也是yolov8-seg卡在这,换完基本没碰过shape报错。
我之前也卡在这玩意儿上好几天,YOLOv8-seg的dynamic shape确实比普通检测头恶心不少,因为mask分支的维度变化会连带影响很多中间算子。
我后来发现TensorRT 8.6对ONNX里某些Resize或者Gather算子的动态支持特别挑,尤其是当你的输入宽高不是32的倍数时,隐式对齐会直接给你搞崩。
建议你先试试把opset拉到17以上,ultralytics默认导出有时候会用16,某些动态shape的边界情况在16里表述得不够清晰。
另外,你转TRT的时候有没有把优化profile的min/opt/max都设成实际会用的范围?我之前就是只设了opt和max,min忘改了,结果一到小分辨率就报你那个错。
还有个土办法,如果你实际部署场景分辨率就那么几种,干脆分别导出几个固定shape的engine,运行时按输入尺寸切换,虽然笨但绝对稳,Jetson Orin上内存也够用。
最后检查一下你ONNX里有没有显式加GridSample或者自定义的mask上采样层,这些算子经常是TRT动态shape崩溃的元凶,实在不行就换成纯卷积上采样。
我之前也被这个折磨过,最后发现是min/max/opt的shape没设对,尤其batch和wh的维度得留够余量,不然TRT优化时直接按固定尺寸编译了。另外opset版本建议统一用17或18,ultralytics默认导出的有时候会带些奇怪的节点,TRT8.6对某些op支持不好。你试试先把dynamicAxes只设在wh上,batch固定1,排除变量干扰看看。如果还崩,检查下ONNX里有没有Resize或者GridSample这类层,TRT对这些的dynamic支持特别拉胯。
我之前也被这个坑过,主要问题往往不在TensorRT的dynamicShapes,而是ONNX导出时opset版本和dynamic_axes的写法不匹配。建议你先检查下导出的ONNX用onnxruntime试跑不同分辨率,如果它都过不了,那TensorRT肯定报错;另外TensorRT 8.6对dynamic shape的优化器需要显式指定优化范围,min/max设得太窄也会崩。还有个小细节,YOLOv8-seg的输出层有多个,dynamic_axes得覆盖到所有输出张量,漏了任何一个都会导致引擎构建时形状推断失败。
我之前也卡这,后来发现是ONNX的dynamic_axes和TRT的min/max没对齐,得把opset提到17试试。
遇到过一模一样的坑,Orin上跑YOLOv8-seg真的折磨人。我后来查了半天才发现,问题不一定在TensorRT的dynamic shape配置上,而是ONNX导出时那个opset版本和segmentation分支的grid_sample算子兼容性有猫腻,8.6的TensorRT对某些opset的dynamic shape支持很迷。建议你先用onnxruntime或者trtexec单独测一下导出的ONNX文件,确认dynamic axes是不是真的生效了,别直接跳到TensorRT推理那步。还有,你min/max/opt形状的设定,height和width得是32的倍数,YOLOv8下采样倍数高,非对齐尺寸会在中间层直接崩,这个最容易忽略。另外,TensorRT 8.6的dynamic shape在Jetson上有个老毛病,如果优化配置里min和opt差距太大,引擎构建时显存分配会出问题,试试把opt设成你实际最常用的分辨率,min和max别拉太开。我之前是把导出脚本里opset手动改成17,然后TensorRT那边用onnx-tensorrt的parser而不是直接trtexec,才勉强稳定下来,你可以对比试一下。如果还不行,建议把报错时TensorRT的日志级别调成verbose,看它具体卡在哪个层,segmentation的mask分支经常是重灾区。
我之前搞yolov5的时候也踩过这坑,最后发现是ONNX里dynamic_axes只对输入输出设了,但中间某些reshape或concat节点的shape被固化了,转TRT时优化器按固定shape推断了。建议你用polygraphy或onnxruntime把导出的模型跑一遍不同分辨率,看哪个节点先崩,或者干脆把opset拉到17试试,8.6的TRT对高版本opset支持还行。另外确认下min/max那组参数是不是设得够宽,有时候输入尺寸范围太小也会莫名报错。
我之前也卡在这好久,后来发现是ONNX导出时dynamic_axes的命名和TensorRT的profile没对齐,尤其batch和height/width的轴名必须完全一致才行。另外8.6版本对动态shape的优化网络(比如Slicing+Resize)支持有bug,试试把opset调到16以上,或者用onnxsimplifier清理下结构。还有个土办法,先固定一个最小尺寸转出engine,再用onnxruntime测下不同输入,对比下输出张量shape是不是被隐式写死了。
我之前也栽在dynamic shape上很久,yolov8-seg这玩意儿带mask头,onnx导出时那些reshape和gather操作特别容易把动态维度的信息搞丢。你用的8.6版本其实对dynamic支持已经算好了,但有个坑是min/shape范围得设得特别保守,尤其是batch和height/width的倍数关系,建议把min设成1,opt设成你常用的训练尺寸,max别超过你显存能扛的极限。另外opset版本也别追新,我试过17和18都有奇奇怪怪的问题,锁在16反而稳。还有一个容易忽略的点,你导出的onnx里有没有用固定shape的pooling或者插值,如果用了,动态shape在trt优化时会被强行静态化,你可以用onnx-simplifier跑一遍,把那些冗余节点清掉。最后实在不行,走一下trt的onnx-parser的legacy模式,有时候新版parser反而过度优化导致shape推断出错。你崩的时候有没有看下trt的日志?一般会提示具体是哪一层出的问题,比瞎猜高效得多。