ncnn源码分析-004-代码流程总结

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类似,至此完成了整个网络的解析工作。

以上内容对应于我们平时使用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变量中,整个流程结束
  • 关于blob的索引操作有三种:
    • 位于layer里的std::vector<int> bottoms,std::vector<int> tops,里面存放的是blob的index,分别指示与当前layer相关的blob;
    • 位于net里的std::vector<blob> blobs,是net的类成员变量,里面存放整个网络的blob的索引;
    • 位于net里的std::vector<Mat> blob_mats,存放着整个网络的所有blob的实际数据。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值