部分内容参考自博客:http://blog.csdn.net/mounty_fsc/article/category/6136645
以下内容以Lenet网络为例子
一、train函数过程
1.1 main函数,在tools\caffe.cpp
解析命令,从而调用train、test、time、device_query中的一个,训练则调用train函数。
1.2 train函数,在tools\caffe.cpp
根据solver.prototxt信息实例化SolverParameter和caffe::Solver对象,整个网络初始化都在创建Solver对象时完成。
1.3 train函数调用solver.cpp的Solve函数,开始训练网络
Solve总入口为void Solver<Dtype>::Solve(const char* resume_file),调用Step函数。
1.4 Step函数,进行max_iter次迭代
迭代过程中,最开始测一次,然后每隔test_interval轮进行一次测试,调用TestAll函数。
每次迭代,都执行反向传播,前向计算损失loss,并计算loss关于权值的偏导,调用ForwardBackward函数。
调用UpdateSmoothedLoss来平滑loss。
调用ApplyUpdate通过反向传播计算的偏导更新权值。
每隔snapshot则保存训练结果。
1.5 TestAll函数
对于每个测试网络test_net(注意不是训练网络train_net),都进行测试,调用Test函数,在Lenet中,只有一个测试网络。
1.6 Test函数
测试集大小= test_iter* batchsize,测试时候进行test_iter次迭代。
主要做两件事:
1)执行前向计算网络,调用test_net的Forward函数获取loss。
2)通过测试网络的accuracy层与loss层结果统计accuracy与loss信息。
具体过程参考第二部分。
1.7 ForwardBackward函数,在net.hpp中
先Forward函数:计算网络损失loss。可参考第二部分Test函数过程。
再Backward函数:计算loss关于网络权值的偏导。具体过程参考第三部分。
1.8 UpdateSmoothedLoss函数
由于Caffe的训练方式是SGD,无法把所有的数据同时放入模型进行训练,那么部分数据产生的Loss就可能会和全样本的平均Loss不同,在必要时候将Loss和历史过程中更新的Loss求平均就可以减少Loss的震荡问题。方法较简单,就是求平均。
1.9 ApplyUpdate函数,在sgd_solver.cpp中
分别给网络的每一层layer都更新权值。
在caffe中,可以分为三个阶段:
- 前向计算阶段,这个阶段计算fW
- 反向传播阶段,这个阶段计算∇fW
- 权值更新阶段,这个阶段通过∇fW,∇r(W)等计算ΔW从而更新W
在lenet中,只有conv1、conv2、ip1、ip2四层有参数
1.10 Snapshot函数
保存训练的中间结果,caffemod el文件和solverstate文件。
二、1.6Test函数过程
测试网络结构:
调用Forward函数,在\caffe\net.cpp中,Forward调用ForwardFromTo函数
2.1 ForwardFromTo函数
对每层网络前向计算,共12层网络,调用每层网络的Forward函数,根据配置情况选择调用Forward_gpu还是Forward_cpu。
2.2 第一层DataLayer
DataLayer未实现Forward_cpu或Forward_gpu,其父类BasePrefetchingDataLayer实现了,在src\caffe\layers\base_data_layer.cpp中。内容为从BasePrefetchingDataLayer的数据缓存队列BlockingQueue<Batch*>取出一个Batch的数据放入DataLayer的Top Blob中,其中Top[0]存放数据,Top[1]存放标签。
2.3 第二层SplitLayer
在src\caffe\layers\split_layer.cpp中,调用Forward_cpu函数。SplitLayer有两个Top Blob label_mnist_1_split_0和label_mnist_1_split_1,在其Forward_cpu中,从它的BottomBlob,也就是DataLayer的第二个Top Blob,label中把指向数据的指针复制到label_mnist_1_split_0和label_mnist_1_split_1中(即共享了数据)。
2.4 第三层ConvolutionLayer
在src\caffe\layers\conv_layer.cpp中,调用Forward_cpu函数。对每一个BottomBlob中的Batch的每一个样本进行卷积。对卷积进行了优化,把要卷积的局部图像和卷积核拉成列,然后求两矩阵内积。
lr_mult: 1 //学习率的系数,最终的学习率是这个数乘以solver.prototxt配置文件中的base_lr
lr_mult: 2 //如果有两个lr_mult,则第一个表示权值的学习率,第二个表示偏置项的学习率。一般偏置项的学习率是权值学习率的两倍
2.5 第四层PoolingLayer
Caffe中实现了Max Pooling和Average Pooling两种方法
2.6 第七层InnerProductLayer
在src\caffe\layers\inner_product_layer.cpp中,调用Forward_cpu函数。全连接层就是求两个矩阵的积。
2.7 第八层ReLUForward
在src\caffe\layers\relu_layer.cpp中,调用Forward_cpu函数。ReLu层很简单,就是实现了公式out=max(0,in),negative_slope一般取0。
2.8 第十一层AccuracyLayer
在src\caffe\layers\accuracy_layer.cpp中,调用Forward_cpu函数。
在ip2中输出100*10的blob,对应每个类的概率,统计最大的概率,计算与label相同的个数,个数除以总数就是accuracy。
2.9 第十二层SoftmaxWithLossLayer
在src\caffe\layers\softmax_loss_layer.cpp中,调用Forward_cpu函数。
在实现细节上,train时候在最后接上SoftmaxWithLossLayer,test的时候换成SoftmaxLayer即可。
SoftmaxLayer:100个样本,每个样本特征数量为10,计算这100个样本分别在10个类别上的概率。计算公式如下:
SoftmaxWithLossLayer:n个样本,y为样本对应的类别(标签),损失如下公式计算:
三、1.7Backward函数过程
net.cpp中,实现反向传播的计算过程。在Lenet中,训练网络共9层。训练网络结构:
3.1 Net::Backward()函数中调用BackwardFromTo函数
从网络最后一层到网络第一层反向调用每个网络层的Backward。
3.2 第九层SoftmaxWithLossLayer
softmax_loss_layer.cpp的Backward_cpu函数。SoftmaxWithLossLayer是没有学习参数的,因此不需要对该层的参数做调整,只需要计算bottom_diff(理解反向传播算法的链式求导,求bottom_diff对上一层的输出求导,是为了进一步计算调整上一层权值)。
3.3 之后各层主要是求导数和偏导数,具体公式详见:
http://blog.csdn.net/mounty_fsc/article/details/51379395