1. 简介
1)官方教材
2)Hacker's guide to Neural Networks(计算梯度实例)
3)Convolution in Caffe: a memo
4)Caffe上手教程
6)DIY Deep Learning for Vision: a Hands-On Tutorial with Caffe
7)Caffe-Android-Lib Caffe-android-demo
9)Model Zoo
1.1 Caffe代码层次
1)Blob:是基础的数据结构,是用来保存学习到的参数以及网络传输过程中产生数据的类。2)Layer:是网络的基本单元,由此派生出了各种层类。修改这部分的人主要是研究特征表达方向的。
3)Net:是网络的搭建,将Layer所派生出层类组合成网络。
4)Solver:是Net的求解,修改这部分人主要会是研究DL求解方向的。
1.2 Blob
数据及其导数以blobs的形式在层间流动。Blob是用以存储数据的4维数组,例如
1) 对于数据:Number*Channel*Height*Width
2) 对于卷积权重:Output*Input*Height*Width
3) 对于卷积偏置:Output*1*1*1
Caffe支持CUDA,在数据级别上也做了一些优化,这部分最重要的是知道它主要是对protocol buffer所定义的数据结构的继承,Caffe也因此可以在尽可能小的内存占用下获得很高的效率。(追求性能的同时Caffe也牺牲了一些代码可读性)
在更高一级的Layer中Blob用下面的形式表示学习到的参数:
vector<shared_ptr<Blob<Dtype> > > blobs_;
这里使用的是一个Blob的容器是因为某些Layer包含多组学习参数,比如多个卷积核的卷积层。
以及Layer所传递的数据形式,后面还会涉及到这里:
vector<Blob<Dtype>*> ⊥
vector<Blob<Dtype>*> *top;
1.3 Layer
Caffe层的定义由2部分组成:层属性与层参数,例如name:"conv1"
type:CONVOLUTION
bottom:"data"
top:"conv1"
convolution_param{
num_output:20
kernel_size:5
stride:1
weight_filler{
type: "xavier"
}
}
前面4项为层属性,后面的为层参数。
1.3.1 Layer主要派生类型
Caffe十分强调网络的层次性,也就是说卷积操作,非线性变换(ReLU等),Pooling,权值连接等全部都由某一种Layer来表示。具体来说分为5大类Layer:1)NeuronLayer类:
定义于neuron_layers.hpp中,其派生类主要是元素级别的运算(比如Dropout运算,激活函数ReLu,Sigmoid等),运算均为同址计算(in-place computation,返回值覆盖原值而占用新的内存)。
2)LossLayer类:
定义于loss_layers.hpp中,其派生类会产生loss,只有这些层能够产生loss。
3)数据层:
定义于data_layer.hpp中,作为网络的最底层,主要实现数据格式的转换。
4)特征表达层:
定义于vision_layers.hpp(为什么叫vision这个名字,我目前还不清楚),实现特征表达功能,更具体地说包含卷积操作,Pooling操作,他们基本都会产生新的内存占用(Pooling相对较小)。
5)网络连接层和激活函数:
定义于common_layers.hpp,Caffe提供了单个层与多个层的连接,并在这个头文件中声明。这里还包括了常用的全连接层InnerProductLayer类。
1.3.2 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
}
1.3.3 Layer的重要成员变量
/** The vector that indicates whether each top blob has a non-zero weight in
* the objective function. */
vector<Dtype> loss_;
每一层又有一个loss_值,只不过大多数Layer都是0,只有LossLayer才可能产生非0的loss_。计算loss是会把所有层的loss_相加。
/** The vector that stores the learnable parameters as a set of blobs. */
vector<shared_ptr<Blob<Dtype> > > blobs_;
前面提到过的,Layer学习到的参数。
1.4 Net
Net用容器的形式将多个Layer有序地放在一起,其自身实现的功能主要是对逐层Layer进行初始化,以及提供Update( )的接口(更新网络参数),本身不能对参数进行有效地学习过程。
/// @brief Individual layers in the net
vector<shared_ptr<Layer<Dtype> > > layers_;
同样Net也有它自己的Forward和Backward函数:
/**
* @brief Run Forward and return the result.
*
*/
const vector<Blob<Dtype>*>& Forward(Dtype* loss = NULL);
/**
* The network backward should take no input and output, since it solely
* computes the gradient w.r.t the parameters, and the data has already been
* provided during the forward pass.
*/
void Backward();
他们是对整个网络的前向和反向传导,各调用一次就可以计算出网络的loss了。
1.5 Solver
这个类中包含一个Net的指针,主要实现了训练模型参数所采用的优化算法,它所派生的类就可以对整个网络进行训练了。
shared_ptr<Net<Dtype> > net_;
不同的模型训练方法通过重载函数ComputeUpdateValue( )实现计算update参数的核心功能:
virtual void ComputeUpdateValue(int param_id, Dtype rate);
最后当进行整个网络训练过程(也就是你运行Caffe训练某个模型)的时候,实际上是在运行caffe.cpp中的train( )函数,而这个函数实际上是实例化一个Solver对象,初始化后调用了Solver中的Solve( )方法。而这个Solve( )函数主要就是在迭代运行下面这两个函数,就是刚才介绍的哪几个函数。
ComputeUpdateValue();
net_->Update();
1.6 Loss
2. Caffe使用第三方工具
1)Caffe生成的数据分为2种格式:Lmdb和Leveldb
它们都是键/值对(Key/Value Pair)嵌入式数据库管理系统编程库。虽然lmdb的内存消耗是leveldb的1.1倍,但是lmdb的速度比leveldb快10%至15%,更重要的是lmdb允许多种训练模型同时读取同一组数据集。因此lmdb取代了leveldb成为Caffe默认的数据集生成格式。
2)Google Logging library (Glog): 一个C++语言的应用级日志记录框架,提供了C++风格的流操作和各种助手宏.
3)CBLAS library(CPU版本的矩阵操作)
4)CUBLAS library (GPU 版本的矩阵操作)
3. Caffe架构
- 预处理图像的leveldb构建
输入:一批图像和label (2和3)
输出:leveldb (4)
指令里包含如下信息:- conver_imageset (构建leveldb的可运行程序)
- train/ (此目录放处理的jpg或者其他格式的图像)
- label.txt (图像文件名及其label信息)
- 输出的leveldb文件夹的名字
- CPU/GPU (指定是在cpu上还是在gpu上运行code)
-
CNN网络配置文件
- Imagenet_solver.prototxt (包含全局参数的配置的文件)
- Imagenet.prototxt (包含训练网络的配置的文件)
- Imagenet_val.prototxt (包含测试网络的配置文件)
4. Caffe训练步骤
使用 Caffe 训练的一般步骤就是:
- 数据预处理;
- 模型定义;
- 求解策略定义;
- 运行。
4.1 数据流程
5. 前向传播(Forward)
1)从高层次到低层次的调用Forward函数:Solver->Net->Layer->Specific Layer (Convolution等...)2)核心函数:
- forward_cpu()
- forward_gpu()
6. 反向传播(Backpropagation)
1)误差顺着Chain Rule法则回流到前面的层2)核心函数:
- backward_cpu()
- backward_gpu()