Caffe Tutorial(Nets,Layers,and Blobs:对caffe模型的结构剖析)

Caffe Tutorial

一、Nets,Layers,and Blobs:对caffe模型的结构剖析

深度网络是组合模型,自然地表示为在大量数据上工作的互联层集合。caffe在其模型结构中通过layer-by-layer方式定义网络。网络采用bottom-to-top的方式从输入数据到损失函数定义整个模型。随着数据和导数经过前向和后向流经网络,Caffe存储,传达和操作信息为blob结构:blob是caffe框架的标准矩阵和统一内存接口。 layer是模型和计算的基础。 网络作为layer的集合和连接。 blob的细节描述了信息在层和网络中如何存储和传输。

单独配置解决方案以解耦建模和优化。

1、blob存储和通信

Blob是Caffe处理和传递的实际数据的封装,并且还提供CPU和GPU之间的同步功能。 在数学上,blob是以C连续方式存储的N维数组。

Caffe使用blob存储和传输数据。 Blob提供了保存数据的统一的存储器接口; 例如图像批量(batch),模型参数和用于优化的导数。Blob根据需要从CPU主机同步到GPU设备来隐藏混合CPU / GPU操作的计算和内存开销。 主机和设备上的内存按需分配以提高内存使用率。针对批量图像数据的常规blob维度是数量N×通道K×高度H×宽度W。blob内存在布局中是行主要的,所以最后/最右尺寸变化最快。 例如,在4D blob中,索引(n,k,h,w)处的值物理上位于索引((n * K + k)* H + h)* W + w。

    数量/ N是数据的批量大小(batchsize)。 批处理为通信和设备处理实现更好的吞吐量。 对于batch为256个图像的ImageNet训练任务N= 256。

     信道/ K是特征维度,例如 对于RGB图像K = 3。

请注意,尽管Caffe示例中的许多blob是针对图像应用程序的4D坐标,但对非图像应用程序使用blob完全有效。 例如,如果您只需要像传统多层感知器那样的全连接网络,就可以使用2Dblob(shape(N,D))并调用InnerProductLayer(我们稍后会介绍)。

参数blob尺寸根据图层的类型和配置而有所不同。 对于具有11×11空间维度和3个输入的96个滤波器的卷积层,blob为96×3×11×11。对于具有1000个输出通道和1024个输入通道的内积/完全连接层,参数blob为1000×1024。对于自定义数据,可能需要采用自己的预处理工具或数据层。 但是,一旦你的数据在你的工作中准备完成。模块化的层设计可以完成剩下的工作。

2、使用细节

由于我们通常对Blob内存储的值和梯度感兴趣,Blob使用两处内存,存储数据和数值差分。 前者是我们传递的正常数据,后者是网络计算的梯度。

此外,由于实际值可以存储在CPU和GPU上,因此有两种不同的访问方式:不改变值的const方式和改变值的mutable方式:

const Dtype* cpu_data() const;
Dtype* mutable_cpu_data();

这种设计的原因是,blob使用SyncedMem类来同步CPU和GPU之间的值,以隐藏同步细节并最大限度地减少数据传输。一个经验法则是,如果你不想改变值,总是使用const调用,并且不要讲指针存储在你自己的对象中。每次你使用blob时,调用函数来获取指针,因为SyncedMem将需要这个来确定何时复制数据。

在实际中,当GPU存在时,将数据从磁盘加载到CPU代码中的blob,调用设备内核来执行GPU计算,并将blob运送到下一层,忽略低层细节,同时保持高层性能。只要所有图层都具有GPU实现,所有中间数据和梯度将保留在GPU中。

如果你想看看Blob何时会复制数据,下面是一个例子:

// Assuming that data are on the CPU initially, 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-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.
foo = blob.cpu_data(); // data copied gpu->cpu, since the gpu side has modified the data
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.
bar = blob.mutable_cpu_data(); // data copied gpu->cpu.

3、层级计算和连接

层是模型的本质和计算的基本单位。层包含滤波器(filter),池化(pool),获取内部数据,应用非线性函数如rectified-liner和sigmoid等元素变换,规范化(normalize),加载数据(load data),计算损失函数(compute losses),如softmax和hinge。

一个图层通过底部连接获取输入,并通过顶部连接进行输出。

每个图层类型定义了三个关键计算:setup,forward和backward。

    setup:在模型初始化时初始化图层及其连接

    forward:给定来自底部的输入计算输出并发送到顶部

    backward:给定梯度w.r.t. 顶部输出计算梯度w.r.t. 到输入并发送到底部。 带参数的图层计算渐变w.r.t. 到其参数并将其存储在内部。

更具体地说,将会实现两个前向和后向功能,一个用于CPU,另一个用于GPU。如果您没有实现GPU版本,该层将作为备份选项回退到CPU功能。如果你想做快速实验,虽然它可能带来额外的数据传输成本(它的输入将从GPU复制到CPU,其输出将从CPU复制回GPU),但这可能会派上用场。

图层对于整个网络的操作有两个主要责任:一个接收输入并产生输出的正向通道,以及相对于输出接收梯度的反向通道,并计算关于参数和输入的梯度,然后反过来传播到更早的层。 这些传递只是每个图层向前和向后的构图。传递过程只是每个层前向和后向的组合。

开发自定义层仅需要极少的工作,包括对网络和代码的模块化组合。定义层的setup\forward\backward,并且将其包含在网络中。

4、网络定义和计算

网络通过组合和自动分化来共同定义函数及其梯度。每层构成的输出计算完成一个给定功能的任务,每层的反向传播计算学习任务中损失的梯度。Caffe模型是端到端的机器学习引擎。

网络是一组连接在计算图中的层 - 一个有向无环图(DAG)。 Caffe完成所有DAG层的簿记工作,以确保前向和反向传播的正确性。 一个典型的网络始于一个数据层,该数据层从磁盘加载并以loss层结束,loss层计算诸如分类或重建等任务目标。

网络被定义为一组明文建模语言中的一组图层及其连接。 一个简单的逻辑回归分类器


定义为

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和图层来支持整个DAG(对于C ++geeks:网络将在其生命周期中保留blob和图层的所有权),并调用图层的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

请注意,网络的构建是设备不可知的 - 回想一下我们先前的解释,blob和图层隐藏了模型定义中的实现细节。构建完成后,通过设置在Caffe :: mode()中定义的单个开关并通过Caffe :: set_mode()设置,可以在CPU或GPU上运行网络。图层带有相应的CPU和GPU例程,可以产生相同的结果(高达数值错误,并通过测试来保护它)。CPU / GPU切换是无缝的,与模型定义无关。对于研究和部署,最好划分模型和实现。

5、模型格式

模型在纯文本协议缓冲区模式(prototxt)中定义,而学习模型被序列化为二进制协议缓冲区(binaryproto).caffemodel文件。
模型格式由caffe.proto中的protobuf模式定义。 源文件主要是不言自明的,因此我们鼓励您检查一下。
Caffe讲述Google协议缓冲区有以下优点:序列化时的最小尺寸二进制字符串,高效序列化,与二进制版本兼容的人类可读文本格式,以及多种语言的高效接口实现,最显着的是C ++和Python。 这一切都有助于Caffe建模的灵活性和可扩展性。





猜你喜欢

转载自blog.csdn.net/littlestudent12/article/details/80838427
今日推荐