【ncnn android】算法移植(六)——onnx2ncnn源码阅读理解/设计思路

本文主要是介绍【ncnn android】算法移植(六)——onnx2ncnn源码阅读理解/设计思路,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

上一篇写道:onnx2ncnn的时候,不支持sigmoid,upsample层,于是想着阅读onnx2ncnn的源码。目的:

  • 理解ncnn中onnx2ncnn的主要流程
  • 自定义upsample层(最高要求)

1. 相关资料

  1. Open Neural Network Exchange - ONNX ,onnx的文档
  2. https://github.com/Tencent/ncnn,注意ncnn的不同版本代码是不一样,这里以20180704为准。

2. 主要流程

2.1 ncnn.param保存网络结构参数的格式

2.2 onnx关键api

  1. graph
    GraphProto: graph定义了模型的计算逻辑以及带有参数的node节点,组成一个有向图结构;
    const onnx::GraphProto& graph = model.graph();
    关键属性
    在这里插入图片描述
  • initializer:好像是预训练的权重
  1. node
    NodeProto: 网络有向图的各个节点OP的结构,通常称为层,例如conv,relu层;
    const onnx::NodeProto& node = graph.node(i);
    关键属性
    在这里插入图片描述

  2. attribute
    AttributeProto:各OP的参数,通过该结构访问,例如:conv层的stride,dilation等;
    const onnx::AttributeProto& attr = node.attribute(i);
    在这里插入图片描述

  3. tensor
    TensorProto: 序列化的tensor value,一般weight,bias等常量均保存为该种结构;

// batchnorm
const onnx::TensorProto& scale = weights[node.input(1)];
const onnx::TensorProto& B = weights[node.input(2)];
const onnx::TensorProto& mean = weights[node.input(3)];
const onnx::TensorProto& var = weights[node.input(4)];

一些疑问

  • node.attribute怎么确定?比如conv,batchnorm有不同的参数
    猜想: 在pytorch2onnx中是不是又具体的定义或代码?

  • 比如batchnorm又多个预训练权重的保存顺序
    猜想: 还是在pytorch2onnx中定义的

4. 一些例子

主要分为两类,无结构参数,如batchnorm,直接保存到bin文件中(注意各个参数的顺序);第二类,有结构参数,无预训练权重。就需要将结构参数保存到ncnn.param网络结构参数中。

4.1 batchnorm

float epsilon = get_node_attr_f(node, "epsilon", 1e-5f);const onnx::TensorProto& scale = weights[node.input(1)];
const onnx::TensorProto& B = weights[node.input(2)];
const onnx::TensorProto& mean = weights[node.input(3)];
const onnx::TensorProto& var = weights[node.input(4)];int channels = get_tensor_proto_data_size(scale);fprintf(pp, " 0=%d", channels);		// batchnorm的通道数fwrite_tensor_proto_data(scale, bp);	// batchnorm的缩放变量
fwrite_tensor_proto_data(mean, bp);		// 均值

4.2 pooling

pooling层是没有预训练的参数,但是有很多类型(maxpool,averagepool),和网络参数(kernel_size, pads)等。

std::string auto_pad = get_node_attr_s(node, "auto_pad");//TODO
std::vector<int> kernel_shape = get_node_attr_ai(node, "kernel_shape");
std::vector<int> strides = get_node_attr_ai(node, "strides");
std::vector<int> pads = get_node_attr_ai(node, "pads");int pool = op == "AveragePool" ? 1 : 0;
int pad_mode = 1;if (auto_pad == "SAME_LOWER" || auto_pad == "SAME_UPPER")
{
// TODO
pad_mode = 2;
}fprintf(pp, " 0=%d", pool);if (kernel_shape.size() == 1) {
fprintf(pp, " 1=%d", kernel_shape[0]);
} else if (kernel_shape.size() == 2) {
fprintf(pp, " 1=%d", kernel_shape[1]);
fprintf(pp, " 11=%d", kernel_shape[0]);
}if (strides.size() == 1) {
fprintf(pp, " 2=%d", strides[0]);
} else if (strides.size() == 2) {
fprintf(pp, " 2=%d", strides[1]);
fprintf(pp, " 12=%d", strides[0]);
}if (pads.size() == 1) {
fprintf(pp, " 3=%d", pads[0]);
} else if (pads.size() == 2) {
fprintf(pp, " 3=%d", pads[1]);
fprintf(pp, " 13=%d", pads[0]);
} else if (pads.size() == 4) {
fprintf(pp, " 3=%d", pads[1]);
fprintf(pp, " 13=%d", pads[0]);
fprintf(pp, " 14=%d", pads[3]);
fprintf(pp, " 15=%d", pads[2]);
}fprintf(pp, " 5=%d", pad_mode);

reference

  1. https://blog.csdn.net/SilentOB/article/details/102863944

这篇关于【ncnn android】算法移植(六)——onnx2ncnn源码阅读理解/设计思路的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1056867

相关文章

深入理解Apache Kafka(分布式流处理平台)

《深入理解ApacheKafka(分布式流处理平台)》ApacheKafka作为现代分布式系统中的核心中间件,为构建高吞吐量、低延迟的数据管道提供了强大支持,本文将深入探讨Kafka的核心概念、架构... 目录引言一、Apache Kafka概述1.1 什么是Kafka?1.2 Kafka的核心概念二、Ka

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

Android实现打开本地pdf文件的两种方式

《Android实现打开本地pdf文件的两种方式》在现代应用中,PDF格式因其跨平台、稳定性好、展示内容一致等特点,在Android平台上,如何高效地打开本地PDF文件,不仅关系到用户体验,也直接影响... 目录一、项目概述二、相关知识2.1 PDF文件基本概述2.2 android 文件访问与存储权限2.

Android Studio 配置国内镜像源的实现步骤

《AndroidStudio配置国内镜像源的实现步骤》本文主要介绍了AndroidStudio配置国内镜像源的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、修改 hosts,解决 SDK 下载失败的问题二、修改 gradle 地址,解决 gradle

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下:

在Android平台上实现消息推送功能

《在Android平台上实现消息推送功能》随着移动互联网应用的飞速发展,消息推送已成为移动应用中不可或缺的功能,在Android平台上,实现消息推送涉及到服务端的消息发送、客户端的消息接收、通知渠道(... 目录一、项目概述二、相关知识介绍2.1 消息推送的基本原理2.2 Firebase Cloud Me

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

Android中Dialog的使用详解

《Android中Dialog的使用详解》Dialog(对话框)是Android中常用的UI组件,用于临时显示重要信息或获取用户输入,本文给大家介绍Android中Dialog的使用,感兴趣的朋友一起... 目录android中Dialog的使用详解1. 基本Dialog类型1.1 AlertDialog(

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很