解剖caffe mode:Blobs, Layers和Nets

Implementation Details

一个Blob存储两块内存的数据:data和diff。data是沿着神经网络传递的正常数据,diff则是网络计算的梯度。

另外,实际数值可能存储在CPU或GPU。有两种方式访问数据,对GPU、CPU和diff同样适用:

1、常值访问(const):不改变数值

2、可变访问(mutable): 改变数值

例如,

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

之所以这样设计,是由于Blob使用SyncedMem类来同步CPU和GPU的数值,来隐藏同步化细节和减少数据传输。一种简单并粗略的方法是若不想改变数值,总是使用常值访问,不把指针存储到所拥有的object。每次影响一个Blob,调用函数来得到指针,因为SyncedMem类需要指针来明白什么时候需要复制数据。

实践中,当存在GPUs,在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.
Layer computation and connections

层是模型的本质和计算的基本单元。卷基层、池化层采用内积,并使用非线性等按元素的转换,诸如rectified-linear和sigmoid,归一化,数据加载,类似softmax和hinge来计算损失。最先进的深度学习任务所需的层类别可参考http://caffe.berkeleyvision.org/tutorial/layers.html

一个layer使用bottom连接作为输入,并通过top连接输出。

每一层的type定义三种关键的计算:setup, forward, backward

  • setup: 在模型初始化时,初始化该层和相关连接一次
  • forward:使用来自bottom的输入来计算输出,并发送给top
  • backward: 若给出关于top输出的梯度,计算关于输入的梯度并发送到bottom。具有参数的一层计算关于参数的梯度,并内部存储。

更具体的说,要实现两个Forward和backward函数,分别在CPU和GPU。若不实现GPU版本,layer转而求助于CPU函数作为后备选项。若你想要做快速实验,这将会派上用场,但是会带来额外的数据传输成本(输入将会从CPU复制到GPU,输出将会从CPU复制回GPU)。

对网络运行来说,layer有两个关键的责任:

  • forward pass:使用输入计算输出
  • backward pass:使用关于输出的梯度(误差敏感度),来计算关于参数和关于输入的梯度,并一次反向传播到更早的层。

这些传递是每一层前向和反向的组成部分。

通过使用网络的组合性和代码的模块化,开发自定义层轻而易举的完成。对一层定义setup, forward, backward,就是做好了准备放入网络。

Net definition and operation

网络net共同的定义一个函数和他的梯度通过组合和自动差分。对于给定的任务,每一层输出的组合计算函数,并且每一层反向的组合从损失计算梯度来学习任务。Caffe模型是端到端机器学习引擎。

Net是在计算图中连接的层集合-准确来说,是有向非循环图DAG。对于层的DAG,caffe做所有的统计来确保前向和反向传递的正确性。一个典型的net在数据层开始(从磁盘中加载数据),在损失层结束(计算诸如分类或重建等任务的目标函数)。net定义为层集合并且连接定义为普通文本的模型化语言。一个简单的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()操作模型初始化,初始化主要做两件事情:

  1. 搭建整个DAG的支架:创建blobs和layer(对于C++怪才们,网络将会保留blobs和layers的终生所有权)
  2. 调用层的setup()函数:
  3. 也会做其他统计事情,来保证整个网络架构的正确性

在网络初始化期间,网络通过记录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的解释,他们从模型定义上隐藏了实现细节。网络构建后是运行在CPU还是GPU上,通过在Caffe::mode()中定义的单极开关来设置,可通过Caffe::set_mode()设置。对应于CPU和GPU例程的层产生相同的结果(最多数值错误,并有测试来保护)。CPU/GPU转换是无缝的,并且独立于模型定义。对于研究和部署,最好划分模型和实现。
Model format

模型使用纯文本协议缓冲区格式(prototext)定义,同时学习的模型序列化为二进制协议缓冲区文件.caffemodel(binaryproto).

模型格式被caffe.proto的协议格式定义模型格式。源文件大多有说明,建议看看。

caffes使用Google协议缓冲具备下述优点,对于caffe模型的灵活性和可扩展性都有贡献:

  1. 序列化所拥有的最小尺寸的二进制字符串
  2. 高效的串行化(序列化)
  3. 与二进制版本兼容的可读文本格式
  4. 多种语言的高效接口实现,最突出的C++/Python

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值