yolov3修改替换onnx模型节点(Resize-->DConv)

该博客介绍了如何在ONNX模型中将Resize操作替换为ConvTranspose(反卷积)。首先定位到Resize节点,然后创建新节点(反卷积节点),接着删除旧节点并插入新节点,初始化输入参数权重,并将权重添加到模型的初始器中,最后保存修改后的模型。整个过程涉及到ONNX模型的节点操作和权重管理。
摘要由CSDN通过智能技术生成

1. 定位

找到将要替换的节点位置, model.graph.node中节点参数是受保护变量不能直接循环,需要通过索引访问, model.graph.node[i]

model = onnx.load('../onnx_model/yolov3.onnx')
node = model.graph.node

#查找模型中Resize算子节点位置
for i in range(len(node)):
    if node[i].op_type == 'Resize':
        node_rise = node[i]
        print(i)

2. 创建新节点

# 新建新节点(反卷积)
deconv1_node = onnx.helper.make_node(
    name="ConvTranspose_143",           #节点名称
    op_type="ConvTranspose",            #算子类型
    inputs=["639", 'deconv1.weight'],   #输入参数,可以为多个,如['X','W','b']
    outputs=["644"],                    #输出节点名字(下一层输入节点名称)
    output_padding=[1,1],           #输出特征图填充
    group=1,
    kernel_shape=[3,3],
    pads=[1,1,1,1],
    strides=[2,2],
)

3. 删除旧节点,插入新节点

# 删除老节点
old_node = model.graph.node[index]
model.graph.node.remove(old_node)
# 插入新节点
model.graph.node.insert(index, new_node[i])

4. 初始化输入参数(W,b,等)

# 权重shape
deconv1_weight_shape = [256, 256, 3, 3]
# 生成weight数据
weight1 = np.random.ranf(256 * 256 * 3 * 3).astype(np.float32)
#转换成onnx类型weight数据
deconv1_weight = helper.make_tensor('deconv1.weight', TensorProto.FLOAT, deconv1_weight_shape, weight1)

5.  初始化权重W

#将生成的onnx权重数据添加到 model.graph.initializer中
model.graph.initializer.append(deconv1_weight)

6. 保存到新模型

onnx.save(model, "my.onnx")

7. 注意

使用helper.make_tensor()生成onnx权重时,'deconv1.weight' 是生成的权重名称,也是model.graph.initializer中显示的名称,要和节点input中的输入名称一样,这样将权重添加到model.graph.initializer时才能被正确识别

  

helper.make_tensor('deconv1.weight', TensorProto.FLOAT, weight_shape, weight1)

8. 完整代码

import numpy as np
import onnx
from onnx import helper, TensorProto

# kernel 2x2的反卷积替换2倍的resize上采样(最近邻插值)

def node_replace(model, new_node:list, old_index:list, weight:list, output):
    # 目前仅支持替换为不含bias的反卷积算子
    """

    :param model:  onnx模型
    :param new_node: 需要替换的新节点信息列表
    :param old_index: 需要替换的旧节点索引列表
    :param weight: 新节点的权重列表
    :param output: 保存的新模型名字 ,如:my.onnx
    :return: None
    """
    for i, index in enumerate(old_index):
        # 删除老节点
        old_node = model.graph.node[index]
        model.graph.node.remove(old_node)
        # 添加新节点
        model.graph.node.insert(index, new_node[i])
        # 初始化权重
        model.graph.initializer.append(weight[i])
    onnx.save(model, output)

model = onnx.load('../onnx_model/yolov3.onnx')
# model = onnx.load('my.onnx')

node = model.graph.node

# 1.2搜索目标节点
for i in range(len(node)):
    if node[i].op_type == 'Resize':
        node_rise = node[i]
        print(i)
# print(node[159])


# 节点信息
deconv1_weight_shape = [256, 256, 2, 2]
deconv2_weight_shape = [128, 128, 2, 2]

# input_shape = [1, 256, 13, 13]
# deconv1_output_shape = [1, 256, 26, 26]
# deconv2_output_shape = [1, 128, 52, 52]

# 生成weight
weight1 = np.zeros([256,256,2,2]).astype(np.float32)
weight2 = np.zeros([128,128,2,2]).astype(np.float32)
# 初始化权值
for i in range(256):
    weight1[i, i, :, :] = 1
for i in range(128):
    weight2[i, i, :, :] = 1
print(weight1.shape)
print(weight2.shape)

deconv1_weight = helper.make_tensor('deconv1.weight', TensorProto.FLOAT, deconv1_weight_shape, weight1)
deconv2_weight = helper.make_tensor('deconv2.weight', TensorProto.FLOAT, deconv2_weight_shape, weight2)

# output = s*(in-1) + output_padding + ((k-1)*d + 1) - 2p
# # 新建新节点
deconv1_node = onnx.helper.make_node(
    name="ConvTranspose_143",
    op_type="ConvTranspose",
    inputs=["639", 'deconv1.weight'],
    outputs=["644"],
    output_padding=[0,0],
    group=1,
    kernel_shape=[2,2],
    pads=[0,0,0,0],
    strides=[2,2],
    dilations=[1,1]
)

# # 新建新节点
deconv2_node = onnx.helper.make_node(
    name="ConvTranspose_161",
    op_type="ConvTranspose",
    inputs=["667", 'deconv2.weight'],
    outputs=["672"],
    output_padding=[0,0],
    group=1,
    kernel_shape=[2,2],
    pads=[0,0,0,0],
    strides=[2,2],
    dilations=[1,1]
)

node_replace(model, [deconv1_node,deconv2_node],[142,159],[deconv1_weight,deconv2_weight], 'out.onnx')

# ##模型检查
try:
    onnx.checker.check_model(model)
except onnx.checker.ValidationError as e:
    print('The model is invalid: %s' % e)
else:
    print('The model is valid!')




  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,下面是一个简单的示例代码,仅供参考: ```c++ #include <iostream> #include <vector> #include <opencv2/opencv.hpp> #include <onnxruntime_cxx_api.h> class YOLOv5 { public: YOLOv5(const std::string& model_path) { // 创建会话 Ort::SessionOptions options; options.SetIntraOpNumThreads(1); options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED); session_ = Ort::Session(env_, model_path.c_str(), options); } std::vector<cv::Rect> detect(const cv::Mat& image) { // 预处理图像 cv::Mat resized_image; cv::resize(image, resized_image, cv::Size(input_size_, input_size_)); cv::Mat float_image; resized_image.convertTo(float_image, CV_32FC3, 1.0 / 255.0); float* input_data = float_image.ptr<float>(); // 准备输入张量 Ort::MemoryInfo input_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU); std::vector<int64_t> input_shape = {1, 3, input_size_, input_size_}; Ort::Value input_tensor = Ort::Value::CreateTensor<float>(input_info, input_data, input_shape.data(), input_shape.size()); // 准备输出张量 Ort::MemoryInfo output_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU); std::vector<const char*> output_names = {"output"}; std::vector<int64_t> output_shape = {1, 25200, 85}; std::vector<float> output_data(25200 * 85); // 运行模型 Ort::RunOptions run_options; session_.Run(run_options, &input_names_[0], &input_tensor, 1, &output_names[0], 1, &output_info, &output_data[0], output_data.size()); // 解析输出张量 std::vector<cv::Rect> detections; for (int i = 0; i < 25200; i++) { float* data = &output_data[i * 85]; int class_id = std::max_element(data + 5, data + 85) - data - 5; float confidence = data[class_id + 5]; if (confidence >= confidence_threshold_) { int x1 = static_cast<int>((data[0] - data[2] / 2.0) * image.cols); int y1 = static_cast<int>((data[1] - data[3] / 2.0) * image.rows); int x2 = static_cast<int>((data[0] + data[2] / 2.0) * image.cols); int y2 = static_cast<int>((data[1] + data[3] / 2.0) * image.rows); detections.emplace_back(x1, y1, x2 - x1, y2 - y1); } } return detections; } private: Ort::Env env_{ORT_LOGGING_LEVEL_ERROR, "yolov5"}; Ort::Session session_; int input_size_ = 640; float confidence_threshold_ = 0.5; std::vector<const char*> input_names_ = {"input"}; }; ``` 这个类使用了 ONNX Runtime C++ API,可以加载 ONNX 模型并进行推理。在这个示例中,模型的输入张量名为 "input",输出张量名为 "output",模型的输入尺寸为 640x640,置信度阈值为 0.5。 在使用时,只需要创建 YOLOv5 对象并调用 detect 方法即可: ```c++ YOLOv5 yolo("yolov5.onnx"); cv::Mat image = cv::imread("test.jpg"); std::vector<cv::Rect> detections = yolo.detect(image); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lookaroundd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值