Caffe内部维护一个注册表用于查找特定Layer对应的工厂函数(Layer Factory的设计用到了设计模式里的工厂模式)。Caffe的Layer注册表是一组键值对(key, value)( LayerRegistry里用map数据结构维护一个CreatorRegistry list, 保存各个Layer的creator的函数句柄),key为Layer的类型(Layer类名去掉后面的”Layer”字符串),value为其对应的工厂函数(creator的函数句柄):

typedef shared_ptr<Layer<Dtype> > (*Creator)(const LayerParameter&);
typedef std::map<string, Creator> CreatorRegistry;

注册表类型为CreatorRegistry,实际类型为std::map<string, Creator>。可以通过Registry 函数获取注册表的全局单例。而注册的过程就是一个map操作。

Caffe是通过宏定义的方式注册各种Layer,在编译阶段自动执行宏替换就注册了所有的Layer. 每一个Layer type只允许注册一次。使用两组宏来控制Layer的注册:

#define REGISTER_LAYER_CREATOR(type, creator)                                  \
  LayerRegisterer<float> g_creator_f_##type(#type, creator<float>);     \
  LayerRegisterer<double> g_creator_d_##type(#type, creator<double>)    \

#define REGISTER_LAYER_CLASS(type)                                             \
  template <typename Dtype>                                                    \
  shared_ptr<Layer<Dtype> > Creator_##type##Layer(const LayerParameter& param) \
  {                                                                            \
    return shared_ptr<Layer<Dtype> >(new type##Layer<Dtype>(param));           \
  }                                                                            \
  REGISTER_LAYER_CREATOR(type, Creator_##type##Layer)

REGISTER_LAYER_CLASS宏可以实现将指定Layer注册到全局注册表中,首先定义一个工厂函数用来产生Layer对象,然后调用REGISTER_LAYER_CREATOR将工厂函数和Layer的类型名进行注册,支持两种Layer的数据类型,float和double。两个变量一个对应float,一个对应double,这两个变量的初始化,也就是它们的构造函数实际上完成Layer的注册动作。REGISTER_LAYER_CLASS实际上是为每一个Layer创建一个creator函数.

LayerRegisterer对象初始化时(会调用LayerRegisterer类构造函数)实际上又是调用LayerRegistry类的静态方法 AddCreator函数。

以下是对Caffe code中layer_factory.hpp文件的注释:

/**
 * @brief A layer factory that allows one to register layers.
 * During runtime, registered layers could be called by passing a LayerParameter
 * protobuffer to the CreateLayer function:
 *
 *     LayerRegistry<Dtype>::CreateLayer(param);
 *
 * There are two ways to register a layer. Assuming that we have a layer like:
 *
 *   template <typename Dtype>
 *   class MyAwesomeLayer : public Layer<Dtype> {
 *     // your implementations
 *   };
 *
 * and its type is its C++ class name, but without the "Layer" at the end
 * ("MyAwesomeLayer" -> "MyAwesome").
 *
 * If the layer is going to be created simply by its constructor, in your c++
 * file, add the following line:
 *
 *    REGISTER_LAYER_CLASS(MyAwesome);
 *
 * Or, if the layer is going to be created by another creator function, in the
 * format of:
 *
 *    template <typename Dtype>
 *    Layer<Dtype*> GetMyAwesomeLayer(const LayerParameter& param) {
 *      // your implementation
 *    }
 *
 * (for example, when your layer has multiple backends, see GetConvolutionLayer
 * for a use case), then you can register the creator function instead, like
 *
 * REGISTER_LAYER_CREATOR(MyAwesome, GetMyAwesomeLayer)
 *
 * Note that each layer type should only be registered once.
 */

#ifndef CAFFE_LAYER_FACTORY_H_
#define CAFFE_LAYER_FACTORY_H_

#include <map>
#include <string>

#include "caffe/common.hpp"
#include "caffe/proto/caffe.pb.h"

namespace caffe {

template <typename Dtype>
class Layer;

// LayerRegistry:注册类,将每一个Layer的type(std::string)和对应的creator(函数指针)存放到一个map中
template <typename Dtype>
class LayerRegistry {
 public:
  // LayerRegistry里用map数据结构, 维护一个CreatorRegistry list, 保存各个layer的creator的函数句柄
  typedef shared_ptr<Layer<Dtype> > (*Creator)(const LayerParameter&); // 函数指针,返回Layer<Dtype>类型的指针
  typedef std::map<string, Creator> CreatorRegistry;

  // 获取注册表,内部注册表,静态函数,仅第一次调用时会new,其它直接return
  static CreatorRegistry& Registry() { // 只创建一个map实例
    // 全局静态变量(map实例)
    static CreatorRegistry* g_registry_ = new CreatorRegistry();
    return *g_registry_;
  }

  // Adds a creator.
  // AddCreator函数用来向Registry列表中添加一组<type, creator>
  // 向map中加入一个映射
  static void AddCreator(const string& type, Creator creator) {
    CreatorRegistry& registry = Registry();
    CHECK_EQ(registry.count(type), 0)
        << "Layer type " << type << " already registered.";
    registry[type] = creator;
  }

  // Get a layer using a LayerParameter.
  // 在net.cpp中会被调用,在初始化整个网络的时候会根据参数文件中的层的类型去创建该层的实例
  static shared_ptr<Layer<Dtype> > CreateLayer(const LayerParameter& param) {
    if (Caffe::root_solver()) {
      LOG(INFO) << "Creating layer " << param.name();
    }
    const string& type = param.type(); // 从LayerParameter中获得字符串type
    CreatorRegistry& registry = Registry(); // 获取注册表指针
    // 验证是否查找到给定type的creator
    CHECK_EQ(registry.count(type), 1) << "Unknown layer type: " << type
        << " (known types: " << LayerTypeList() << ")";
    return registry[type](param); // 根据layer name, 调用相应creator函数
  }

 private:
  // Layer registry should never be instantiated - everything is done with its
  // static variables.
  // 禁止实例化
  LayerRegistry() {}

  // 返回layer type
  static string LayerTypeList() {
    CreatorRegistry& registry = Registry(); // 获取注册表指针
    string layer_types;
    // 遍历注册表
    for (typename CreatorRegistry::iterator iter = registry.begin();
         iter != registry.end(); ++iter) {
      if (iter != registry.begin()) {
        layer_types += ", ";
      }
      layer_types += iter->first;
    }
    return layer_types;
  }
};

// LayerRegisterer:Layer注册器,供后面的宏使用
template <typename Dtype>
class LayerRegisterer {
 public:
  // 向LayerRegistry的registry list中, 添加一个layer的creator
  LayerRegisterer(const string& type,
                  shared_ptr<Layer<Dtype> > (*creator)(const LayerParameter&)) {
    // LOG(INFO) << "Registering layer type: " << type;
    LayerRegistry<Dtype>::AddCreator(type, creator);
  }
};

// 通过宏定义注册各种Layer
// 将创建layer对象的函数指针加入map
#define REGISTER_LAYER_CREATOR(type, creator)                                  \
  LayerRegisterer<float> g_creator_f_##type(#type, creator<float>);     \
  LayerRegisterer<double> g_creator_d_##type(#type, creator<double>)    \

#define REGISTER_LAYER_CLASS(type)                                             \
  template <typename Dtype>                                                    \
  shared_ptr<Layer<Dtype> > Creator_##type##Layer(const LayerParameter& param) \
  {                                                                            \
    return shared_ptr<Layer<Dtype> >(new type##Layer<Dtype>(param));           \
  }                                                                            \
  REGISTER_LAYER_CREATOR(type, Creator_##type##Layer)

}  // namespace caffe

#endif  // CAFFE_LAYER_FACTORY_H_

以下是用于获取所有层名的函数:

#include "funset.hpp"
#include "common.hpp"

int get_layer_type_list()
{
	caffe::LayerRegistry<double>::CreatorRegistry& registry = caffe::LayerRegistry<double>::Registry();

	std::vector<std::string> layers_list;
	for (caffe::LayerRegistry<double>::CreatorRegistry::iterator iter = registry.begin(); iter != registry.end(); ++iter) {
		layers_list.push_back(iter->first);
	}

	fprintf(stdout, "layer count: %d\n", layers_list.size());
	for (int i = 0; i < layers_list.size(); i++) {
		fprintf(stdout, "%d:    %s\n", i+1, layers_list[i].c_str());
	}

	return 0;
}

执行结果如下:

GitHubhttps://github.com/fengbingchun/Caffe_Test

Caffe中Layer注册机制的更多相关文章

  1. caffe layer注册机制

    Caffe内部维护一个注册表用于查找特定Layer对应的工厂函数(Layer Factory的设计用到了设计模式里的工厂模式).Layer_factory的主要作用是负责Layer的注册,已经注册完事 ...

  2. TensorFlow中的设备管理——Device的创建与注册机制

    背景 [作者:DeepLearningStack,阿里巴巴算法工程师,开源TensorFlow Contributor] 作为一款优秀的异构深度学习算法框架,TensorFlow可以在多种设备上运行算 ...

  3. 分享:Android中利用机器码注册机制防止破解(转)

    转自:http://blog.csdn.net/huzgd/article/details/6684094 最近做一个Android应用时遇到这个问题,客户要求功能必须注册才能使用,而程序本身又不是联 ...

  4. 怎样在caffe中添加layer以及caffe中triplet loss layer的实现

    关于triplet loss的原理.目标函数和梯度推导在上一篇博客中已经讲过了.详细见:triplet loss原理以及梯度推导.这篇博文主要是讲caffe下实现triplet loss.编程菜鸟.假 ...

  5. Batch Normalization 与 Caffe中的 相关layer

    在机器学习领域,通常假设训练数据与测试数据是同分布的,BatchNorm的作用就是深度神经网络训练过程中, 使得每层神经网络的输入保持同分布. 原因:随着深度神经网络层数的增加,训练越来越困难,收敛越 ...

  6. .Net中Remoting通信机制简单实例

    .Net中Remoting通信机制 前言: 本程序例子实现一个简单的Remoting通信案例 本程序采用语言:c# 编译工具:vs2013工程文件 编译环境:.net 4.0 程序模块: Test测试 ...

  7. 浅谈Linux中的信号处理机制(二)

    首先谢谢 @小尧弟 这位朋友对我昨天夜里写的一篇<浅谈Linux中的信号处理机制(一)>的指正,之前的题目我用的“浅析”一词,给人一种要剖析内核的感觉.本人自知功力不够,尚且不能对着Lin ...

  8. caffe中权值初始化方法

    首先说明:在caffe/include/caffe中的 filer.hpp文件中有它的源文件,如果想看,可以看看哦,反正我是不想看,代码细节吧,现在不想知道太多,有个宏观的idea就可以啦,如果想看代 ...

  9. 【转】深入Windows内核——C++中的消息机制

    上节讲了消息的相关概念,本文将进一步聊聊C++中的消息机制. 从简单例子探析核心原理 在讲之前,我们先看一个简单例子:创建一个窗口和两个按钮,用来控制窗口的背景颜色.其效果 图1.效果图  Win32 ...

随机推荐

  1. Oracle 18c新特性一览

    1. 一般新特性 1.1. Shadow Lost Write Protection Shadow lost write protection检测到一个丢失的写,它会导致一个主要的数据损坏.可以在不需 ...

  2. RHEL7:如何配置I / O调度程序

    介绍 I / O调度程序用于优化磁盘上的读/写. RHEL 7中有三种类型的I / O调度程序(也称为I / O电梯): CFQ(C fullly F air Q ueuing)促进来自实时流程的I ...

  3. LinkedHashSet 元素唯一,存储取出有序

      package cn.itcast_04; import java.util.LinkedHashSet; /* * LinkedHashSet:底层数据结构由哈希表和链表组成. * 哈希表保证元 ...

  4. vue弹出框的封装

    依旧是百度不到自己想要的,就自己动手丰衣足食 弹出框做成单独的组件confirm.vue; <template> <transition name="mask-bg-fad ...

  5. python伪装网页访问

    # -*- coding:utf8 -*-#import urllib.request#url =' http://www.douban.com/'#webPage=urllib.request.ur ...

  6. 【转】Android应用如何跳转到应用市场详情页面

    Android应用开发过程中,可能会有需求,比如:推广时跳转到应用市场下载应用,跳转到应用市场给自己的应用打分,跳转到应用市场更新自己的应用.那如何跳转到应用市场呢? 可能跳转的方法大家都是知道的,方 ...

  7. Hive学习之路 (十八)Hive的Shell操作

    一.Hive的命令行 1.Hive支持的一些命令 Command Description quit Use quit or exit to leave the interactive shell. s ...

  8. Day10 MVC

    经典三层 表述层(表示层):  前台交互,调用后台   web 业务逻辑层:   处理业务              service 数据持久层:   与数据库之间进行交互  dao 面向对象原则 面 ...

  9. SQL Server 断开某个数据库所有连接(还原的时候需要)

    问题描述: SQL Server数据库备份还原后,在数据库名称后会出现“受限制访问”字样 解决办法: 右键点击数据库 -> 属性 -> 选项 -> 状态 -> 限制访问 -&g ...

  10. github与git基本操作(一)

    一.git上传本地项目到github 前提:github创建一个空仓库(得到“https://自己的仓库url地址”)1.第一步:就是要进入这个目录下,cmd2.第二步:输入git init3.第三步 ...