1. Problem
I want to design my own layer in caffe.
In my opinion, layer is kernel in caffe, even in deep learning. It represents a universal-computing framework.
A lot of algorithms can be represented in this form. So design layer is a must-skill.
2. Solution
Two things need to do:
1. Add layer parameter in src/caffe/caffe.proto
2. Create layer.hpp, layer.cpp, layer.cu
2.1 caffe.proto
message DuinoParameter {
optional float bias = 1;
}
All layer parameter is called via LayerParameter. We need to add DuinoParameter to LayerParameter.
message LayerParameter{
...
optional DuinoParameter duino_param = 147;
}
Number 147 should occur only once.
Since we change caffe.proto, we need to update caffe.pb.h and caffe.pb.cc
cd src/caffe/proto/
protoc caffe.proto --cpp_out=../../../include/caffe/proto --python_out=../../../python/caffe/proto
2.2 Create .hpp, .cpp, .cu
touch include/layers/duino_layer.hpp src/layers/duino_layer.cpp src/layers/duino_layer.cu
duino_layer.hpp
#ifndef CAFFE_DUINO_LAYER_HPP_
#define CAFFE_DUINO_LAYER_HPP_
#include <vector>
#include "caffe/blob.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"
#include <string>
namespace caffe{
template <typename Dtype>
class DuinoLayer : public Layer<Dtype> {
public:
explicit DuinoLayer(const LayerParameter& param)
: Layer<Dtype>(param){}
virtual void LayerSetup(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*> &top);
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual inline const char* type() const { return "Duino"; }
virtual inline int ExactNumBottomBlobs() const { return 1; }
virtual inline int ExactNumTopBlobs() const { return 1; }
protected:
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Backward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& top);
virtual void Backward_gpu(const vector<Blob<Dtype>*>& bottom,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& top);
};
}
#endif
About these functions, caffe official site gives a brief explanation.
duino_layer.cpp
#include <vector>
#include "caffe/layers/duino_layer.hpp"
#include "caffe/util/math_functions.hpp"
namespace caffe{
template <typename Dtype>
void DuinoLayer<Dtype>::LayerSetup(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top)
{
}
template <typename Dtype>
void DuinoLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top){}
template <typename Dtype>
void DuinoLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top)
{
// get input and output data
const Dtype* bottom_data = bottom[0]->cpu_data();
Dtype* top_data = top[0]->mutable_cpu_data();
const int count = bottom[0]->count();
// get parameter
Dtype bias = this->layer_param_.duino_param().bias();
// compute
for (int i = 0; i < count; ++i){
top_data[i] = bottom_data[i] + bias;
}
}
template <typename Dtype>
void DuinoLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<bool>& propagate_down,
const vector<Blob<Dtype>*>& top){}
INSTANTIATE_CLASS(DuinoLayer);
REGISTER_LAYER_CLASS(Duino);
}
duino_layer.cu
#include <vector>
#include "caffe/layers/duino_layer.hpp"
#include "caffe/util/math_functions.hpp"
namespace caffe{
template <typename Dtype>
void DuinoLayer<Dtype>::Forward_gpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top){}
template <typename Dtype>
void DuinoLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& bottom,
const vector<bool>& propagate_down,
const vector<Blob<Dtype>*>& top){}
INSTANTIATE_LAYER_GPU_FUNCS(DuinoLayer);
}
3. Test
Use in lenet.proto
name: "Lenet"
...
layer {
name: "duino1"
type: "Duino"
bottom: "ip2"
top: "ip2"
duino_param {
bias: 1
}
}
...
Use train_lenet.sh to train LeNet.
Done.