针对2017年2月时caffe官网版本。
官方增加新层的流程可见如下链接:
https://github.com/BVLC/caffe/wiki/Development
include/caffe/layers/your_layer.hpp
. type
overriding the method virtual inline const char* type() const { return "YourLayerName"; }
replacing YourLayerName
with your layer’s name.{*}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.*_gpu
declarations if you’ll only be implementing CPU code.src/caffe/layers/your_layer.cpp
. 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)Forward_gpu
and Backward_gpu
in layers/your_layer.cu
.proto/caffe.proto
, using (and then incrementing) the “next available layer-specific ID” declared in a comment above message LayerParameter
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);
GetConvolutionLayer
in caffe/layer_factory.cpp
.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)
// message that stores paremeters used to maxout layers
message MyMaxoutParameter {
// the number of output for this layer
optional uint32 num_output = 1;
}
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
#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
从layer层派生一定得实现四个虚函数SetUp
,Reshape
,Forward_cpu
,Backward_cpu
;从neuron派生则不需要实现Reshape
函数。
虚函数的函数名,函数返回值,函数参数以及参数类型必须得和基类中的定义一致,建议直接从基类中拷贝。否则可能在编译时报如下错误:
./include/caffe/layer_factory.hpp"135:67: error: cannot allocate an object of abstract type