针对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 methodvirtual inline const char* type() const { return "YourLayerName"; }
replacingYourLayerName
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.
- Include an inline implementation of
- 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)
- (Optional) Implement the GPU versions
Forward_gpu
andBackward_gpu
inlayers/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 abovemessage LayerParameter
- Instantiate and register your layer in your cpp file with the macro provided in
layer_factory.hpp
. Assuming that you have a new layerMyAwesomeLayer
, 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
incaffe/layer_factory.cpp
. - Write tests in
test/test_your_layer.cpp
. Usetest/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层。需要实现虚函数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_;
};
}
#endif
Step4: 增加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 caffe
Step5: 重新编译
make clean
make all -j16
3. 注意事项
从layer层派生一定得实现四个虚函数
SetUp
,Reshape
,Forward_cpu
,Backward_cpu
;从neuron派生则不需要实现Reshape
函数。虚函数的函数名,函数返回值,函数参数以及参数类型必须得和基类中的定义一致,建议直接从基类中拷贝。否则可能在编译时报如下错误:
./include/caffe/layer_factory.hpp"135:67: error: cannot allocate an object of abstract type