查看: 1518|回复: 0

Caffe添加自定义的层

[复制链接]

166

主题

616

帖子

1万

积分

xdtech

Rank: 5Rank: 5

积分
10819
发表于 2019-2-21 22:30:51 | 显示全部楼层 |阅读模式
1、介绍
在使用Caffe时,可能已有的层不满足需求,需要实现自己的层,最好的方式是修改caffe.proto文件,增加对应cpp、h、cu的声明和实现,编译caffe库即可。
本文参考http://blog.csdn.net/kkk584520/article/details/52721838实现,使用caffe-windows版本编译,将遇到的问题写下。
本文实现的Layer名称为AllPassLayer(肯定不能和已有的层同名),将输入到该层的数据不做任何改变直接输出到下一层。AllPassLayer层的Forward和Backwark实现非常容易,直接将输入的blob数据复制到输出的blob中,因此可以加入到任何已有网络中,不影响训练和测试的结果。


2、AallPassLayer代码实现
添加新的层AallPassLayer实现,需要同其他的Layer类一样,分成声明和实现两个部分,对应放在.hpp和.cpp文件中,如果有cuda实现,还应有.cu文件。其中.hpp头文件放在/caffe-windows/include/caffe/layers/文件夹下,而 .cpp 和 .cu 放入/caffe-windows/src/caffe/layers下。为方便起见,这里仅实现CPU上的代码。
需要注意的地方,已经添加了注释。


2.1、头文件all_pass_layer.hpp
// added by wanggao   TEST  [12/20/2017 14:20 wg]


#ifndef CAFFE_ALL_PASS_LAYER_HPP_  
#define CAFFE_ALL_PASS_LAYER_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 AllPassLayer : public NeuronLayer<Dtype> { //注意:从NeuronLayer继承
public:
    explicit AllPassLayer(const LayerParameter& param)
        : NeuronLayer<Dtype>(param) {}
    virtual inline const char* type() const { return "AllPass"; }


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>*>& 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);
};


}  // namespace caffe  
#endif  // CAFFE_ALL_PASS_LAYER_HPP_  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2.2、实现文件all_pass_layer.cpp
// added by wanggao   TEST  [12/20/2017 14:20 wg]


#include <algorithm>  
#include <vector>  


#include "caffe/layers/all_pass_layer.hpp"  


#include <iostream>  
using namespace std;
#define DEBUG_AP(str) cout<<str<<endl  
namespace caffe {


template <typename Dtype>
void AllPassLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
    const vector<Blob<Dtype>*>& top) {
    const Dtype* bottom_data = bottom[0]->cpu_data();
    Dtype* top_data = top[0]->mutable_cpu_data();
    const int count = bottom[0]->count();


    //for (int i = 0; i < count; ++i) {
    //  top_data = bottom_data;
    //}
    caffe_copy(count, bottom_data, top_data);


    //注意:获取参数的两个变量 "all_pass_param" 和 "key"
    DEBUG_AP("Here is All Pass Layer, forwarding.");
    DEBUG_AP(this->layer_param_.all_pass_param().key());
}


template <typename Dtype>
void AllPassLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
    const vector<bool>& propagate_down,
    const vector<Blob<Dtype>*>& bottom) {
    if (propagate_down[0]) {
        //const Dtype* bottom_data = bottom[0]->cpu_data();
        const Dtype* top_diff = top[0]->cpu_diff();
        Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();
        const int count = bottom[0]->count();
        //for (int i = 0; i < count; ++i) {
        //  bottom_diff = top_diff;
        //}
        caffe_copy(count, top_diff, bottom_diff);
    }
    //同上
    DEBUG_AP("Here is All Pass Layer, backwarding.");
    DEBUG_AP(this->layer_param_.all_pass_param().key());
}


#ifdef CPU_ONLY  
STUB_GPU(AllPassLayer);
#endif  


//注意:下面2行对层进行注册,必不可少;注意宏参数不能弄错
INSTANTIATE_CLASS(AllPassLayer); // 类的名称 AllPassLayer
REGISTER_LAYER_CLASS(AllPass);   // 层的名称 AllPass
}  // namespace caffe  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
3、修改caffe.proto文件
某些情况下我们训练或者测试时,不需要从ptototxt的layer中获取参数,比如split层,concat层等,如果我们这里的.cpp实现文件中注释掉四个DEBUG_AP行,就是一个不带参数的层,那么这里就不用修改caffe.proto文件,直接可以编译caffe.lib库了。
当我们的层需要指定参数时,如卷积层Convolution的参数convolution_param中卷积核数量num_output、卷积核尺寸kernel_size、步长stride等,需要在cpp代码中使用,我们就必须先修改caffe.proto文件,利用ProtoBuffer生成caffe.pb.h和caffe.pb.cc文件,编译生成caffe.lib库。
下面针对AllPassLayer层,对caffe.proto文件进行修改。


第一步 找到message LayerParameter { … } 部分代码,并在其中新添
optional AllPassParameter all_pass_param = 147;,增加后的效果如下


// 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 LayerParameter {
  optional string name = 1; // the layer name
  optional string type = 2; // the layer type
  repeated string bottom = 3; // the name of each bottom blob
  repeated string top = 4; // the name of each top blob
  //... 中间部分省略
  optional ThresholdParameter threshold_param = 128;
  optional TileParameter tile_param = 138;
  optional WindowDataParameter window_data_param = 129;


  // 注意:下面为新增加的一行
  optional AllPassParameter all_pass_param = 147;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
注意新增加行中的ID数值147,可以为其他值(如200,500等),但是不能与当前已经存在的层参数ID相同。在Note中已经提示下一个可用的参数ID为147。


第二步 添加 message AllPassParameter { },指定参数名称


message AllPassParameter {  
  optional float key = 1 [default = 0];  
}  
1
2
3
注意:在上面2个步骤中,可以看到两个参数 “all_pass_param” 和 “key”是和cpp中调用参数的代码对应起来的(后面讲解中,这2个参数还与.prototxt有关)


EBUG_AP(this->layer_param_.all_pass_param().key());
1
最后,完成上面2个步骤后,就可以成功编译生成caffe.lib库了。


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表