[caffe笔记004]: caffe添加新层之添加maxout层

针对2017年2月时caffe官网版本。

1. caffe官网中添加新层的流程

官方增加新层的流程可见如下链接:
https://github.com/BVLC/caffe/wiki/Development

  • Add a class declaration for your layer to include/caffe/layers/your_layer.hpp.
    • Include an inline implementation of type overriding the method virtual inline const char* type() const { return "YourLayerName"; } replacing YourLayerName with your layer’s name.
    • Implement the {*}Blobs() methods to specify blob number requirements; see /caffe/include/caffe/layers.hpp to enforce strict top and bottom Blob counts using the inline {*}Blobs() methods.
    • Omit the *_gpu declarations if you’ll only be implementing CPU code.
  • Implement your layer in src/caffe/layers/your_layer.cpp.
    • (optional) LayerSetUp for one-time initialization: reading parameters, fixed-size allocations, etc.
    • Reshape for computing the sizes of top blobs, allocating buffers, and any other work that depends on the shapes of bottom blobs
    • Forward_cpu for the function your layer computes
    • Backward_cpu for its gradient (Optional – a layer can be forward-only)
  • (Optional) Implement the GPU versions Forward_gpu and Backward_gpu in layers/your_layer.cu.
  • If needed, declare parameters in proto/caffe.proto, using (and then incrementing) the “next available layer-specific ID” declared in a comment above message LayerParameter
  • Instantiate and register your layer in your cpp file with the macro provided in layer_factory.hpp. Assuming that you have a new layer MyAwesomeLayer, you can achieve it with the following command:
INSTANTIATE_CLASS(MyAwesomeLayer);
REGISTER_LAYER_CLASS(MyAwesome);
  • Note that you should put the registration code in your own cpp file, so your implementation of a layer is self-contained.
  • Optionally, you can also register a Creator if your layer has multiple engines. For an example on how to define a creator function and register it, see GetConvolutionLayer in caffe/layer_factory.cpp.
  • Write tests in test/test_your_layer.cpp. Use test/test_gradient_check_util.hpp to check that your Forward and Backward implementations are in numerical agreement.

2. 增加新层实践

因为官网的只给出了流程,并没给出具体例子,因此将结合添加maxout层对整个流程进行说明。
旧版本添加maxout以及maxout原理可参考下面博客。
http://blog.csdn.net/kuaitoukid/article/details/41865803

Step1: 确定要添加的层的基类

相比于上面博客中旧版caffe对层的分类,现在的caffe中层的分类有所改变,去掉了vision层,直接由layer层派生。除此之外还有loss层,neuron层,以及data层。
- loss层和data层顾名思义,不加赘述
- 输入blob和输出blob的大小一样,从neuron层派生。例如激活层ReLU,以及逐点操作的exp层和power层。需要实现虚函数SetUpForward_cpuBackward_cpu
- 输入blob和输出blob的大小不一样,直接从layer层派生。例如conv层,将要添加的maxout层。需要实现虚函数SetUpReshapeForward_cpuBackward_cpu

Step2: caffe.proto定义该层的参数
- 添加Maxout LayerParameter的ID
message LayerParameter最后一行添加MyMaxoutParameter,并将ID按顺序设置为没有用过的数字。

optional MyMaxoutParameter my_maxout_param = 147;

message LayerParameter的注释中有最后添加的层名以及可用的ID号,为了便于以后使用,建议更改一下。

// NOTE
// Update the next available ID when you add a new LayerParameter field.
//
// LayerParameter next available layer-specific ID: 147 (last added: recurrent_param)
  • 添加Maxout layer的参数消息
    在caffe.proto任意位置添加Maxout layer的参数消息:
// message that stores paremeters used to maxout layers
message MyMaxoutParameter {
  // the number of output for this layer
  optional uint32 num_output = 1;
}
  • 添加Maxout layer的层名
    message V1LayerParameter中的enum LayerType添加Maxout层的层名:
MYMAXOUT = 40;

同时添加:

optional MyMaxoutParameter my_maxout_param = 43;

数字只要不重复就可以。

Step3: 增加maxout层的头文件到./include/caffe/layers/mymaxout.hpp

主要在MyMaxoutLayer类中定义构造函数和SetUpReshapeForward_cpuBackward_cpu函数以及一些变量。

#ifndef CAFFE_MY_MAXOUT_LAYER_HPP_
#define CAFFE_MY_MAXOUT_LAYER_HPP_

#include 

#include "caffe/blob.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"

namespace caffe {  
template <typename Dtype>  
class MyMaxoutLayer : public Layer {  
 public:  
  explicit MyMaxoutLayer(const LayerParameter& param)  
      : Layer(param) {}  
  // initialize the bottom and top blobs
  virtual inline const char* type() const { return "MyMaxout"; }
  virtual void SetUp(const vector*>& bottom, vector*>& top);
  virtual void Reshape(const vector*>& bottom, const vector*>& top);

 protected:  
  virtual void Forward_cpu(const vector*>& bottom, const vector*>& top);  
  //virtual Dtype Forward_gpu(const vector*>& bottom,  
  //    vector*>* top);  
  virtual void Backward_cpu(const vector*>& top,
      const vector<bool>& propagate_down, const vector*>& bottom);
  //virtual void Backward_gpu(const vector*>& top,  
  //    const vector& propagate_down, vector*>* bottom);  

  int num_output_;  
  int num_;  
  int channels_;  
  int height_;  
  int width_;  
  int group_size_;  
  Blob max_idx_;  

};  

}
#endif

Step4: 增加maxout层的源文件到./src/caffe/layers/mymaxout.cpp
SetUp: 进行check
Reshape: 更改top blob的大小
Forward_cpu: 实现正向传播
Backward_cpu: 实现反向传播
REGISTER_LAYER_CLASS: 最后注册层。

#include   

#include "caffe/util/im2col.hpp"   
#include "caffe/util/math_functions.hpp"  
#include "caffe/layers/my_maxout_layer.hpp"  

namespace caffe {   
template <typename Dtype>  
void MyMaxoutLayer::SetUp(const vector*>& bottom,   
      vector*>& top) {
  const MyMaxoutParameter& my_maxout_param = this->layer_param_.my_maxout_param();
  CHECK(my_maxout_param.has_num_output())
      << "num_output should be specified.";   
}  

template <typename Dtype>  
void MyMaxoutLayer::Reshape(const vector*>& bottom, const vector*>& top)
{
  num_output_ = this->layer_param_.my_maxout_param().num_output();   
  CHECK_GT(num_output_, 0) << "output number cannot be zero.";  
  // bottom 
  num_ = bottom[0]->num();  
  channels_ = bottom[0]->channels();  
  height_ = bottom[0]->height();    
  width_ = bottom[0]->width();     

  // TODO: generalize to handle inputs of different shapes.    
  for (int bottom_id = 1; bottom_id < bottom.size(); ++bottom_id) {    
    CHECK_EQ(num_, bottom[bottom_id]->num()) << "Inputs must have same num.";    
    CHECK_EQ(channels_, bottom[bottom_id]->channels())    
        << "Inputs must have same channels.";    
    CHECK_EQ(height_, bottom[bottom_id]->height())    
        << "Inputs must have same height.";    
    CHECK_EQ(width_, bottom[bottom_id]->width())    
        << "Inputs must have same width.";    
  }  

  // Set the parameters, compute the group size
  CHECK_EQ(channels_ % num_output_, 0)   
      << "Number of channel should be multiples of output number.";   
  group_size_ = channels_ / num_output_;  

    top[0]->Reshape(num_, num_output_, height_, width_); 
    max_idx_.Reshape(num_, num_output_, height_, width_);
}


template <typename Dtype>   
void MyMaxoutLayer::Forward_cpu(const vector*>& bottom,
      const vector*>& top) {
    int featureSize = height_ * width_;  
    Dtype* mask = NULL;  
    mask = max_idx_.mutable_cpu_data();  
//printf("1.maxout_forward\n");  
    const int top_count = top[0]->count();  
    caffe_set(top_count, Dtype(0), mask);  
//printf("2.maxout_forward\n");  
    for (int i = 0; i < bottom.size(); ++i) {  
        const Dtype* bottom_data = bottom[i]->cpu_data();  
        Dtype* top_data = top[i]->mutable_cpu_data();    
        for (int n = 0; n < num_; n ++) {  
            for (int o = 0; o < num_output_; o ++) {  
                for (int g = 0; g < group_size_; g ++) {   
                    if (g == 0) {  
                        for (int h = 0; h < height_; h ++) { // á?2??-?·óDμ??ù?aà?  
                            for (int w = 0; w < width_; w ++) {  
                                int index = w + h * width_;  
                                top_data[index] = bottom_data[index];  
                                mask[index] = index;  
                            }  
                        }  
                    }  
                    else {  
                        for (int h = 0; h < height_; h ++) {  
                            for (int w = 0; w < width_; w ++) {  
                                int index0 = w + h * width_;  
                                int index1 = index0 + g * featureSize;  
                                if (top_data[index0] < bottom_data[index1]) {  
                                    top_data[index0] = bottom_data[index1];  
                                    mask[index0] = index1;  
                                }                                 
                            }  
                        }  
                    }  
                }  
                bottom_data += featureSize * group_size_;  
                top_data += featureSize;  
                mask += featureSize;  
            }  
        }  
    }    
}  



template <typename Dtype>  
void MyMaxoutLayer::Backward_cpu(const vector*>& top,
      const vector<bool>& propagate_down, const vector*>& bottom) {  
    if (!propagate_down[0]) {  
        return;  
    }  
    // const Dtype* top_diff = top[0]->cpu_diff();  
    Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();  
    caffe_set(bottom[0]->count(), Dtype(0), bottom_diff);  
    const Dtype* mask = max_idx_.mutable_cpu_data();  
    int featureSize = height_ * width_;  
    for (int i = 0; i < top.size(); i ++) {  
        const Dtype* top_diff = top[i]->cpu_diff();  
        Dtype* bottom_diff = bottom[i]->mutable_cpu_diff();    
        for (int n = 0; n < num_; n ++) { 
            for (int o = 0; o < num_output_; o ++) {  
                for (int h = 0; h < height_; h ++) { // á?2??-?·óDμ??ù?aà?  
                    for (int w = 0; w < width_; w ++) {  
                        int index = w + h * width_;  
                        int bottom_index = mask[index];  
                        bottom_diff[bottom_index] += top_diff[index];  
                    }  
                }  
                bottom_diff += featureSize * group_size_;  
                top_diff += featureSize;  
                mask += featureSize;  
            }  
        }  
    }  
}  

#ifdef CPU_ONLY
STUB_GPU(MyMaxoutLayer);
#endif

INSTANTIATE_CLASS(MyMaxoutLayer);
REGISTER_LAYER_CLASS(MyMaxout); 
}  // namespace caffe  

Step5: 重新编译

make clean
make all -j16

3. 注意事项

  1. 从layer层派生一定得实现四个虚函数SetUpReshapeForward_cpuBackward_cpu;从neuron派生则不需要实现Reshape函数。

  2. 虚函数的函数名,函数返回值,函数参数以及参数类型必须得和基类中的定义一致,建议直接从基类中拷贝。否则可能在编译时报如下错误:

./include/caffe/layer_factory.hpp"135:67: error: cannot allocate an object of abstract type

你可能感兴趣的:(caffe点滴)