首页 > 编程知识 正文

caffe是什么,caffe官网

时间:2023-05-06 16:25:48 阅读:220607 作者:2395

Blobs,Layers与Nets

BlobsLayers与Nets BlobsLayersNets后记

参考链接:http://caffe.berkeleyvision.org/tutorial/net_layer_blob.html

Caffe主要就是有Blobs,Layers与Nets三部分组成,当然少不了Caffe内部的自动化机制,但是对于使用者来说,与我们密切相关的就是本文要介绍的这三个数据结构。

Blobs

Caffe里面的数据存储的结构就是blob,它把用作前向传播的数据以及后向传播的梯度都保存了起来。再数学层面上,一个blob是一个内存分布连续的N维数组。

比如,对一个batch size为N,具有C通道的图像数据,blob的尺寸是N*C*H*W,那么如果下标为(n,c,h,w)的数据就存放在相对偏移为 n∗N∗C∗W+c∗C∗W+h∗W+w=((n∗C+c)∗H+h)∗W+w 的内存空间里,由此我们也可以看到,blob如果顺序访问最右边的维会是最快的,因为空间连续。

当然,Blob不仅仅用作图像计算,也可以用作非图像计算,这根据数据,相应的调整Blob的维数即可。

使用Blob的原因很大程度上是为了隐藏数据传输与同步的细节,在深度学习里,我们通常会用到CPU和GPU(CPU是无论如何都需要的),然而CPU和GPU的内存并非共享的,因此,在做GPU计算时,我们需要通过CPU发送指令,把内存的数据传输到GPU的内存里(GPU就像是一个军队,下达命令的永远是它的头CPU),然后GPU开始计算并更新了GPU内存的数据,此时CPU与GPU的数据就发生了分歧,这时就需要同步两边的数据,很简单的,就是把数据从GPU的内存又拷贝回CPU的内存去(仍然是CPU下指令)。这样的过程反反复复,对于编程者来说这是个非常头疼的事,因为编程者需要时时刻刻记住数据在什么时候可能会被修改,什么时候需要将数据进行同步等。为了解决这个问题,Caffe的blob引入了四种数据访问方式。
- const CPU: const Dtype* cpu_data() const;
- const GPU: const Dtype* gpu_data() const;
- mutable CPU: Dtype* mutable_cpu_data();
- mutable GPU: Dtype* mutable_gpu_data();

顾明思议,const的访问模式是只读的,因此如果我们不需要对数据进行修改,就尽可能的用只读,而mutable是可读可写,当执行了mutable的访问方式,Caffe会认为数据已经被修改,并在下次访问不同步的数据时自动更新。下面是来自Caffe官网的例子。

// 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 datafoo = blob.gpu_data(); // no data copied since both have up-to-date contentsbar = 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,那么即使数据并没做任何修改,在切换到另一个模式下(CPU/GPU切换)的时候,数据都会进行重新的拷贝,当然这个过程是对我们不可见的,我们并不担心会有差错,但是我们要注意,能避免mutable就避免mutable。其次我们还需要注意的是,由于数据访问的形式是以指针的形式,我们不应该在自己的创造的数据结构里面存储这些指针,因为blob同步的时候很有可能会迁移数据,而导致之前的指针悬空,因此,需要用的时候,请通过blob来获得指针,就如上面的例子所示。

Layers

在Caffe里,绝大多数的运算发生在layer里面。一个layer可以接收下面层的blobs的data,然后通过forward计算的过程更新上层的新的blobs,也可以接收上层传来的blobs的diff,然后通过backward计算的过程更新下层blob数据。直观来看,一个典型的结果如下图所示

在Caffe中,网络的结构是从下往上的。

从实现的角度来说,一个layer的实现必须包含三个方法

setupforwardbackward

其中setup方法在网络进行初始化的时候会被统一调用,用作参数的设置等。forward方法会在做前向计算的时候调用,backward方法会在做后向传播的时候调用。更具体的,forward和backward还需实现GPU版本的,否则当计算模式处于GPU的时候,会先退回到CPU里面执行,结束后在转回GPU,这个过程也是我们不可见的,但是,我们知道,这样会存在内存同步的问题,会减慢执行效率。


Nets

Caffe通过Net将blobs和layers以有向无环图的形式组合起来,注意一定是无环的,否则将无法进行后向传播。
在定义Net的时候,是通过protobuf格式定义的。protobuf是由谷歌提出的数据传输的格式,类似于json。下面引用官网的例子。

这样的一个logistc classification问题,我们可以通过下面的protobuf定义

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()执行的,在执行Init的时候有向无环图的的所有结构都会被创建(在创layer的时候会调用setup,大家还记得吗?)。
下面是在初始化上面的例子的log信息。

I0902 22:52:17.931977 2079114000 net.cpp:39] Initializing net from parameters:name: "LogReg"[...model prototxt printout...]# construct the network layer-by-layerI0902 22:52:17.932152 2079114000 net.cpp:67] Creating Layer mnistI0902 22:52:17.932165 2079114000 net.cpp:356] mnist -> dataI0902 22:52:17.932188 2079114000 net.cpp:356] mnist -> labelI0902 22:52:17.932200 2079114000 net.cpp:96] Setting up mnistI0902 22:52:17.935807 2079114000 data_layer.cpp:135] Opening leveldb input_leveldbI0902 22:52:17.937155 2079114000 data_layer.cpp:195] output data size: 64,1,28,28I0902 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 ipI0902 22:52:17.938617 2079114000 net.cpp:394] ip <- dataI0902 22:52:17.939177 2079114000 net.cpp:356] ip -> ipI0902 22:52:17.939196 2079114000 net.cpp:96] Setting up ipI0902 22:52:17.940289 2079114000 net.cpp:103] Top shape: 64 2 (128)I0902 22:52:17.941270 2079114000 net.cpp:67] Creating Layer lossI0902 22:52:17.941305 2079114000 net.cpp:394] loss <- ipI0902 22:52:17.941314 2079114000 net.cpp:394] loss <- labelI0902 22:52:17.941323 2079114000 net.cpp:356] loss -> loss# set up the loss and configure the backward passI0902 22:52:17.941328 2079114000 net.cpp:96] Setting up lossI0902 22:52:17.941328 2079114000 net.cpp:103] Top shape: (1)I0902 22:52:17.941329 2079114000 net.cpp:109] with loss weight 1I0902 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 outputsI0902 22:52:17.941800 2079114000 net.cpp:208] This network produces output loss# finish initialization and report memory usageI0902 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::set_mode(..)即可切换计算模式。

后记

毕竟小编也是刚学,有很多东西可能并不是讲得十分到位,希望读者如果有什么异议或建议都可以跟小编我讲,大家一起学习~:)下一节会介绍下Caffe的Solver~

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。