最近在部署一个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 条我之前也被这玩意儿折磨过一阵,最后发现大概率不是opset的问题,而是TensorRT的profile没设置好。min/opt/max这三个shape不光要设,而且opt的尺寸最好是你实际推理中最常见的那个,不然引擎优化时选的kernel可能就不适配。另外你试试用trtexec直接跑一下导出的onnx,把报错信息贴出来看看,能定位到是reshape还是concat哪一层出的问题,比盲猜快多了。
试试把onnx的opset拉到17以上,之前我也是8.6被dynamic shape坑惨,换高版本opset加固定batch就稳了。
我之前也被这玩意儿折磨过,最后发现是ONNX导出的opset版本和TensorRT 8.6的兼容性问题,建议你试试把opset固定到17或18,别用ultralytics默认的。另外dynamic shape那边min/max的设定很关键,尤其是batch和wh的维度,你试试把min设成1,opt和max设成实际跑的最大值,别偷懒全设成一样的。还有个小坑,如果模型里有resize或者gather这类算子,最好在ONNX里先简化一下,不然TensorRT解析时容易出幺蛾子。
我之前也被这个折磨过一阵,最后发现坑多半不在TensorRT的dynamicShapes参数,而是ONNX导出时dynamic_axes没把batch、height、width三个维度全部声明对。YOLOv8-seg的输出里还有mask系数那个分支,如果只给主输出设了动态轴,辅助输出还是固定shape,TensorRT在优化profile时会觉得各输出之间shape约束矛盾,然后就报那种不兼容的错。你可以先试着把ONNX里所有输出的dynamic_axes都加上,尤其是那个1x32x8400的proto输出,有时候它没跟着变就会炸。另外opset版本我建议固定到17,8.6的TensorRT对opset 18以上的某些算子支持有点迷,特别是Resize和Slice组合起来容易生成奇怪的子图。还有个经验是,min/opt/max三个profile里的shape得跟实际推理时的长宽比例保持接近,比如你训练时是640x640,但推理想用1280x720,那opt维度最好设成接近这个比例的尺寸,别图省事全设成640的倍数。最后实在不行,可以先关掉FP16用FP32跑一遍,如果FP32正常而FP16崩,那就是精度校准的问题,跟dynamic shape无关,得在构建引擎时加些静态校准数据。
我之前也被这个坑过,大概率不是opset的问题,而是ONNX导出时dynamic_axes的命名和TensorRT的--minShapes参数没对上。你试试在转TensorRT时把min/opt/max三个shape都显式指定成不同分辨率,比如min=(1,3,320,320)、opt=(1,3,640,640)、max=(1,3,1280,1280),同时确认ONNX里batch维和空间维都用了同一个动态轴名。另外8.6对Dynamic Shape的优化有点迷,可以试试固定batch只动态wh,或者反过来,能避开很多隐雷。
我之前也被这个坑过,TensorRT 8.6对dynamic shape的优化范围卡得挺死的,min/max设得太宽反而容易崩,建议先用固定shape跑通再逐步放宽。另外ultralytics导出的ONNX有时会带一些自定义op,转TRT前最好用trtexer检查下有没有不支持节点。opset版本的话12以上一般够用,但重点看下你的输入输出名有没有在dynamic_axes里写全,漏一个都会报不兼容。
大概率是ONNX里dynamic_axes的name和TensorRT的profile没对上,检查下输入名是否一致。我之前卡了好久,最后发现是opset版本太低,升到17就好了。
八成是ONNX里dynamic axes没对齐TensorRT的profile范围,试试把min/opt/max设成同一个尺寸先跑通再说。
这问题我上个月刚踩完坑,跟你说下我的排查过程。我这边是YOLOv8检测模型,也是8.6的TRT,后来发现核心问题出在ONNX导出的dynamic_axes设置上——ultralytics官方脚本默认只给batch和height/width维度设了dynamic,但如果你同时导出了segmentation分支,那个protos输出层的维度绑定可能没跟着一起动态化,导致TRT那边的优化profile和实际输入对不上。建议先别用官方脚本,自己手写导出代码,把所有输出的axes都显式声明一遍,尤其是mask系数那个输出。另外opset版本我试过17和18,18在TRT8.6上反而更容易出兼容问题,降到17就好很多。还有个小坑是min/max/opt shape的设定,你如果只设置了动态范围但没给opt shape一个合理的中间值,TRT会按最坏情况优化,显存直接爆掉。我最后是把opt设成训练时最常见的分辨率,比如640x640,min设成320,max设成1280,这样才稳定。你那边如果还崩,可以试试先固定宽高比,只在batch维度做动态,至少能先跑通流程。
我之前也被这个坑过,最后发现是ONNX导出的opset版本和TensorRT的dynamic shape优化器不兼容,试了下把opset调到17以上,然后用onnx-simplifier清理一遍,问题少了很多。另外你检查下min/max的profile范围没,Jetson上显存有限,范围设太大也容易崩,先固定一两个常用分辨率跑通再加动态。还有个细节,YOLOv8-seg的输出层有多个,动态维度得全配上,漏一个就报你那个错。
之前搞过一段时间的yolov8-seg部署,也撞过这个dynamic shape的墙。感觉你这个问题大概率不是opset版本的事,8.6的TensorRT对ONNX的dynamic shape支持其实还算可以,但坑主要出在min/max/opt shape的设置上,特别是segmentation这种带额外分支输出的模型,输出特征图的尺寸变化会导致优化profile时的显存分配和实际推理不匹配。建议你把ONNX导出的dynamic_axes范围设窄一点,比如height和width固定到(320, 1280),步长最好设成32的倍数,这样TensorRT在选kernel时会更稳。另外检查一下你转engine时用的--minShapes、--maxShapes、--optShapes是不是分别对应了实际可能用到的分辨率,有时候optShapes设置不当会导致显存优化选错路径。还有个容易忽略的点,如果用了ultralytics官方脚本,它默认输出的后处理部分可能带了非动态的reshape,你可以试着把导出时的nms选项关掉,用纯原始输出再转,然后在推理代码里自己处理。最后建议你加个环境变量CUDA_LAUNCH_BLOCKING=1看看崩溃时打印的栈,多半能定位到是某个层在动态尺寸下不支持。
我上周刚踩完这个坑,八成不是opset的问题,是你TensorRT里min/shape和opt/shape没配对好。YOLOv8-seg导出时那个mask分支的输出维度是动态的,经常是这里没对齐导致崩。建议你先固定batch为1,然后只让宽高动态,min设成1x3x320x320,opt和max设成1x3x640x640试试,我之前这么改完就稳了。另外确认下ONNX里有没有多余的动态维度,有时ultralytics导出会带个num_dets之类的额外动态输出,TensorRT那边要显式ignore掉。
之前也被这个坑过,八成是ONNX导出时dynamic_axes的axes没对齐,检查下opset版本和min/max/opt设的够不够。
我之前也被这个坑过,后来发现问题多半出在ONNX导出时dynamic_axes的写法上,YOLOv8-seg有两个输出,第二个mask输出的shape也得一起设动态,漏了的话TensorRT那边优化就会锁死形状。另外TensorRT 8.6对动态shape的min/mid/max约束很敏感,你试试把min的H和W设成32的倍数,比如min=64,opt=640,max=1280,有时候能绕开一些奇怪的兼容性报错。还有个小建议,别直接用ultralytics导出的ONNX,先拿onnx-simplifier跑一遍,有些冗余节点会让TensorRT解析出问题。如果还崩的话,可以开一下TensorRT的verbose日志,看具体是哪个层报的错,我之前就是靠这个定位到是Resize节点的问题。
我之前也卡在这块好久,后来发现大概率不是opset的问题,而是你转TensorRT时min/max/opt的shape没对齐,尤其是YOLOv8-seg那个mask分支输出维度也跟着变,得把三个都设成实际会跑到的尺寸范围。另外8.6版本对dynamic shape的优化有点迷,建议先用固定shape试一遍确认模型没问题,再逐步放开动态范围,崩的时候多半是某个层在边界shape下触发了bug。你试试把ONNX里那个Resize的coordinate_transformation_mode改成half_pixel,我这边改完就稳多了。
我之前也卡在这过,后来发现是ONNX导出时dynamic_axes里把batch和height/width都设成dynamic了,但TensorRT的min/max范围没给够,尤其height/width的min太小,比如设了32,实际输入是16就直接崩。你可以试试把min设成实际可能的最小值,比如8或16,然后opset建议用17或18,8.6对opset 17支持比较稳。另外确认下ONNX里有没有多余的自定义算子,YOLOv8-seg的mask分支有时候会带些奇怪的节点,用onnxsimplify清理一遍再转,成功率会高很多。
试试把onnx的opset降到17,之前我遇到类似问题就是这么解决的,你用的opset是多少?
我之前搞yolov5的时候也踩过这个坑,TensorRT 8.6对dynamic shape的优化范围卡得挺死的,尤其min/max的宽高比不能差太大,不然优化器直接摆烂。建议你先把min设成和实际推理最小输入一致,比如640x640,别用1x1这种极端值,然后确认下ONNX里的opset版本,11以上比较稳,ultralytics默认导出有时候会带些奇怪的节点。另外你试过用trtexec单独转一次看具体报错日志吗?有时候崩是显存碎片问题,Orin上内存池分配不对也会这样。
我之前也踩过这个坑,八成不是opset的锅,是ONNX导出时dynamic_axes的写法跟TensorRT的profile没对齐。你检查下导出的ONNX里输入shape的name是不是跟trt builder里设置的完全一致,大小写和名字都不能差。另外Jetson上8.6对某些op支持有问题,建议先用固定shape跑通再逐步加动态,或者试试把min和opt设成同一个值,max设大点,能绕过不少玄学报错。
我之前搞yolov5的时候也踩过一模一样的坑,查了半天最后发现是onnx里dynamic_axes的name和tensorrt的profile没对上。你导出时检查一下input的name是不是叫images,如果名字对不上,tensorrt的--minShapes里写的键名也得跟着改,这俩不一致就会报不兼容。另外opset版本也建议直接上17或18,8.6对老版本的支持有些边界情况就是会抽风。还有个容易忽略的点是,你的输入shape必须把batch维也设成动态,不然只动态宽高时,tensorrt内部优化可能还是按固定batch去推理,分辨率一变就崩。最后就是min/opt/max这三个profile的宽高比例最好保持和实际部署场景一致,比如全是16的倍数,否则tensorrt可能会为了对齐而选一个奇怪的中间尺寸,导致运行时报错。我后来是直接把min设成实际最小输入,max设成最大,opt取中间值,然后再把pad到32的倍数,才算彻底稳定下来。你那边如果还不行,可以试试先固定batch=1,把动态全放在宽高上,排除一下是不是batch维度在作妖。