Torch 转 ONNX遇到UnsupportedOperatorError: Exporting the operator ::resolve_conj to ONNX opset version

博客主要讲述Torch转ONNX时遇到UnsupportedOperatorError的问题,当model中存在print时会出现该错误,给出的解决办法是注释或删除model结构中所有的print。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Torch 转 ONNX遇到UnsupportedOperatorError: Exporting the operator ::resolve_conj to ONNX opset version

import torch
from torch import nn

class BadFirst(nn.Module):
    def __init__(self):
        super().__init__()

    def forward(self, x):
        x_firsts = x[:, 0]
        ## 在转换onnx时,一定要注释或删除model结构中所有的print
        print(f"x_firsts: {x_firsts}") # removing this line fixes the issue.
        return x_firsts


m = BadFirst().eval()
x = torch.rand(10, 5)

res = m(x) # this works
torch.onnx.export(m, x, "m.onnx") # this fails due to a resolve_conj op

在这里插入图片描述

如果model中存在print则会出现以下错误。

解决办法:注释或删除model结构中所有的print

<think>我们遇到了一个错误:尝试将包含 `aten::linalg_inv` 运算符的PyTorch模型导出到ONNX时,由于ONNX opset版本15不支持该运算符而失败。 ### 解决方案概述 1. **升级ONNX opset版本**:尝试使用更高的ONNX opset版本(如16或17)可能支持该运算符。 2. **替代方法**:使用其他ONNX支持的运算符组合来替代矩阵求逆运算。 3. **自定义运算符**:如果必须使用该运算符且没有直接替代,可以自定义ONNX运算符(但较复杂)。 ### 详细步骤与代码 #### 方法1:升级ONNX opset版本 PyTorchONNX导出函数允许指定opset版本。我们可以尝试使用更高的opset版本(如16或17)来支持`linalg_inv`。 ```python import torch import torch.onnx # 示例模型(包含矩阵求逆) class ModelWithInverse(torch.nn.Module): def forward(self, x): # 假设x是一个方阵,我们要求逆 return torch.linalg.inv(x) # 创建模型实例 model = ModelWithInverse() # 创建输入(随机方阵) input_tensor = torch.randn(3, 3, dtype=torch.float32) # 导出模型到ONNX,尝试使用opset版本16 try: torch.onnx.export( model, input_tensor, "model_with_inverse.onnx", opset_version=16, # 使用更高的opset版本 input_names=['input'], output_names=['output'], dynamic_axes={'input': {0: 'dim0', 1: 'dim1'}} # 如果需要动态尺寸 ) print("导出成功!") except Exception as e: print(f"导出失败: {e}") ``` **注意**:ONNX opset版本16开始支持`linalg_inv`操作符(通过`org.onnx`扩展)。如果使用opset版本16仍然失败,可以尝试17或18。 #### 方法2:使用替代运算符(矩阵分解方法) 如果升级opset版本不可行,我们可以使用矩阵分解(如LU分解)来实现求逆,因为ONNX支持基本的矩阵运算。 ```python class ModelWithLUInverse(torch.nn.Module): def forward(self, x): # 使用LU分解实现矩阵求逆 # 注意:此实现仅用于演示,可能不适用于所有情况(如奇异矩阵) lu, pivots = torch.linalg.lu_factor(x) return torch.linalg.lu_solve(lu, pivots, torch.eye(x.size(0), dtype=x.dtype, device=x.device)) # 创建模型实例 model = ModelWithLUInverse() # 导出模型(使用opset版本15,因为现在使用了基本操作) torch.onnx.export( model, input_tensor, "model_with_lu_inverse.onnx", opset_version=15, # 使用15即可,因为LU分解和求解在较低版本已支持 input_names=['input'], output_names=['output'], dynamic_axes={'input': {0: 'dim0', 1: 'dim1'}} ) ``` **注意**:此替代方法可能效率较低,且对于非方阵或奇异矩阵需要额外处理。 #### 方法3:自定义运算符(高级) 如果上述方法均不可行,可以考虑自定义ONNX运算符。这需要: 1.ONNX中注册自定义运算符 2. 实现该运算符的推理函数 3. 在导出的模型中使用该自定义运算符 此方法较为复杂,通常作为最后手段。 ### 错误处理建议 1. **检查PyTorchONNX版本**:确保使用最新版本的PyTorch,因为新版本可能增加了对更多运算符的支持。 ```bash pip install --upgrade torch onnx ``` 2. **查阅ONNX运算符文档**:确认目标opset版本是否支持`linalg_inv`: - ONNX opset 16开始支持`org.onnx.aten`命名空间中的一些实验性运算符,包括`ATenInv`(对应`linalg_inv`)。 3. **简化模型**:如果可能,避免在模型中使用矩阵求逆,考虑是否可以用其他运算替代。 ### 示例:使用opset 16成功导出 以下是使用opset 16成功导出的完整示例: ```python import torch import torch.onnx class SimpleInvModel(torch.nn.Module): def forward(self, x): return torch.linalg.inv(x) model = SimpleInvModel() x = torch.randn(3, 3, dtype=torch.float32) # 导出为ONNX opset 16 torch.onnx.export( model, x, "inv_model.onnx", opset_version=16, input_names=['input'], output_names=['output'], dynamic_axes={'input': {0: 'dim0', 1: 'dim1'}, 'output': {0: 'dim0', 1: 'dim1'}} ) ``` ### 验证导出的ONNX模型 可以使用ONNX Runtime验证导出的模型: ```python import onnxruntime as ort import numpy as np # 创建随机输入 input_data = np.random.randn(3, 3).astype(np.float32) # 运行ONNX模型 ort_session = ort.InferenceSession("inv_model.onnx") ort_inputs = {ort_session.get_inputs()[0].name: input_data} ort_outs = ort_session.run(None, ort_inputs) # 与PyTorch结果比较 torch_inv = torch.linalg.inv(torch.tensor(input_data)) print("ONNX输出:\n", ort_outs[0]) print("PyTorch输出:\n", torch_inv.numpy()) ``` ### 常见问题 1. **动态形状支持**:如果输入矩阵的维度是动态的(即每次运行可能不同),需要正确设置`dynamic_axes`。 2. **批处理支持**:`torch.linalg.inv`支持批处理(例如输入形状为[batch, dim, dim]),在导出时需确保输入输出动态轴设置正确。 §§相关问题§§: 1. 如何将PyTorch模型导出为ONNX格式时支持动态批量大小? 2. ONNX opset版本之间有哪些主要差异? 3. 除了ONNX,还有哪些常用的模型交换格式? 4. 如何在PyTorch中实现自定义运算符以支持ONNX导出?</think># 解决 `torch.onnx.errors.UnsupportedOperatorError: Exporting the operator 'aten::linalg_inv' to ONNX opset version 15 is not supported.` 错误 当尝试将包含 `torch.linalg.inv()`(底层表示为 `aten::linalg_inv`)的 PyTorch 模型导出到 ONNX 时,您遇到了这个错误。这是因为 ONNX opset 版本 15 不支持矩阵求逆操作符。下面我将提供完整的解决方案。 ## 解决方案 ### 方法 1: 升级 ONNX opset 版本(推荐) ONNX opset 版本 16 及以上支持 `linalg_inv` 操作符: ```python import torch import torch.onnx # 示例模型(包含矩阵求逆) class MatrixInverseModel(torch.nn.Module): def forward(self, x): return torch.linalg.inv(x) # 创建模型实例 model = MatrixInverseModel() # 创建输入(3x3矩阵) input_tensor = torch.randn(3, 3, dtype=torch.float32) # 导出模型到ONNX,使用opset版本16 torch.onnx.export( model, input_tensor, "matrix_inverse_model.onnx", opset_version=16, # 关键:使用支持linalg_inv的opset版本 input_names=['input'], output_names=['output'], dynamic_axes={ 'input': {0: 'dim0', 1: 'dim1'}, # 支持动态尺寸 'output': {0: 'dim0', 1: 'dim1'} } ) ``` **解释**: - `opset_version=16`:使用支持 `linalg_inv` 的 ONNX opset 版本 - `dynamic_axes`:允许输入矩阵具有任意尺寸(如 3x3、4x4 等) ### 方法 2: 使用替代实现(兼容旧版 opset) 如果必须使用 opset 15 或更早版本,可以通过其他操作组合实现矩阵求逆: ```python import torch import torch.onnx # 使用LU分解实现矩阵求逆(兼容旧版ONNX) class LUInverseModel(torch.nn.Module): def forward(self, x): # 1. 进行LU分解 lu, pivots = torch.linalg.lu_factor(x) # 2. 创建单位矩阵 identity = torch.eye(x.size(-1), dtype=x.dtype, device=x.device) # 3. 使用LU分解结果求解逆矩阵 return torch.linalg.lu_solve(lu, pivots, identity) # 创建模型实例 model = LUInverseModel() input_tensor = torch.randn(3, 3, dtype=torch.float32) # 导出模型(使用opset 15) torch.onnx.export( model, input_tensor, "lu_inverse_model.onnx", opset_version=15, # 现在可以使用旧版opset input_names=['input'], output_names=['output'], dynamic_axes={ 'input': {0: 'dim0', 1: 'dim1'}, 'output': {0: 'dim0', 1: 'dim1'} } ) ``` **解释**: 1. `torch.linalg.lu_factor`:执行LU分解(ONNX opset 15 支持) 2. `torch.eye`:创建单位矩阵 3. `torch.linalg.lu_solve`:使用LU分解结果求解线性系统(ONNX opset 15 支持) ### 方法 3: 自定义符号函数(高级方法) 对于更复杂的场景,可以注册自定义符号函数: ```python import torch import torch.onnx from torch.onnx import symbolic_helper # 自定义符号函数 def linalg_inv_symbolic(g, self): # 1. 使用现有的ONNX操作实现逆矩阵 identity = g.op("Constant", value_t=torch.eye(self.type().sizes()[-1]).to(self.type().dtype())) # 2. 使用求解线性系统的方法实现逆矩阵 return g.op("Solve", self, identity) # 注册自定义符号函数 torch.onnx.register_custom_op_symbolic( 'aten::linalg_inv', linalg_inv_symbolic, opset_version=15 ) # 使用自定义函数导出模型 model = MatrixInverseModel() torch.onnx.export( model, input_tensor, "custom_inverse_model.onnx", opset_version=15, input_names=['input'], output_names=['output'], dynamic_axes={ 'input': {0: 'dim0', 1: 'dim1'}, 'output': {0: 'dim0', 1: 'dim1'} } ) ``` ## 验证导出模型 使用 ONNX Runtime 验证导出的模型: ```python import onnxruntime as ort import numpy as np # 创建测试输入 input_data = np.random.randn(3, 3).astype(np.float32) # 加载ONNX模型 ort_session = ort.InferenceSession("matrix_inverse_model.onnx") # 运行推理 ort_inputs = {ort_session.get_inputs()[0].name: input_data} ort_outs = ort_session.run(None, ort_inputs) # 使用PyTorch计算参考结果 torch_inv = torch.linalg.inv(torch.tensor(input_data)) # 比较结果 print("ONNX输出:\n", ort_outs[0]) print("PyTorch输出:\n", torch_inv.numpy()) print("最大差异:", np.max(np.abs(ort_outs[0] - torch_inv.numpy()))) ``` ## 常见问题及解决方案 1. **动态尺寸支持**: - 确保在导出时指定 `dynamic_axes` - 对于批量矩阵求逆,使用更高维度的输入: ```python # 批量矩阵求逆(支持N个3x3矩阵) input_tensor = torch.randn(5, 3, 3) dynamic_axes = { 'input': {0: 'batch_size', 1: 'dim0', 2: 'dim1'}, 'output': {0: 'batch_size', 1: 'dim0', 2: 'dim1'} } ``` 2. **数据类型问题**: - ONNX 主要支持 float32,确保输入为 float32: ```python input_tensor = input_tensor.float() # 换为float32 ``` 3. **奇异矩阵处理**: - 添加检查防止奇异矩阵: ```python class SafeInverseModel(torch.nn.Module): def forward(self, x): # 检查行列式避免奇异矩阵 det = torch.det(x) if torch.any(torch.abs(det) < 1e-6): # 处理奇异矩阵情况 return torch.eye(x.size(-1), device=x.device) return torch.linalg.inv(x) ``` ## 最佳实践 1. **使用最新稳定版本**: ```bash pip install --upgrade torch onnx onnxruntime ``` 2. **检查ONNX支持的操作符**: - 参考 [ONNX 操作符文档](https://github.com/onnx/onnx/blob/main/docs/Operators.md) - 使用 PyTorch 的 `torch.onnx.export` 时添加 `verbose=True` 查看详细换过程 3. **复杂模型的导出策略**: ```python torch.onnx.export( model, (input_tensor,), "model.onnx", opset_version=16, do_constant_folding=True, # 常量折叠优化 export_params=True, # 导出模型参数 input_names=['input'], output_names=['output'], dynamic_axes=dynamic_axes, training=torch.onnx.TrainingMode.EVAL, # 确保在评估模式 operator_export_type=torch.onnx.OperatorExportTypes.ONNX ) ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值