一、前言
本篇主要转载自一个视频教程,主要实现在caffe中新增自己的层。
二、具体做法
自定义一个计算层,实现y=x^power+bur的功能,事实上这个新层为激活函数层
三、实现的方法思路
(1)任何一个层都可以被继承,然后进行重写函数
(2)尽量确保要实现的功能是否必须要自己写,不然尽量用已有的层,每一个层在caffe/include/caffe/layers
源码中都有详细的介绍
四、整体步骤
1. 创建新定义的头文件include/caffe/layers/my_neuron_layer.hpp
重新Layer名的方法:virtual inline const char* type() const { return "MyNeuron"; }
如果只是需要cpu方法的话,可以注释掉forward/backward_gpu()这两个方法
2. 创建对应src/caffe/src/my_neuron_layer.cpp的源文件
重写方法LayerSetUp,实现从能从prototxt读取参数,这个没有从prototxt读取新的参数,则不需要重写
重写方法Reshape,如果对继承类没有修改的话,就不需要重写
重写方法Forward_cpu
重写方法Backward_cpu(非必须)
如果要GPU支持,则还需要创建src/caffe/src/my_neuron_layer.cu,同理重写方法Forward_gpu/Backward_gpu(非必须)
3. 到proto/caffe.proto 注册新的layer
4. 在my_neuron_layer.cpp添加注册的宏定义
INSTANTIATE_CLASS(MyNeuronLayer);
REGISTER_LAYER_CLASS(MyNeuron);
如果有my_neuron_layer.cu 文件,则还要添加
INSTANTIATE_LAYER_GPU_FUNCS(MyNeuronLayer);
5. 重新make和install
五、具体做法(仅仅用CPU)
5.1、prototxt文件中新层的描述
由于需要从prototxt文件中读取参数初始化 power 和 bur,所实现新层为激活函数的功能,故可参考sigmoid_layer 的写法,先参看sigmoid在prototxt文件的描述:
layer {
name: "sigmoid"
type: "Sigmoid"
bottom: "fc1"
top: "fc1"
}
新增参数 power 和 bur,在prototxt文件中描述可这样写:
layer {
name: "myneuron"
type: "MyNeuron" #name和type不一样
bottom: "fc1"
top: "fc1"
my_neuron_param {
power: 3
bur: 1
}
}
5.2、新增 myNeuron_layer.hpp 和 myNeuron_layer.cpp
方便的做法是参考相似层的写法,这里将sigmoid_layer.hpp和sigmoid_layer.cpp 改为相应名字后,分别保存至include/caffe/layers/ 和 src/caffe/layers/ , 并分别修改如下:
//myNeuron_layer.hpp
#ifndef CAFFE_MY_NEURON_LAYER_HPP_
#define CAFFE_MY_NEURON_LAYER_HPP_
#include
#include "caffe/blob.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/layers/neuron_layer.hpp"
namespace caffe {
template
class MyNeuronLayer : public NeuronLayer {
public:
explicit MyNeuronLayer(const LayerParameter& param)
: NeuronLayer(param) {}
virtual void LayerSetUp(const vector*>& bottom,
const vector*>& top);
virtual inline const char* type() const { return "MyNeuron"; }
protected:
virtual void Forward_cpu(const vector*>& bottom,
const vector*>& top);
virtual void Forward_gpu(const vector*>& bottom,const vector*>& top);
virtual void Backward_cpu(const vector*>& top,
const vector& propagate_down, const vector*>& bottom);
virtual void Backward_gpu(const vector*>& top,const vector& propagate_down, const vector*>& bottom);
Dtype power_;
Dtype bur_;
};
} // namespace caffe
#endif // CAFFE_MY_NEURON_LAYER_HPP_
//myNeuron_layer.cpp
#include
#include "caffe/layers/myNeuron_layer.hpp"
#include "caffe/util/math_functions.hpp"
namespace caffe {
template
void MyNeuronLayer::LayerSetUp(const vector*>& bottom,const vector*>& top){
NeuronLayer::LayerSetUp(bottom,top);
power_ = this->layer_param_.my_neuron_param().power();
bur_ = this->layer_param_.my_neuron_param().bur();
}
// Compute y = x^power
template
void MyNeuronLayer::Forward_cpu(const vector*>& bottom,const vector*>& top){
Dtype* top_data = top[0]->mutable_cpu_data();
const int count = bottom[0]->count();
caffe_powx(count, bottom[0]->cpu_data(), Dtype(power_), top_data);
caffe_add_scale(count, Dtype(bur_), top_data);
}
template
void MyNeuronLayer::Backward_cpu(const vector*>& top,const vector& propagate_down,const vector*>& bottom){
const int count = top[0]->count();
const Dtype* top_diff = top[0]->cpu_diff();
if(propagate_down[0]){
const Dtype* bottom_data = bottom[0]->cpu_data();
Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();
caffe_powx(count, bottom_data, Dtype(power_ - 1), bottom_diff);
caffe_scal(count, Dtype(power_), bottom_diff);
caffe_mul(count, bottom_diff, top_diff, bottom_diff);
}
}
#ifdef CPU_ONLY
STUB_GPU(MyNeuronLayer);
#endif
INSTANTIATE_CLASS(MyNeuronLayer);
REGISTER_LAYER_CLASS(MyNeuron);
}// namespace caffe
5.3、注册
接下来需要进行注册。
(1) 到src/caffe/proto/caffe.proto 注册 prototxt中的 my_neuron_param
message LayerParameter {
...
optional MyNeuronParameter my_neuron_param = 150;
...
}
...
message MyNeuronParameter{
optional float power = 1 [default = 2];
optional float bur = 2 [default = 1];
}
...
message V1LayerParameter {
...
MYNEURON = 40; #大写
...
}
V1layer注册的是全大写的类型名字,这点有点不知为何
(2) 在cpp文件中最后注册cpp中所创建的新类和 相应 prototxt中的类型名
INSTANTIATE_CLASS(MyNeuronLayer);
REGISTER_LAYER_CLASS(MyNeuron); //注意这里和V1LayerParameter保持一致
5.4、测试
重新make之后,创建deploy.prototxt 和 test_my_neuron.py 如下
name: "CaffeNet"
input: "data"
input_shape {
dim: 1 # batchsize
dim: 1 # number of colour channels - rgb
dim: 28 # width
dim: 28 # height
}
layer {
name: "myneuron"
type: "MyNeuron"
bottom: "data"
top: "data_out"
my_neuron_param {
power : 2
bur : 1
}
}
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import os
import sys
deploy_file = "./deploy.prototxt"
test_data = "./5.jpg"
if __name__ == '__main__':
sys.path.append("/home/zjy/caffe/python")
import caffe
net = caffe.Net(deploy_file,caffe.TEST)
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})
transformer.set_transpose('data', (2, 0, 1))
img = caffe.io.load_image(test_data,color=False)
net.blobs['data'].data[...] = transformer.preprocess('data', img)
print net.blobs['data'].data[0][0][14]
out = net.forward()
print out['data_out'][0][0][14]
6.1、prototxt文件中新层的描述
保持不变
6.2、新增 myNeuron_layer.hpp 和 myNeuron_layer.cu
myNeuron_layer.hpp 保持不变,在src/caffe/layers/ 增加 myNeuron_layer.cu
#include
#include "caffe/layers/myNeuron_layer.hpp"
#include "caffe/util/math_functions.hpp"
#include
using namespace std;
namespace caffe {
template
void MyNeuronLayer::Forward_gpu(
const vector*>& bottom, const vector*>& top) {
const int count = top[0]->count();
Dtype* top_data = top[0]->mutable_gpu_data();
caffe_gpu_powx(count, bottom[0]->gpu_data(), Dtype(power_), top_data);
}
template
void MyNeuronLayer::Backward_gpu(const vector*>& top,
const vector& propagate_down, const vector*>& bottom) {
const int count = top[0]->count();
const Dtype* top_diff = top[0]->gpu_diff();
if (propagate_down[0]) {
const Dtype* bottom_data = bottom[0]->gpu_data();
Dtype* bottom_diff = bottom[0]->mutable_gpu_diff();
const Dtype* bottom_data_w = bottom[0]->cpu_data();
const Dtype* bottom_diff_w = bottom[0]->cpu_diff();
cout << "bottom_data[0]: " << bottom_data_w[0] << endl;
cout << "bottom_diff[0]: " << bottom_diff_w[0] << endl;
caffe_gpu_powx(count, bottom_data, Dtype(power_ - 1), bottom_diff);
bottom_diff = bottom[0]->mutable_gpu_diff();
bottom_data_w = bottom[0]->cpu_data();
bottom_diff_w = bottom[0]->cpu_diff();
cout << "bottom_data[0]: " << bottom_data_w[0] << endl;
cout << "bottom_diff[0]: " << bottom_diff_w[0] << endl;
caffe_gpu_scal(count, Dtype(power_), bottom_diff);
bottom_diff = bottom[0]->mutable_gpu_diff();
bottom_data_w = bottom[0]->cpu_data();
bottom_diff_w = bottom[0]->cpu_diff();
cout << "bottom_data[0]: " << bottom_data_w[0] << endl;
cout << "bottom_diff[0]: " << bottom_diff_w[0] << endl;
caffe_gpu_mul(count, bottom_diff, top_diff, bottom_diff);
bottom_diff = bottom[0]->mutable_gpu_diff();
bottom_data_w = bottom[0]->cpu_data();
bottom_diff_w = bottom[0]->cpu_diff();
cout << "bottom_data[0]: " << bottom_data_w[0] << endl;
cout << "bottom_diff[0]: " << bottom_diff_w[0] << endl;
}
}
INSTANTIATE_LAYER_GPU_FUNCS(MyNeuronLayer);
} // namespace caffe
5.3、注册
(1) 保持不变
(2) 在 cu 文件后添加
INSTANTIATE_LAYER_GPU_FUNCS(MyNeuronLayer);
5.4、测试
重新 make之后,创建 deploy.prototxt 和 test_my_neuron_gpu.py , deploy.prototxt 保持不变,test_my_neuron_gpu.py
如下:
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import os
import sys
deploy_file = "./deploy.prototxt"
test_data = "./5.jpg"
if __name__ == '__main__':
sys.path.append("/home/zjy/caffe/python")
import caffe
caffe.set_mode_gpu()
net = caffe.Net(deploy_file,caffe.TEST)
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})
transformer.set_transpose('data', (2, 0, 1))
img = caffe.io.load_image(test_data,color=False)
net.blobs['data'].data[...] = transformer.preprocess('data', img)
print net.blobs['data'].data[0][0][14]
out = net.forward()
print out['data_out'][0][0][14]