caffe中layer_factory的机制实现

caffe中的layer都是由layer_factory创建的,是典型的工厂模式。
我们看caffe的源码,就会发现,不管创建哪种layer,都可以用如下的函数来创建:

shared_ptr<Layer<Dtype> > layer = LayerRegistry<Dtype>::CreateLayer(layer_param);

这个函数就定义在layer_factory.hpp文件中,是 LayerRegistry<Dtype> class的static函数.在这个函数中,它会根据传入的param中的type变量来创建对应的layer,非常方便。那么,它是怎么实现的呢?我们下面来详细讲解一下:
1.在layer_factory.hpp中定义了两个宏,如下:

#define REGISTER_LAYER_CREATOR(type, creator)                                    \
  static LayerRegisterer<float> g_creator_f_##type(#type, creator<float>);        \
  static 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)

在每个具体layer的cpp文件的最后,都调用了REGISTER_LAYER_CLASS宏,那么这个宏是干什么用的呢?我们看code会发现,REGISTER_LAYER_CLASS宏首先定义了一个函数,可以看出就是根据传入的type来生成一个creator_xxLayer的函数,在这个函数内部,new了一个该layer对象。然后就调用了REGISTER_LAYER_CREATOR宏。那REGISTER_LAYER_CREATOR这个宏又干了什么呢?我们可以看到,它定义了两个static 变量g_creator_f_xxtype。这两个变量的其实是一个模板类的两个实例,一个是float, 一个是double. 我们看一个就可以了,这个模板类为LayerRegisterer。 下面我们看一下这个类是干什么的,code如下:

template <typename Dtype>
class LayerRegisterer {
 public:
  LayerRegisterer(const string& type,
                  shared_ptr<Layer<Dtype> > (*creator)(const LayerParameter&)) {
    // LOG(INFO) << "Registering layer type: " << type;
    LayerRegistry<Dtype>::AddCreator(type, creator);
  }
};

我们可以看到,这个类就定义了一个构造函数,所以上面宏定义里面创建该类的两个对象时,就自动调用了这个构造函数。我们可以发现,在该构造函数中,调用了另外一个类LayerRegistry<Dtype>的一个static函数AddCreator(type, creator).到目前为止,我们知道的信息为在每个具体layer的实现cpp文件里面,都定义了一个creator_xxLayer函数,还定义了两个static变量。定义static变量的时候,自动调用了另外一个类的AddCreator函数。现在,我们来看这个函数和这个函数所属的类。
2.这个类的名字为LayerRegistry, code如下:

emplate <typename Dtype>
class LayerRegistry {
 public:
  typedef shared_ptr<Layer<Dtype> > (*Creator)(const LayerParameter&);
  typedef std::map<string, Creator> CreatorRegistry;

  static CreatorRegistry& Registry() {
    static CreatorRegistry* g_registry_ = new CreatorRegistry();
    LOG(INFO) << "-----------------------CreatorRegistry point is " << g_registry_;
    LOG(INFO) << "g_registry size is " << g_registry_->size();
    return *g_registry_;
  }

  // Adds a creator.
  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.
  static shared_ptr<Layer<Dtype> > CreateLayer(const LayerParameter& param) {
    if (Caffe::root_solver()) {
      LOG(INFO) << "Creating layer " << param.name();
    }
    const string& type = param.type();
    if (Caffe::root_solver()) {
      LOG(INFO) << "layer type is " << type;
    }
    CreatorRegistry& registry = Registry();
    for (auto &it : registry) {
      LOG(INFO) << "--------------registry type is " << it.first;
    }
    CHECK_EQ(registry.count(type), 1) << "Unknown layer type: " << type
        << " (known types: " << LayerTypeListString() << ")";
    return registry[type](param);
  }

  static vector<string> LayerTypeList() {
    CreatorRegistry& registry = Registry();
    vector<string> layer_types;
    for (typename CreatorRegistry::iterator iter = registry.begin();
         iter != registry.end(); ++iter) {
      layer_types.push_back(iter->first);
    }
    return layer_types;
  }

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

  static string LayerTypeListString() {
    vector<string> layer_types = LayerTypeList();
    string layer_types_str;
    for (vector<string>::iterator iter = layer_types.begin();
         iter != layer_types.end(); ++iter) {
      if (iter != layer_types.begin()) {
        layer_types_str += ", ";
      }
      layer_types_str += *iter;
    }
    return layer_types_str;
  }
};

我们可以发现,该类的构造函数为private, 所以不能实例化对象,同时他的所有public函数都是static函数,所以可以在外部直接调用。现在我们发现,在上面调用的AddCreator函数中,他调用了Registry函数,在这个函数中,new出来了一个map.这里有一个关键点,就是static变量的初始化只执行一次,所以这里虽然每次都会调用static CreatorRegistry* g_registry_ = new CreatorRegistry();,但只有第一次调用时真的new了一个map对象,后面的调用都没有再new对象出来,这是第一个关键点。分析上面的code,我们会发现,AddCreator函数其实就是将一个创建类的函数和对应的type注册到一个全局static map中,就是上面创建的g_registry_。这样当我们需要创建某个layer时,就可以根据它的type在这个map中找到对应的create layer函数,就可以创建对应的layer了。是不是很巧妙。

总结一下,主要有两个关键点:
1.就是static的初始化只执行一次,所以每个layer调用REGISTER_LAYER_CLASS宏时执行到static CreatorRegistry* g_registry_ = new CreatorRegistry()时,只有第一次真正创建了对象,其他后续的执行都是直接使用这个static指针。
2. 就是各个layer的create函数是什么时候注册到这个map中的,这里就是利用了宏REGISTER_LAYER_CREATOR中那两个变量的定义。每个layer的这两个变量都是static变量,在程序运行之前就会首先被创建。正是在他们的创建构造函数里,将本layer的create函数注册到了这个map中,所以当你写的程序比如caffe开始运行时,所有声明了REGISTER_LAYER_CLASS的layer都已经自动注册到g_registry_这个map中了,直接使用就可以了,不用自己再注册(如果加log调试,我们会发现,这几个变量的创建在main函数运行之前)。这种方式虽然比较绕,但是对于caffe这种开源的软件,不同的开发者会开发不同的layer, 如果需要自己去注册的话,上层的app需要经常修改,不是很方便,这种方式下,只要开发者开发自己的layer的时候把这个宏声明一下,就自动注册到全局map中去了,非常方便。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值