Caffe代码梳理
1、caffe目录结构
2、caffe阅读路线:
- src/caffe/proto/caffe.proto 了解基本数据结构内存对象和磁盘文件的一一映射,主要由ProtoBuffer工具完成;
- include头文件 理解整个框架,从基类向派生类顺藤摸瓜;
- .cpp和.cu文件 caffe框架不需要大改,只需要按需求派生出新的类实现即可;
- 编写各类工具集成到caffe内部 如tools下的工具;
注:快速追踪关键字
(1)多个终端,用vi的查找命令追踪;
(2)使用linux grep,在caffe根目录下运行:
$grep -n -H -R "XXXXX"
[ -n : 显示行号; -H : 显示文件名; -R : 递归查找每个子目录;]
3、caffe支持的速度学习特性
卷积层和全连接层统称为权值层,具有可学习参数(权值):
3.1、卷积层:
卷积层计算步骤由二维增加至三维、四维卷积,多了“通道(channel)”,每个通道进行二维卷积,没有“翻转”,而是与输入图片做滑动窗口“相关”计算;
多个通道与多个卷积核分别进行二维卷积,得到多通道输出,“合并”为一个通道;
注:其中L、I、J参数可以在.prototxt中找到,图像大小M*N,输出通道K从日志文件可以找到。
3.2、全连接层
全连接层每个节点与相邻层的所有节点都连接,计算类型为矩阵-向量乘;
y=Wx (输入节点向量x;维度D;输出节点向量y,维度V;W为V.D维权值矩阵)
则:
单样本前向传播计算量:CaculationsMAC=V.D
参数统计:Params =V.D
CPR值 : CPR=Caculations/Params=1(始终为1,与输入输出维度无关,权值重复利用率很低)
卷积层vs全连接层:参数量全连接层是卷积层的16倍;计算量只有25%;(得益于权值共享&局部连接)
3.3、激活函数
常用的激活函数:
在caffe中位于:include/caffe/neural_layers.hpp
src/caffe/proto/caffe.proto
message ReLUParameter {
optional float negative_slope = 1 [default = 0];
enum Engine {
DEFAULT = 0;
CAFFE = 1;
CUDNN = 2;
}
optional Engine engine = 2 [default = DEFAULT];
}
Relu.layer
#ifndef CAFFE_RELU_LAYER_HPP_
#define CAFFE_RELU_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 {
/**
* @brief Rectified Linear Unit non-linearity @f$ y = \max(0, x) @f$.
* The simple max is fast to compute, and the function does not saturate.
*/
template <typename Dtype>
class ReLULayer : public NeuronLayer<Dtype> { //派生于NeuronLayer,实现Relu激活函数的计算
public:
/**
* @param param provides ReLUParameter relu_param,
* with ReLULayer options:
* - negative_slope (\b optional, default 0).
* the value @f$ \nu @f$ by which negative values are multiplied.
*/
//显示构造函数
explicit ReLULayer(const LayerParameter& param)
: NeuronLayer<Dtype>(param) {}
//返回类名字符串
virtual inline const char* type() const { return "ReLU"; }
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_RELU_LAYER_HPP_
src/caffe/layers/relu_layer.cpp
#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,
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();
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