最近在把训练好的一个图像分类模型(ResNet50)转成ONNX,部署到移动端用。按照文档设了dynamic_axes={‘input’: {0: ‘batch_size’}},导出也成功了。但用onnxruntime推理时,只要batch_size不是1就报错,说“输入形状不匹配”?试了opset版本11和12都一样。是不是导出时少设了什么参数,比如输入尺寸的dynamic_axes必须和模型内部结构对应?还是说模型里有不支持动态batch的层(比如BatchNorm)?求有经验的大佬指点一下,我实在不想把batch固定为1。
PyTorch模型转ONNX时,动态轴设了但推理报错,有老哥踩过坑吗?
全部回复
共 165 条可能是BatchNorm里的running mean对动态batch不兼容,试试把模型设成eval模式再导出。
这问题我遇到过类似的,关键点在于dynamic_axes只控制了导出的接口,但模型内部如果有reshape或flatten操作硬编码了固定维度,推理时就会报形状不匹配。ResNet50本身没啥动态batch的限制,建议先检查一下onnx模型里是否有像“Reshape”节点绑死了第二维的形状。另外可以试试用onnx-simplifier简化一下,或者直接设一个动态的batch维度但把输入的实际shape同时改小一点测试。
试试把BatchNorm层设成eval模式再导出,训练模式下的动态batch确实容易翻车。
我也遇到过类似问题,动态轴设了但推理报错,后来发现是模型里有些reshape或flatten操作没有处理好,导致导出的onnx对动态batch支持不完整。你可以先用onnx-simplifier优化一下图结构,再检查下是否有不支持动态尺寸的算子,比如某些版本的Resize。另外,onnxruntime的session选项里记得把optimization_level调低点,有时候优化反而会破坏动态shape逻辑。
BatchNorm在ONNX里对动态batch支持确实有问题,试试把eval模式下的bn层固定住再导出。
动态轴设置看起来没问题,但报错大概率是ResNet里的BatchNorm在导出时对动态batch支持不完整,你可以试试把opset升到15以上,新版本对动态形状处理更好。另外检查下模型里有没有reshape或者flatten操作依赖了固定尺寸,这些层在动态batch下容易翻车。我之前也遇到过类似问题,最后是用onnx-simplifier优化了一遍才跑通。
这个问题大概率不是dynamic_axes没设对,而是模型里某些操作(比如reshape、view或者张量拼接)依赖了固定batch size,导致onnxruntime推理时shape推导出问题。ResNet50本身没有BatchNorm不支持动态batch的说法,倒是可以检查下导出时是不是用了torch.onnx.export的input_names和output_names,同时把dynamic_axes写全,比如输出也得加上batch维度。我之前踩过类似的坑,最后发现是第三方库的某个模块在转onnx时偷偷写死了shape,得手动改模型结构。
一般遇到这种情况,大概率是ONNX导出的输入形状没写死,但模型内部某些op(比如reshape或者gather)依赖了静态形状信息,导致动态batch推理时计算图对不上。可以试试用onnx-simplifier或者直接torch.onnx.export时把input的shape设成[1,3,224,224]但动态轴只保留batch,然后跑个batch=2的测试看看报错具体卡在哪个节点。BatchNorm在eval模式下不影响动态batch,反而是全连接层前的Flatten或AdaptiveAvgPool2d容易出问题。
这个坑我也踩过,问题大概率出在模型内部有些op对动态shape支持不友好,比如reshape或者某些自定义层。你可以先试试把opset升到15或者17,新版本对动态轴兼容性更好。另外导出的onnx可以用netron看一下图结构,检查下有没有硬编码了固定维度的节点,比如某些全连接层后面跟了Flatten。如果还不行,试试把BatchNorm换成GroupNorm或者InstanceNorm,有时候是bn的running_mean/var维度锁死了。
检查下onnxruntime的session配置,可能默认锁了输入shape,加个GraphOptimizationLevel试试。
可能和你模型里reshape或view操作有关,这些层在ONNX里默认不支持动态batch,得手动改一下。
我之前也遇到过跟你一模一样的问题,搞了一整天才发现是torch.onnx.export时忘了设input_names和output_names,虽然动态轴设了,但onnxruntime推理时内部节点的形状信息没跟着变。另外ResNet50里的BatchNorm在ONNX里是支持的,但如果你在forward里用了torch.squeeze这种改变维度的操作,容易把batch维度搞死,建议检查一下模型里有没有对batch维度做硬编码的reshape。最后可以试试把opset升到13以上,有些旧版本对动态batch支持不太行。
这个问题我也踩过,一开始也以为是dynamic_axes设了就万事大吉,后来发现坑在模型内部某些算子对动态shape的支持其实有限。ResNet50本身结构里BatchNorm和全局平均池化按理说是支持动态batch的,但如果你在模型里用了torch.view或者reshape写死了某个维度的数值,导出时torch.onnx.export的input_names和output_names对应的shape参数可能没正确传递动态信息。建议你检查一下onnx模型里input的shape是不是真的带有动态标记,比如用netron可视化看看,或者用onnx.shape_inference跑一下。另外,opset版本可以试试13或更高,有些动态shape相关的bug在后续版本修了。还有一个骚操作是导出时把input的shape设成[1,3,224,224],但dynamic_axes里把batch_size从0到1024都标上,然后在推理时用ort的IOBinding手动reshape输入输出,这样能绕过一些算子检查。最后,如果实在不行,考虑在模型入口加个unsqueeze/squeeze的预处理,动态batch用循环推理替代,虽然性能差点但至少能跑。
老实说,我当年也在这块卡了好久,你这情况太典型了。动态轴导出成功不代表推理时完全兼容,ONNX对动态batch的支持其实挺玄学的,尤其是ResNet50这种带BatchNorm的结构——虽然BN层本身在推理模式下是固定的,但ONNX导出时如果模型里用了torch.nn.BatchNorm2d,并且你导出时用的是model.eval(),那它的running_mean和running_var会被固定成常量,按理说不该影响动态batch。但你试试输入形状里除了batch_size,有没有把C,H,W也设成动态?有时候onnxruntime对部分动态维度支持不好,比如它要求所有动态维度的实际值必须在同一个输入中保持一致。还有个坑:检查下你导出时有没有用torch.onnx.export里的input_names和output_names参数,并且确认dynamic_axes里的名字和这几个名字严格对应。如果还不行,建议用onnxsim简化下模型,或者直接用onnxruntime的session选项里设置“优化级别”为0,排除掉图优化带来的形状冲突。最后实在不行的话,我见过有人用onnxruntime的IOBinding接口手动绑张量绕过去的,虽然麻烦但能解。
你这大概率是模型里某个reshape或concat操作把batch维写死了,试试用onnx-simplifier修一下。
这个问题我之前也遇到过,动态轴设了但实际跑不起来,大概率是模型里某个算子对固定shape有依赖。ResNet50的BatchNorm层其实不影响动态batch,反而是最后的GlobalAveragePooling或Flatten操作在某些opset下会隐式假定输入维度固定。你可以试下把opset升到13或更高,或者导出时显式把input的shape设成[None, 3, 224, 224]再试试。另外,用onnx-simplifier简化一下模型结构,有时候能自动修复这类形状冲突。
这个坑我当初也踩过,搞了两天才发现是BatchNorm层在捣鬼。虽然PyTorch里BatchNorm默认是支持动态batch的,但转ONNX时如果模型里有训练模式和eval模式的差异没处理好,导出的静态图可能会把batch维度的计算方式写死。你可以试试导出前把模型设成model.eval(),再把torch.onnx.export里的do_constant_folding设为False,有时候常量折叠会错误地优化掉动态轴。另外检查一下你的输入数据是不是真的用了Variable或者requires_grad=True,这些细节也容易导致形状推导出问题。还有个偏方:把opset版本降到10试过没?我有个模型在11和12上死活不行,降到10反而正常了,虽然官方说动态轴支持更完善,但实际兼容性有时候新版反而抽风。如果还不行,建议用onnx-simplifier先简化一下模型结构,把那些花里胡哨的shape操作抹平,再配合onnxruntime的session选项里加个graph_optimization_level=0,强行关掉优化看看。
这个问题我刚好遇到过,ResNet50里BatchNorm在动态batch下确实没问题,但很可能是模型里某些reshape或view操作写死了形状,比如用了tensor.size(0)这种硬编码,导出时ONNX trace会把batch维度固定住。你可以试试用torch.onnx.export时加上dynamic_axes的同时,把输入的真实shape设成[2,3,224,224]或[4,3,224,224]跑一遍看看有没有warning,或者检查下模型里有没有类似x.view(-1, 2048)这种写法,改成动态适配的版本应该能解决。
这个坑我确实也踩过,折腾了好几天才发现问题不在dynamic_axes本身,而是模型里有些算子对动态shape支持不完善。你提到BatchNorm,其实BN在推理模式下是冻结的,理论上不影响动态batch,但ONNX导出时如果用了torch.onnx.export的默认参数,它可能把某些reshape或view操作写死了形状,导致batch维度被硬编码。我建议你先用onnxruntime的session.run_with_ort_value验证一下导出的onnx模型输入输出的shape描述,看看是不是input的shape里batch维度标记成了固定值。另外,ResNet50里有个adaptive_avg_pool2d,它在某些opset版本下对动态batch支持有问题,你可以试试把opset升到13或更高,或者手动把模型里的adaptive pooling替换成固定尺寸的avg pool。还有个取巧的办法:导出时用torch.onnx.export时加上dynamic_axes后再加个input_names参数,并且确保dummy_input的batch_size设为1但shape里对应维度写None,这样能骗过一些检查。如果还不行,建议用onnx-simplifier工具简化一下模型,有时候是torch内部的一些复合操作展开成了动态不兼容的子图。
我最近也遇到过类似的问题,后来发现是模型里有个reshape操作没处理好,导出时onnx会把它变成静态shape。你可以检查下模型里有没有用到view或reshape,最好用onnx::Reshape或者加个Symbolic函数把动态维度传进去。另外,BatchNorm对动态batch是支持的,问题大概率出在reshape或者某些自定义层上。