最近在部署一个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的name和trt的profile没对齐,你查查这个。
之前搞yolov5的时候也踩过这坑,多半不是opset的问题,是onnx导出的dynamic_axes只设了输入没给输出也配上,tensorrt那边min/max/opt的shape得跟onnx里完全对齐才行。另外8.6对某些op的dynamic支持有bug,建议先固定一个维度测试,确认没问题再上全动态。你试试把输出层的names和axes也加上,然后trt这边用onnx-tensorrt的parser直接构建,别用trtexec那套命令行,有时候配置会被吞掉。
我之前也踩过这个坑,大概率不是opset的问题,而是TensorRT的profile范围没设对。dynamic shape必须显式指定min/opt/max三个档,而且opt形状最好选实际部署时最常用的分辨率,不然引擎优化时容易崩。另外ultralytics导出的ONNX里有些动态维度是多余的,建议先用onnxsim精简一下,再手动检查下输入输出的shape名称是否对得上。Jetson上显存有限,min和max跨度别太大,不然引擎构建会非常慢甚至OOM。
大概率是min/max/opt没给对,尤其opt得是实际推理常见尺寸,别都填同一个值。
我之前也卡这,后来发现ONNX里动态轴名字和TRT的binding得完全对上才行。
试试把min/max/opt设成一样的尺寸跑一遍,排除是profile范围的问题,8.6对动态shape挺敏感的。
我之前也卡在这玩意儿上好几天,最后发现是ONNX导出时dynamic_axes里的输入输出名没跟TensorRT那边对齐,你试试看是不是名字对不上。另外8.6的builder对动态shape的优化range特别敏感,min/max设太宽容易崩,建议先把范围缩到实际会用到的尺寸附近。还有个小坑,ultralytics官方脚本导出的模型带了NMS层,转TRT时建议先去掉,不然动态shape经常跟NMS的输入尺寸冲突。你检查下engine的profile是不是只绑了一个shape,如果是的话,得显式加多个profile才行。
这问题我踩过一模一样的坑,多半不是opset的锅,而是ONNX里dynamic_axes的写法跟TensorRT的min/max/opt没对齐。你导出时得确认一下输入shape的维度名是不是完全一致,然后转TRT时三个范围别设成一样的值,特别是opt要取你实际推理最常用的分辨率。另外8.6对YOLOv8-seg的op支持有点老,建议直接拉最新版TensorRT试试,之前我升级后莫名其妙就好了。
opset版本大概率没事,重点查下min/max/opt三个shape设得合不合理,或者试试固定一个维度只动态另一个。
之前搞分类模型也栽这过,后来发现是trt缓存没删干净,清掉重新build就好了。
我之前也被这个坑过,YOLOv8-seg的dynamic shape特别容易在TensorRT的profile设置上出问题,你检查过min/max的尺寸范围吗?有时候ONNX导出的dynamic axes只绑定了输入,但模型内部的resize或concat层也需要显式指定。另外,建议先把opset固定到17或18试试,8.6对更高版本支持不太好,我上次就是降到17才跑通的。
遇到过,八成是min/max没设对,或者onnx里dynamic_axes的name和trt的profile没对齐,检查下这两个。
试试把ONNX的opset统一到17,然后TensorRT的min/opt/max三个维度全设成一样,八成是优化配置没对齐。
之前也栽在这上面,后来发现是输出的mask尺寸没跟着动态走,检查下ONNX里有没有把seg头的输出也标成dynamic。
我之前也卡在这,后来发现是onnx导出的dynamic_axes和trt的min/max范围没对齐,你检查下是不是只有两个维度设了动态。
我之前也卡在这块挺久的,TensorRT 8.6对dynamic shape的优化范围其实是有限制的,不是所有层都支持任意尺寸变化,尤其是seg这种带上采样和concat的模型。建议你先固定宽高试试,确认能跑通,再逐步放宽范围,别一上来就设1到无穷大。另外ONNX导出时opset版本最好用17或18,ultralytics默认的16有时会生成些奇怪的节点,TensorRT解析容易炸。你可以先用trtexec加--minShapes和--optShapes分别测一下,看是不是min那边没配对导致的,我之前就是这么定位到是profile设置的问题。
我之前也卡在dynamic shape上好一阵,后来发现多数坑其实不在TensorRT本身,而是ONNX导出的图里带了太多固定shape的中间节点。你试过用onnx-simplifier处理一下导出文件吗?有时候那些reshape和gather操作会把动态维度偷偷写死,TensorRT读出来就认为你是静态输入。另外你只说了设了dynamicShapes,但min和max有没有给够范围?我记得YOLOv8-seg的mask分支对输入分辨率特别敏感,如果min设成640而实际跑1280,那输出张量的大小直接对不上,崩是正常的。opset版本的话,我建议至少用17以上,8.6的TensorRT对opset 17的兼容性比低版本好不少,之前用opset 12导出的时候碰过一些奇怪的算子不支持。还有个小技巧,你可以先固定一个shape跑通,再用trtexec加--minShapes和--optShapes去测不同分辨率,看是哪个层在报错,这样定位问题比直接推理快得多。你那边崩的时候有没有打印出具体的engine层信息?我怀疑是那个Segmentation输出头的动态维度没绑对。
这个问题大概率是ONNX导出时dynamic_axes的rank没对齐,试试固定batch维度只动态H/W,或者换opset 17看看。
大概率是ONNX里dynamic axes没对齐TensorRT的min/opt/max,试试显式固定三个档位尺寸重新导出。
之前搞yolov5的时候也踩过这个坑,TensorRT的dynamic shape不光要设min/max,还得保证onnx那边的dynamic_axes和trt的profile范围完全对齐,不然很容易出这种玄学报错。你试试先把opset固定到17,然后用trtexec单独跑一下onnx看具体报错是哪个维度不匹配,另外Jetson上8.6对某些算子的动态shape支持确实有bug,可以考虑降到8.5试试。
遇到过一模一样的坑,opset版本和dynamic shape的min/max范围都得查。我之前用opset 11导出一直崩,换成opset 17配合TensorRT 8.6才稳定,但前提是ONNX里每个动态维度的上下限必须显式写清楚,不能只设symbolic name,否则TensorRT优化时会拿默认的0到无穷大去算,肯定炸。你那个报错八成是min/max没对,试试在导出时把axes的min设成实际可能的最小分辨率,比如640,max设成最大,比如1280,然后optimization profile里也手动指定三个档位,别只靠命令行参数。另外ultralytics官方脚本导出的模型有时候会带一些自定义op,比如NMS或者grid解码,这些在动态shape下特别容易出问题,建议导ONNX时把end2end和nms都关掉,只留纯backbone加seg head,后面自己写后处理。还有个细节,Jetson上TensorRT的显存分配策略跟PC不一样,动态shape时显存碎片化可能导致随机崩溃,试试减少max batch或者把workspace调小点。如果还不行,把报错堆栈贴出来,我帮你看看是不是profile没绑定到execution context上。
我之前也卡在这个坑里好久,后来发现多半不是opset的问题,而是TensorRT的dynamic shape优化器对profile范围特别敏感。你设的min/max范围如果跨度太大,比如从320到1280,引擎可能会为每个可能的shape都生成优化方案,内存直接爆掉,而且某些中间层(比如yolov8-seg的mask分支)在动态维度下会有隐式reshape,这些reshape在TRT里经常被隐式优化掉,导致实际输入shape和引擎内部预期对不上。建议你先试试固定batch和宽高,只把height和width设成动态,而且min和max别拉太大,比如固定一个常用分辨率附近的范围,像640到960,先跑通再慢慢扩展。另外,ultralytics官方导出的onnx里有些节点是冗余的,你可以用onnx-simplifier处理一下,再检查一下有没有用到TensorRT 8.6不支持的op,比如Resize的antialias参数。还有一个很隐蔽的点,就是你的输入tensor名字和onnx里的实际名字不一致,或者你在转trt时用了--input-shape但没跟onnx里的动态轴顺序对齐,也会报这个错。我最后是直接在TRT的python API里用network定义来排错的,比命令行好定位问题,你可以试试把报错的层级单独打印出来看看。
我上周刚踩完这个坑,你试试ONNX导出的时候把opset调到17以上,然后TensorRT那边用trtexec先转一次看看具体报错信息,别直接上代码。另外min/max的形状范围一定要给足,比如高度宽度至少留出32的倍数余量,YOLO的下采样倍数很容易让人忽略这点。我之前就是min设太小导致动态范围不够,改成64的倍数就好了,你可以先拿固定尺寸跑通再慢慢加动态。