Caffe 初学拾遗(三) Blobs, Layers, and Nets

17 篇文章 0 订阅

{承接CNN学习入门,笔者在这里对Caffe官方网站上的相关介绍进行了翻译总结,欢迎大家交流指正}

本文基于此刻最新的release,Caffe-rc3


Introduction:

Deep Networks是一种组合模型,自然而然的由处理成块数据的交织层组合而成。

Caffe依照自身的模型架构(.prototxt),逐层的定义网络,自下而上(bottom to top),自数据输入层(input data)到损失函数层(loss)。

数据(data)以及偏导数(derivatives)以forward pass和backward pass的形式在网络中传播。

而Caffe以blob类的形式存储,处理和通信这些数据。对于框架而言,blob是一种标准化的序列存储形式,也提供了一种统一的内存操作接口。


Layer是模型及计算的基础,而Net又是Layer的堆叠与连接后的集合体。

blob类的实现细节交代了data以及derivatives是如何在Layer中存储又是如何在Net中传播的。


Blob storage and communication:

Blob是在Caffe中被处理和传递的真实数据的一种封装,同时在后台实现数据在CPU及GPU间的同步。

从数学角度来说,blob是以C风格连续存储的N维数组


Blob提供了可以hold住,比如batches of images,模型参数(cpu_data/gpu_data),优化目标的偏导(cpu_diff_data/gpu_diff_data)等数据的统一的内存接口。并且对用户屏蔽了混合了CPU与GPU的操作,为了实现同步,需要将数据由CPU host迁移到GPU Device上。

内存空间是按需分配的(lazily),保证高效的内存使用。


传统意义上,对应于一个batch的图像数据的Blob的维度应该是number N x channel K x height H x width W,Blob在内存中以行优先形式存储,因此,最右侧下标应变化最快。举例而言,对于4维的Blob,位于坐标(n, k, h, w)处的值,物理上存储在下标 ((n * K + k) * H + h) * W + w处。

E.g.

Number or N 是一组数据的 batch-size,按照batch处理数据,在通信代价以及设备利用率上都将达到较好的效果。

对于ImageNet数据集而言,在training过程设定batch-size=256,那么N就是256。

channel or K 是特征的维度,对于RGB图像而言,其K=3。


需要注意,尽管在Caffe的示例中,很多Blob都是4维的图像相关应用,但是使用blob处理非图像应用也是完全可行的。

举个例子,如果你只需要像传统的多层感知机那种全连接网络,使用2维的blob,并调用Inner Product Layer就可以胜任。


Parameter Blob的维度根据Layer的类型及配置文件确定,对于一个拥有96个三通道且分辨率为11x11卷积核(Kernel == filter)的卷积层而言,其维度为96x3x11x11。对于一个inner product 或者称为 fully-connected 的层来说,其拥有1K个输出通道与1024个输入通道,那么其维度为1000x1024。


对于自定义的输入数据,需要先完成数据预处理或者定义data layer。一旦数据准备就绪,模块化的网络将会完成剩余的工作。


Implementation Details:

由于我们通常关心Blob中的权值与梯度数据,Blob存储了两块内存数据,data与diff。

前者用于pass过程,后者为网络反传时的梯度数据。

除此之外,由于真实数据被存储在CPU Host或GPU Device上,有两种方式去访问他们,const与mutable,顾名思义,只读与可变方式:

const Dtype* cpu_data() const;
Dtype* mutable_cpu_data();
GPU与diff也是类似。


这种设计的原因在于,Blob使用SyncedMem类在CPU Host与GPU Device之间同步数据以最小化数据传输代价。

根据经验,如果你不需要改变数值,请使用const指针访问数据,也请不要存储指针。

任何时候,请通过调用函数来获得指针,SyncedMem类需要依此判断是否需要进行数据拷贝。


在实践中,使用GPU时,CPU部分代码(.cpp)将会将数据由存储调入blob中,继而调用Device Kernel执行GPU运算。

然后将Blob中的数据逐层前向或后向传递。屏蔽了底层细节又维持了较高性能。只要是所有Layer都包含GPU实现(.cu)

那么所有的中间权值与梯度都将被保存在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.
{译者注: 调用mutable函数意味着潜在的memory修改,因此需要考虑是否进行copy}


Layer computation and connections:

Layer是model与计算的核心,包括Convolutional Layer, inner product Layer, Pooling Layer, BN, LRN, Softmax等等用于最新的Deep learning Task的层类型都可以在这里找到。


Input -> bottom connections ->[neurons] ->top connections -> Output


每层都定义了三种重要的计算:setup forward backward

setup : 在model初始化时初始化层参数及连接

Forward : 依据给出的源自bottom的input计算output并传递给top

Backward: 依据给出的源自top output的梯度计算对应于input的梯度,并将其传递给bottom。含权值参数的层将会计算关于其权值参数的梯度,并将其存储在blob中。


具体来说,有两种Forward以及Backward函数,一种应用与CPU,一种应用于GPU,如果GPU版本未定义,那么CPU版本的函数将会作为备份选择。容易验证,但是这将带来额外的数据传输的cost ( input将会由GPU Device拷贝到CPU Host上而output将会由CPU Host再拷贝到GPU Host上 ) 。


开发自定义的Layer,由于网络与代码的模块化将很容易,只需要定义setup forward backward操作,该层将会融入网络中。


Net definition and operation:

网络每层的输出模块计算给定task的目标函数值,反传模块计算关于loss的梯度,Caffe模型是一种端到端的机器学习引擎。


网络是由computation graph(有向无环图DAG)形式连接的Layer的组合。Caffe记录下DAG中的所有层,确保forward与backward的正确性。典型的网络以data layer起始,自存储中装载数据,以loss layer结束,计算比如分类等任务的目标函数。


网络以Layer集合的形式定义,其连接关系以浅显的模型语言说明(.prototxt):

举例来说,简单的logistic regression 分类器:

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() 初始化,主要包含两步:

像搭脚手架一样,根据DAG逐层创建blobs以及layers(在net的整个生存周期中,blob及layer都将被保持),

然后调用layer的Setup()函数。也会打印出一些冗长的信息,比如确定网络的结构正确性。

在初始化过程中,Net将会说明其初始化的进程,利用LOG(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
在网络建立之后,是在CPU上运行还是在GPU上运行只需要简单设定一个switch,其定义在Caffe::mode()中,通过Caffe::set_mode()来设定(译者注:在 solver.prototxt 文件中可以设定mode)。CPU与GPU运行结果应当一致,排除数值精度问题。


Model format:

网络结构模型定义在.prototxt文件中,而网络参数模型则以二进制序列化(binaryproto)的.caffemodel文件形式保存。

caffe.proto文件(/src/caffe/proto目录下)定义了对于网络结构模型的解释方法。

{译者注:对于proto及其序列化二进制存储,可以简单理解为一种依照.prototxt文件对.caffemodel文件进行数据压缩与还原的过程}


了解更多关于Google Proto
























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值