本文主要是介绍【ncnn android】算法移植(六)——onnx2ncnn源码阅读理解/设计思路,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
上一篇写道:onnx2ncnn的时候,不支持sigmoid,upsample层,于是想着阅读onnx2ncnn的源码。目的:
- 理解ncnn中onnx2ncnn的主要流程
- 自定义upsample层(最高要求)
1. 相关资料
- Open Neural Network Exchange - ONNX ,onnx的文档
- https://github.com/Tencent/ncnn,注意ncnn的不同版本代码是不一样,这里以20180704为准。
2. 主要流程
2.1 ncnn.param保存网络结构参数的格式
2.2 onnx关键api
- graph
GraphProto: graph定义了模型的计算逻辑以及带有参数的node节点,组成一个有向图结构;
const onnx::GraphProto& graph = model.graph();
关键属性
- initializer:好像是预训练的权重
-
node
NodeProto: 网络有向图的各个节点OP的结构,通常称为层,例如conv,relu层;
const onnx::NodeProto& node = graph.node(i);
关键属性
-
attribute
AttributeProto:各OP的参数,通过该结构访问,例如:conv层的stride,dilation等;
const onnx::AttributeProto& attr = node.attribute(i);
-
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
- https://blog.csdn.net/SilentOB/article/details/102863944
这篇关于【ncnn android】算法移植(六)——onnx2ncnn源码阅读理解/设计思路的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!