caffe源码分析——使用caffe在MNIST数据上训练LeNet网络

part one 初步使用caffe

参考链接:http://caffe.berkeleyvision.org/gathered/examples/mnist.html

网易云课堂(程序中文件放在目录~/Documents/caffe/projects/mnist中):https://study.163.com/course/courseLearn.htm?courseId=1003491001#/learn/video?lessonId=1004081127&courseId=1003491001

caffe操作步骤:准备数据(数据)->定义Net(模型)->配置Solver(算法)->Run->分析结果    然后循环以上步骤

Caffe主要由Blob,Layer,Net,Solver这几个部分组成。

Blob:主要用来表示网络中的数据,包括训练数据,网络各层自身的参数(包括权值、偏置以及它们的梯度)

Layer:是对神经网络中各种层的一个抽象

Net:对整个网络的表示,由各种Layer前后连接组合而成,也是我们所构建的网络模型

Solver:定义了针对Net网络模型的求解方法,自定义Solver能够实现不同的网络求解方式

LeNet:MNIST分类模型

组成:卷积层-池化层-卷积层-池化层-全连接层-全连接层

将层定义在$CAFFE_ROOT/examples/mnist/lenet_train_test.prototxt

定义MNIST网络

top表示输出,bottom表示输入

①数据层:类型为Data,读入数据为lmdb格式,即bottom,top为两个blobs,一个是数据blob,一个是标签blob

②卷积层:类型为Convolution,bottom为data,top为conv1,使用定义weight_filler和bias_filler来进行初始化,lr_mults表示学习率

③池化层:类型为Pooling,bottom为conv1,top为pool1

④全连接层:类型为InnerProduct,bottom为pool2,top为ip1

⑤ReLU层:类型为ReLU,bottom为ip1,top为ip1(用了相同的变量,为了节省内存)

⑥Loss层:类型为SoftmaxWithLoss,bottom是ip2和label

(The softmax_loss layer implements both the softmax and the multinomial logistic loss (that saves time and improves numerical stability).)

定义MNIST Solver

$CAFFE_ROOT/examples/mnist/lenet_solver.prototxt

训练和测试模型

在你写好定义网络的protobuf和solver protobuf文件,即lenet_train_test.prototxtlenet_solver.prototxt两个文件,训练模型就变得简单了。

只需要运行:

cd $CAFFE_ROOT
./examples/mnist/train_lenet.sh

会出现以下:

I1203 solver.cpp:204] Iteration 100, lr = 0.00992565
I1203 solver.cpp:66] Iteration 100, loss = 0.26044
...
I1203 solver.cpp:84] Testing net
I1203 solver.cpp:111] Test score #0: 0.9785
I1203 solver.cpp:111] Test score #1: 0.0606671

训练阶段的输出:lr是迭代的learning rate,loss是训练函数

测试阶段的输出:score 0是accuracy,score 1是测试阶段的loss function

几分钟之后,训练成功,得到一个最终的模型

lenet_iter_10000.caffemodel

这个训练好的模型就可以在实际的应用中进行使用。

part two 源码分析

代码分析参考链接:https://buptldy.github.io/2016/10/09/2016-10-09-Caffe_Code/

训练LeNet

build/tools/caffe是Caffe框架的主要框架,由tools/caffe.cpp文件编译而来

在Caffe中,网络模型的描述及其求解、模型的参数实现加载和存储、包括 CPU 与 GPU 之间的无缝切换都是通过 protobuf配置来实现的,不需要通过硬编码的方式实现。

网络初始化

网络的初始化就是从下面一行代码新建一个solver指针开始一步一步的调用SolverNet,Layer,Blob类的构造函数,完成整个网络的初始化。

在训练LeNet的例子中,传入的第二个参数为train,所以调用的函数为caffe.cpp中的int train()函数,其中构造了solver指针,总体的流程大概就是新建一个Solver对象,然后调用Solver类的构造函数,然后在Solver的构造函数中又会新建Net类实例,在Net类的构造函数中又会新建各个Layer的实例,一直具体到设置每个Blob,大概就介绍完了网络初始化的工作。

训练过程

完成初始化之后,就可以开始对网络进行训练了,指向Solver类的指针solver开始调用Solver类的成员函数Solve(),Solve函数其实主要就是调用了Solver的另一个成员函数Step()来完成实际的迭代训练过程。

!!!在GPU显存不够用的时候如何处理?

Step()函数的主要代码,首先是一个大循环设置了总的迭代次数,在每次迭代中训练iter_size x batch_size个样本,这个设置是为了在GPU的显存不够的时候使用,比如我本来想把batch_size设置为128,iter_size是默认为1的,但是会out_of_memory,借助这个方法,可以设置batch_size=32,iter_size=4,那实际上每次迭代还是处理了128个数据。

Step()函数主要分为三部分:

loss += net_->ForwardBackward();

调用了Net中的代码,主要完成了前向后向的计算,前向用于计算模型的最终输出和Loss,后向用于计算每一层网络和参数的梯度

UpdateSmoothedLoss();

这个函数主要做Loss的平滑。由于Caffe的训练方式是SGD,我们无法把所有的数据同时放入模型进行训练,那么部分数据产生的Loss就可能会和全样本的平均Loss不同,在必要时候将Loss和历史过程中更新的Loss求平均就可以减少Loss的震荡问题

ApplyUpdate();

这个函数是 Solver类的纯虚函数,需要派生类来实现,比如 SGDSolver类实现的 ApplyUpdate();函数如下,主要内容包括:设置参数的学习率;对梯度进行Normalize;对反向求导得到的梯度添加正则项的梯度;最后根据SGD算法计算最终的梯度;最后的最后把计算得到的最终梯度对权值进行更新。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值