记录ncnn导出模型的权重读取的一点思路

ncnn的.param和.bin文件的读取,参考这个基本上可以触类旁通


前言

大概找了找全网使用ncnn框架导出权重模型的博客(可以参考的只有2篇,一篇过时了,没法儿使用;另一篇的函数重载似乎提及了也没什么用处,因为另一篇提到的函数重载事实上在ncnn中已经代码体现了,但是未能深入逐层输出,而且我运行了发现出了bug),翻了ncnn的github,有大量技术发烧友都在追求ncnn工具导出后的FP16、INT8模型的权重、偏置参数读取,然而苦于ncnn框架规模实在太大,C++技术高度密集,权重输出确实不太容易。

我在本文中利用线上开源的onnx转ncnn FP16的网站将nanodet-plus.onnx模型转变为.param和.bin,并在此基础上对ncnn进行修改和编译,验证框架使用我修改后的批量目标检测的nanodet.cpp,该部分代码不提供,我只提供结果。另外,不对ncnn进行深入分析,本文只提供如何输出权重值的思路和代码实现。再次重申:

本文只提供:

1、ncnn中我用于输出权重值的修改后若干文件(您可以替换之后重现我的结果);

2、nanodet-plus在ncnn框架下使用ncnn格式的模型作为批量图片的目标检测的一个输入(图片是另一个输入)的结果(您可以使用nanodet-plus官方原装的单张图片的目标检测作为测试框架,道理一样,不用纠结,我懒得改了);

3、提供权重输出的思路(相信会对你有帮助);

本文不提供:

1、批量图片的目标检测代码;

2、不提供算法优化;

3、不提供整个模型输出的方案;


一、ncnn的.param格式解读

这边可以直接参考nihui的ncnn github官网来解读,不过要是不想看英文,想直接快速上手,直接看下面。

图片来源:https://blog.csdn.net/qq_25105061/article/details/131457787 

layer:描述网络一共有多少层,例如:ReLU、Conv 都叫一个layer;

blob:表示数据节点,例如一个Conv就有一个输入blob和输出blob;

bottom_count:当前层接收输入blob的层个数;

top_count:将当前层的输出作为输入blob的层个数;

bottom_name:当前层输入数据的生产层名字;

blob_name: 消费当前的输出结果的层名字;

差不多够用!继续往下!

二、ncnn中眼花缭乱的load_param和load_model

根据文件引用关系:net.cpp的line1293、line1736分别指向load_param和load_model的最接近Layer类的模型和参数加载函数,随后lin1293的load_param指向line1449的pd.load_param(dr),该函数需要到paramdict.cpp中寻找,找到int Net::load_param()从而配合line1293该函数的其余内容完整输出.param文件。随后看load_model,到net.cpp的line1763,找到layer->load_model,该函数需要到layer.cpp和layer.h中寻找定义和声明,layer就是基类,因此我们随便找一个基于Layer基类的派生类Convolution,寻找convolution.cpp,查找其内的load_param和load_model,我们只关注load_model,因为load_param过于直观了,load_model需要回到modelbin.cpp中查找Mat ModelBin::load(int w, int h, int type) const,我们根据“Mat mret = m.reshape(w, h);”查找mat.cpp中的reshape函数,并设置使用指针来输出权重值。

1. net.cpp

图示cout代码输出.param中的layer_cout和blob_cout。

图示cout代码用于确认逐层layer参数载入,且可以确认参数载入来自于Layer类,由于Layer已经是基类,因此我们随便选择Layer派生类比如Convolution。 

2. convolution.cpp

这部分是load_param的内容。

这部分是load_model的内容。

我们可以看到weight_data和bias_data都通过mb类实例下的load函数得到,我们可以确认mb实例是对ModelBin的例化。接下来我们看ModelBin类定义。

3. modelbin.cpp

有效的部分是该load函数,我对此处进行了修改,原内容直接返回m.reshape(w, h),我多增加一步以确定是否经过该步。接下来我们确认确实是reshape函数起作用,因此我们需要查找Mat类定义。

4. net.cpp

其中起作用的包含两部分: 

 

此处红色框内我对权重值进行了打印!

最后的部分输出结果:

三、ncnn重新编译步骤和nanodet-plus编译运行

其实前面两部分基本上对输出权重做了流程化的介绍,接下来就是把ncnn中已经更改的文件、终端输出的内容和实现步骤讲讲,该拿出来的拿出来,该讲明白的讲明白。

1. 实现步骤

从添加更改文件后的命令如下:

1、重新编译并安装ncnn:

mkdir build

cd build

cmake ..

make -j32

make install

2、增加路径:export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

3、在编译运行nanodet-ncnn工程之前:

export ncnn_DIR=/root/autodl-tmp/ncnn/build/install/lib/cmake/ncnn

4、清除./nanodet/demo_ncnn/build的内容:rm -rf *

5、加入.param和.bin:cp -r ../nanodet.param ../nanodet/model ./

6、编译工程:

cmake ..

make

7、运行工程(以批量检测为例):

./nanodet_demo 1 /root/autodl-tmp/nanodet/demo_ncnn/pic/(这个参考nanodet官方)

2. 文件内容

我应该已经上传到csdn了。


总结

简单对ncnn的权重参数进行了输出,够累,只能说对AI编译器的理解又进了一步!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

DentionY

谢谢您的支持

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

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

打赏作者

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

抵扣说明:

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

余额充值