input layer name 赋值_NCNN-Breakdown(6) create_layer的实现

d6c060648919b6aacac771f7fb7cffe8.png

开始

在第三节我们实现了读取网络proto的基本流程,但是有一个功能没有实现,就是我们在实例化Layer的时候,其实是实例化了父类的Layer,并没有实例化子类的Layer,这样后面就没法调用子类Layer的方法.在上一节,我们实现了squeezenet中用到的所有子类Layer,所以在这一节,我们就实现如何对在读取网络proto的时候,智能的实例化不同的子类Layer.

我们继续插上新的小红旗:

47ff2d4faef8c105f9d6e12e65a7dc3b.png

作用

其实父类Layer的目的,就是为了能够利用C++中的多态和重载机制,然后智能化的实例化不同的子类Layer.

在上一节中,我们实现的子类Layer,有些自己的方法,又会继承父类Layer的方法.这样就可以优雅的用同一个接口去调用不同子类Layer的不同功能.

实现

由于在load_proto中,代表该Layer属于哪个子类的标识只有layer_type这个char*的标识,所以为了自动的把layer_type和开辟各个子空间的函数应起来.

第一步,我们需要一个layer_type和返回对应子类指针的函数(给子类开辟空间)的结构体

//定义返回Layer*的函数指针的别名,layer_creator_func,
//用于开辟各个子类的空间
typedef Layer* (*layer_creator_func)();  

struct layer_registry_entry {
    const char* name;   //layer_type
    layer_creator_func creator; //函数指针,返回Layer*,
};

第二步,我们需要实现给每一个子类开辟空间的函数

#include "layer/input.h"
namespace ncnn{
    Layer* Input_final_layer_creator()
    {return new Input();}
}
#include "layer/convolution.h"
namespace ncnn{
    Layer* Convolution_final_layer_creator()
    {return new Convolution();}
}
#include "layer/relu.h"
namespace ncnn{
    Layer* Relu_final_layer_creator()
    {return new Relu();}
}
...
...

不过上面挨个需要定义每一个Layer子类的开辟空间的函数,较为繁琐,我们发现每一个Layer子类的开辟空间的函数都较为类似,唯一不同的是Layer名字的变化.所以我们可以定义一个宏来做这些重复的操作

#define DEFINE_LAYER_CREATOR(name) 
    ncnn::Layer* name##_final_layer_creator() { return new name; }
}

#include "layer/input.h"
namespace ncnn {
DEFINE_LAYER_CREATOR(Input)
}
#include "layer/convolution.h"
namespace ncnn {
DEFINE_LAYER_CREATOR(Convolution)
}
#include "layer/relu.h"
namespace ncnn {
DEFINE_LAYER_CREATOR(ReLU)
}

第三步,接下来,我们实现一个类似于"字典"的功能,该"字典"的目的是使得从proto读入的layer_type和每一个Layer子类的开辟空间的函数对应

由于我们上面定义了layer_registry_entry这样一个结构体,这个结构体本质上的目的就是使得layer_type的char*变量和给Layer子类的开辟空间的函数对应.于是,我们基于layer_registry_entry来构造一个数组,它包含了所有用到的对应关系:

static const layer_registry_entry layer_registry[] = {  
    {"ReLU", ReLU_final_layer_creator},           
    {"Input", Input_final_layer_creator},         
    {"Pooling", Pooling_final_layer_creator},     
    {"Convolution", Convolution_final_layer_creator},   
    {"Split", Split_final_layer_creator},         
    {"Concat", Concat_final_layer_creator},       
    {"Dropout", Dropout_final_layer_creator},    
    {"Softmax", Softmax_final_layer_creator},     
};

第四步,有了这样的一个数组,我们只需要当layer_type传进来的时候,找到对应的结构体就好了,于是我们先确定应该输出上述数组的哪个元素,即layer_type对应的layer_registry_entry的数组id

//确定一共有多少个layer                                        
static const int layer_registry_entry_count =     
   sizeof(layer_registry) / sizeof(layer_registry_entry);

int layer_to_index(const char* type) {             
    for (int i = 0; i < layer_registry_entry_count; i++) {           
        if (strcmp(type, layer_registry[i].name) == 0) return i;     
    }                                                    
    return -1;                                                       
}

第五步,找到了数组id,我们就可以构建我们的函数了:

//基于id在数组里面找constructor                                                    
Layer* create_layer(int index) {                                                   
    if (index < 0 || index >= layer_registry_entry_count) return 0;                

    layer_creator_func layer_creator = 0;                                          
    { layer_creator = layer_registry[index].creator; }                             

    if (!layer_creator) return 0;                                                  

    Layer* layer = layer_creator();                                                
    layer->typeindex = index;                                                      
    return layer;                                                                  
}

我们最后总结一些create_layer的流程:

  1. 构建返回Layer*的函数指针的别名:layer_creator_func
  2. 构建layer_type和layer_creator_func对应的结构体:layer_registry_entry
  3. 实现给每一个子类开辟空间的函数
  4. 基于layer_registry_entry来构造一个数组,它包含了所有layer_type和create函数用到的对应关系
  5. 基于layer_type找到该layer_type在layer_registry_entry中的id
  6. 基于上述id,找到layer_type对应开辟空间的函数.
  7. 开辟子类空间,返回子类指针.

代码示例

测试程序放到了这里.

代码结构如下:

b0407c224df67970f6df6c25bc8ed7f9.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值