学习caffe官网教程,粗略翻译一下供以后参考。
Blob存储和传递(数据)
Blob是封装后的数据,用来在Caffe中处理和传递数据,并且可以在CPU和GPU之间同步数据。Blob结构从数学上来讲,是一个N维数组,按照C-contiguous方式存储数据(C-contiguous fashion指内存存储是连续的、不间断存储的)。
Caffe通过Blob来存储和传递数据,Blob提供了统一的内存接口来保存数据,例如图像数据(batches of images)、模型参数和优化模型时的导数等。
Blob隐藏了计算及内部处理细节,实现了按需从CPU到GPU同步数据。主机和设备间的内存按需分配以提高内存使用效率。
约定用于处理图像数据的Blob的维度是:N × K(channel) × H(height) × W(width). Blob内存是按行优先方式(row-major)存储的,所以最下边和最右边(last/rightmost)的维度变化很快。例如:一个4维Blob(n,k,h,w)的物理内存地址为:((nK+k)H+h)*W+w。
- N是一次处理数据的大小,对计算和设备处理来说,批量处理数据能获得更好的输出。
- K是特征维度,例如:对RGB图像,K=3。
尽管Caffe示例中许多Blob的图像应用都是4维的,但Blob对非图像应用也是完全有效的。例如,如果你只需要像传统的多层感知器那样的全连接网络,可以使用2维Blob(N,D)并且调用InnerProductLayer。
参数Blob维度根据网络层的类型和配置而有所不同。对3输入,96个11*11滤波器(filter)的卷积层,其Blob是96 × 3 × 11 × 11。对1024个输入通道(input channel),1000个输出通道(output channel)的内部的全连接层,其Blob为:1000 × 1024。
对于自定义数据,可能有必要写自己的输入准备代码或数据层。然而,一旦数据搞定。蹭的模块化将完成剩下的工作。
实现细节
Blob在内存中存储了两块数据:data
和diff
,data
是我们传递的正常数据,diff
是网络计算的梯度。
此外,由于实际值可以存储在CPU和GPU上,因此有两种不同的方式来处理他们:the const way
:不改变值;the mutable way
:改变值。
const Dtype* cpu_data() const; // the const way
Dtype* mutable_cpu_data(); // the mutable way
// 对GPU和diff也是一样的
这样设计的原因是Blob使用SyncedMem
类在CPU和GPU之间同步数据,以隐藏实现细节并最小化数据传输量。一个经验法则是如果不需要改变值的话总是使用const
来调用,并且永远不要在自己的对象中存储指针。每次使用Blob时,调用函数来获得指针,因为SynceMen
将需要这个来确定何时复制数据。
在实践中,当GPU存在时,将数据从磁盘加载到CPU代码中的Blob,调用内核设备来执行GPU计算,并将Blob运送到下一层,在保持高层性能的同时忽略低层细节、只要所有层都有GPU的实现,所有中间数据和梯度都会保存在GPU中。
如果想查看Blob何时复制数据,下面是解说示例。
//Assuming that dat aera on the CPU initialluy, and we have a blob.
const Dtype* foo
Dtype* bar;
foo = blob.gpu_data(); //data copied cpu->gpu.
foo = blob.cpu_data(); //no data copied since both have up-to-data contens.
bar = blob.mutable_gpu_data(); //no data copied.
//... some operations ...
bar = blob.mutable_gpu_data(); //no data copied when we are still on GPU
foo = blob.cpu_data(); //data copied gpu->cpu, since the gpu side has moditied the data
foo = blob.gpu_data(); //no data copied since both have up-to-data contens
bar = blob.mutable_cpu_data(); // still no data copied.
bar = blob.mutable_gpu_data(); // data copied cpu->gpu.
bar = blob.mutable_cpu_data(); // data copied gpu->cpu.
Layer计算和连接
Layer是Caffe最重要的部分,也是计算的基本单元,Layer层中可以进行很多运算:
convolve filters 卷积层
pool 池化层
inner products 内积运算
apply nonlinearities like rectified-linear and sigmoid 进行非线性映射,例如relu函数和sigmoid函数
normalize 归一化
load data 载入数据
compute losses like softmax and hinge** 计算损失函数,例如softmax和hinge
可以在Layer页面查看所有运算。包括了所有深度学习任务所涉及的最先进的类型。
每个层定义了了三种重要的计算:初始化(setup)、前向迭代(forward)、后向迭代(backward)。
- setup:模型初始化时初始化Layer及其连接。
- forward:从bottom接受输入,进行计算后,从top输出。
- backward:从top获得输出,计算梯度,并传递到bottom。
更具体的说,将会实现两个前向和后向功能,一个用于CPU,一个用于GPU。如果没有实现GPU版本,Layer将会作为备份退回到CPU功能。虽然会带来而外的输出传输成本,但如果想快速实验,可能会派上用场。
Layer在整个网络操作中有两个主要责任:前向通道获得输入,计算产生输出;反向通道获得输出的梯度,和关于参数参数的梯度传递给输入,进而传播到更早的层次(此句翻译还需斟酌)。这些传播只是每层前向和后向的结构。
开发自定义层只需很少的工作量,主要是修改网络结构和模块代码;为层定义setup、forward、backward后就可以加入到网络中了。
Net定义和操作
网络通过组合和自动差分定义了一个函数,每层输出的组合完成计算给定任务的功能,每层后向迭代的组合从损失中计算梯度来学习任务。Caffe模型是端到端的机器学习引擎。
网络是一组连接在计算图中的层------一个有向无环图(DAG)。Caffe记录所有层的DAG来保证前向传播和反向传播的正确性。一个典型的网络始于数据层,该层从磁盘载入数据,终于损失层,该层计算诸如分类和重建等任务。
Net被定义为一组层及其连接,用一种明文建模语言描述(a plaintext modeling language)。一个简单的逻辑分类回归器如下图所示。
逻辑分类回归器
定义如下
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;然后调用层的SetUp()
函数。还做一些其他的记录,如验证所有网络结构的正确性。此外,初始化期间网络通过日志记录其初始化过程:
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
注意网络的构建设备是未知的,回想先前的解释,Blob和Layer在模型定义中隐藏了实现细节。构建完成后,网络通过设置在Caffe::mode()
中定义的Caffe::set_mode()
来运行在CPU或GPU上。Layer在CPU和GPU模式下运算的结果是一致的。CPU和GPU和实现无缝切换,与模型定义无关。
model格式
模型在prototxt中定义,而学习模型被序列化为binaryproto,位于***.caffemodel文件中。模型格式由caffe.proto中的protobug模式定义。
网友评论