caffe源码学习记录(一)

参考自《21天实战caffe》及知乎:深度学习caffe的代码怎么读?

caffe是由伯克利视觉和学习中心(BVLC)开发的基于C++/CUDA/python实现的深度学习框架。

一. 特点

  1. 实现卷积神经网络架构(CNN)
  2. 速度快,采用google的ProtoBuffer数据格式,提高效率
  3. 完全开源,由C++语言编写
  4. caffe提供了一整套工具集,可用于训练、预测、数据预处理等。
  5. caffe自带一系列参考模型和快速教程

二. 代码结构


如上图为caffe的数据结构,一个CNN模型由Net表示,Net由多个Layer组成,Layer输入输出数据由Blob数据结构保存。

Blob,Layer,Net,Solver这几个大类,自下而上,环环相扣,贯穿了整个caffe的结构。

  1. Blob是基础的数据结构、存储单元,是用来保存学习到的参数以及网络传输过程中产生数据的类。
  2. Layer是网络的基本计算单元,由此派生出了各种层类。修改这部分的人主要是研究特征表达方向的。
  3. Net是网络的搭建,代表一个完整CNN,将Layer所派生出层类组合成网络。Solver:是Net的求解,修改这部分人主要会是研究DL求解方向的。

三. 具体代码结构

  • Blob
  • Blob类型描述

Caffe内部采用的数据类型主要是对protocol buffer所定义的数据结构的继承,因此可以在尽可能小的内存占用下获得很高的效率,虽然追求性能的同时Caffe也会牺牲了一些代码可读性。

Blob在内存中表示4维数组,维度从低到高为(width_,height_,channels_,num_),可分别表示图像的宽和高、颜色通道、图像为第几帧。Blob存储数据或权值(data)和权值增量(diff)。

Blob为模板类。声明在 include/caffe/blob.hpp,使用时需包含该头文件 #include<caffe/blob.hpp> 并使用命名空间caffe。类的实现在 src/caffe/proto/caffe.proto。

BlobProto对象可以进行磁盘与内存中数据的交互。

  • Blob重要成员函数和变量
shared_ptr<SyncedMemory> data_; //数据
shared_ptr<SyncedMemory> diff_;//梯度
void Blob<Dtype>::Reshape(const int num, const int channels, const int height,const int width); //重新修改Blob形状,根据形状申请动态内存数据和梯度
inline int count(int start_axis, int end_axis) const; //计算Blob需要的基本数据单元的数量
  • Layer
  • Layer类型描述

Layer至少有一个输入Blob(Bottom Blob)和一个输出Blob(Top Blob),部分Layer带有权值(weight)和偏执项(Bias),有两个运算方向:前向传播(Forward)和反向传播(Backward)。其中前向传播对输入Blob进行处理得到输出Blob,反向传播计算则对输出Blob的diff进行某种处理,得到输入Blob的diff。

Layer为抽象类,不能直接创建Layer对象,其大部分函数都只有虚函数,函数的真正实现在其派生类中。

头文件 include/caffe/layer.hpp  源文件src/caffe/layer.cppBlob重要成员函数和变量

  • Layer重要成员函数和变量
vector<Dtype> loss_ //每一层都会有一个loss值,但只有LossLayer才会产生非0的loss
vector<shared_ptr<Blob<Dtype> > > blobs_ ;//Layer所学习的参数,包括权值和偏差
virtual void Forward(const vector<Blob<Dtype>*> &bottom,vector<Blob<Dtype>*> *top) = 0;// Layer内部数据正向传播,从bottom到top方向
virtual void Backward(const vector<Blob<Dtype>*> &top,const vector<bool> &propagate_down,vector<Blob<Dtype>*> *bottom) = 0;
//Layer内部梯度反向传播,从top到bottom方向

在Layer内部,数据主要有两种传递方式,Forward和Backward,分别有CPU和GPU(部分有)两种实现。

Layer函数:

layers {
  bottom: "decode1neuron"   // 该层底下连接的第一个Layer
  bottom: "flatdata"        // 该层底下连接的第二个Layer
  top: "l2_error"           // 该层顶上连接的一个Layer
  name: "loss"              // 该层的名字
  type: EUCLIDEAN_LOSS      // 该层的类型
  loss_weight: 0
}
  • Net
  • Net类型描述

Net用容器的形式将多个Layer有序地放在一起,其自身实现的功能主要是对逐层Layer进行初始化,以及提供Update( )的接口(更新网络参数),本身不能对参数进行有效地学习过程。

  • Net的重要成员函数和变量
vector<shared_ptr<Layer<Dtype> > > layers_ ;//构成该net的layers
void Init(const NetParameter& param);
//根据NetParameter进行net初始化,简单的来说就是先把网络中所有层的bottom Blobs&top Blobs(无重复)实例化,并从输入层开始,逐层地进行Setup的工作,从而完成了整个网络的搭建,为后面的数据前后传输打下基础。
vector<Blob<Dtype>*>& Forward(const vector<Blob<Dtype>* > & bottom,Dtype* loss = NULL);
void Net<Dtype>::Backward();
//是对整个网络的前向和反向传导,各调用一次就可以计算出网络的loss了。
  • Solver
  • Solver的类型描述

Solver类中包含一个Net的指针,主要是实现了训练模型参数所采用的优化算法,根据优化算法的不同会派生不同的类,而基于这些子类就可以对网络进行正常的训练过程。

  • Solver的重要成员函数和变量
shared_ptr<Net<Dtype> > net_ ;//net对象
virtual void ComputeUpdateValue() = 0;//不同的模型训练方法通过重载函数ComputeUpdateValue( )实现计算update参数的核心功能
最后当进行整个网络训练过程(也就是你运行Caffe训练某个模型)的时候,实际上是在运行caffe.cpp中的train( )函数,而这个函数实际上是实例化一个Solver对象,初始化后调用了Solver中的Solve( )方法。而这个Solve( )函数主要就是在迭代运行下面这两个函数。
ComputeUpdateValue();
net_->Update();

caffemodel为训练好的模型,权重文件。

solverstate为求解器状态文件,存放训练的中间过程,可暂停后继续训练

猜你喜欢

转载自blog.csdn.net/qq_32274259/article/details/80182670