ONNX 转 TensorRT Bug 记录:IIfConditionalOutputLayer

server/2024/12/28 22:38:12/

1. 问题描述

环境:TensorRT-8.6.1.6、CUDA-11.8

报错:Error[4]: /If_OutputLayer: IIfConditionalOutputLayer inputs must have the same shape. Shapes are [-1,384] and [-1,1,384].

复现代码:

python">import os
import torch
import torch.nn as nn
import torch.nn.functional as Fclass TestModel(torch.nn.Module):def __init__(self, mode):super().__init__()self.mode = modeself.conv = nn.Conv1d(512, 512, 3, 2, 1)def forward(self, x, mask):if self.mode == 1:return self.forward1(x, mask)elif self.mode == 2:return self.forward2(x, mask)elif self.mode == 3:return self.forward3(x, mask)elif self.mode == 4:return self.forward4(x, mask)else:raise ValueError("Invalid mode")def forward1(self, x, mask):mask = mask.unsqueeze(1)x = self.conv(x)mask = F.interpolate(mask, size=x.size(-1), mode="nearest")x = x * maskmask = mask.squeeze(1)return x, maskdef forward2(self, x, mask):mask = mask.unsqueeze(1)x = self.conv(x)mask = F.interpolate(mask, size=384, mode="nearest")x = x * maskmask = mask.squeeze(1)return x, maskdef forward3(self, x, mask):mask = mask.unsqueeze(1)x = x * maskmask = mask.squeeze(1)return x, maskdef forward4(self, x, mask):mask = mask.unsqueeze(1)x = self.conv(x)mask = F.interpolate(mask, size=x.size(-1), mode="nearest")x = x * maskb = x.shape[0]mask = mask.reshape(b, -1)return x, maskfake_input = torch.randn(1, 512, 768)
fake_mask = torch.randn(1, 768)
model1 = TestModel(1)
model2 = TestModel(2)
model3 = TestModel(3)
model4 = TestModel(4)with torch.no_grad():print([x.shape for x in model1(fake_input, fake_mask)])print([x.shape for x in model2(fake_input, fake_mask)])print([x.shape for x in model3(fake_input, fake_mask)])print([x.shape for x in model4(fake_input, fake_mask)])dynamic = {"x_input": {0: "batch"},"masks_input": {0: "batch"},"x_output": {0: "batch"},"masks_output": {0: "batch"}
}save_dir = "log"
os.makedirs(save_dir, exist_ok=True)for i, model in enumerate((model1, model2, model3, model4)):torch.onnx.export(model.cpu().eval(),(fake_input.cpu(), fake_mask.cpu()),os.path.join(save_dir, f'dynamic_{i+1}.onnx'),verbose=True,opset_version=17,do_constant_folding=True,input_names=["x_input", "masks_input"],output_names=["x_output", "masks_output"],dynamic_axes=dynamic)torch.onnx.export(model.cpu().eval(),(fake_input.cpu(), fake_mask.cpu()),os.path.join(save_dir, f'static_{i+1}.onnx'),verbose=True,opset_version=17,do_constant_folding=True,input_names=["x_input", "masks_input"],output_names=["x_output", "masks_output"],dynamic_axes=None)
exec="/home/sfy/SFY/camera/TensorRT-8.6.1.6/bin/trtexec"
dir="log"
rm "$dir"/*.txt "$dir"/*.plan: <<'EOF'
# onnx-simplifier 移除冗余节点
for file in "$dir"/*.onnx; doif [[ ! -f "$file" ]]; thenecho "No .onnx files found in $dir."continuefifn=$(basename "$file")python -m onnxsim "$file" "$dir/${fn%.onnx}_simp.onnx"
done
EOFfor file in "$dir"/*.onnx; doif [[ ! -f "$file" ]]; thenecho "No .onnx files found in $dir."continuefifn=$(basename "$file")if [[ $fn == dynamic* ]]; then$exec \--onnx="$file" \--saveEngine="$dir/${fn%.onnx}.plan" \--minShapes=x_input:1x512x768,masks_input:1x768 \--optShapes=x_input:2x512x768,masks_input:2x768 \--maxShapes=x_input:4x512x768,masks_input:4x768 \--verbose \> "$dir/${fn%.onnx}.txt" 2>&1elif [[ $fn == static* ]]; then$exec \--onnx="$file" \--saveEngine="$dir/${fn%.onnx}.plan" \--verbose \> "$dir/${fn%.onnx}.txt" 2>&1elsecontinuefi
done

  上述代码对源代码做了简化,仅剥离出造成问题的部分,用于复现和测试 Bug,输入可以看作 1 个 Batch 有 768 张图像序列,每个图像用 512 维特征向量表示;masks 原本是 bool 类型代表 768 张图像是真实数据还是填充数据。

  经过测试发现 Bug 由动态模式、F.interpolatesqueeze 组合引发,因此代码推理阶段分为以下 4 种模式:
(1)F.interpolate(mask, size=x.size(-1), mode="nearest") + mask.squeeze(1) 报错
(2)F.interpolate(mask, size=384, mode="nearest") + mask.squeeze(1) 动态模式报错,静态模式通过
(3)mask.squeeze(1) 通过
(4)F.interpolate(mask, size=x.size(-1), mode="nearest") + mask.reshape(b, -1)mask.view(b, -1) 通过

2. 解决方法

  用 reshapeview 代替 squeeze

3. 原因分析

  总结:squeeze 操作需要判断对应维度是否等于 1,而 F.interpolate 改变了张量形状、动态模式引入了维度数值的不确定性,这些使得 onnx 无法确定该维度是否等于 1(即使看上去可以推导出数值),导致添加了 If 节点,而 If 节点在不同状态下分别执行 SqueezeIdentity 导致输出形状不统一。采用 Reshape 操作可以直接规避此问题。

  下面是不同模式 ONNX 的结构图对比,以及分析细节。

mode1

在这里插入图片描述在这里插入图片描述
  mode1 在插值时使用 x.size(-1),在动态模式下需要通过 Shape 等一系列节点来获取维度信息;在静态模式下 x 所有初始维度固定,把 x.size(-1) 看作固定常量。

mode2

在这里插入图片描述在这里插入图片描述
  动态模式下 masks 经过 Resize 维度信息是不确定的,导致需要判断 masks.shape[1] == 1 才能执行 masks.squeeze(1),问题在于当不等于 1 时执行的是 Identity 导致输出形状不一致。

  比较令人费解的是对比 mode1 和 mode2 静态模式,可以发现 Resize 之前的结构完全相同,但是 mode1 在 Resize 之后仍引入了 If 节点导致异常。查到的解释是 squeeze 操作比较保守,mode1 原本 Resize 的维度是动态的依赖 x.size(-1),即使通过推导将动态转变为静态,但仍保留了动态逻辑(If 节点)。
  启用脚本中 onnx-simplifier 移除冗余节点的部分可以去除 mode1 静态模式中的 If 节点,但此方法对动态模式无效。

mode3

在这里插入图片描述在这里插入图片描述
  masks 维度的动态(不确定性)由 Resize 引入。

mode4

在这里插入图片描述在这里插入图片描述
  不使用 squeeze 便不需要判断维度是否符合要求,直接规避此问题。


http://www.ppmy.cn/server/154040.html

相关文章

私域电商逆袭密码:AI 智能名片小程序与商城系统如何梦幻联动

摘要&#xff1a;在当今数字化电商蓬勃发展的浪潮下&#xff0c;AI 智能名片小程序与商城系统的整合成为推动电商业务进阶的关键着力点。本文聚焦于入口融合、数据共享以及联合推广三大核心整合策略&#xff0c;结合时尚、运动等多领域实例&#xff0c;深入剖析二者整合如何优化…

Mono里运行C#脚本3—mono_jit_init

前面已经介绍了配置参数的读取,这样就可以把一些特殊的配置读取进来,完成了用户配置阶段的参数,接着下来就需要进行大工程的建造了。 为什么这样说呢,因为需要解释并执行C#编译的受托管的代码,相当于就是建立一个C#代码运行的虚拟机,而这个虚拟机还是很复杂的,不但要支…

【Linux】ChatGLM-4-9B模型之All Tools

一、摘要 最近在研究GLM4模型&#xff0c;发现自带的All Tools比较感兴趣&#xff0c;它具有完整工具调用能力的对话模式&#xff0c;原生支持网页浏览、代码执行、图表生成、图片生成&#xff0c;并支持自定义工具。它能够满足大模型私有化部署的个性定制&#xff0c;因此记录…

ADC(二):外部触发

有关ADC的基础知识请参考标准库入门教程 ADC&#xff08;二&#xff09;&#xff1a;外部触发 1、TIM1的CC1事件触发ADC1DMA重装载2、TIM3的TRGO事件(的更新事件)触发ADC1DMA重装载3、TIM3的TRGO事件(的捕获事件)触发ADC1DMA重装载4、优化TIM3的TRGO事件(的捕获事件)触发ADC1D…

Effective C++ 条款 23:宁以 non-member、non-friend 替换 member 函数

文章目录 条款 23&#xff1a;宁以 non-member、non-friend 替换 member 函数核心思想示例代码注意事项 条款 23&#xff1a;宁以 non-member、non-friend 替换 member 函数 核心思想 更高的封装性 non-member 函数不需要访问类的 private 或 protected 成员&#xff0c;减少对…

vscode添加全局宏定义

利用vscode编辑代码时&#xff0c;设置了禁用非活动区域着色后&#xff0c;在一些编译脚本中配置的宏又识别不了 遇到#ifdef包住的代码就会变暗色&#xff0c;想查看代码不是很方便。如下图&#xff1a; 一 解决&#xff1a; 在vscode中添加全局宏定义。 二 步骤&#xff1a…

Java项目中Oracle数据库开发过程中相关内容

目录 1、连接数据库 2、创建用户和授权 3、统计的时候——把列变成行 4、Oracle12c数据库中&#xff0c;根据时间倒序返回最新一条数据 5、其他SQL相关记录 总结一些和Oracle相关的内容 1、连接数据库 使用oracle12c数据库自带的SQL Plus 链接数据库 打开SQL Plus工具&#xf…

头歌-边缘检测

第1关&#xff1a;边缘检测的基本原理与图像增强 任务描述 本关任务&#xff1a;理解边缘检测的基本概念&#xff0c;掌握 Roberts 算子的原理与操作。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 1.边缘检测的概念&#xff1b; 2. Roberts 算子的原理与操…