ONNXRUNTIME_EXCEPTION : Non-zero status code returned while running Where node. Name:‘Where‘

本文详细介绍了在ONNX模型转换过程中遇到的错误处理,如InvalidArgument和RuntimeException,并提供了使用Netron工具检查模型网络图的方法。重点讨论了torch.where、torch.gather和torch.split等Tensor运算,解释了它们的功能和用法。对于torch.where,它是条件选择操作;torch.gather用于按索引收集数据;torch.split则用于将张量切分成多个块。理解并正确使用这些运算能有效解决ONNX转换问题。

遇到此类错误,如:

onnxruntime.capi.onnxruntime_pybind11_state.InvalidArgument: [ONNXRuntimeError] : 2 : INVALID_ARGUMENT : Non-zero status code returned while running Gather node. Name:'Gather_4445' Status Message: indices element out of data bounds, idx=8 must be within the inclusive range [-3,2]

或:

RUNTIME_EXCEPTION : Non-zero status code returned while running Where node. Name:'Where' Status Message...

可以配合Netron工具(安装方法:pip install netron,使用时终端输入netron)查看导出的onnx模型网络图,可以查找相应的Node(如:Where_XXXX),再去代码中找对应代码,将其改为onnx支持的tensor运算方式即可解决相应问题。

根据在ONNX导出时遇到的问题比较麻烦的是和torch.gather、torch.where、torch.split等Tensor运算方法。

1. torch.where函数

torch.where(condition,x,y)->tensor

当满足condition,则来自于a,反之来自b

import torch
condition=torch.randn(2,2)
# tensor([[ 0.2589, -0.5600],
#        [ 0.9056, -0.3915]])
a=torch.tensor([[0,0],[0,0]])
b=torch.tensor([[1,1],[1,1]])
torch.where(cond>0.5,a,b)

得到结果

tensor([[1, 1],
        [0, 1]])

输出为0的代表来源为a,输出为1的代表来源为b

2. torch.gather(查表的过程)

torch.gather(input,dim,index,out=None)->tensor

就像是给了数据以后,查表得到对应参数,再收集回来进行输出。

gather函数即为gather(对应的参数表,dim,数据表)

import torch
prob=torch.randn(4,4)
#tensor([[-0.9845,  0.5094, -0.5014, -0.5354],
#        [-1.8514,  0.2640,  0.7895, -0.1660],
#        [ 0.3955,  0.7571,  0.1451,  0.1970],
#        [ 0.3674, -0.8006, -0.5625,  1.3455]])
idx=prob.topk(dim=1,k=2)
idx=idx[1]
#tensor([[1, 2],
#        [2, 1],
#        [1, 0],
#        [3, 0]]))
label=torch.arange(4)+100
#tensor([100, 101, 102, 103])
torch.gather(label.expand(4,4),dim=1,index=idx.long())

 输出结果:

tensor([[101, 102],
        [102, 101],
        [101, 100],
        [103, 100]])

3. torch.split

含义:将一个张量分为几个chunks

torch.split(tensor, split_size_or_sections, dim=0)

参数

  • tensor(Tensor) -张量分裂。

  • split_size_or_sections(int) 或者(list(int)) -单个块的大小或每个块的大小列表

  • dim(int) -沿其分割张量的维度。

如果split_size_or_sections 是整数类型,那么tensor将被分成大小相等的块(如果可能)。如果沿给定维度 dim 的张量大小不能被 split_size 整除,则最后一个块会更小。

如果 split_size_or_sections 是一个列表,那么 tensor 将根据 split_size_or_sections 被拆分为大小在 dim 中的 len(split_size_or_sections) 块。

示例:

>>> a = torch.arange(8).reshape(4,2)
>>> a
tensor([[0, 1],
        [2, 3],
        [4, 5],
        [6, 7]])
>>> torch.split(a, 3)
(tensor([[0, 1],
         [2, 3],
         [4, 5]]),
 tensor([[6, 7]]))
>>> torch.split(a, [1,3])
(tensor([[0, 1]]),
 tensor([[2, 3],
         [4, 5],
         [6, 7]]))

4. Tensor.scatter_函数

TORCH.TENSOR.SCATTER_

Tensor.scatter_(dimindexsrcreduce=None) → Tensor

Writes all values from the tensor src into self at the indices specified in the index tensor. For each value in src, its output index is specified by its index in src for dimension != dim and by the corresponding value in index for dimension = dim.

For a 3-D tensor, self is updated as: 

self[index[i][j][k]][j][k] = src[i][j][k]  # if dim == 0
self[i][index[i][j][k]][k] = src[i][j][k]  # if dim == 1
self[i][j][index[i][j][k]] = src[i][j][k]  # if dim == 2
This is the reverse operation of the manner described in gather().

selfindex and src (if it is a Tensor) should all have the same number of dimensions. It is also required that index.size(d) <= src.size(d) for all dimensions d, and that index.size(d) <= self.size(d) for all dimensions d != dim. Note that index and src do not broadcast.

Moreover, as for gather(), the values of index must be between 0 and self.size(dim) - 1 inclusive.

Parameters

  • dim (int) – the axis along which to index

  • index (LongTensor) – the indices of elements to scatter, can be either empty or of the same dimensionality as src. When empty, the operation returns self unchanged.

  • src (Tensor or float) – the source element(s) to scatter.

  • reduce (stroptional) – reduction operation to apply, can be either 'add' or 'multiply'.

总结:scatter函数就是把src数组中的数据重新分配到output数组当中,index数组中表示了要把src数组中的数据分配到output数组中的位置,若未指定,则填充0.

举例:

>>> src = torch.arange(1, 11).reshape((2, 5))
>>> src
tensor([[ 1,  2,  3,  4,  5],
        [ 6,  7,  8,  9, 10]])
>>> index = torch.tensor([[0, 1, 2, 0]])
>>> torch.zeros(3, 5, dtype=src.dtype).scatter_(0, index, src)
tensor([[1, 0, 0, 4, 0],
        [0, 2, 0, 0, 0],
        [0, 0, 3, 0, 0]])
>>> index = torch.tensor([[0, 1, 2], [0, 1, 4]])
>>> torch.zeros(3, 5, dtype=src.dtype).scatter_(1, index, src)
tensor([[1, 2, 3, 0, 0],
        [6, 7, 0, 0, 8],
        [0, 0, 0, 0, 0]])

>>> torch.full((2, 4), 2.).scatter_(1, torch.tensor([[2], [3]]),
...            1.23, reduce='multiply')
tensor([[2.0000, 2.0000, 2.4600, 2.0000],
        [2.0000, 2.0000, 2.0000, 2.4600]])
>>> torch.full((2, 4), 2.).scatter_(1, torch.tensor([[2], [3]]),
...            1.23, reduce='add')
tensor([[2.0000, 2.0000, 3.2300, 2.0000],
        [2.0000, 2.0000, 2.0000, 3.2300]])

<think>我们正在处理一个关于ONNXRuntime量化YOLO 11模型时出现的错误。错误信息如下: ``` onnxruntime.capi.onnxruntime_pybind11_state.RuntimeException: [ONNXRuntimeError] : 6 : RUNTIME_EXCEPTION : Non-zero status code returned while running ReduceMax node. Name:'/Concat_2_output_0_ReduceMax' Status Message:' ``` 已知ONNX模型在非量化模式下运行正常,但进行量化(可能是动态量化或静态量化)时出现此错误。 ### 问题分析 该错误发生在运行`ReduceMax`节点时,节点名为`/Concat_2_output_0_ReduceMax`。这通常意味着在量化过程中,该节点的输入数据或节点本身的属性导致了一个运行时异常。 可能的原因包括: 1. **量化类型不匹配**:ReduceMax操作可能不支持某些量化类型(如int8)或量化方式。 2. **输入数据范围问题**:量化过程中,ReduceMax节点的输入数据可能包含超出量化范围的值(如NaN或Inf)。 3. **模型结构问题**:模型中的某些操作(如Concat)可能产生了不兼容的数据类型,导致ReduceMax无法处理。 4. **ONNXRuntime量化限制**:ONNXRuntime的量化功能可能不支持某些特定算子(ReduceMax)在特定配置下的量化。 ### 解决方案 #### 1. 检查ReduceMax节点的输入 使用Netron或其他ONNX模型可视化工具打开模型,定位到`/Concat_2_output_0_ReduceMax`节点,检查其输入来源(即上一个节点`Concat_2`的输出)的数据类型和形状。确保在量化后,输入数据是有效的(没有NaN或Inf)。 #### 2. 尝试不同的量化方法 如果当前使用的是静态量化(需要校准数据集),尝试使用动态量化(不需要校准数据集): ```python from onnxruntime.quantization import quantize_dynamic, QuantType quantize_dynamic( 'model.onnx', 'model_quant.onnx', weight_type=QuantType.QUInt8 # 或尝试QuantType.QInt8 ) ``` 或者,如果使用的是动态量化,尝试静态量化: ```python from onnxruntime.quantization import QuantFormat, QuantType, StaticQuantConfig, quantize_static # 准备校准数据集(示例代码片段) # 需要提供一个校准数据生成器 def get_calibration_dataset(): # 返回一个迭代器,每次产生一个输入数据字典:{input_name: tensor} ... quantize_static( 'model.onnx', 'model_quant.onnx', StaticQuantConfig( calibration_data_reader=get_calibration_dataset(), quant_format=QuantFormat.QOperator, activation_type=QuantType.QUInt8, weight_type=QuantType.QUInt8 ) ) ``` #### 3. 检查ReduceMax节点的版本 在ONNX中,不同版本的算子支持不同的特性。使用Netron查看ReduceMax节点的算子版本(opset version),确保ONNXRuntime支持该版本算子的量化。 #### 4. 尝试忽略ReduceMax节点的量化 如果上述方法无效,可以尝试在量化时排除ReduceMax节点: ```python from onnxruntime.quantization import quantize_static, QuantType, CalibrationMethod # 创建量化配置,并指定需要跳过的节点 quant_config = StaticQuantConfig( calibration_data_reader=get_calibration_dataset(), op_types_to_quantize=['Conv', 'MatMul', 'Add'], # 只量化这些类型的节点 nodes_to_quantize=[...], # 指定要量化的节点名称列表(不包括ReduceMax节点) nodes_to_exclude=['/Concat_2_output_0_ReduceMax'] # 排除ReduceMax节点 ) quantize_static( 'model.onnx', 'model_quant.onnx', quant_config ) ``` #### 5. 更新ONNXRuntime版本 确保使用的是最新版本的ONNXRuntime,因为更新的版本可能修复了相关bug: ```bash pip install --upgrade onnxruntime ``` #### 6. 检查模型优化 在量化之前,先使用ONNXRuntime的模型优化工具对模型进行优化,排除一些可能引起问题的冗余操作: ```python from onnxruntime.tools import optimize_model optimized_model = optimize_model('model.onnx') optimized_model.save('model_optimized.onnx') ``` 然后对优化后的模型进行量化。 ### 总结 1. 首先尝试更新ONNXRuntime并检查模型输入是否正常。 2. 尝试不同的量化方法(动态/静态)和参数(如量化类型)。 3. 如果问题节点非关键,可以尝试跳过该节点的量化。 4. 使用模型优化工具预处理模型。 §§相关问题§§ 1. ONNXRuntime量化模型时如何选择合适的量化方法(动态/静态)? 2. 如何查看ONNX模型中某个节点的详细信息? 3. 如何处理ONNXRuntime量化过程中不支持的节点? 4. 如何生成ONNXRuntime静态量化所需的校准数据集? </think>以下是针对 YOLO11 ONNX 模型量化时出现 `ReduceMax` 节点错误的解决方案分析: ### 问题诊断 该错误通常发生在动态量化过程中,表明 `ReduceMax` 算子实现存在兼容性问题: $$ \text{Error} = \begin{cases} \text{ONNXRuntime 版本 < 1.15} \\ \text{量化参数冲突} \\ \text{节点输入维度不匹配} \end{cases} $$ ### 解决方案 #### 1. 升级 ONNXRuntime 版本 ```bash pip install onnxruntime-gpu --upgrade # GPU版本 # 或 pip install onnxruntime --upgrade # CPU版本 ``` > **验证版本**:需 ≥ 1.15.1(修复了ReduceMax量化兼容性)[^1] #### 2. 显式指定量化配置 在量化脚本中强制指定 ReduceMax 的输入类型: ```python from onnxruntime.quantization import QuantType, quantize_dynamic quantize_dynamic( "yolo11.onnx", "yolo11_quant.onnx", op_types_to_quantize=["Conv", "MatMul", "ReduceMax"], # 显式包含ReduceMax per_channel=True, weight_type=QuantType.QInt8, # 尝试QInt8或QUInt8 nodes_to_quantize=["/Concat_2_output_0"] # 指定问题节点路径 ) ``` #### 3. 修改模型预处理 在量化前添加输入维度检查: ```python import onnx model = onnx.load("yolo11.onnx") for node in model.graph.node: if node.name == "/Concat_2_output_0_ReduceMax": # 确保输入维度有效 if node.input[0].type.tensor_type.shape.dim[0].dim_value == 0: node.input[0].type.tensor_type.shape.dim[0].dim_value = 1 onnx.save(model, "yolo11_fixed.onnx") ``` #### 4. 备用量化方案 ```python # 方案A:仅量化权重 quantize_dynamic(..., weight_type=QuantType.QUInt8, optimize_model=False) # 方案B:使用静态量化 from onnxruntime.quantization import CalibrationDataReader class CustomDataReader(CalibrationDataReader): ... # 实现校准数据集 quantize_static( "yolo11.onnx", "yolo11_quant.onnx", calibration_data_reader=CustomDataReader(), activation_type=QuantType.QUInt8 ) ``` ### 验证步骤 1. 检查量化后模型结构: ```bash python -m onnxruntime.tools.check_onnx_model yolo11_quant.onnx ``` 2. 测试推理: ```python import onnxruntime as ort sess = ort.InferenceSession("yolo11_quant.onnx") outputs = sess.run(None, {"input": sample_data}) ``` ### 常见错误原因 | 序号 | 原因 | 解决方案 | |------|------|----------| | 1 | ONNX算子版本冲突 | 导出模型时指定`opset_version=15` | | 2 | 空输入张量 | 添加维度检查预处理 | | 3 | 量化范围溢出 | 使用`CalibrationDataReader`校准 |
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值