在<剖析Caffe源码之Layer>,对Layer代码进行了基本分析,可以知道Layer是所有的其他Layer的基本类,由此来扩展出各个神经网络中所需要的Layer,体现了caffe的可扩展性.那么问题来了,既然caffe中定义了各种Layer,那边在实际运行中,caffe是如何知道执行到做需要的Layer type的Layer?,下面将这层洋葱给剥掉.

在这里将不得不提到caffe中的LayerRegistry类,该类的主要功能就是将caffe中已有的各种类型Layer注册到caffe中,caffe以便知道执行的是那个Layer

Layer_factory

Layer_factory主要是提供向caffe中提供Layer注册功能,以便caffe知道运行时能够找到相关的Layer.,其主要代码位于caffe\include\caffe\layer_factory.hpp和caffe\scr\caffe\layer_factory.cpp文件中.主要功能是在layer_factory.hpp中

LayerRegistry

在\layer_factory.hpp文件中可以看到LayerRegistry类,该类主要是提供Layer功能,在public中CreatorRegistry类型

CreatorRegistry

可以看到CreatorRegistry为一个map类型,其里面的key-value对应的值分别为string, Creator, 其中string为Layer的type,而Creator可以看到是一个函数指针,其入参为LayerParameter,其意义为相对应用的Layer creator函数用于产生相对应的Layer,该设计模式为C++的工厂模式,是caffe中用到的比较关键的模式.

由上述定义可以大概猜到CreatorRegistry是用于记录Layer的注册的数据结构,key-value对应的Layer type和其对应的Layer Creator函数.继续深入挖掘,既然CreatorRegistry是注册的数据结构,那边其对应的具体数据在哪里?一定会有一个类似的全局变量采用该结构,用于记录实际注册的Layer状况.

Registry()

继续深入layer_factory.hpp代码,看到LayerRegistry类中,定义的Regis 遗留问题,不知道为什么,在用例代码中没有Net<float> nn(proto, caffe::TEST);这句代码即不会有Layer注册,只有加载网络才有Layer注册try()方法:

函数里面定义了静态变量g_registry_,由static关键字作用可知,其静态变量的存储区域是在静态变量区域,不会随着函数调用完毕后而消失,故记录Layer注册信息是存储在静态变量区中的g_registry_中,该函数仅会在第一次调用时会new申请一个CreatorRegistry空间,后面多次调用不再申请新的空间.因为g_registry_为静态变量.

AddCreator()

AddCreator()函数为LayerRegistry类中提供的注册layer接口,其代码如下:

将其相对应的Layer Creator函数指针以及Layer type注册到g_registry_中.首先调用Registry()获取到g_registry_指针(仅在第一次申请新的内存,其他相当与获取到g_registry_指针)

LayerRegisterer

LayerRegisterer主要提供了对外接口注册功能,到现在,基本知道了Layer注册内部实现,其实内部就是个map,记录其注册Layer信息,那么对外接口主要在LayerRegisterer中,对外提供的注册接口是一个宏:

REGISTER_LAYER_CLASS(type)

该宏主要提供了对外接口,在caffe中搜索该宏:

可以看到在各个layer中都有使用到该宏,比如data layer中如下:

其注册的type为protxt中使用到的Type,一般是使用Layer名字前的作为Type, 比如DataLayer中以Data作为type, ExpLayer以Exp作为Type,方便编译维护.那么现在知道了Layer是怎么注册的,主要是通过REGISTER_LAYER_CLASS()函数,到此完成了其入门,但是如果想继续深入下去,可以继续探讨REGISTER_LAYER_CLASS源码

REGISTER_LAYER_CLASS源码

REGISTER_LAYER_CLASS源码如下:

这段代码其实看起来比较晦涩,可以看到里面有很多#到底是做什么用的?

这里要插播下C++里面的一个基础知识,#的主要作用是将宏参数不经扩展地转换成字符串常量,比如一下代码:

#define NAME(name)  #name

定义如上宏,在实际定义宏参数name,其实并不知道其name的真正类型,如果要将name变量的值作为字符传,那边可以在其前面加上#,如何name为Sam 

NAME(Sam);

最后结果为字符串Sam

##作用其实就是作为链接符号,将其值作为其一部分,来链接起来,例子如下:

#define NAME(name)  name_is_##name

如何name为Sam, 那么其实结果相当与name_is_Sam.

那么可以尝试对其REGISTER_LAYER_CLASS(Data)进行展开为如下代码:

  template <typename Dtype>                                                shared_ptr<Layer<Dtype> > Creator_DataLayer(const LayerParameter& param) {                                                                            return shared_ptr<Layer<Dtype> >(new typeData<Dtype>(param));           }                                                                           REGISTER_LAYER_CREATOR(type, Creator_DataLayer)

REGISTER_LAYER_CREATOR宏定义如下:

那么最终展开如下:

  template <typename Dtype>                                                shared_ptr<Layer<Dtype> > Creator_DataLayer(const LayerParameter& param) {                                                                            return shared_ptr<Layer<Dtype> >(new DataLayer<Dtype>(param));           }                                                                           static LayerRegisterer<float> g_creator_f_Data(Data, Creator_DataLayer);     static LayerRegisterer<double> g_creator_d_Data(Data, Creator_DataLayer);

在DataLayer中其最终展开的结果如上,相当于创建了两个静态LayerRegisterer全局变量类的g_creator_f_Data和g_creator_d_Data,类型分别为float和double.相当与将Creator_DataLayer函数指针注册上去.

继续查看LayerRegisterer类的构造函数,如下:

最后是调用的LayerRegistry类中的AddCreator,进行注册.

那么问题来了既然REGISTER_LAYER_CLASS()能够实现Layer注册功能,何时才能能够真正调用注册.还要继续补充static关键字作用,static关键子修饰变量是存储在静态存储区中,不依赖于类的具体实例,它是在main函数运行之前就已经开始申请内存空间,那么既然注册最终是通过g_creator_f_Data和g_creator_d_Data,两个静态变量实现的,所以在main函数运行之前就会新建LayerRegisterer空间,进而调用到LayerRegisterer的构造函数.

那么就可以得出使用REGISTER_LAYER_CLASS宏注册的类,其在main函数之前就实现了注册Layer功能,这就是LayerRegisterer设计精妙之处.

另外一个问题Layer是如何产生的?

首先看一下CreateLayer源码

CreateLayer()函数如上图,其最终调用的是注册的Creator()函数指针,而Creator注册的是各个Layer注册函数,以DataLayer为例子,最终展开代码:

  template <typename Dtype>                                                shared_ptr<Layer<Dtype> > Creator_DataLayer(const LayerParameter& param) {                                                                            return shared_ptr<Layer<Dtype> >(new DataLayer<Dtype>(param));           }

相当与new 创建了一个DataLayer,创建过程中将会调用DataLayer的构造函数,在Layer中已经讲解了其构造函数,可以查看剖析Caffe源码之Layer

用例

用例遍历访问所有注册的Layer,代码如下:

#include <vector>
#include <iostream>
#include <caffe/net.hpp>using namespace std;
using namespace caffe;int main(void)
{std::string proto("lenet_train_test.prototxt");//cout<<"Test 11111111111"<<endl;//Net<float> nn(proto, caffe::TEST);LayerRegistry<double>::CreatorRegistry & registry = LayerRegistry<double>::Registry();cout<<registry.size()<<endl;for(LayerRegistry<double>::CreatorRegistry::iterator iter=registry.begin();iter != registry.end();++iter){cout<<iter->first<<endl;}Net<float> nn(proto, caffe::TEST);return 0;
}

运行结果如下:

遗留问题,不知道为什么,在用例代码中没有Net<float> nn(proto, caffe::TEST);这句代码即不会有Layer注册,只有加载网络才有Layer注册

总结

现在终于明白了两个问题:Layer是如何注册的,以及Layer如何创建的整个.

尽管layer_factory.hpp文件代码比较简单,但是并不容易阅读,需要很大耐心.

剖析Caffe源码之Layer_factory相关推荐

  1. 剖析Caffe源码之Net---Net构造函数

    目录 Net构造函数 读取Prototxt ReadProtoFromTextFile UpgradeNetAsNeeded 设置网络状态 Init函数 FilterNet InsertSplits ...

  2. 剖析Caffe源码之Net类变量

    在<解析Net的构造函数源码>过程中,可以看到Net类有很多变量,用于存储网络中的各种信息,caffe中类的变量命名规则统一在变量名中加上'_',查看net.hpp代码可以看到使用了很多变 ...

  3. 剖析Caffe源码之Layer

    目录 Layer介绍 Layer分析 LayerParameter Class Layer Layer派生类 Layer源码 Layer构造函数 SetUp函数 Forward函数 Backward函 ...

  4. 剖析Caffe源码之Net(上)---NetParameter参数

    前面几篇文章主要分析了Caffe中的Blob和Layer源码,了解到了Caffe中的参数数据结构文件caffe.proto,掌握了各个Layer是如何注册到Caffe中,下面将分析Net层. 在分析N ...

  5. 剖析Caffe源码之InputLayer

    ImageDataLayer可以完成caffe自动读取图片进行模型训练和推断,但是在实际的应用中一般图像都是通过sensor采集而来,将采集得到的图片送到训练好的模型中进行识别.推断,此时就需要用到I ...

  6. 剖析Caffe源码之ImageDataLayer

    目录 ImageDataLayer参数 Source root_folder new_height.new_width is_color crop_size Prototxt配置 Class Imag ...

  7. 剖析Caffe源码之Net---NetParameter参数

    前面几篇文章主要分析了Caffe中的Blob和Layer源码,了解到了Caffe中的参数数据结构文件caffe.proto,掌握了各个Layer是如何注册到Caffe中,下面将分析Net层. 在分析N ...

  8. 剖析Caffe源码之Blob

    目录 介绍 用例 Class Blob Blob主要源码分析 Blob构造函数 Blob各个维度大小设置 Blob维度信息获取 Blob计算偏移量 Blob数据存储与读取相关操作 Blob数据操作 B ...

  9. caffe源码分析-layer

    本文主要分析caffe layer层,主要内容如下: 从整体上说明下caffe的layer层的类别,以及作用 通过proto定义与类Layer简要说明下Layer的核心成员变量; Layer类的核心成 ...

最新文章

  1. centOS外部浏览器无法访问tomcat8000端口解决办法
  2. Shell脚本攻略02-玩转变量与环境变量
  3. boost::allocator_destroy的实例
  4. KVM虚拟化笔记(七)------kvm虚拟机VNC的配置
  5. 企业微信_H5应用如何本地及真机调试_host配置及代理相关
  6. Java try catch finally语句
  7. php tipask yii 单点登录_php实现单点登录实例
  8. SQL-server 如何与 visual studio 建立连接
  9. JScript中正则表达函数的说明与应用
  10. vue-symbol
  11. 计算机音量程序是哪个键,电脑如何设置音量快捷键
  12. MEPR500+电子护照阅读器|识读模块嵌入式安装与应用说明
  13. Hex Editor Neo Ultimate系统要求
  14. 【2021春招】2021年阿里笔试真题3.6/3.8/3.10/3.12记录
  15. origin python控制台怎么用_如何在标准python控制台中访问BPY?BPY是python的混合器...
  16. python软件是什么架构_Python应用程序的最佳项目结构是什么? [关闭]
  17. C++实现伪大素数生成算法(费马小定理判别法、米勒拉宾素数判定法)
  18. C语言例题理解(小写字母转换成大写字母,兔子生兔子问题,求100以内勾股数,整数逆序输出)
  19. bd09转wgs84 java_各种坐标系互相转换(WGS84转换BD-09主要)
  20. 基于java的商城后台管理系统计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署

热门文章

  1. JEEWX微信开发更便捷,Ngrok 内网穿透利器应用
  2. ResNet在分别在Keras和tensorflow框架下的应用案例
  3. 【KERAS/直方图均衡化】图像数据集扩充
  4. PostgreSQL SSL启用与CA证书生成、配置
  5. 在 CSS 中,width 和 height 指的是内容区域的宽度和高度
  6. Javascript实现计数器,定时警告和停止
  7. 【大数据新手上路】“零基础”系列课程--MySQL 数据整库迁移到 MaxCompute
  8. svn: E180001: Unable to open an ra_local session to URL问题解决方案
  9. IOS--CALayer的介绍及使用技巧
  10. 嵌入式开发之davinci--- spi 中的时钟极性CPOL和相位CPHA