onnx模型的修改与调试demo

主要参考:
模型部署入门教程(五):ONNX 模型的修改与调试
第五章:ONNX 模型的修改与调试

使用netron 可视化模型

读写onnx

构造onnx

创建一个描述线性函数 output = ax+b 的onnx模型。 需要两个节点,第一个节点计算 c = ax ,第二个节点计算 output = c+b
整体输入: a、 x、 b,输出output

import onnx
from onnx import helper
from onnx import TensorProto
# 输入 输出
a = helper.make_tensor_value_info('a', TensorProto.FLOAT, [10, 10])
x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [10, 10])
b = helper.make_tensor_value_info('b', TensorProto.FLOAT, [10, 10])
output = helper.make_tensor_value_info('output', TensorProto.FLOAT, [10, 10])
# 节点
mul = helper.make_node('Mul', ['a', 'x'], ['c'])
add = helper.make_node('Add', ['c', 'b'], ['output'])
# 构建计算图
graph = helper.make_graph([mul, add], 'linear_func', [a, x, b], [output])  # 节点、图名称、输入张量信息,输出张量信息
model = helper.make_model(graph)
# 确认是否满足onnx模型标准
onnx.checker.check_model(model)
print(model)
# 模型保存
onnx.save(model, 'linear_func.onnx')

out:

ir_version: 9
graph {
  node {
    input: "a"
    input: "x"
    output: "c"
    op_type: "Mul"
  }
  node {
    input: "c"
    input: "b"
    output: "output"
    op_type: "Add"
  }
  name: "linear_func"
  input {
    name: "a"
    type {
      tensor_type {
        elem_type: 1
        shape {
          dim {
            dim_value: 10
          }
          dim {
            dim_value: 10
          }
        }
      }
    }
  }
  input {
    name: "x"
    type {
      tensor_type {
        elem_type: 1
        shape {
          dim {
            dim_value: 10
          }
          dim {
            dim_value: 10
          }
        }
      }
    }
  }
  input {
    name: "b"
    type {
      tensor_type {
        elem_type: 1
        shape {
          dim {
            dim_value: 10
          }
          dim {
            dim_value: 10
          }
        }
      }
    }
  }
  output {
    name: "output"
    type {
      tensor_type {
        elem_type: 1
        shape {
          dim {
            dim_value: 10
          }
          dim {
            dim_value: 10
          }
        }
      }
    }
  }
}
opset_import {
  version: 19
}

onnx runtime 运行模型,查看是否正确

import onnxruntime 
import numpy as np 
 
sess = onnxruntime.InferenceSession('linear_func.onnx') 
a = np.random.rand(10, 10).astype(np.float32) 
b = np.random.rand(10, 10).astype(np.float32) 
x = np.random.rand(10, 10).astype(np.float32) 
 
output = sess.run(['output'], {'a': a, 'b': b, 'x': x})[0] 
 
assert np.allclose(output, a * x + b) 

读取并修改 onnx

import onnx 
model = onnx.load('linear_func.onnx') 
 
node = model.graph.node 
node[1].op_type = 'Sub' 
 
onnx.checker.check_model(model) 
onnx.save(model, 'linear_func_2.onnx') 

调试ONNX模型

子模型提取

ONNX 官方为开发者提供了子模型提取(extract)的功能。子模型提取,顾名思义,就是从一个给定的 ONNX 模型中,拿出一个子模型。这个子模型的节点集、边集都是原模型中对应集合的子集。让我们来用 PyTorch 导出一个复杂一点的 ONNX 模型,并在它的基础上执行提取操作:

import torch

class Model(torch.nn.Module):

    def __init__(self):
        super().__init__()
        self.convs1 = torch.nn.Sequential(torch.nn.Conv2d(3, 3, 3),
                                          torch.nn.Conv2d(3, 3, 3),
                                          torch.nn.Conv2d(3, 3, 3))
        self.convs2 = torch.nn.Sequential(torch.nn.Conv2d(3, 3, 3),
                                          torch.nn.Conv2d(3, 3, 3))
        self.convs3 = torch.nn.Sequential(torch.nn.Conv2d(3, 3, 3),
                                          torch.nn.Conv2d(3, 3, 3))
        self.convs4 = torch.nn.Sequential(torch.nn.Conv2d(3, 3, 3),
                                          torch.nn.Conv2d(3, 3, 3),
                                          torch.nn.Conv2d(3, 3, 3))
    def forward(self, x):
        x = self.convs1(x)
        x1 = self.convs2(x)
        x2 = self.convs3(x)
        x = x1 + x2
        x = self.convs4(x)
        return x

model = Model()
input = torch.randn(1, 3, 20, 20)

torch.onnx.export(model, input, 'whole_model.onnx')

这个模型的可视化结果如下图所示:

在这里插入图片描述
在官网上,对节点进行了标号,然后方便提取,但是我在根据官网的知道写序号时,报错,keyError:22 。
然后将 序号,改成了 netron 上查看的块的output。
在这里插入图片描述
提取:

import onnx

onnx.utils.extract_model('whole_model.onnx', 'partial_model.onnx', ['22'], ['28'])

报错,keyerror 22 .

修改为:

import onnx

onnx.utils.extract_model('whole_model.onnx', 'partial_model.onnx', ['/convs1/convs1.1/Conv_output_0'], ['/Add_output_0'])

提取后如下:
在这里插入图片描述

添加额外输出

onnx.utils.extract_model('whole_model.onnx', 'submodel_1.onnx', ['/convs1/convs1.1/Conv_output_0'], ['/convs3/convs3.1/Conv_output_0', '31'])

在这里插入图片描述

添加冗余输入

# 添加冗余输入
import onnx
onnx.utils.extract_model('whole_model.onnx', 'submodel_2.onnx', ['/convs1/convs1.1/Conv_output_0','input.1'], ['/Add_output_0'])

在这里插入图片描述

输出onnx中间节点的值

在使用 ONNX 模型时,最常见的一个需求是能够用推理引擎输出中间节点的值。这多见于深度学习框架模型和 ONNX 模型的精度对齐中,因为只要能够输出中间节点的值,就能定位到精度出现偏差的算子。我们来看看如何用子模型提取实现这一任务。

方法跟添加额外输出类似
在这里插入图片描述

问题

  1. "这里 make_graph 的节点参数有一个要求:计算图的节点必须以拓扑序给出。

拓扑序是与有向图的相关的数学概念。如果按拓扑序遍历所有节点的话,能保证每个节点的输入都能在之前节点的输出里找到(对于 ONNX 模型,我们把计算图的输入张量也看成“之前的输出”)。"
啥意思?
解决: 用节点的 output 代替拓扑序。

猜你喜欢

转载自blog.csdn.net/weixin_39107270/article/details/131050602