Caffe框架整理

引言

Caffe 全称Convolutional Architecture for Fast Feature Embedding,是一个计算 CNN 相关算法的框架,用C++ 和 Python实现的。

Caffe 的优点与局限性:

优点: 第一个主流的工业级深度学习工具; 专精于图像处理

局限性:它有很多扩展,但是由于一些遗留的架构问题,不够灵活且对递归网络和语言建模的支持很差;基于层的网络结构,其扩展性不好,对于新增加的层,需要自己实现(forward, backward and gradient update)

Caffe 目录结构(2016年VS2019年)

--data/  用于存放下载的训练数据

--docs/ 帮助文档

--examples/  代码样例

--matlab/  MATLAB接文件

--python/  PYTHON接文件

--models/ 一些配置好的模型参数

--scripts/  一些文档和数据会用到的脚本核心代码

--tools/  保存的源码是用于生成二进制处理程序的,caffe 在训练时实际是直接调用这些二进制文件

--include/  Caffe 的实现代码的头文件

--src/ 实现Caffe 的源文件

$ tree -d   (2019年)
.
├── cmake                               //cmake编译时会用到<非后续重点学习>
│   ├── External
│   ├── Modules
│   └── Templates
├── data                                //存放原始数据或数据获取方式的脚本文件
│   ├── cifar10                         //cifar10数据集
│   ├── ilsvrc12                        //ImageNet数据集
│   └── mnist                           //mnist手写数字数据
├── docker                              //Docker工具便于迁移<非后续重点学习>
│   ├── cpu
│   └── gpu
├── docs                                //doxygen工程文件放在这里<非后续重点学习>
│   ├── _layouts
│   ├── images
│   ├── stylesheets
│   └── tutorial
│       ├── fig
│       └── layers
├── examples                            //caffe的简单例程
│   ├── cifar10
│   ├── cpp_classification
│   ├── feature_extraction
│   ├── finetune_flickr_style
│   ├── finetune_pascal_detection
│   ├── hdf5_classification
│   ├── imagenet
│   ├── images
│   ├── mnist
│   ├── net_surgery
│   ├── pycaffe
│   │   └── layers
│   ├── siamese
│   └── web_demo
│       └── templates
├── include                             //caffe的头文件主要放在此目录下<后续学习的重点>
│   └── caffe
│       ├── layers
│       ├── test
│       └── util
├── matlab                              //caffe的Matlab接口
│   ├── +caffe
│   │   ├── +test
│   │   ├── imagenet
│   │   └── private
│   ├── demo
│   └── hdf5creation
├── models                              //存放示例模型
│   ├── bvlc_alexnet
│   ├── bvlc_googlenet
│   ├── bvlc_reference_caffenet
│   ├── bvlc_reference_rcnn_ilsvrc13
│   └── finetune_flickr_style
├── python                              //caffe的python接口
│   └── caffe
│       ├── imagenet
│       └── test
├── scripts                             //存放脚本
│   └── travis
├── src                                 //caffe的源码<后续学习的重点>
│   ├── caffe
│   │   ├── layers
│   │   ├── proto
│   │   ├── solvers
│   │   ├── test
│   │   │   └── test_data
│   │   └── util
│   └── gtest
└── tools                               //常用工具的源码<后续学习的重点>
    └── extra
69 directories

Caffe 核心代码

-- blob[.cpp .h]  基本的数据结构类Blob

--common[.cpp .h] 定义Caffe 类

--internal_thread[.cpp .h] 使用 boost::thread 线程库

--net[.cpp .h] 网络结构类 Net

--solver[.cpp .h] 优化方法类 Solver

--data_transformer[.cpp .h] 输入数据的基本操作类 DataTransformer

--syncedmem[.cpp .h]  分配内存和释放内存类 CaffeMallocHost,用于同步CPU和GPU数据

--layer[.cpp .h] 层类 Layer

Caffe 三级结构

--Blob:用于数据的保存、交换和操作,Caffe基础存储结构

--Layer:用于模型和计算的基础

--Net:整合连接Layers

Blobs 结构

Blob 在内存中表示4维数组,在caffe/blob.hpp 中,维度包括(width_, height_, channels_, num_)num_用于存储数据或权值(data)和权值增量(diff)。 Blob 在caffe 源码 blob.hpp 中是一个模板类。protected 的成员变量有:data_, diff_, count_, capacity_, 其中data_ 和 diff_ 是共享 SyncedMemory 类(在syncedmem的源码中定义)的智能指针,shape_ 是 int 型的 vector,  count_ 和 capacity_ 是整型变量。成员函数主要有:Reshape、ReshapeLike、 ShareData、Updata 等。blob.hpp 包含了 caffe.pd.h,说明 caffe protobuf 会向 blob 传递参数。

“caffe/proto/caffe.pb.h”     caffe.pb.h是google protocol buffer根据caffe.proto自动生成的,可以到src/caffe/proto/caffe.proto里看下caffe里面用到的各个数据的定义,比如BlobProto,Datum,NetParameter等。使用这个protocol buffer看起来确实方便,一方面可以用文本文件定义结构化的数据类型,另一方面可以生成查询效率更高、占空间更小的二进制文件。

“caffe/common.hpp”     主要 singleton 化 Caffe类,并封装了boost和CUDA随机数生成的函数,提供了统一的接口。

“caffe/syncedmem.hpp”     定义了以下的接⼝口: inline void CaffeMallocHost(void** ptr, size_t size) 和 inline void CaffeFreeHost(void* ptr) 。主要是分配内存和释放内存的。而class SyncedMemory定义了内存分配管理和CPU与GPU之间同步的函数。

“caffe/util/math_functions.hpp”    封装了很多cblas矩阵运算。

# caffe.proto里面BlobProto的定义

message BlobProto {
  optional BlobShape shape = 7;
  repeated float data = 5 [packed = true];
  repeated float diff = 6 [packed = true];
  repeated double double_data = 8 [packed = true];
  repeated double double_diff = 9 [packed = true];

  // 4D dimensions -- deprecated.  Use "shape" instead.
  optional int32 num = 1 [default = 0];
  optional int32 channels = 2 [default = 0];
  optional int32 height = 3 [default = 0];
  optional int32 width = 4 [default = 0];
}

# 对于BlobProto,可以看到定义了四个optional的int32类型的名字(name)num、channels、height和width。
# optional意味着Blob可以有一个或者没有这个参数,每个名字(name)后面都有一个数字,这个数字是其名字的一个标签。
# 这个数字就是用来在生成的二进制文件中搜索查询的标签。关于这个数字,1到15会花费1byte的编码空间,16到2047花费2byte。
# 所以⼀一般建议把那些频繁使用的名字的标签设为1到15之间的值。
# 而后面的repeated意味着float类型的data和diff可以重复任意次,而加上[packed = true]是为了更高效的编码。
# 主要数据有两个data和diff,用num、channels、height和width这四个维度来确定数据的具体位置,做一些数据查询和Blob reshape的操作。

对于图片数据来说,Blob可以表示为(N*C*H*W)这样一个4D数组。其中N表示图片的数量,C表示图片的通道数,H和W分别表示图片的高度和宽度。

在模型中设定的参数,也是用Blob来表示和运算。它的维度会根据参数的类型不同而不同。比如:在一个卷积层中,输入一张3通道图片,有96个卷积核,每个核大小为11*11,因此这个Blob是96*3*11*11. 而在一个全连接层中,假设输入1024通道图片,输出1000个数据,则Blob为1000*1024。
 

Layer 的类型

所有的Pooling,Convolve,apply nonlinearities等操作都在这里实现。在Layer中input data用bottom表示,output data用top表示。每一层定义了三种操作setup(Layer初始化), forward(正向传导,根据input计算output), backward(反向传导计算,根据output计算input的梯度)。forward和backward有GPU和CPU两个版本的实现。层是网络模型的组成要素和计算的基本单位。层的类型比较多,如Data,Convolution,Pooling,ReLU,Softmax-loss,Accuracy等,一个层的定义大至如下图:
 

从bottom进行数据的输入 ,计算后,通过top进行输出。图中的黄色多边形表示输入输出的数据,蓝色矩形表示层。每一种类型的层都定义了三种关键的计算:setup,forward and backword。

setup: 层的建立和初始化,以及在整个模型中的连接初始化。

forward: 从bottom得到输入数据,进行计算,并将计算结果送到top,进行输出。

backward: 从层的输出端top得到数据的梯度,计算当前层的梯度,并将计算结果送到bottom,向前传递。

正向传播的是数据,反向传播的是误差损失和梯度。

Net 结构

Net由一系列的Layer组成(无回路有向图DAG),Layer之间的连接由一个文本文件描述。模型初始化 Net::Init() 会产生blob和layer并调用 Layer::SetUp。在此过程中Net会报告初始化进程。这里的初始化与设备无关,在初始化之后通过 Caffe::set_mode() 设置Caffe::mode() 来选择运行平台CPU或GPU,结果是相同的。

就像搭积木一样,一个net由多个layer组合而成。现给出 一个简单的2层神经网络的模型定义( 加上loss 层就变成三层了),先给出这个网络的拓扑。

第一层:name为mnist, type为Data,没有输入(bottom),只有两个输出(top), 一个为data, 一个为label 
第二层:name为ip,type为InnerProduct,  输入数据data,  输出数据ip 
第三层:name为loss,  type为SoftmaxWithLoss,有两个输入,一个为ip,一个为label,有一个输出loss, 没有画出来
对应的配置文件prototxt就可以这样写:

name: "LogReg"
layer {
  name: "mnist"
  type: "Data"
  top: "data"
  top: "label"
  data_param {
    source: "input_leveldb"
    batch_size: 64
  }
}
layer {
  name: "ip"
  type: "InnerProduct"
  bottom: "data"
  top: "ip"
  inner_product_param {
    num_output: 2
  }
}
layer {
  name: "loss"
  type: "SoftmaxWithLoss"
  bottom: "ip"
  bottom: "label"
  top: "loss"
}

优化求解过程Solver

solver算是caffe的核心的核心,它协调着整个模型的运作。caffe程序运行必带的一个参数就是solver配置文件。运行代码一般为:

./build/tools/caffe train --solver=examples/myfile/solver.prototxt -gpu all &> examples/myfile/output/output.log
#或者
./build/tools/caffe train -solver examples/myfile/solver.prototxt -gpu all &> examples/myfile/output/output.log

优化算法

caffe提供了六种优化算法来求解最优参数,在solver配置文件中,通过设置type类型来选择。

Stochastic Gradient Descent (type: "SGD"),
AdaDelta (type: "AdaDelta"),
Adaptive Gradient (type: "AdaGrad"),
Adam (type: "Adam"),
Nesterov’s Accelerated Gradient (type: "Nesterov")
RMSprop (type: "RMSProp")

Solver的流程

1. 设计好需要优化的对象,以及用于学习的训练网络和用于评估的测试网络。(通过调用另外一个配置文件test.prototxt来进行)

2. 通过forward和backward迭代的进行优化来更新参数。

3. 定期的评价测试网络(可设定多少次训练后,进行一次测试)

4. 在优化过程中显示模型和solver的状态

在每一次的迭代过程中,solver做了这几步工作:

• 1、调用forward算法来计算最终的输出值,以及对应的loss
• 2、调用backward算法来计算每层的梯度
• 3、根据选用的slover方法,利用梯度进行参数更新
• 4、记录并保存每次迭代的学习率、快照,以及对应的状态。

示例:

net: "examples/mnist/lenet_train_test.prototxt"
test_iter: 100
test_interval: 500
base_lr: 0.01
momentum: 0.9
type: SGD
weight_decay: 0.0005
lr_policy: "inv"
gamma: 0.0001
power: 0.75
display: 100
max_iter: 20000
snapshot: 5000
snapshot_prefix: "examples/mnist/lenet"
solver_mode: CPU

参考链接 

猜你喜欢

转载自blog.csdn.net/JACK_YOUNG007/article/details/89087598