1.caffe文件目录结构
caffe文件目录结构如下图:
- 如使用make编译,通过更改Makefile.config来更改编译配置。
- 如使用cmake编译,通过更改CMakeLists.txt来更改编译配置。
- 可以直接使用docker文件夹下的Dockerfile来快速构建包含caffe所需依赖环境的系统容器。
- 可以使用gtest快速测试caffe工程。
- caffe工程使用Protocol Buffers来实现结构化数据的序列号与反序列化。需要使用protoc编译器编译数据存储格式描述文件。
- model文件夹提供了几种常见模型的网络结构及配置文件。更多模型可以参考Caffe的model zoo。
2.caffe 结构
Caffe中的四大基本组件。Blob,Layer,Net和Solver。分别对应数据,层,网络,求解器。这几个组件之间的关系如下:
- Blob
- (blob.hpp)为数据载体。是其他几个组件间交互的基本单元。
- 输入数据,层间传递数据,层中参数均由Blob封装。
- 其中数据成员由SyncedMemory封装,负责CPU,GPU内存管理及同步。在存储格式上通常以NCHW方式。
- Blob主要成员如下:
data_ -->数据
diff_ -->梯度
shape_data_ -->GPU维度信息
shape_ --> 维度信息
count -->元素个数
capacity_ -->当前分配的内存容量
- Layer
- (layer.hpp)为为网络中的层。负责层的初始化及前向/后向传递。
- Layer初始化会校验底层和顶层网络个数,初始化层中参数,计算并设置本层的输出大小。
- 会根据当前mode选择CPU/GPU实现来进行前向/后向传递。若没有GPU实现会默认使用CPU实现。每次执行前向传递前都会重新计算并设置本层的输出大小。
- 各layer官方详细介绍。
- Layer主要成员如下:
blobs_ -->层中参数
layer_propagate_ -->是否需要计算梯度
phase_ -->训练/测试阶段 - 自定义layer主要过程
向caffe.proto添加自定义层的参数描述。
实现LayerSetUp() -->层的初始化
实现Reshape() -->计算并设置本层的输出大小
实现Forward_cpu/gpu() -->前向传递的cpu/gpu实现
实现Backward_cpu/gpu() -->后向传递的cpu/gpu实现
实例化并注册该自定义层
- Net
- (net.hpp)为网络。负责构建整个网络,前向/后向传递。
- 调用Forward()和Backward()实现整个网络的一次前向和后向传递。
- Update()负责更新layer中的学习参数。
- Net主要成员如下:
layers_ -->网络层列表
blobs_ -->网络层输出结果列表
params_ -->参数列表
learnable_params_ -->需要学习的参数列表
- Solver
- (solver.hpp)为求解器。负责模型参数优化。
- 调用ComputeUpdateValue()根据后向传递计算的梯度计算参数更新量。
- Solver主要成员如下:
net_ -->网络
test_nets_ -->测试网络
losses_ -->损失值
iter_ -->迭代次数
smoothed_loss_ -->平滑损失值
3. 运行流程时序图
caffe源代码的入口在./tools/caffe.cpp,caffe训练的主要流程如下:
-
初始化
主要步骤:- 使用RegisterBrewFunction宏在程序载入时通过类的实例化来将函数注册到g_brew_map这个查找表中。包括device_query(), train(), test()和time()。
- 初始化命令行解析及日志功能。
- GetBrewFunction() 通过命令行的输入参数在g_brew_map中查找并执行对应的函数。
- 执行训练过程。
4.1 通过ReadSolverParamsFromTextFileOrDie()函数从solver配置文件中把数据读入并填入SolverParameter结构。
4.2 配置GPU,设置计算模式。
4.3 新建一个信号处理实例。
4.4 通过工厂模式新建一个solver求解器。初始化solver求解器。
4.5 配置signal处理。例如收到Ctrl+C命令先执行Snapshot保存参数后再退出程序。
4.6 若果是继续训练,通过Restore()函数进行恢复再继续训练。网络初始化会通过LoadNetWeights()函数恢复权重参数值。
4.7 调用Solve()开始训练。
-
Solver初始化
主要步骤:
1.Solver初始化。
2.设置随机数种子。
3.初始化训练网络。
4.读取网络配置参数。
5.新建网络。网络初始化。
1.处理过滤规则及多路输出的网络。
2.根据网络配置参数为每一层网络实例化12.对于该层的所有bottom blob,调用AppendBottom()进行进行处理。添加一个下层到网络中。
13.对于该层所有的top blob,调用AppendTop()函数进行处理。添加一个上层到网络中。
15.调用当前层的SetUp()函数。包括校验底层/顶层数量,初始化网络层,计算并设置输出大小。
22.对于该层中所有的param blob,调用AppendParam()函数来处理。记录名字和blob对应的index。
24.载入权值。
25.初始化测试网络。与初始化训练网络相似。 -
训练过程
主要步骤:
2.调用Step()函数进行训练,参数为需要迭代的次数。该函数为训练的主循环。循环的index从iter_到iter_+iters。循环次数由参数iters指定。
3.调用Net的ClearParamDiffs()函数初始化参数的梯度信息。
4.读取网络配置参数。
5.ForwardBackward()函数完成一次前向和后向传递。
15.UpdateSmoothedLoss()函数计算平滑后的loss值。
16.ApplyUpdate()函数应用更新权值参数值。通过GetLearningRate()函数计算学习率
通过ClipGradients()函数来clip梯度
调用Normalize(),Regularize()和ComputeUpdateValue()三个函数,使用优化方法计算权值参数更新量。
21.更新权值参数。
23.保存权值文件(如果需要保存)。 -
小结
通过分析caffe的框架结构以及训练过程,可以增强对源码的理解。对于加深理解深度学的整个流程有很大的帮助。