针对2017年2月时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 blobsForward_cpu for the function your layer computesBackward_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 LayerParameterInstantiate 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.因为官网的只给出了流程,并没给出具体例子,因此将结合添加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层。需要实现虚函数SetUp,Forward_cpu,Backward_cpu。 - 输入blob和输出blob的大小不一样,直接从layer层派生。例如conv层,将要添加的maxout层。需要实现虚函数SetUp,Reshape,Forward_cpu,Backward_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类中定义构造函数和SetUp,Reshape,Forward_cpu,Backward_cpu函数以及一些变量。
#ifndef CAFFE_MY_MAXOUT_LAYER_HPP_ #define CAFFE_MY_MAXOUT_LAYER_HPP_ #include <vector> #include "caffe/blob.hpp" #include "caffe/layer.hpp" #include "caffe/proto/caffe.pb.h" namespace caffe { template <typename Dtype> class MyMaxoutLayer : public Layer<Dtype> { public: explicit MyMaxoutLayer(const LayerParameter& param) : Layer<Dtype>(param) {} // initialize the bottom and top blobs virtual inline const char* type() const { return "MyMaxout"; } virtual void SetUp(const vector<Blob<Dtype>*>& bottom, vector<Blob<Dtype>*>& top); virtual void Reshape(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top); protected: virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top); //virtual Dtype Forward_gpu(const vector<Blob<Dtype>*>& bottom, // vector<Blob<Dtype>*>* top); virtual void Backward_cpu(const vector<Blob<Dtype>*>& top, const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom); //virtual void Backward_gpu(const vector<Blob<Dtype>*>& top, // const vector<bool>& propagate_down, vector<Blob<Dtype>*>* bottom); int num_output_; int num_; int channels_; int height_; int width_; int group_size_; Blob<Dtype> max_idx_; }; } #endifStep4: 增加maxout层的源文件到./src/caffe/layers/mymaxout.cpp SetUp: 进行check Reshape: 更改top blob的大小 Forward_cpu: 实现正向传播 Backward_cpu: 实现反向传播 REGISTER_LAYER_CLASS: 最后注册层。
#include <vector> #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<Dtype>::SetUp(const vector<Blob<Dtype>*>& bottom, vector<Blob<Dtype>*>& 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<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& 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<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& 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<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top, const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& 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 caffeStep5: 重新编译
make clean make all -j16从layer层派生一定得实现四个虚函数SetUp,Reshape,Forward_cpu,Backward_cpu;从neuron派生则不需要实现Reshape函数。
虚函数的函数名,函数返回值,函数参数以及参数类型必须得和基类中的定义一致,建议直接从基类中拷贝。否则可能在编译时报如下错误:
./include/caffe/layer_factory.hpp"135:67: error: cannot allocate an object of abstract type