假設我們要重新定義一個激活函數,這個層的名字是Reverse。
1. reverse.hpp
首先我們需要創建一個caffe/include/caffe/layers/reverse.hpp,仿照caffe-master中的代碼編寫:
//首先聲明:
#ifndef CAFFE_REVERSE_HPP_
#define CAFFE_REVERSE_HPP_
#include <vector>
#include "caffe/blob.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/layers/neuron_layer.hpp"
namespace caffe {
//定義不同類型的成員變量
template <typename Dtype>
class ReverseLayer : public NeuronLayer<Dtype> {
public:
/**
* @param param provides ReverseParameter reverse_param,
* with ReverseLayer options:
* - negative_slope (\b optional, default 0).
* the value @f$ \nu @f$ by which negative values are multiplied.
*/
explicit ReverseLayer(const LayerParameter& param)
: NeuronLayer<Dtype>(param) {}
virtual inline const char* type() const { return "Reverse"; }
protected:
//定義4個純虛函數,代表cpu/gpu下訓練的前向反向過程
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>*>& 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, const vector<Blob<Dtype>*>& bottom);
};//class reverselayer
}//namespace caffe
...//根據自己的需求定義不同類型的成員變量或者成員函數
#endif // CAFFE_REVERSE_HPP_
注:如果不用gpu那麼上述虛函數中Forward_gpu()/Backward_gpu()無需做定義,對應的也只需要編寫.cpp文件,不用寫.cu文件。
2.reverse.cpp/reverse.cu
然後創建caffe/src/caffe/layers/reverse.cpp和caffe/src/caffe/layers/reverse.cu,針對cpu和gpu模式的源代碼:
reverse.cpp:
#include <algorithm>
#include <vector>
//調用上一步已經定義好的對應的頭文件
#include "caffe/layers/reverse_layer.hpp"
namespace caffe {
//定義頭文件中創建的虛函數,即具體的前向反向的過程
template <typename Dtype>
void ReverseLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {...}
template <typename Dtype>
void ReLULayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down,
const vector<Blob<Dtype>*>& bottom) {...}
#ifdef CPU_ONLY
STUB_GPU(ReLULayer);
#endif
INSTANTIATE_CLASS(ReverseLayer);
}// namespace caffe
reverse.cu:
#include <algorithm>
#include <vector>
#include "caffe/layers/reverse.hpp"
namespace caffe {
template <typename Dtype>
__global__ void ReverseForward(const int n, const Dtype* in, Dtype* out,
Dtype negative_slope) {
CUDA_KERNEL_LOOP(index, n) {...}
template <typename Dtype>
void ReverseLayer<Dtype>::Forward_gpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {...}
template <typename Dtype>
__global__ void ReverseBackward(const int n, const Dtype* in_diff,
const Dtype* in_data, Dtype* out_diff, Dtype negative_slope) {
CUDA_KERNEL_LOOP(index, n) {...}
template <typename Dtype>
void ReverseLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down,
const vector<Blob<Dtype>*>& bottom) {...}
INSTANTIATE_LAYER_GPU_FUNCS(ReLULayer);
}// namespace caffe
注:.cu文件會調用CUDA,需要環境配置成功,並且在成員函數中寫:CUDA_POST_KERNEL_CHECK;
3.修改caffe.proto
在上面的兩步中我們已經把需要修改的源代碼核心部分編寫完畢,如果你的層定義中有添加的新的參數,那麼現在要爲我們新定義的層的參數添加ID。
打開caffe/src/caffe/proto/caffe.proto文件:
- 在message V0LayerParameter{}中添加新參數的ID:
optional 類型 參數名 = ID [default = *];
無論有沒有添加新的參數都需要對層添加ID,並且添加message消息
- 在message LayerParameter {}中添加新參數信息,並且添加建一個唯一的ID:
optional ReverseParameter reverse_param = ***;
- 在caffe.proto中添加messga消息函數:
//例如ReLU函數的:
message ReLUParameter {
optional float negative_slope = 1 [default = 0];
enum Engine {
DEFAULT = 0;
CAFFE = 1;
CUDNN = 2;
}
optional Engine engine = 2 [default = DEFAULT];
}
- 在caffe.proto中找到message V1LayerParameter函數:
message V1LayerParameter{
...
enum LayerType {
...
REVERSE = **;//爲Reverse添加唯一的ID
...
}
...
optional ReverseParameter reverse_param = **;//繼續添加一個唯一的ID,不與上面的相同
...
}
4.layer_factory.cpp
在caffe/src/caffe/layer_factory.cpp中寫入新的層定義:
// Get reverse layer according to engine.
template <typename Dtype>
shared_ptr<Layer<Dtype> > GetReverseLayer(const LayerParameter& param) {...}
REGISTER_LAYER_CREATOR(Reverse, GetReverseLayer);
5.upgrade_proto.cpp
在caffe/src/caffe/util/upgrade_proto.cpp中找到:
- V1LayerParameter_LayerType UpgradeV0LayerType(const string& type) {…}
- bool UpgradeV1LayerParameter(const V1LayerParameter&v1_layer_param,LayerParameter* layer_param) {…}
- const char* UpgradeV1LayerType(const V1LayerParameter_LayerType type) {switch (type) {…}}
根據其他的層定義仿寫即可,具體代碼略。
6.重新編譯caffe
到這一步基本上該做修改的底層代碼已經修改完畢,需要重新編譯Caffe,檢查自己的代碼是否書寫正確。
打開Terminal,進入caffe-master,依次輸入以下語句:
make clean
make -j all
make -j test
make -j runtest
make pycaffe
如果無報錯,恭喜你已經成功的定義一個新的層結構啦~~
寫在後面:最近幾天在開始研究Caffe底層代碼,記錄了一下自己的改層過程,但是自己的C++能力有限,有問題的地方還望不吝賜教,十分感謝。