最近在把训练好的一个图像分类模型(ResNet50)转成ONNX,部署到移动端用。按照文档设了dynamic_axes={‘input’: {0: ‘batch_size’}},导出也成功了。但用onnxruntime推理时,只要batch_size不是1就报错,说“输入形状不匹配”?试了opset版本11和12都一样。是不是导出时少设了什么参数,比如输入尺寸的dynamic_axes必须和模型内部结构对应?还是说模型里有不支持动态batch的层(比如BatchNorm)?求有经验的大佬指点一下,我实在不想把batch固定为1。
PyTorch模型转ONNX时,动态轴设了但推理报错,有老哥踩过坑吗?
全部回复
共 165 条我之前也卡过一模一样的问题,最后定位到是onnxruntime的session配置里没开动态shape支持。你导出的时候dynamic_axes只是告诉转换器哪些维度可变,但推理端还得显式设置providers和session_options里的optimized_model_filepath,有时候还要加上set_optimization_level配合,否则默认优化会把动态轴当成固定值处理。另外ResNet50里的BatchNorm在ONNX里一般是折叠进Conv的,不太可能是它的问题,但你可以先用onnx.shape_inference.infer_shapes跑一遍看输出shape对不对。我上次还发现是输入数据没转成NCHW,移动端读图经常是NHWC,batch维度对不上自然报错。你试试在推理代码里把输入reshape成(1,3,224,224)或者(2,3,224,224)再喂进去,如果还报错就打印一下实际输入和模型期望的shape对比。还有个小坑是动态轴的名字必须和输入张量名完全一致,你检查下导出时是不是写了'input'但模型里实际叫'data'或者别的。如果这些都排除了,可以试试opset 13以上,有些算子对动态shape的支持更完善。
检查下onnxruntime的session输入输出名和动态轴对不对应,我之前就是名字对不上白折腾半天。
我之前也栽在过这上面,大概率不是BatchNorm的问题,而是onnxruntime那边没吃到你动态轴的shape信息。导出时除了设dynamic_axes,还得在onnxruntime的session options里明确把input的shape设成[-1, 3, 224, 224]这种,或者用IOBinding手动指定维度,不然它默认拿固定shape的初始值。另外你检查下ResNet50里有没有global average pooling之后的flatten操作,那个reshape如果写死了batch维度也会出这种幺蛾子。我后来是直接改用opset 13,然后把动态轴同时加到input和output上才消停的,你可以试试。
之前也遇到过一模一样的情况,最后发现是resize或者view这类层在trace的时候把shape写死了,动态轴只对最外层生效。你试试导出前把模型里的view改成reshape,或者给那几个固定shape的层也加上dynamic_axes试试,应该能解决。
我猜你推理报错可能跟onnxruntime的优化器有关,某些pass会把动态shape的图优化成静态。可以试试用onnxruntime的graph_optimization_level设为ORT_DISABLE_ALL跑一遍,看是不是还报错,能定位到是哪一步出的问题。
另外BatchNorm本身是支持动态batch的,不会有问题,重点检查下模型里有没有flatten或者adaptive_avg_pool这种操作,它们经常导致维度推断出错。实在不行可以装个onnxsim简化一下图,有时候能自动处理好这些。
之前搞yolov5转onnx也遇到过这坑,动态轴设了以后,导出时最好把模型的输入也顺带固定成(1,3,224,224)这个形状,推理时再手动改输入张量的shape为实际batch,否则某些算子会按静态shape做优化。另外检查下有没有用view或者reshape把batch维度写死了,ResNet50一般没这问题,但onnxruntime对动态shape支持确实有点迷,建议试下opset13+或者用onnx-simplifier过一遍图。
我这边最后是直接在导出时把dynamic_axes的batch维度改成‘None’,然后推理输入用np.zeros((batch,3,224,224)),反而好了,你可以试试把batch_size改成None看报错信息有没有变化。
遇到过一模一样的情况,折腾了我一整个周末。你设的dynamic_axes本身没问题,问题大概率出在onnxruntime的session配置上,光设置动态轴还不够,推理时得显式指定input的shape,比如用ort的IOBinding或者直接给input_dict传个shape为(4,3,224,224)的numpy数组,不然它默认按静态图执行。另外ResNet50里BatchNorm在导出时通常会被折叠进卷积层,一般不会卡动态batch,倒是最后的GlobalAveragePooling和Flatten可能对维度敏感,你检查下导出的onnx图里有没有什么Reshape或者Squeeze是写死维度的。还有个坑是opset版本,12对动态轴支持更完善,但如果你用了torch的旧版本导出,某些算子会默认把batch维度当成常量,建议升级torch到2.0以上再试。最笨但有效的办法是导出后用onnxsim精简一下图,有时能自动修正一些不必要的固定shape。如果还不行,贴一下你onnxruntime的报错堆栈,我帮你看看是哪一层出的问题。
之前也卡这过,多半是模型里插了view或reshape把batch写死了,查下导出前的forward里有没有固定维度的操作。
我之前也卡过这个坑,问题多半不在dynamic_axes本身,而是onnxruntime的session选项没开动态形状支持。你试试在推理时给session配置里加上providers和enable_cpu_mem_arena=false,或者直接检查一下输入tensor的shape是不是被固定成[1,3,224,224]了,有时候导出时模型里其他节点(比如reshape或flatten)会把batch维度写死,跟dynamic_axes冲突。另外BatchNorm在推理模式下是没问题的,不会限制batch,你不如把导出的onnx用onnxsim简化一遍,再手动打印各节点输入输出shape排查,我之前就是被一个隐形的view节点坑了。
换个思路,你确认下onnxruntime的版本,老版本对动态轴支持有bug,升到1.16+基本能解决。还有个很容易忽略的点:虽然你设了dynamic_axes,但导出时如果用了torch.onnx.export的input_names和output_names,且没在dynamic_axes里把output也标上,推理时输出维度会锁死,导致内部计算链断了。建议把output的batch维度也一起加进去,然后测试时用ort.InferenceSession的run接口传入[batch, 3, H, W],别
遇到过同样的问题,折腾了好久发现大概率是模型里有个全局池化或者reshape把batch维度写死了,ResNet50的avgpool后面如果跟了flatten,动态轴就只对前面生效,后面形状全固定了。你可以导出后把onnx图打印出来看看,找找有没有shape是硬编码的节点,尤其关注view/reshape那块。另外onnxruntime的session选项里可以试试设置optimized_model_filepath,有时候图优化会重新推断shape导致报错,绕开它说不定就好了。如果还不行,就检查下input的value_info里shape是不是写成了[1,3,224,224],导出时加个dynamic_axes的output声明,把最后一层输出也标上动态batch,我那次就是只标了输入没标输出才出的幺蛾子。
我之前也卡过这问题,后来发现是resize那层没跟着设动态轴,ONNX导出时只动input还不够,中间层的shape推理会固化。你试试把dynamic_axes同时加到所有reshape和resize的输出上,或者干脆用onnx-simplifier过一遍。另外BatchNorm本身不吃batch size,应该不是它的问题,但你要是用了adaptive avg pool,得确认下输出维度是不是写死了。
遇到过,多半不是BatchNorm的问题,ResNet50里BN在推理时是固定参数的,动态batch不影响。你检查下onnxruntime的session配置,需要显式设置providers和execution_mode,有时候默认CPU执行器对动态shape支持不友好。另外导出的onnx用onnxsimplifier优化一下,把shape相关的冗余节点清理掉,我之前就是模型里有个reshape把batch维度写死了,精简后才正常。如果还不行,试试把input的dynamic_axes同时给output也配上,有些推理后端要求输入输出维度都声明动态。
我之前也卡过这问题,最后发现是onnxruntime的session配置里没开动态shape,光设dynamic_axes不够,还得用set_providers或者session options里把输入维度声明成动态才行。另外BatchNorm本身不吃动态batch,但ResNet里的全连接层对输入维度是写死的,你检查下导出时有没有把flatten之后的维度也设成动态,不然中间张量形状对不上。我当时是把opset升到13,然后强制用onnxsim简化一遍,再在推理时显式传input_dict带具体shape就好了,你可以试试这个组合。
之前踩过,光设dynamic_axes不够,onnxruntime要配input的shape,把batch那维设成-1或者用set_input_shapes试试。
我之前也卡过这问题,多半不是BatchNorm的锅,那玩意儿对动态shape是兼容的。你试试把dynamic_axes里input和output都写上,而且batch维度要明确标成'symbolic',别只写一个input。另外检查下模型里有没有reshape或者view操作,它们会把batch维写死,导出时最好用onnx的symbolic shape推理或者干脆把固定shape的层改成支持动态的版本。实在不行,先用onnx-simplifier过一遍模型,有时候能自动修掉这类隐性问题。
试下在导出的时候把input和output的dynamic_axes都写上,我之前漏了output那边,结果跟你一样的情况。另外ResNet50里的BatchNorm在ONNX里是支持动态batch的,问题多半出在reshape或者flatten层,建议用onnxsim简化一下图看看。还有你推理时输入数据的shape是不是(N,C,H,W)排对了,有时报错其实是数据维度顺序搞反了。
大概率是ONNX显式batch维和模型内部reshape逻辑冲突了,试试导出时把dynamic_axes的input和output都绑上,或者检查下ResNet里有没有硬编码view。
我之前也遇到过一模一样的坑,问题多半不在dynamic_axes设置上,而是ResNet里adaptive avg pool或者flatten层对shape有硬编码。你试试用onnxsim精简一下图,再检查下中间节点的输出shape,或者干脆用onnxruntime的symbolic shape debug工具定位到具体层。另外确认下你导出时是不是只设了input的dynamic,但中间tensor的batch维度还是静态的,需要把关键的reshape/transpose也加上动态轴才行。
我之前也卡过这问题,后来发现是onnxruntime的session options里没开动态shape支持,得显式配一下优化级别或者用onnxruntime-tools把模型优化一遍,不然它默认按静态图跑。另外你检查下ResNet里的GlobalAveragePooling,这层对batch维度其实没问题,但有些实现会把flatten写死成二维,导致动态轴失效。还有个小坑是导出时input的dynamic_axes只设了第0维,但模型里其他中间tensor的batch维度可能被推理时隐式固定了,建议用onnx-simplifier跑一遍看看有没有冗余的shape节点。我最后是干脆把batch设成1导出,然后推理时用循环伪造batch,虽然慢点但稳。
我之前也遇到过一模一样的坑,问题大概率不在dynamic_axes本身,而是你导出时输入张量的shape写死了。试试在torch.onnx.export里把input_names对应的示例输入改成[1, 3, 224, 224],然后dynamic_axes只标batch那一维,别动其他维度,这样ONNX的输入符号才会真正变成动态。另外BatchNorm在推理模式是纯逐元素操作,跟batch大小无关,不是它的锅。如果还不行,检查下onnxruntime的session选项,把execution_mode设为ORT_PARALLEL或者关掉图优化再跑一次,有时是优化器把动态轴搞坏了。
大概率是ONNX里有个Reshape或者Flatten把batch维写死了,查下导出后的图结构吧。