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

Posted ganchunsheng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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变量中,整个流程结束

以上是关于ncnn源码分析-004-代码流程总结的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段

Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段

《Docker 源码分析》全球首发啦!

[Android] Retrofit 执行流程源码分析

Vue.js源码解析-Vue初始化流程

Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段