Caffe学习2——Blobs, Layers, and Nets: anatomy of a Caffe model

深度网络是一个组合模型,可以很自然的认为是数据块之间的连接。Caffe是通过layer-by-layer(这里跟tensorflow是不同的)来定义网络模型。该网络模型根据input-data到loss来定义bottom-to-top的网络。Caffe中的数据及其相关导数在网络中前向和后向传播的存储,交流和操作信息,都在blobs中。Caffe的blob是一个标准的数组和统一的内存接口。Layer是模型和计算的基础,网络是描述layers的集合和连接信息。Blob的详细信息描述了如何在层和网之间以及跨层和网络存储和传递信息。

Solving是用来对模型和优化进行解耦的。接下来对这些组成部分的细节部分进行描述。

Blob storage and communication

Blob是Caffe在处理和传递数据之间的包装器,并且提供了CPU和GPU之间的数据传递机制。从数学上讲,blob是存储为C-contiguous形式的N-dimensional的数组。

Caffe存储和交流数据是使用blobs的。Blobs提供保存数据的统一内存接口,例如batches of images, modelparameters, and derivatives for optimization.

Blobs通过根据需要来同步CPU与GPU算子来隐藏了CPU/GPU的计算等开销。内存在主机和设备上按需进行分配(lazily)来使得内存的有效使用。

通常Batches of images的blob维度的大小为number N * channel K * height H * width W. Blob在内存中的存储是以row-major,因此最后/最右边的维度是变化最快。例如,在一个4D的blob中,(n, k, h,w)上的值具体存储在index为((n * K + k) *H + h) * W + w.

  • Number/N是数据batch的大小。Batch可以有更好的通信和设备吞吐量。
  • Channel/K是特征的维度。例如,RGB图片的image K=3。

请注意,尽管Caffe示例中,blob都是用于图像的带有axes的4D结构,但是将它用于非图像的应用也是完全可行的。例如,如果你需要简单全连接网络(传统的多层感知机),使用2D的blobs(shape(N, D)),使用InnerProductLayer就可以实现。

参数blob的尺寸是根据layer的类型和配置是不同的。对于convolution layer有96个filters,且spatial dimension为11x11以及有3个input channel,则该layer的blob的维度为96*3*11*11。对于inner product/fully-connected layer有1000个output channels和1024个input channels,则其blob为1000 * 1024。

对于自定义数据,可能需要将自己的输入数据通过准备好的工具或者数据层进行转换。但是,一旦你的数据准备好,那么你的工作就完成了。剩下的工作,模块化的layers会帮你完成。

Implementation Details

我们通常对blob中的值或者值的梯度感兴趣,因此我们在blob中保存了两块内存,data和diff。data是正常的前向传播的数据,diff是后向传播的梯度。

进一步,数据可以存储在CPU或者GPU中,并且这里提供了两种不同的方法来获取它们:一种是const方法,其不会改变值的大小;另一种是mutable方法,其可以改变值的大小:

Const Dtype*cpu_data() const;

Dtype*mutable_cpu_data();

对于GPU和diff的获取也是类似的。

设计成这样的原因是,Blob使用一个SyncedMem来同步CPU和GPU之间的数据,来隐藏同步的细节以及最小化数据的转化。一个使用的经验是若不想改变数据,通常使用const数据。并且不要在自己的object中存储指针,而是在每一次要对blob进行操作的时候,都通过函数来得到指针,这是因为SyncedMem需要根据这个来确定何时复制数据。

实际上,当存在GPU时,将数据从磁盘加载到CPU中的Blob中,调用设备的内核来进行GPU计算,并将blob数据传送到下一层layer。忽略其中的low-level细节,这可以有高性能的表现。只要所有的layers都使用GPU,所有中间的data和diff都保留在GPU中。

如果想到查看Blob是何时复制数据的,这里给出了一个说明的例子:

// Assuming that data are on the CPUinitially, and we have a blob.

const Dtype* foo;

Dtype* bar;

foo = blob.gpu_data(); // data copiedcpu->gpu.

foo = blob.cpu_data(); // no data copiedsince both have up-to-date contents.

bar = blob.mutable_gpu_data(); // no datacopied.

// ... some operations ...

bar = blob.mutable_gpu_data(); // no datacopied when we are still on GPU.

foo = blob.cpu_data(); // data copiedgpu->cpu, since the gpu side has modified the data

foo = blob.gpu_data(); // no data copiedsince both have up-to-date contents

bar = blob.mutable_cpu_data(); // still nodata copied.

bar = blob.mutable_gpu_data(); // data copiedcpu->gpu.

bar = blob.mutable_cpu_data(); // data copiedgpu->cpu.

Layer computation and connections

Layer是模型的本质,是计算的基础单元。Layers提供convolve filters, pool, take inner products, nonlinearities(rectified-linearand sigmoid)以及elementwise transformations, normalize,load data和计算loss的softmax、hinge。对于各个算子的细节,可以参考layer catalogue。最优的深度学习模型中所需要的算子在Caffe都能够支持。

 

 

Layer通过bottom的连接来获取数据,通过top的连接来输出数据。

每个layer都至少定义了三个计算部分:setup、forward和backward。

  • Setup:在model初始化的时候,初始化该layer以及其的连接关系
  •  Forward:通过初始数据,计算bottom的input得到output,并传给top
  • Backward:通过top output来计算gradient,并传递给bottom。Layer计算每个参数的gradient,并在内部进行存储。

特别地,这里实现了两个forward和backward函数,分别是CPU和GPU版本的。如果你并没实现GPU版本,那么该layer将回退到CPU版本。如果你想进行快速进行试验,这可能会十分方便,但是会带来额外的数据传输(因为会将input数据从GPU拷贝到CPU中,将output从CPU拷贝到GPU中)。

Layers在网络中有两个重要的操作功能:前向部分(使用input计算得到output)以及后向传播(计算output的gradient,计算parameter的gradient,并传递给更早的layer)。这些不好走是每个layer的forward和backward的简单组成。

通过网络的组合和代码的模块化,开发自定义的layer只需要做最小的工作。给layer定义setup,forward和backward,则其就准备好被一个net所包含。

Net definition and operation

Net通过组合layer和自动微分来定义了一个函数以及其的梯度。每个layer的output的组合来拟合function,而通过每个layer的backward所计算的loss的梯度来学习获得function。Caffe的模型是一个end-to-end的machine learning engines。

Net是一个通过computation graph来给定layers连接的layers集合。该连接是一个有向无环图(directed acyclic graph,DAG)。Caffe记录所有DAG中layers的数据,以保证其forward和backward的正确性。一个典型的网络开始于一个data layer,其从磁盘中加载数据,结束语一个loss layer,其计算诸如classification或者reconstruction的目标拟合程度。

Net定义为一个通过文本语言来描述layers的集合,及layers的连接关系。一个简单的逻辑回归分类器为

 可以写成

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()来执行。这个初始化主要进行两个事情:通过创建blob和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

这里需要指出,构造的网络是跟设备无关的——回忆我们之前的解释,blobs和layers隐藏了模型的执行的细节。在构造网络之后,network会执行在CPU或者GPU中,这通过Caffe::set_mode()来设定,Caffe::mode()来查看。Layers里都实现了GPU和GPU版本,两个方式会带来相同的结果(最多会有数值错误,并通过测试来进行保护)。CPU/GPU的切换是无缝的,与模型的定义无关。并且,最好将模型和实施分开。

Model format

模型的相关信息定义在prototxt文件中,学习后模型保存在二进制文件caffemodel文件中。模型格式由caffe.proto中的protobuf模型定义,可以让使用的人进行仔细检查查看。Caffe采用这种方式是由以下优势的:序列化时的最小二进制字符串,高效的序列化,与二进制版本兼容的人类可读文本格式,以及多种语言的高效接口实现,尤其是C ++和Python。 这一切都有助于Caffe建模的灵活性和可扩展性。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值