0.调用实例
先看一个调用实例,顺着调用流程探寻ncnn内部具体实现细节。
#include "net.h"
int main(int argc, char **argv)
{
ncnn::Mat in;
ncnn::Mat out;
ncnn::Net net;
net.load_param("model.param");
net.load_model("model.bin");
ncnn::Extractor ex = net.create_extractor();
ex.set_light_mode(true);
ex.set_num_threads(4);
ex.input("data", in);
ex.extract("output", out);
return 0;
}
1.blob结构
class Blob
{
public:
std::string name; //blob名字
int producer; //指明该blob来自哪个层的输出,层索引
std::vector<int> consumers; //指明该blob作为哪个层的输入,层索引
}
在blob的构造函数中初始化producer=-1
2.layer
class Layer
{
public:
int typeindex; //类型ID
std::string type; //类型名字
std::string name; //层的名字
std::vector<int> bottoms; //当前层所有输入blob的索引
std::vector<int> tops; //当前层所有输出blob索引
int load_param(const ParamDict &pd);
int load_model(const ModelBin &mb);
int forward(const std::vector<Mat> &bottom_blobs, std::vector<Mat> &top_blobs, const Option &opt = get_default_option());
}
- layer进行前向传播时,根据bottoms索引值找到bottom数据,作为forward的输入,计算结果存入tops对应的blob里,完成一层的inferecnce
- load_param和load_model有三种定义
- 第一个是在net里定义为
int load_param(FILE *fp);
,主要功能是从param文件中读取数据,网络层数,网络blob个数,每一个输入输出blob的类型、名字等信息。涉及到网络结构的参数(不是训练参数),例如滤波器个数、padding、stride等信息由ParamDict里的load_param负责读取。 - 第二个是在ParamDict里,定义为
int load_param(FILE *fp);
fp位置接net里的位置,该函数将参数读取到ParamDict的一个类实例pd里,以pair对的形式存储,不考虑具体参数含义,只需按照key,value存储你即可。 - 第三个是在layer里,定义为
int load_param(const ParamDict &pd);
这个load_param负责从pair对里根据不同层对key的定义解析成和每一个层对应的参数,参数的不同决定了相同类型层的差异性,比如同样是卷积层,但是滤波器个数不同。 - load_model和load_param类似,至此完成了整个网络的解析工作。
- 第一个是在net里定义为
以上内容对应于我们平时使用ncnn的以下代码形式:
ncnn::Net net;
net.load_param("model.param");
net.load_model("model.bin");
2.net解析
class Net
{
public:
int usewinograd_convolution;
int use_sgemm_convolution;
int use_int8_inference;
int use_vulkan_compute;
int load_param(FILE *fp);
int load_model(FILE *fp);
Extractor create_extractor();
protected:
std::vector<Blob> blobs;//网络的所有blob
std::vector<Layer*> layers;//网络的所有层指针
int forward_layer(int layer_index, std::vector<Mat> &blob_mats, Options &opt);
int find_blob_index_by_name(const char* name);
int find_layer_index_by_name(const char *name);
}
class Extractor
{
public:
int Extractor::input(const char *blob_name, const Mat &in);
int Extractor::input(int blob_index, VkMat &feat, VkCompute &cmd);
int Extractor::extract(const char *blob_name, Mat &feat);
int Extractor::extract(blob_index, const Mat &feat);//次函数直接forward_layer()
protected:
friend Extractor Net::create_extractor() const;
Extractor(const Net *net, int blob_count);
private:
const Net *net;
std::vector<Mat> blob_mats;
Option opt;
}
- Net类里的成员变量包含了另一个类create_extractor方法,该方法实际上就是调用Extractor类的构造函数,返回一个Extractor实例。
Extractor Net::create_extractor() const
{
return Extractor(this, blobs.size());
}
调用Extractor::input(const char *blob_name, const Mat &in)
设置输入数据,这里比较简单,通过输入blob名字找到对应的索引,然后根据索引取到真实的blob数据。
- 整个网络的核心是
Extractor::extract(const char *blob_name, Mat &feat)
- 该函数调用另一个重载函数
Extractor::extract(blob_index, const Mat &feat)
两个输入参数分别是要获取数据blob索引和存放数据的输出变量,通过blob_index在blobs(net的类成员变量,用于存放整个网络的所有blob)找到对应的blob - 调用blob的类成员变量producer来找到该blob是哪个layer的输出,也就是layer_index
- 接下来调用
forward_layer(layer_index, blob_mats, opt)
完成整个网络的前向传播,逐层前传使用递归完成,blob_mats里存放真正的blob数据,是net的私有成员变量std::vector<Mat> blob_mats
,Mat是自定义类型 - 完成forward_layer后,
feat = blob_mats[blob_index]
,feat是调用例子中out的引用,将blob数据存放在feat变量中,整个流程结束
- 该函数调用另一个重载函数
原文地址:https://www.cnblogs.com/ganchunsheng/p/11686703.html
时间: 2024-10-04 14:13:40