Caffe学习笔记一 Caffe的结构

Caffe的三级结构

原文地址:Caffe学习笔记6-Caffe结构简析(知乎同步)

  • caffe分为三个层次:

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

Blobs

Blob是Caffe的基本数据结构,具有CPU和GPU之间同步的能力,它是4维的数组(Num, Channels, Height, Width)。

设Blob数据维度为 number N x channel K x height H x width W,Blob是row-major保存的,因此在(n, k, h, w)位置的值物理位置为((n * K + k) * H + h) * W + w,其中Number/N是batch size。

  • row-major

    原文地址:行向量,列向量,行主序矩阵,列主序矩阵(row vector,column vector,row major-matrix,column-major matrix)

    矩阵的存储方式有两种,一种是“行主序(row-major order)/行优先”,另一种就是“列主序(column-major order)/列优先” 。

    例如:Direct3D采用row-major,OpenGL采用column-major。存储顺序说明了线性代数中的矩阵如何在线性的内存数组中存储,d3d 将每一行在数组中按行存储,而opengl将每一列存储到数组的每一行中。

Blob同时保存了data和diff(梯度),访问data或diff有两种方法:

const Dtype* cpu_data() const; //不修改值
Dtype* mutable_cpu_data();     //修改值

Layer

所有的Pooling,Convolve,apply nonlinearities(非线性变换,如ReLU等)等操作都在这里实现。在Layer中input data用bottom表示output data用top表示。每一层定义了三种操作setup(Layer初始化), forward(正向传导,根据input计算output), backward(反向传导计算,根据output计算input的梯度)。forward和backward有GPU和CPU两个版本的实现。

Layer组成

  • NeuronLayer类:定义于neuron_layers.hpp中,其派生类主要是元素级别的运算(比如Dropout运算,激活函数ReLu,Sigmoid等),运算均为同址计算(in-place computation,返回值覆盖原值而占用新的内存)。
  • LossLayer类:定义于loss_layers.hpp中,其派生类会产生loss,只有这些层能够产生loss。
  • 数据层:定义于data_layer.hpp中,作为网络的最底层,主要实现数据格式的转换。
  • 特征表达层(我自己分的类):定义于vision_layers.hpp,实现特征表达功能,更具体地说包含卷积操作,Pooling操作,他们基本都会产生新的内存占用(Pooling相对较小)。
  • 网络连接层和激活函数(我自己分的类):定义于common_layers.hpp,Caffe提供了单个层与多个层的连接,并在这个头文件中声明。这里还包括了常用的全连接层InnerProductLayer类

Layer的重要成员函数

在Layer内部,数据主要有两种传递方式,正向传导(Forward)和反向传导(Backward)。Forward和Backward有CPU和GPU(部分有)两种实现。Caffe中所有的Layer都要用这两种方法传递数据。

virtual void Forward(const vector<Blob<Dtype>*> &bottom,
                     vector<Blob<Dtype>*> *top) = 0;
virtual void Backward(const vector<Blob<Dtype>*> &top,
                      const vector<bool> &propagate_down, 
                      vector<Blob<Dtype>*> *bottom) = 0;

Layer类派生出来的层类通过实现这两个虚函数,产生了各式各样功能的层类。Forward是从根据bottom计算top的过程,Backward则相反(根据top计算bottom)。注意这里为什么用了一个包含Blob的容器(vector),对于大多数Layer来说输入和输出都各连接只有一个Layer,然而对于某些Layer存在一对多的情况,比如LossLayer和某些连接层。在网路结构定义文件(*.proto)中每一层的参数bottom和top数目就决定了vector中元素数目。

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

Net

Net由一系列的Layer组成(无回路有向图DAG),用容器的形式将多个Layer有序地放在一起,Layer之间的连接由一个文本文件描述。其自身实现的功能主要是对逐层Layer进行初始化,以及提供Update( )的接口(更新网络参数),本身不能对参数进行有效地学习过程。模型初始化Net::Init()会产生blob和layer并调用Layer::SetUp。在此过程中Net会报告初始化进程。这里的初始化与设备无关,在初始化之后通过Caffe::set_mode()设置Caffe::mode()来选择运行平台CPU或GPU,结果是相同的。

vector<shared_ptr<Layer<Dtype> > > layers_;

Solver

这个类中包含一个Net的指针,主要是实现了训练模型参数所采用的优化算法,它所派生的类就可以对整个网络进行训练了。

shared_ptr<Net<Dtype> > net_;

不同的模型训练方法通过重载函数ComputeUpdateValue( )实现计算update参数的核心功能。

virtual void ComputeUpdateValue() = 0;

最后当进行整个网络训练过程(也就是你运行Caffe训练某个模型)的时候,实际上是在运行caffe.cpp中的train( )函数,而这个函数实际上是实例化一个Solver对象,初始化后调用了Solver中的Solve( )方法。而这个Solve( )函数主要就是在迭代运行下面这两个函数,就是刚才介绍的哪几个函数。

ComputeUpdateValue();
net_->Update();

你可能感兴趣的:(caffe)