caffe源码串联(一)

自己在学习过程中遇到了很多坑,由于基础太差,基本是寸步难行,看到繁杂的caffe源码就想按下电脑电源键强制关机,就在我要放弃之刻,我遇到了老司机志轩,志轩老湿的一番话让我茅塞顿开,故决定开博文写自己的caffe学习之旅,反正不会就小窗口志轩,烦也要烦死他,之后的博文会都会在结尾附上志轩老师的博客,有需要的同志看一下,说不定就有有用的东西。


此次学习内容为relu.layer,从blob到layer,以后的每一个层都采取这样串联的方式,希望可以系统的帮助到别人。


  1. 需要的一点caffe基础:
    从caffe.proto看起,这个源码文件在 src/caffe/proto,编译后产生c++文件在build/src/caffe/proto目录下有两个文件,分别为caffe.pb.h和caffe.pb.cc ,这几个文件有什么关系呢?别急,志轩原话:“那个caffe.pb.cc是对应caffe消息经过protobuf编译后的c++代码”,见我懵逼,志轩老师举了个例子:比如你在caffe.proto写了一个消息,然后编译,在caffe.pb.cc中会找到对应消息的c++源码(好像是重复了上面的意思。)

  2. 从caffe.proto开始到caffe.pb.cc

用文本编辑器打开caffe.proto文件,映入眼帘的是如下代码:

message BlobShape {
  repeated int64 dim = 1 [packed = true];
}

看着很简单嘛,三行代码,我们再看看经过protobuf编译后产生的caffe.pb.h中有多少c++代码,打开c++.pb.h,对应638到728行之间的c++代码,protobuf简直就是神器呀,对我等代码弱鸡,可谓是福利。
好了,继续返回caffe.proto, 找到如下代码:

```
message BlobProto {
  optional BlobShape shape = 7;
  repeated float data = 5 [packed = true];
  repeated float diff = 6 [packed = true];
  repeated double double_data = 8 [packed = true];
  repeated double double_diff = 9 [packed = true];

  // 4D dimensions -- deprecated.  Use "shape" instead.
  optional int32 num = 1 [default = 0];
  optional int32 channels = 2 [default = 0];
  optional int32 height = 3 [default = 0];
  optional int32 width = 4 [default = 0];
}

上述代码做的事情是规定了blob的维度,具体看:data是数据,diff是梯度,分别分float和double,下面是规定blob的维度,num*channels*height*width,规定了一个blob的大小,先不管repeated,opthonal是个啥意思,暂时当repeated对应在c++代码里是STL的vector,optional对应变量。

开始今天的第一个层:Relu层。

第一步:先看relu_layer.hpp,位于caffe根目录下的include/src/caffe/layers/relu_layer.hpp目录下,打开发现如下代码,

 `template <typename Dtype>
class ReLULayer : public NeuronLayer<Dtype> {

这个c++代码说明了ReLU层继承于类NeuronLayer层,实际上NeuronLayer层继承于Layer(这是志轩老师给我说的,我暂时强迫自己接受),继续往下看,发现ReLU讲并不是很复杂的实现,讲了两个东西:一个forward传播,一个backward传播,并分了两种实现形式:CPU和GPU。具体代码此处不贴了。看完relu.pb.h(主要讲了relu实现了什么,具体怎么实现继续看),我们看一下relu.pb.cc,了解一下relu.pb.h里说的一些功能在relu.pb.cc里都是怎么实现的。打开relu.pb.cc,这里代码不多,我就直接全贴上来。


include <algorithm>
#include <vector>

#include "caffe/layers/relu_layer.hpp"

namespace caffe {

template <typename Dtype>
void ReLULayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,   //定义bottom,top(都是blob)
    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();     //count=num*channels*h*w,即所有像素。
  Dtype negative_slope = this->layer_param_.relu_param().negative_slope();
  for (int i = 0; i < count; ++i) {
    top_data[i] = std::max(bottom_data[i], Dtype(0))
        + negative_slope * std::min(bottom_data[i], Dtype(0));
  }
}

template <typename Dtype>
void ReLULayer<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();
    Dtype negative_slope = this->layer_param_.relu_param().negative_slope();
    for (int i = 0; i < count; ++i) {
      bottom_diff[i] = top_diff[i] * ((bottom_data[i] > 0)
          + negative_slope * (bottom_data[i] <= 0));
    }
  }
}


#ifdef CPU_ONLY
STUB_GPU(ReLULayer);
#endif

INSTANTIATE_CLASS(ReLULayer);

}  // namespace caffe

注意看哦,首先include “caffe/layers/relu_layer.hpp”,类似函数声明,上面我注释了一下,感觉还算清楚,但具体语法别问我,我是c++菜比,forward里,遍历所有像素,实现的运算是:top=max(x,0)+negative_slope*min(x,0),这里negative_slope默认为0,所以relu的forward传播实现的实际上是max(x,0),再看backward传播,注意,同样的东西,只不过实现的函数是上述top表达式对x的导数,为啥,为啥?自己悟。好啦,到现在为止一个relu_layer的实现已经看完了,forward和backward。

最后,说一下写此博文的目的,第一:记录自己的caffe学习历程。第二:感谢志轩同学的慷慨帮助,有啥不懂的问他他也会给你解答。第三,自己学习caffe的过程才刚开始,之前都是在caffe的框架外围兜圈,从没想过看源码,借这个机会,记录下,希望帮助到可以帮助的人,会不定期更新,想写一套完整的教程,希望和志轩老师的约定能实现吧。水平有限,有需要改正的地方评论一下,或者有什么问题可以留言讨论。
“`

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值