CAFFE源码学习笔记之七-layer_factory以及layer基类

一、前言
caffe的几大基本模块中之一——Blob已经梳理完毕,现在该轮到Layer了。这一章先梳理layer基类和工厂类,为data_layer做一下铺垫。

Layer定义了layer的初始化,前向传播,后向传播。前向传播可以计算loss,后向传播计算梯度信息。

layer类是caffe中最为庞大的一类,所以作者使用工厂模式,这样当我们使用caffe中已经有的层或者自己定义的层时只需要注册就行了。

在运行期间,可以通过将LayerParameter protobuf参数传入函数就可以调用该层:

LayerRegistry<Dtype>::CreateLayer(param);

注册层的方式有两种:
其一、如果新建层是由其自身的构造函数创建的,注册如下:

REGISTER_LAYER_CLASS(layername);

其二、如果新建层由其他ctreator函数创建,形如:

  template <typename Dtype>
  Layer<Dtype*> GetlayernameLayer(const LayerParameter& param) {
  // your implementation
  }

那么可以通过如下方式注册:

REGISTER_LAYER_CREATOR(layername, GetlayernameLayer)

注意:每层只能注册一次。
二、源码分析
1、layer类
(1)成员变量

  /**  protobuf 中存储的层参数*/
  LayerParameter layer_param_;
  /** 状态: TRAIN or TEST */
  Phase phase_;
  /** 学习参数 */
  vector<shared_ptr<Blob<Dtype> > > blobs_;
  /** 是否进行后向传播的标志,数据层等都不进行后向传播 */
  vector<bool> param_propagate_down_;

  /** loss权重,一般只有最后一层权重不为0,其余层都不计算loss */
  vector<Dtype> loss_;

(2)层的构造
构造函数完成层参数的传入,包括该层用于训练还是测试,层中存储权值的blob_。

注意构造函数不能是虚函数。

  explicit Layer(const LayerParameter& param)
    : layer_param_(param) {
      phase_ = param.phase();
      if (layer_param_.blobs_size() > 0) {
        blobs_.resize(layer_param_.blobs_size());//通常blob_[0]是w,blob_[1]是b
        for (int i = 0; i < layer_param_.blobs_size(); ++i) {
          blobs_[i].reset(new Blob<Dtype>());
          blobs_[i]->FromProto(layer_param_.blobs(i));
        }
      }
    }

SetUp函数是自定义层的“构造函数”,主要做:
第一、检查输入和输出的数量;
第二、真正的setup layer;
第三、根据输入和内部结构重构输出的维度;
第四、设置loss权重;

  void SetUp(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {
    CheckBlobCounts(bottom, top);
    LayerSetUp(bottom, top);
    Reshape(bottom, top);
    SetLossWeights(top);
  }

下面是每个函数的详解:

  virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {}//bottom是输入数据,也就是上一层的输出;
      //top是输出,但是这个输出没有经过reshape;
      //该函数主要完成的是读入层参数完成层的搭建。
  virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) = 0;
      //根据输入数据的维度修改输出和中间buffer的维度信息

检查输入输出的个数,有些层需要多个输入或者输出

virtual void CheckBlobCounts(const vector<Blob<Dtype>*>& bottom,
                               const vector<Blob<Dtype>*>& top) 

设置每层的loss权重

inline void SetLossWeights(const vector<Blob<Dtype>*>& top) {
    const int num_loss_weights = layer_param_.loss_weight_size();
    if (num_loss_weights) {
      CHECK_EQ(top.size(), num_loss_weights) << "loss_weight must be "
          "unspecified or specified once per top blob.";
      for (int top_id = 0; top_id < top.size(); ++top_id) {
        const Dtype loss_weight = layer_param_.loss_weight(top_id);
        if (loss_weight == Dtype(0)) { continue; }//如果权重为0,直接跳过
        this->set_loss(top_id, loss_weight);
        const int count = top[top_id]->count();
        Dtype* loss_multiplier = top[top_id]->mutable_cpu_diff();
        caffe_set(count, loss_weight, loss_multiplier);//将权重信息存在diff_blob中。
      }
    }
  }

(3)前向传播
在前向传播之前需要保证reshape函数已经将top修正到合适的维度。
前向传播可以计算loss,具体的前向计算在具体的层中不一样,所以后面单独讲。
(4)后向传播
后向传播不是每次一层都需要的。

  inline bool param_propagate_down(const int param_id) {
    return (param_propagate_down_.size() > param_id) ?
        param_propagate_down_[param_id] : false;
  }
  /**
   * @brief Sets whether the layer should compute gradients w.r.t. a
   *        parameter at a particular index given by param_id.
   */
  inline void set_param_propagate_down(const int param_id, const bool value) {
    if (param_propagate_down_.size() <= param_id) {
      param_propagate_down_.resize(param_id + 1, true);
    }
    param_propagate_down_[param_id] = value;
  }

二、layer_factory
1、创建层注册表:

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

  static CreatorRegistry& Registry() {
    static CreatorRegistry* g_registry_ = new CreatorRegistry();
    return *g_registry_;
  }

2、注册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;
  }

3、根据参数调用层:

  static shared_ptr<Layer<Dtype> > CreateLayer(const LayerParameter& param) {
    if (Caffe::root_solver()) {
      LOG(INFO) << "Creating layer " << param.name();
    }
    const string& type = param.type();
    CreatorRegistry& registry = Registry();
    CHECK_EQ(registry.count(type), 1) << "Unknown layer type: " << type
        << " (known types: " << LayerTypeListString() << ")";
    return registry[type](param);//regisry[type]是函数指针

该类不能被实例化,怎么做?将构造函数置于private域中。
在c++11中,可以直接跟“=delete”

privateLayerRegistry() {}

最后,作者将注册写成了宏:

#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)

}  // namespace caffe
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值