caffe架构分析

来源:互联网 发布:中国旅游数据统计 编辑:程序博客网 时间:2024/06/03 15:57

本文摘自知乎深度学习caffe的代码怎么读?的回复。

个人感觉各位大神讲述各有重点和特色,摘录供以后细细品味。感谢大家的互联共享。


1.  形象概述

作者:米八
链接:https://www.zhihu.com/question/27982282/answer/55260722
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

知道了神经网络的结构,forward跟backward是怎样的一个过程,基本上就知道了caffe的结构了。按照神经网络的运行过程看Caffe就好了。

既然说神经网络,那么就得有一个网络出来,caffe里面就用Net这个类来记录这个网络。

那么网络是由很多个layer构成,自然而然就会有Layer这个类,为了统一管理这些类,自然而然就想要抽象,那么Layer这个抽象类作为一个最基本的单元出现了,接下来就会有实现各种功能的layer出现,如:Convolution/ReLU/Softmax等。

Layer间需要连接啊,Layer需要有输入输出啊,caffe里面用Bottom来表示输入,Top表示输出,前一层Layer的top是后一层layer的bottom,这样,连接搞掂了,输入输出也搞掂了。

网络搞掂了,layer搞掂了,那就要搞个求解算法啊,那么Solver这个类就出现了,这个就是用来求解网络的。


2.摊开caffe概述分析

作者:Fiberleif
链接:https://www.zhihu.com/question/27982282/answer/94031317
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

================================================================
Caffe's Abstract Framework:
就像楼上一些回答里说到的,要读懂caffe,首先要熟悉Blob,Layer,Net,Solver这几个大类。这四个大类自下而上,环环相扣,贯穿了整个caffe的结构,下面先分别简单地介绍一下这四个类的主要作用。
  • Blob:作为数据传输的媒介,无论是网络权重参数,还是输入数据,都是转化为Blob数据结构来存储
  • Layer:作为网络的基础单元,神经网络中层与层间的数据节点、前后传递都在该数据结构中被实现,层类种类丰富,比如常用的卷积层、全连接层、pooling层等等,大大地增加了网络的多样性
  • Net:作为网络的整体骨架,决定了网络中的层次数目以及各个层的类别等信息
  • Solver:作为网络的求解策略,涉及到求解优化问题的策略选择以及参数确定方面,修改这个模块的话一般都会是研究DL的优化求解的方向。
================================================================
Caffe's Concrete Framework:
1. Blob:
1.1. Blob的类型描述
Caffe内部采用的数据类型主要是对protocol buffer所定义的数据结构的继承,因此可以在尽可能小的内存占用下获得很高的效率,虽然追求性能的同时Caffe也会牺牲了一些代码可读性。
直观来说,可以把Blob看成一个有4维的结构体(包含数据和梯度),而实际上,它们只是一维的指针而已,其4维结构通过shape属性得以计算出来。
1.2. Blob的重要成员函数和变量
shared_ptr<SyncedMemory> data_ //数据shared_ptr<SyncedMemory> diff_ //梯度
void Blob<Dtype>::Reshape(const int num, const int channels, const int height,const int width)
重新修改Blob的形状(4维),并根据形状来申请动态内存存储数据和梯度。
inline int count(int start_axis, int end_axis) const
计算Blob所需要的基本数据单元的数量。2. Layer:
2.1. Layer的类型描述

Layer是网络模型和计算的核心,在数据存储上,主要分成bottom_vecs、top_vecs、weights&bias三个部分;在数据传递上,也主要分为LayerSetUp、Reshape、Forward、Backward四个过程,符合直观上对层与层之间连接的理解,贴切自然。
2.2. Layer的重要成员函数和变量
vector<Dtype> loss_ //每一层都会有一个loss值,但只有LossLayer才会产生非0的lossvector<shared_ptr<Blob<Dtype> > > blobs_ //Layer所学习的参数,包括权值和偏差
virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,      const vector<Blob<Dtype>*>& top) 
通过bottom Blob对象的形状以及LayerParameter(从prototxt读入)来确定Layer的学习参数(以Blob类型存储)的形状。
 virtual void Reshape(const vector<Blob<Dtype>*>& bottom,      const vector<Blob<Dtype>*>& top) 
通过bottom Blob对象的形状以及Layer的学习参数的形状来确定top Blob对象的形状。
virtual void Forward(const vector<Blob<Dtype>*> &bottom,                                          vector<Blob<Dtype>*> *top) = 0
Layer内部数据正向传播,从bottom到top方向。
virtual void Backward(const vector<Blob<Dtype>*> &top,                      const vector<bool> &propagate_down,                       vector<Blob<Dtype>*> *bottom) = 0
Layer内部梯度反向传播,从top到bottom方向。
3. Net:
3.1. Net的类型描述
Net用容器的形式将多个Layer有序地放在一起,其自身实现的功能主要是对逐层Layer进行初始化,以及提供Update( )的接口(更新网络参数),本身不能对参数进行有效地学习过程。
3.2. Net的重要成员函数和变量
vector<shared_ptr<Layer<Dtype> > > layers_ //构成该net的layersvector<vector<Blob<Dtype>*> > bottom_vecs_ //每一层layer中的bottom Blobsvector<vector<Blob<Dtype>*> > top_vecs_ //每一层layer中的top Blobsvector<shared_ptr<Blob<Dtype> > > params_ //整个net中的learnable parameter
void Init(const NetParameter& param)
根据NetParameter进行net初始化,简单的来说就是先把网络中所有层的bottom Blobs&top Blobs(无重复)实例化,并从输入层开始,逐层地进行Setup的工作,从而完成了整个网络的搭建,为后面的数据前后传输打下基础。
vector<Blob<Dtype>*>& Forward(const vector<Blob<Dtype>* > & bottom,                              Dtype* loss = NULL)void Net<Dtype>::Backward()
是对整个网络的前向和方向传导,各调用一次就可以计算出网络的loss了。
4. Solver
4.1. Solver的类型描述
Solver类中包含一个Net的指针,主要是实现了训练模型参数所采用的优化算法,根据优化算法的不同会派生不同的类,而基于这些子类就可以对网络进行正常的训练过程。
4.2. Solver的重要成员函数和变量
shared_ptr<Net<Dtype> > net_ //net对象
void Step(int iters)
对已初始化后的网络进行固定次数的训练迭代过程。
ComputeUpdateValue();net_->Update();

不同的模型训练方法通过重载函数ComputeUpdateValue( )实现计算update参数的核心功能。
===============================================================
说到这儿,大家可能已经对Caffe的自底向上的主要结构有了一个大致的了解~
除了这些之外呢,Caffe里面还涉及到了许多特色模块,比如google的轻量级高效数据传输工具protobuf、负责stream输出模块的glog和负责命令行便捷输入的gflags、factory宏定义等等...
最后想在这里向caffe的contributors以及caffe blog的博主们表示深深的感激~ 由于本人的C++经验不多,所以是花了较长的时间才大致理解caffe的实现(呜呜TT),但现在回头想来确实收获很大,不管是在代码架构方面,还是在实现细节方面,caffe都有很多值得借鉴的地方,相信大家在阅读完caffe之后也一定会受益匪浅~~~



3. 进阶学习推荐

作者:Gein Chen
链接:https://www.zhihu.com/question/27982282/answer/80242005
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1.学习程序的第一步,先让程序跑起来,看看结果,这样就会有直观的感受。
Caffe的官网上Caffe | Deep Learning Framework 提供了很多的examples,你可以很容易地开始训练一些已有的经典模型,如LeNet。我建议先从 LeNet MNIST Tutorial开始,因为数据集很小,网络也很小但很经典,用很少的时间就可以跑起来了。当你看到terminal刷拉拉的一行行输出,看到不断减少的loss和不断上升的accuracy,训练结束你得到了99+%的准确率,感觉好厉害的样子。你可以多跑跑几个例子,熟悉一下环境和接口。

2.单步调试,跟着Caffe在网络里流动
当玩了几天之后,你对Caffe的接口有点熟悉了,对已有的例子也玩腻了,你开始想看看具体是怎么实现的了。我觉得最好的方法是通过单步调试的方式跟着程序一步一步的在网络里前向传播,然后再被当成误差信息传回来。

Caffe就像一个你平常编程中Project,你可以使用IDE或者GDB去调试它,这里我们不细说调试的过程。你可以先跟踪前向传播的过程,无非就是从高层次到低层次的调用Forward函数, Solver->Net->Layer->Specific Layer (Convolution等...).后向传播也类似,但因为你对Caffe里面的各种变量运算不熟悉,当你跟踪完前向传播时可能已经头晕眼花了,还是休息一下,消化一下整个前向传播的流程。

刚刚开始你没有必要对每个Layer的计算细节都那么较真,大概知道程序的运算流程就好,这样你才可以比较快的对Caffe有个大体的把握。

3.个性化定制Caffe
到这里,你已经可以说自己有用过Caffe了,但是还不能算入门,因为你还不知道怎么修改源码,满足自己特定的需求。我们很多时候都需要自己定义新的层来完成特定的运算,这时你需要在Caffe里添加新的层。

你一开肯定无从下手,脑子一片空白。幸运的是Caffe github上的Wiki Development · BVLC/caffe Wiki · GitHub已经有了教程了,而且这是最接近latest Caffe的源码结构的教程,你在网上搜到的Blog很多是有点过时的,因为Caffe最近又重构了代码。你可以跟着它的指导去添加自己的层。

虽然你已经知道要在哪里添加自己的东西了,但你遇到最核心的问题是如何写下面这四个函数。
  • forward_cpu()
  • forward_gpu()
  • backward_cpu()
  • backward_gpu()
你可以先模仿已有的层去实现这四个函数,而且我相信forward函数很快就可以写出来了,但backward的还是一头雾水。这时我们就要补补神经网络里最核心的内容了——Backpropagation.

4.理解并实现Backpropagation
这个我觉得是与平台无关的,不管你是使用Caffe、Torch 7,还是Theano,你都需要深刻理解并掌握的。因为我比较笨,花了好长时间才能够适应推导中的各种符号。其实也不难,就是误差顺着Chain rule法则流回到前面的层。我不打算自己推导后向传播的过程,因为我知道我没有办法将它表达得很好,而且网上已经有很多非常好的教程了。下面是我觉得比较好的学习步骤吧。
  • 从浅层的神经网络(所谓的全连接层)的后向传播开始,因为这个比较简单,而且现在我们常说的CNN和LSTM的梯度计算也最终会回归到这里。
    • 第一个必看的是Ng深入浅出的Ufldl教程UFLDL Tutorial,还有中文版的,这对不喜欢看英语的同学是个好消息。当然你看一遍不理解,再看一遍,忘了,再看,读个几遍你才会对推导过程和数学符号熟悉。我头脑不大行,来来回回看了好多次。
    • 当然,Ufldl的教程有点短,我还发现了一个讲得更细腻清晰的教程, Michael Nielsen写的Neural networks and deep learning。它讲得实在太好了,以至于把我的任督二脉打通了。在Ufldl的基础上读这个,你应该可以很快掌握全连接层的反向传播。
    • 最后在拿出standford大牛karpathy的一篇博客Hacker's guide to Neural Networks,这里用了具体的编程例子手把手教你算梯度,并不是推导后向传播公式的,是关于通用梯度计算的。用心去体会一下。
  • 这时你跃跃欲试,回去查看Caffe源码里Convolution层的实现,但发现自己好像没看懂。虽说卷积层和全连接层的推导大同小异,但思维上还是有个gap的。我建议你先去看看Caffe如何实现卷积的,Caffe作者贾扬清大牛在知乎上的回答在 Caffe 中如何计算卷积?让我茅塞顿开。重点理解im2col和col2im.
  • 这时你知道了Convolution的前向传播,还差一点就可以弄明白后向传播怎么实现了。我建议你死磕Caffe中Convolution层的计算过程,把每一步都搞清楚,经过痛苦的过程之后你会对反向传播有了新的体会的。在这之后,你应该有能力添加自己的层了。再补充一个完整的添加新的层的教程Making a Caffe Layer • Computer Vision Enthusiast。这篇教程从头开始实现了一个Angle To Sine Cosine Layer,包含了梯度推导,前向与后向传播的CPU和GPU函数,非常棒的一个教程。
  • 最后,建议学习一下基本的GPU Cuda编程,虽然Caffe中已经把Cuda函数封装起来了,用起来很方便,但有时还是需要使用kernel函数等Cuda接口的函数。这里有一个入门的视频教程,讲得挺不错的NVIDIA CUDA初级教程视频

原创粉丝点击