在caffe教程中,介绍了caffe的三级结构:Blobs, Layers,Nets.如下图所示:
深度网络是一个复杂的模型,caffe定义了一个层与层之间连接的网络模型。这个网络定义了从输入层到损失的所有模型。caffe使用blobs结构存储,交换和处理网络中正向和反向迭代时的数据和导数信息,blob是Caffe的标准数组结构,它提供了一个统一的内存接口。Layer是Caffe模型和计算的基本单元。Net是一系列的Layer和其连接的集合,Blob详细描述了信息是如何在Layer和net中存储和交换的。
solving:是求解方法,分别配置解耦模型和优化方法
1. Blob存储与交换
Blob是caffe中负责数据传递和处理的包,并且提供了在CPU与GPU之间同步处理的功能。从数学角度看,Blob是C语言风格的一个连续存数的N维数组。
Caffe利用Blobs存储和交换数据,Blobs提供了统一的内存接口存储某种类型的数据,如批处理的图像数据,模型参数和用与优化算法的导数。
Blobs可以根据CPU主机和GPU设备的需要,屏蔽CPU/GPU计算和混合操作中的开销,主机和设备的内存按需求分配(lazily),以提高内存利用率。
对于批处理的图片来说,Blobs常规的维度为:图像数据的个数N * 通道数K * 图像高度H * 图像宽度W。Blob按行存储, 这使得它最后/最右面的维度更新的最快。比如,在一个4维的blob中,位置为(n,k,h,w)的值的物理位置为((n * K + k) * H + h) * W + w.
- Number/N是每批处理数据的规模,批量处理数据有利于提高设备处理和交换数据的吞吐率。在Imagenet上训练数据每批有256张图像,则N=256.
- Channel/K是特征维度,对于RGB图像K=3.
虽然在Caffe在用于图像数据过程中,Blob都是4维的,但是它完全可以用于非图像数据。例如,如果你仅需要类似于传统多层感知机那样的全连接网络,使用2维的blobs(形式为(N,D)) , 然后调用InnerProdectLayer(全连接层)即可。
Blob的维度参数根据层的类型和配置而变化,对于由96个规模为11*11,输入为3通道的滤波器构成的卷积层,blob的维度为96*3×11*11,对于一个输入是1024维,输出是1000维的内几层和全连接层,blob的维度是1000*1024.
对于一些特定的数据,自己设计输入工具或数据层是有必要的,但是,一旦数据准备完毕,各种层模块会帮你完成剩下的工作。
实现细节:
对于blob中的数据,我们通常会关心它的值和梯度,因此,blob存储了两块数据内容:data和diff。前者是通过网络的普通数据,后者是网络反向计算得到的梯度。
而且,因为数据既可以存储在CPU上,又可以存储在GPU上,因而有两种不同的访问方式:静态方式,不改变数值,动态方式,改变数值。
const Dtype* cpu_data() const; Dtype* mutable_cpu_data();
(gpu和diff类似)
这么设计是为了用SyncedMem类来同步CPU与GPU上的数值,以隐藏同步细节,最小化传送数据。一个准则是,如果你不想改变数值,就一直用常数调用,不要在你的项目中存储指针,每层操作blob时,调用相应的函数获取它的指针,因为SyncedMem需要用这种方式确定何时需要复制数据。
实际上,使用GPU时,Caffe中的CPU 代码先从磁盘加载到blob,调用一个(GPU)设备核使GPU进行计算, 再将算好的blob数据送入下一层,忽略了底层的细节,同时又能维持高效的运算。一旦所有的层都有GPU实现,那么所有中间数据和梯度都会保存在GPU上。
下面的例子解释blub何时复制数据:
// Assuming that data are on the CPU initially, and we have a blob.假定数据在CPU上进行初始化,我们有一个blob const Dtype* foo; Dtype* bar; foo = blob.gpu_data(); // data copied cpu->gpu.数据从cpu复制到gpu foo = blob.cpu_data(); // no data copied since both have up-to-date contents.没有数据复制,两者都有最新的内容 bar = blob.mutable_gpu_data(); // no data copied.没有数据复制 // ... some operations ...一些操作 bar = blob.mutable_gpu_data(); // no data copied when we are still on GPU. 没有数据拷贝,依旧在gpu foo = blob.cpu_data(); // data copied gpu->cpu, since the gpu side has modified the data 数据从gpu复制到cpu,因为gpu端已经修改了数据 foo = blob.gpu_data(); // no data copied since both have up-to-date contents没有数据拷贝,两边都是最新内容 bar = blob.mutable_cpu_data(); // still no data copied.没有数据拷贝 bar = blob.mutable_gpu_data(); // data copied cpu->gpu.数据从cpu到gpu bar = blob.mutable_cpu_data(); // data copied gpu->cpu.数据从gpu到cpu
2. Layer : 计算和连接
这一层是非常重要的模型,是执行计算的基本单元, Layers可以进行的计算有:convolve filters(卷积滤波), pool(池化),inner products(内积), 及一些非线性运算如rectified-linear(限制线性运算),sigmoid及元素级的数据转换,normalize(归一化),数据加载,计算损失,如softmax和hinge。
这一层通过bottom(底部)连接层接收数据,通过top(顶部)连接层输出数据
每个类型的层都定义了三种重要运算:setup, forward, 和backward.
- Setup: 在模型初始化时,初始化层和它的连接
- Forward: 从bottom给定的输入计算输出,传到top层中
- Backward: 给定输出层的梯度,计算相对于输入层的梯度,并传到bottom层,一个有参的layer需要计算相对于各个参数的梯度并存储。
特别的,Forward和Backward函数有CPU和GPU两种执行方式。如果你没有执行GPU版本,layer会转为作为备选的CPU方式,这会增加额外的数据传送成本,但是对于一些快速实验还是很方便的(尽管输入数据要由gpu到cpu,之后输出数据从gpu回到cpu)。
Layers承担了整个网络的两个核心操作: 前向传播,接收输入计算输出;反向传播,接收关于输出的梯度,计算参数的梯度并反向传播给前面的层,由此组成了每个layer的前向和反向通道。
由于caffe网络的组合性和其代码的模块化,定义layer是很容易的,只要定义好setup,forward,backward,就可以将layer接入到网络中。
Net的定义和操作:
net通过组合和自动微分,定义了一个函数和其对应的梯度。通过各个层的输出计算这个函数,执行给定的任务。并通过组合各个层的反向传播过程计算学习任务中损失函数的梯度,caffe模型是一种端到端的机器学习引擎。
net是由一系列层组成的有向无环图,Caffe保留了图中所有层的中间值以确保前向和反向迭代的正确性。一个典型的net开始于数据(data)层,从磁盘中加载数据,结束于损失(loss)层,计算目标任务,如分类和重构。
net模型定义在一个纯文本模型语言中,有一系列的层和它们的连接构成。一个简单的逻辑回归定义如下:
其网络结构定义为:
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" }
模型通过Net::Init()初始化,初始化主要做两件事情:通过创建blobs和layers搭建整个网络的DAG图; 调用layers的Setup()函数。初始化时会有一系列的记录,例如整个网络的结构是否正确构建。同时,Net在初始化阶段会将其日志答应在INFO中:
I0902 22:52:17.931977 2079114000 net.cpp:39] Initializing net from parameters: name: "LogReg" [...model prototxt printout...] # construct the network layer-by-layer I0902 22:52:17.932152 2079114000 net.cpp:67] Creating Layer mnist I0902 22:52:17.932165 2079114000 net.cpp:356] mnist -> data I0902 22:52:17.932188 2079114000 net.cpp:356] mnist -> label I0902 22:52:17.932200 2079114000 net.cpp:96] Setting up mnist I0902 22:52:17.935807 2079114000 data_layer.cpp:135] Opening leveldb input_leveldb I0902 22:52:17.937155 2079114000 data_layer.cpp:195] output data size: 64,1,28,28 I0902 22:52:17.938570 2079114000 net.cpp:103] Top shape: 64 1 28 28 (50176) I0902 22:52:17.938593 2079114000 net.cpp:103] Top shape: 64 (64) I0902 22:52:17.938611 2079114000 net.cpp:67] Creating Layer ip I0902 22:52:17.938617 2079114000 net.cpp:394] ip <- data I0902 22:52:17.939177 2079114000 net.cpp:356] ip -> ip I0902 22:52:17.939196 2079114000 net.cpp:96] Setting up ip I0902 22:52:17.940289 2079114000 net.cpp:103] Top shape: 64 2 (128) I0902 22:52:17.941270 2079114000 net.cpp:67] Creating Layer loss I0902 22:52:17.941305 2079114000 net.cpp:394] loss <- ip I0902 22:52:17.941314 2079114000 net.cpp:394] loss <- label I0902 22:52:17.941323 2079114000 net.cpp:356] loss -> loss # set up the loss and configure the backward pass I0902 22:52:17.941328 2079114000 net.cpp:96] Setting up loss I0902 22:52:17.941328 2079114000 net.cpp:103] Top shape: (1) I0902 22:52:17.941329 2079114000 net.cpp:109] with loss weight 1 I0902 22:52:17.941779 2079114000 net.cpp:170] loss needs backward computation. I0902 22:52:17.941787 2079114000 net.cpp:170] ip needs backward computation. I0902 22:52:17.941794 2079114000 net.cpp:172] mnist does not need backward computation. # determine outputs I0902 22:52:17.941800 2079114000 net.cpp:208] This network produces output loss # finish initialization and report memory usage I0902 22:52:17.941810 2079114000 net.cpp:467] Collecting Learning Rate and Weight Decay. I0902 22:52:17.941818 2079114000 net.cpp:219] Network initialization done. I0902 22:52:17.941824 2079114000 net.cpp:220] Memory required for data: 201476
注意:caffe中网络的构件与设备无关,回想之前解释过的blobs和layers是为了从模型定义中隐藏实现细节的,网络构建完成之后,通过设置Caffe::mode()和Caffe::set_mode(),网络将在CPU或GPU中运行。CPU和GPU的实现结果相同,CPU和GPU可以无缝切换并且独立于模型定义
Model格式
Caffe的模型定义在纯文本的协议上(prototxt),学习好的模型被序列化到二进制protocol buffer(binaryproto).caffemodel文件中。
模型格式用protobuf语言定义在caffe.proto文件中。这部分也会在笔记中解释。
参考资料:
Caffe官网: http://caffe.berkeleyvision.org/tutorial/net_layer_blob.html