# How does Caffe work

1.Blob

* Blob是caffe基本的数据结构，用四维矩阵 Batch×Channel×Height×Weight表示，存储了网络的神经元激活值和网络参数，以及相应的梯度（激活值的残差和dW、db)。其中包含有cpu\_data、gpu\_data、cpu\_diff、gpu\_diff、mutable\_cpu\_data、mutable\_gpu\_data、mutable\_cpu\_diff、mutable\_gpu\_diff这一堆很像的东西，分别表示存储在CPU和GPU上的数据（印象中二者的值好像是会自动同步成一致的），其中带data的里面存储的是激活值和W、b，diff中存储的是残差和dW、db，另外带mutable和不带mutable的一对指针所指的位置是相同的，只是不带mutable的只读，而带mutable的可写。&#x20;

2.Layer

* Layer代表了神经网络中各种各样的层，组合成一个网络。一般一个图像或样本会从数据层中读进来，然后一层一层的往后传。除了数据层比较特殊之外，其余大部分层都包含4个函数：LayerSetUp、Reshape、Forward、Backward。其中LayerSetup用于初始化层，开辟空间，填充初始值什么的。Reshape是对输入值进行维度变换，比如pooling接全连接层的时候要先拉成一个向量再计算。Forward是前向传播，Backward是后向传播。当然对于我这种喜欢偷懒的童鞋一般学习的时候最喜欢看各种层的Backward函数了，最好是对着公式边推导边看，可以有更直观的理解。那么数据是如何在层之间传递的呢？每一层都会有一个（或多个）Bottom和top，分别存储输入和输出，比如bottom\[0]->cpu\_data()存输入的神经元激活值，换成top存输出的，换成cpu\_diff()存的是激活值的残差，换成gpu是存在GPU上的数据，再带上mutable就可写了，这些是神经元激活值相关的，如果这个层前后有多个输入输出层，就会有bottom\[1]，比如accuracy\_layer就有两个输入，fc8和label。而每层的参数会存在this->blobs\_里，一般this->blobs\_\[0]存W，this->blobs\_\[1]存b，this->blobs\_\[0]->cpu\_data()存的是W的值，this->blobs\_\[0]->cpu\_diff()存的梯度dW，b和db也类似，然后换成gpu是存在GPU上的数据，再带上mutable就可写了。。（各种变量好多好晕，但愿我说清楚了。。）哦对了，凡是能在GPU上运算的层都会有名字相同的cpp和cu两个文件，cu文件中运算时基本都调用了cuda核函数，可以在math\_function.cu中查看。&#x20;

3.Net

* Net就是把各种层按train\_val.prototxt的定义堆叠在一起，首先进行每个层的初始化，然后不断进行Update，每更新一次就进行一次整体的前向传播和反向传播，然后把每层计算得到的梯度计算进去，完成一次更新，这里注意每层在Backward中只是计算dW和db，而W和b的更新是在Net的Update里最后一起更新的。而且在caffe里训练模型的时候一般会有两个Net，一个train一个test。刚开始训练网络时前面的一大堆输出，网络的结构什么的也都是这里输出的。&#x20;

4.Solver

* Solver是按solver.prototxt的参数定义对Net进行训练，首先会初始化一个TrainNet和一个TestNet，然后其中的Step函数会对网络不断进行迭代，主要就是两个步骤反复迭代：①不断利用ComputeUpdateValue计算迭代相关参数，比如计算learning rate，把weight decay加上什么的，②调用Net的Update函数对整个网络进行更新。迭代中的一大堆输出也是在这里输出的，比如当前的loss和learning rate什么的。 综上，为了把整个过程串起来，可以从tools/caffe这个我们最常用的函数入手，训练一个网络然后跟着数据的流动方向看看一个网络是怎么更新的，然后找自己比较感兴趣的地方细看。

5.代码调用逻辑

* 首先是初始化的过程：train函数先生成solver类的对象，solver对象中有net类的内部变量，所以solver对象生成的同时生成了net类对象，solver对象是根据train函数中传入的prototxt文件来初始化的。同理net类对象里有一个layer类的vector容器，里面实际将会装进各个不同的层，net类对象会通过传入solver类对象的初始化量来找到xxx.prototxt这样的层描述文件中的内容，来初始化这个layer类的vector容器。然后训练的过程：train函数执行solver的solve函数，solve函数会根据设定的参数循环调用net类的forwardbackward函数，net类的forforward和backward函数主要负责控制前向和反相的顺序，按照顺序调用不同的layer前向和反向的函数，而layer类中的forward和backward函数负责不同层实现的细节。\


6.代码流程图\


![](https://firebasestorage.googleapis.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LGHUhl6VYqrZm4Re77O%2Fuploads%2FnaFphIkNggXkY4WiRZnS%2Ffile.jpeg?alt=media)
