Caffe 源码阅读笔记 [基本模块] Layer和LayerFactory

概述

因为前两篇讲的是数据的读取,本来是打算阅读DataLayer这一块的,但考虑到DataLayer是Layer的子类,因此从Caffe的另一个重要模块Layer开始看起感觉更自然。毕竟Caffe里面最多的代码都是各种各样的Layer,整个Caffe的神经网络都是一个个Layer串起来的,DataLayer只是网络的第一层而已。

Layer

Layer是神经网络的基本单元。每一个Layer的子类都需要实现前向传播函数(通过Layer的输入Blob计算Layer的输出Blob),同时也可能需要实现反向传播函数(根据输出Blob的误差梯度error gradient,计算输入Blob的误差梯度)。注意:每个Layer的bottom是输入数据,经过Layer的计算后会输出到Layer的top上,bottom和top都是Blob的集合。

Layer成员变量

  LayerParameter layer_param_; // 配置Layer
  Phase phase_; // TRAIN or TEST
  vector<shared_ptr<Blob<Dtype> > > blobs_; // Layer的可训练的参数
  // 用于反向传播算法,param_propagate_down_[i]=true表明需要把误差梯度传递给对应的bottom[i](第i个输入blob)
  vector<bool> param_propagate_down_; 
  // 用于前向传播算法,loss函数的值是sum(sum(loss_[i]*top[i]))对所有的top blob
  vector<Dtype> loss_; 

Layer函数

  // 初始化Layer,bottom:输入blob, top:输出blob
  void SetUp(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {
    InitMutex(); // 创建mutex锁
    CheckBlobCounts(bottom, top); // 简单检查bottom和top的数据是否合法
    LayerSetUp(bottom, top); // 调用子类的初始化函数
    Reshape(bottom, top); // 调用子类的Reshape函数
    SetLossWeights(top); // 设置每个top blob的权重,通常是0或者1
  }
  //对应每一个top blob,设置权重loss_weight存到loss_变量里,loss_元素的个数和top元素个数相同,同时更新对应top的diff值为loss_weight
  inline void SetLossWeights(const vector<Blob<Dtype>*>& top) {
    const int num_loss_weights = layer_param_.loss_weight_size();
    if (num_loss_weights) {
      for (int top_id = 0; top_id < top.size(); ++top_id) {
        const Dtype loss_weight = layer_param_.loss_weight(top_id);
        loss_[top_id] = loss_weight;
        // 把top[top_id]->diff_所有元素都设置成loss_weight;
        caffe_set(top[top_id]->count(), 
                  loss_weight, 
                  top[top_id]->mutable_cpu_diff());
      }
    }
  }
  // 子类需要实现前向传播函数Forward_cpu和Forward_gpu
  // Forward函数进行完Forward_xpu得到top后,返回加权总和sum(top.*loss_weights)
  inline Dtype Layer<Dtype>::Forward(const vector<Blob<Dtype>*>& bottom,
    const vector<Blob<Dtype>*>& top) {
    // 获得mutex锁防止并行前向传播
    Lock();
    Dtype loss = 0;
    Reshape(bottom, top);
    switch (Caffe::mode()) {
    case Caffe::CPU:
      Forward_cpu(bottom, top); // GPU会调用Forward_gpu(bottom, top)
      // loss = sum(top->data_.*loss_weight) for all top blobs
      for (int top_id = 0; top_id < top.size(); ++top_id) {
        if (!this->loss(top_id)) { continue; }
        const int count = top[top_id]->count();
        const Dtype* data = top[top_id]->cpu_data();
        const Dtype* loss_weights = top[top_id]->cpu_diff();
        // GPU调用caffe_gpu_dot(count, data, loss_weights, &blob_loss);
        loss += caffe_cpu_dot(count, data, loss_weights);
      }
      break;
    case Caffe::GPU:
      //同上
  Unlock();
  return loss;
}
// 子类需要实现反向传播函数Backward_cpu和Backward_gpu
inline void Layer<Dtype>::Backward(const vector<Blob<Dtype>*>& top,
    const vector<bool>& propagate_down,
    const vector<Blob<Dtype>*>& bottom) {
  switch (Caffe::mode()) {
  case Caffe::CPU:
    Backward_cpu(top, propagate_down, bottom);
    break;
  case Caffe::GPU:
    Backward_gpu(top, propagate_down, bottom);
    break;
  default:
    LOG(FATAL) << "Unknown caffe mode.";
  }
}

LayerFactory

LayerFactory允许我们在运行程序的时候通过LayerParameter来注册新的Layer。你可以通过两种方式来注册你的Layer,假设你的Layer类型名字是MyAwesomeLayer,那么你可以在构造函数里面加上一行REGISTER_LAYER_CLASS(MyAwesome) 来注册它,如果你使用一个单独的函数像Layer<Dtype*> GetMyAwesomeLayer(const LayerParameter& param) 来构造它,那么你需要加上一行REGISTER_LAYER_CREATOR(MyAwesome, GetMyAwesomeLayer) 来注册它。layer_factory.hpp里面定义了两个类,LayerRegistry和LayerRegisterer。下面我们分别分析它们。layer_factory.cpp里面有一些特别定制的CNN的Layer的构造函数如GetConvolutionLayer,这是因为Caffe提供了两种CNN的实现,Caffe自带的实现和CUDNN实现(用Makefile.config里面的USE_CUDNN来控制)

LayerRegistry

LayerRegistry其实是一个单例模式。Registry()函数返回一个type->Creater函数的static map g_registry_。每注册一个新Layer比如MyAwesomeLayer,就会在g_registry_里面加上”MyAwesome”->它的构造函数的映射。

LayerRegistry的函数

  // 往g_registry_注册每个Layer的构造函数
  static void AddCreator(const string& type, Creator creator) {
    CreatorRegistry& registry = Registry();
    registry[type] = creator; 
  }
  // 通过LayerParameter建立一个新的Layer实例
  static shared_ptr<Layer<Dtype> > CreateLayer(const LayerParameter& param) {
    // 获得Layer的类型type
    const string& type = param.type();
    CreatorRegistry& registry = Registry();
    // 通过在g_registry_里注册的构造函数建立新的Layer实例
    return registry[type](param);
  }

LayerRegisterer

template <typename Dtype>
class LayerRegisterer {
 public:
  LayerRegisterer(const string& type,
                  shared_ptr<Layer<Dtype> > (*creator)(const LayerParameter&)) {
    // 构造一个LayerRegisterer实例就是给给定的Layer注册给定的构造函数
    LayerRegistry<Dtype>::AddCreator(type, creator);
  }
};

宏REGISTER_LAYER_CREATOR和REGISTER_LAYER_CLASS

// 通过构造新的LayerRegisterer实例来注册float和double类型的构造函数
#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>)    \
// 首先自动给新的Layer生成一个简单的构造函数Creater_##type##Layer,并注册到LayerRegistry里
#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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值