最近在部署一个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,另外检查下dynamic_axes的keys是不是和输入名完全一致,我上次就是这俩问题折腾了一周。
之前遇到过类似,查下tensorrt的min/shape/opt设置,有时候是onnx导出时输入维度顺序和trt要求的不匹配,改成固定batch再设动态h/w试试。
我之前做分类模型的时候也被这个坑过,后来发现大概率不是opset的问题,而是ONNX里dynamic_axes的写法跟TensorRT的profile没对齐。你官方脚本导出的ONNX,最好先拿onnxruntime或者netron看一眼实际输入的shape是不是真的带动态维度,有时候batch维设了动态但height/width还是写死成640了。另外TensorRT 8.6对dynamic shape的要求挺严格的,min/opt/max三组profile都得覆盖你实际推理时可能用到的所有分辨率组合,而且opt那组最好选你部署时最常用的尺寸,不然显存分配和算子选择都可能出幺蛾子。还有个容易忽略的点是,你转engine的时候如果用了--fp16,某些动态分支在特定shape下会触发bug,可以先纯fp32试一轮排除掉。我之前就是被一个上采样层的动态计算搞得崩溃,后来把opset统一到17,然后手动把ONNX里所有reshape改成静态shape才稳定下来。你如果方便的话,可以把转换命令和报错堆栈贴出来,大家帮你看看具体卡在哪一步。
我之前也踩过这个坑,问题大概率不在dynamic_axes,而是TensorRT的profile没设对。你--dynamicShapes后面有没有给min、opt、max三个范围?尤其opt那个值很关键,必须跟实际输入分辨率成倍数关系,不然引擎构建时优化会玄学报错。另外ultralytics导出的onnx有时会带些自定义op,建议先onnxsimplifier精简一遍再转trt,能解决不少兼容问题。还有个偏方,如果你实际部署就那么几种分辨率,干脆直接固定shape转三个engine,运行时切换,省心很多。
试试把onnx的opset调到17或18,之前我也在这卡了好久,8.6对动态shape支持有点迷。
我之前也被这个坑过,最后发现是ONNX导出时dynamic_axes的axes没写全,只设了batch没设hw,TensorRT那边虽然认了但实际还是按固定尺寸编译了。另外8.6对动态shape支持确实一般,建议先试试固定尺寸跑通,再逐步放宽范围,别一上来就全动态。另外你导出时opset版本多少?我之前用11死活不行,换到13就好了。
之前搞yolov5的时候也踩过这坑,八成不是opset的问题。你试试转onnx时把dynamic_axes只设到batch维度,wh保持固定,然后用tensorrt的python api直接构建engine,把min/shape和opt/shape设成一样,虽然灵活性差点但稳。另外8.6对动态shape的优化挺迷的,有时候onnx里加了Resize或者grid_sample这类算子,转trt时profile范围稍微大点就崩,建议把min和max范围缩小试试。
我之前也卡在这个坑里很久,最后发现大概率不是opset的问题,而是TensorRT的dynamic shape对输入输出的每个维度都必须显式声明范围,尤其YOLOv8-seg这种带mask输出的模型,输出层的维度绑定比普通检测头复杂得多。你检查一下ONNX里有没有用Shape、Gather这类动态算子,它们很容易让TensorRT的优化器直接放弃治疗,建议先把onnx-simplifier跑一遍,把那些动态索引全拆成静态。另外,你设dynamicShapes的时候,min/opt/max三个档位的比例最好保持一致,比如640、960、1280,别把opt设成和min一样,TensorRT优化时是按opt来选kernel的,跨度太大会直接编译失败。我上次还遇到一个奇葩情况,就是输入NCHW里C=3没错,但ONNX导出时把batch维和height维搞反了,推理时数据reshape错乱,表现就是随机崩,你可以先用固定shape跑通,再逐步放开维度,这样能定位是解析器的问题还是运行时的问题。最后多嘴问一句,你Jetson上用的是什么精度,FP16还是INT8?如果是INT8,动态shape下calibration表得重新生成,这个也容易莫名报错。
我之前搞YOLOv8检测模型的时候也踩过这个坑,折腾了快两周才跑通。你用的TensorRT 8.6和ultralytics官方导出的ONNX,按理说opset版本应该问题不大,但我当时发现真正容易出问题的是min/max的profile范围设置——dynamic shape不是光加个flag就行了,必须给三个维度范围都明确指定,而且min那个值别设太小,有时候设为1会触发TensorRT内部某些优化路径的bug。另外你确认一下ONNX导出的dynamic_axes是不是真的覆盖了所有输入输出的batch和height/width维度,YOLOv8-seg的输出不止一个,有个mask头的输出shape和原型mask是关联的,那个维度如果没设对,TensorRT在build engine时可能不报错,但推理时就会崩。还有个小坑,如果你用的是FP16精度,某些分辨率下卷积的channel对齐问题也会导致类似报错,可以试试先FP32跑一遍排除这个因素。最后建议你直接用trtexec工具,把min/shape/opt和max都显式写进去测试,比在代码里调更容易定位是export的问题还是runtime的问题。我最后是把ONNX用opset12重导一次,然后profile的opt设成训练时的常见尺寸,min稍微给大点比如32的倍数,才稳定下来。
opset版本查过没?我之前用16反而比17稳,另外min/max别设太宽,和实际输入匹配下试试。
TensorRT 8.6对动态shape的优化器策略挺挑的,建议把onnx-simplifier跑一遍再转,能省不少事。
我之前也卡在这块好久,最后发现大概率是ONNX导出时dynamic_axes的写法有问题,特别是batch和height、width这几个维度得分开设,不能图省事全绑一起。你用的是ultralytics官方脚本的话,注意它默认导出可能是固定shape的,得手动改一下export函数的参数,把dynamic=True传进去,不然转出来的ONNX压根没带动态维度信息。另外TensorRT 8.6对dynamic shape的支持其实挺挑的,min/opt/max这三个profile必须都设对,尤其opt维度建议用你实际部署时最常出现的分辨率,别随便填个中间值,不然引擎内部优化会选错kernel。还有个小坑是opset版本,我试过17和18都行,但有些自定义算子像grid_sample在seg模型里容易出问题,可以试试opset=16或者干脆把seg头单独拆出来转,别整个模型一起搞。你要是方便的话,把报错时具体的shape和profile配置贴出来,咱们再细看,毕竟Jetson上显存有限,dynamic shape有时候还会触发cudnn的算法选择bug,崩了不一定是shape不兼容,可能是内存对齐问题。
我之前也栽在这上面过,TensorRT 8.6的dynamic shape对ONNX导出的opset版本特别敏感,建议先试试把opset固定到17或18,然后确认下导出时是不是把batch和宽高都设了dynamic,别只动一个维度。另外你检查下ONNX里有没有Resize或者GridSample这类算子,YOLOv8-seg的mask上采样很容易在TRT里生成隐式常量,导致输入shape被悄悄锁死。还有个笨办法,先用固定尺寸(比如640x640)把整个流程跑通,再逐步放宽动态范围,定位是哪个层在报错,比直接看报错信息靠谱得多。
opset版本大概率没问题,重点查下onnx导出的dynamic_axes里batch和hw是不是都标了,TensorRT那边min/opt/max得给全。
八成是onnx里dynamic_axes的name和tensorrt那边没对齐,检查下输入输出名和维度顺序。之前我也卡这,改成固定shape先跑通再调。
我之前搞yolov8检测的时候也踩过这坑,最后发现大概率是ONNX导出时dynamic_axes只设了输入没设输出,或者输出层的batch维度漏了。你检查下导出的ONNX用onnxruntime试几个不同分辨率,如果ORT能跑通但TRT不行,那问题基本就在TensorRT的profile设置上。另外TensorRT 8.6对动态shape的优化其实挺挑的,min/opt/max三个档位不能随便填,opt那个值最好是你实际部署最常用的分辨率,比如720p就写1280x720,别图省事直接照搬官方默认的1x3x640x640。还有个小坑是YOLOv8-seg的输出有两个,一个detection一个mask,这两个的shape都得显式在dynamic_axes里声明,不然TRT会默认按静态处理。你要是方便的话,可以试试用trtexec跑一下--dumpProfile看看具体是哪个层崩的,我怀疑是Resize或者Concat这类算子对动态shape支持有问题。对了,你ONNX的opset用的多少?我之前用17会莫名其妙多出一些Gather节点,降到16反而稳了。
opset版本确实是个坑,我上次用16导出的动态shape在TRT8.6上就报错,换回15就好了。另外你检查下ONNX里输出的shape是不是全动态的,YOLOv8-seg的mask分支经常漏掉dynamic_axes,导致输出维度定死。还有,Jetson上建议先用固定shape跑通再试动态,TRT对动态优化的支持在Orin上好像更敏感。
之前搞YOLOv5的时候也踩过这个坑,八成不是opset的问题,你检查下ONNX里dynamic_axes是不是所有输入输出都加上了,尤其输出端那个shape分支经常漏。TensorRT这边建议先用固定shape导出trt跑通,再加dynamic,一步步排查是转换阶段还是推理阶段出的错。另外Jetson上8.6对某些算子支持有点迷,试试把opset降到17或者18,我之前降到17就稳了。
我之前也卡在这玩意儿上好久,后来发现主要是ONNX导出的opset版本跟TensorRT 8.6的兼容性有坑,建议你把opset固定到17试试,别用默认的。另外dynamic shape的min/max范围别设太宽,比如高度宽度就限制在320到1280之间,范围大了TensorRT优化会炸。还有个小细节,YOLOv8-seg的mask分支输出维度是动态的,你检查下是不是这个输出没绑对dynamic_axes,有时候光设输入不行,输出也得跟上。我最后是直接改ultralytics的导出脚本,把mask输出单独处理才跑通的,你可以参考下这个思路。
我之前也被这个坑过,最后发现多半不是TensorRT的问题,而是ONNX导出时dynamic_axes没写全。你检查下输出的num_dets、boxes、scores、masks这几个头是不是都绑定了动态维度,只绑输入不绑输出的话,TensorRT优化时会把输出shape固化,推理时输入一变,中间张量形状对不上就直接崩。另外ultralytics官方脚本的opset默认可能是17,但TensorRT 8.6对opset 17的某些动态reshape支持有bug,建议降到16试试,我降到16之后报错明显少很多。还有你在转TensorRT时加的--dynamicShapes,具体min和max范围设了多少?如果min设成1,但实际预处理时又做了letterbox导致宽高不是整数倍,TensorRT内部对齐会出问题。我之前是把min固定成模型训练时的尺寸(比如640x640),max设成2倍,opt取中间值,这样反而更稳。另外Jetson Orin上记得用TensorRT自带的trtexec先跑一遍onnx,看它输出的警告信息,经常有提示具体哪个节点不支持动态shape。你试试把opset降到16,然后把min/opt/max三个尺寸都设成同一比例(比如1、1.5、2倍),应该能解决大部分崩溃。如果还不行,把ONNX里所有的resize操作换成固定模式(不传scale),有时候是这些细节在捣鬼。
大概率是ONNX里dynamic axes名字没和TensorRT的min/max/opt对上,试试统一成一样的名字。
试试把onnx的opset拉到17以上,我之前就是这问题,8.6对动态shape支持跟opset版本强相关。