美文网首页
ncnn源码阅读笔记(四)

ncnn源码阅读笔记(四)

作者: 半笔闪 | 来源:发表于2019-10-11 11:26 被阅读0次

    load_model

    前几篇算是把网络结构参数的载入说完了,本篇开始网络权重参数载入load_model。
    在net.cpp中有:

    int Net::load_model(const char* modelpath)
    {
        //打开xxx.bin文件
        FILE* fp = fopen(modelpath, "rb");
        if (!fp)
        {
            fprintf(stderr, "fopen %s failed\n", modelpath);
            return -1;
        }
        //调用int Net::load_model(FILE* fp)
        int ret = load_model(fp);
        fclose(fp);
        return ret;
    }
    

    可以看到最终还是调用了int Net::load_model(FILE* fp)函数,那就来看看这个函数:

    int Net::load_model(FILE* fp)
    {
         //如果layer为空
        if (layers.empty())
        {
            fprintf(stderr, "network graph not ready\n");
            return -1;
        }
    
        // load file
        int ret = 0;
        //从传入的参数中读取模型权重参数,也就是从xxx.bin中读取
        ModelBinFromStdio mb(fp);
        //这里回忆一下net.h中的成员属性layers,一个用来存储layer的vector
        for (size_t i=0; i<layers.size(); i++)
        {
            //遍历layers中的每一个layer
            Layer* layer = layers[i];
            
            //Here we found inconsistent content in the parameter file.
            //如果layer为空,说明没有构造或构造的和网络结构文件不一致
            if (!layer){
                fprintf(stderr, "load_model error at layer %d, parameter file has inconsistent content.\n", (int)i);
                ret = -1;
                break;
            }
            //载入模型权重
            int lret = layer->load_model(mb);
            //返回为0是成功载入
            if (lret != 0)
            {
                fprintf(stderr, "layer load_model %d failed\n", (int)i);
                ret = -1;
                break;
            }
            //根据设置的option参数创建流程管道,opt对象的类型是option类,在layer.h中可以找到声明,opt对象主要作用是配置一些设置(比如线程数,是否使用vulkan加速、是否使用gpu等等设置)
            int cret = layer->create_pipeline(opt);
            //返回0说明创建pipeline成功
            if (cret != 0)
            {
                fprintf(stderr, "layer create_pipeline %d failed\n", (int)i);
                ret = -1;
                break;
            }
        }
        //网络复用
        fuse_network();
    
        return ret;
    }
    

    这里有三个点:

    ModelBinFromStdio mb(fp);                  //xxx.bin文件的解析
    int lret = layer->load_model(mb);          //把解析出的内容载入到创建好的layer中
    int cret = layer->create_pipeline(opt);   //根据opt的设置创建好执行管道
    

    可以看到xxx.bin文件是一个二进制文件(打开是乱码的),ModelBinFromStdio类型的声明在modelbin.h中:

    class ModelBinFromStdio : public ModelBin
    {
    public:
        // construct from file
        ModelBinFromStdio(FILE* binfp);
    
        virtual Mat load(int w, int type) const;
    
    protected:
        FILE* binfp;
    };
    

    可以看到ModelBinFromStdio继承了ModelBin,而ModelBin的声明也在modelbin.h中:

    class ModelBin
    {
    public:
        virtual ~ModelBin();
        // element type
        // 0 = auto
        // 1 = float32
        // 2 = float16
        // 3 = int8
        //一维的向量载入
        virtual Mat load(int w, int type) const = 0;
        //二维的图片载入
        virtual Mat load(int w, int h, int type) const;
        // 三维的dim载入
        virtual Mat load(int w, int h, int c, int type) const;
    };
    

    可以看到只有一个多态的load函数,类型是ncnn自己的Mat类型。
    在modelbin.cpp中中:

    ModelBinFromStdio::ModelBinFromStdio(FILE* _binfp) : binfp(_binfp)
    {
    }
    

    ModelBinFromStdio::ModelBinFromStdio的初始化列表binfp,在.h文件中可以看到binfp是一个FILE*类型的对象。
    在modelbin.cpp中load的实现就很简单了,根据多态的load传入不同的参数,声明一维、二维或三维的Mat,然后把从xxx.bin中解析出的数据放入Mat返回回去就行了。
    至于layer->load_model(mb),我们指定layer其实是解析完模型网络结构的具体的layer,比如卷积Convolution,也就是这个调用其实调用的是layer的子类Convolution层的load_model()函数。所以来看下Convolution层的load_model()函数(在src/layer/convolution.cpp里)

    int Convolution::load_model(const ModelBin& mb)
    {
         //看到这里其实就是把ModelBinFromStdio mb的load函数里解析并返回的模型参数的Mat赋值给这里的weight_data 
        weight_data = mb.load(weight_data_size, 0);
        if (weight_data.empty())
            return -100;
    
        if (bias_term)
        {
            bias_data = mb.load(num_output, 1);
            if (bias_data.empty())
                return -100;
        }
    
        if (int8_scale_term)
        {
            weight_data_int8_scales = mb.load(num_output, 1);
            bottom_blob_int8_scale = mb.load(1, 1)[0];
        }
    
        return 0;
    }
    

    最后是int cret = layer->create_pipeline(opt),

    int Layer::create_pipeline(const Option& /*opt*/)
    {
        return 0;
    }
    

    同样的,举例卷积,create_pipeline也是由src/layer/convolution.cpp里重写的int Convolution::create_pipeline(const Option& opt)来执行。

    相关文章

      网友评论

          本文标题:ncnn源码阅读笔记(四)

          本文链接:https://www.haomeiwen.com/subject/rrqapctx.html