知乎用户 ,Caffe / Torch7 / CNN
知乎用户、任逸风、陈文 等人赞同

我是从Torch7转移到Caffe的人,仅供参考,当你阅读前你应该具备一些有关DL的基础知识,本文集中写Caffe代码结构而非介绍DL知识。
我是去年底开始看Caffe代码的,看代码的时间加在一起也不到一个月,也算半个新手,我的回答是从新手角度作一个入门阶段的经验分享。

本文只涉及Caffe结构的相关问题,不涉及具体实现技巧等细节。

==============================================================

1. 初识Caffe
1.1. Caffe相对与其他DL框架的优点和缺点:
优点:

  • 速度快。Google Protocol Buffer数据标准为Caffe提升了效率。
  • 学术论文采用此模型较多。不确定是不是最多,但接触到的不少论文都与Caffe有关(R-CNN,DSN,最近还有人用Caffe实现LSTM)

缺点:

  • 曾更新过重要函数接口。有人反映,偶尔会出现接口变换的情况,自己很久前写的代码可能过了一段时间就不能和新版本很好地兼容了。(现在更新速度放缓,接口逐步趋于稳定,感谢 评论区王峰的建议)
  • 对于某些研究方向来说的人并不适合。这个需要对Caffe的结构有一定了解,(后面提到)。

1.2. Caffe代码层次。
回答里面有人说熟悉Blob,Layer,Net,Solver这样的几大类,我比较赞同。我基本是从这个顺序开始学习的,这四个类复杂性从低到高,贯穿了整个Caffe。把他们分为三个层次介绍。

  • Blob:是基础的数据结构,是用来保存学习到的参数以及网络传输过程中产生数据的类。
  • Layer:是网络的基本单元,由此派生出了各种层类。修改这部分的人主要是研究特征表达方向的。
  • Net:是网络的搭建,将Layer所派生出层类组合成网络。Solver:是Net的求解,修改这部分人主要会是研究DL求解方向的。

==============================================================

2. Caffe进阶
2.1. Blob:

Caffe支持CUDA,在数据级别上也做了一些优化,这部分最重要的是知道它主要是对protocol buffer所定义的数据结构的继承,Caffe也因此可以在尽可能小的内存占用下获得很高的效率。(追求性能的同时Caffe也牺牲了一些代码可读性)
在更高一级的Layer中Blob用下面的形式表示学习到的参数:

vector<shared_ptr<Blob<Dtype> > > blobs_;

这里使用的是一个Blob的容器是因为某些Layer包含多组学习参数,比如多个卷积核的卷积层。
以及Layer所传递的数据形式,后面还会涉及到这里:

vector<Blob<Dtype>*> &bottom;
vector<Blob<Dtype>*> *top

2.2. Layer:
2.2.1. 5大Layer派生类型
Caffe十分强调网络的层次性,也就是说卷积操作,非线性变换(ReLU等),Pooling,权值连接等全部都由某一种Layer来表示。具体来说分为5大类Layer

  • NeuronLayer类 定义于neuron_layers.hpp中,其派生类主要是元素级别的运算(比如Dropout运算,激活函数ReLu,Sigmoid等),运算均为同址计算(in-place computation,返回值覆盖原值而占用新的内存)。
  • LossLayer类 定义于loss_layers.hpp中,其派生类会产生loss,只有这些层能够产生loss
  • 数据层 定义于data_layer.hpp中,作为网络的最底层,主要实现数据格式的转换。
  • 特征表达层(我自己分的类)定义于vision_layers.hpp(为什么叫vision这个名字,我目前还不清楚),实现特征表达功能,更具体地说包含卷积操作,Pooling操作,他们基本都会产生新的内存占用(Pooling相对较小)。
  • 网络连接层和激活函数(我自己分的类)定义于common_layers.hpp,Caffe提供了单个层与多个层的连接,并在这个头文件中声明。这里还包括了常用的全连接层InnerProductLayer类。

2.2.2. Layer的重要成员函数
在Layer内部,数据主要有两种传递方式,正向传导(Forward)反向传导(Backward)。Forward和Backward有CPU和GPU(部分有)两种实现。Caffe中所有的Layer都要用这两种方法传递数据。

virtual void Forward(const vector<Blob<Dtype>*> &bottom,                     vector<Blob<Dtype>*> *top) = 0;
virtual void Backward(const vector<Blob<Dtype>*> &top,const vector<bool> &propagate_down, vector<Blob<Dtype>*> *bottom) = 0;

Layer类派生出来的层类通过这实现这两个虚函数,产生了各式各样功能的层类。Forward是从根据bottom计算top的过程,Backward则相反(根据top计算bottom)。注意这里为什么用了一个包含Blob的容器(vector),对于大多数Layer来说输入和输出都各连接只有一个Layer,然而对于某些Layer存在一对多的情况,比如LossLayer和某些连接层。在网路结构定义文件(*.proto)中每一层的参数bottom和top数目就决定了vector中元素数目。

layers {bottom: "decode1neuron"   // 该层底下连接的第一个Layerbottom: "flatdata"        // 该层底下连接的第二个Layertop: "l2_error"           // 该层顶上连接的一个Layername: "loss"              // 该层的名字type: EUCLIDEAN_LOSS      // 该层的类型loss_weight: 0
}

2.2.3. Layer的重要成员变量
loss

vector<Dtype> loss_;

每一层又有一个loss_值,只不多大多数Layer都是0,只有LossLayer才可能产生非0的loss_。计算loss是会把所有层的loss_相加。
learnable parameters

vector<shared_ptr<Blob<Dtype> > > blobs_;

前面提到过的,Layer学习到的参数。
2.3. Net:
Net用容器的形式将多个Layer有序地放在一起,其自身实现的功能主要是对逐层Layer进行初始化,以及提供Update( )的接口(更新网络参数),本身不能对参数进行有效地学习过程。

vector<shared_ptr<Layer<Dtype> > > layers_;

同样Net也有它自己的

vector<Blob<Dtype>*>& Forward(const vector<Blob<Dtype>* > & bottom,Dtype* loss = NULL);
void Net<Dtype>::Backward();

他们是对整个网络的前向和方向传导,各调用一次就可以计算出网络的loss了。
2.4. Solver
这个类中包含一个Net的指针,主要是实现了训练模型参数所采用的优化算法,它所派生的类就可以对整个网络进行训练了。

shared_ptr<Net<Dtype> > net_;

不同的模型训练方法通过重载函数ComputeUpdateValue( )实现计算update参数的核心功能

virtual void ComputeUpdateValue() = 0;

最后当进行整个网络训练过程(也就是你运行Caffe训练某个模型)的时候,实际上是在运行caffe.cpp中的train( )函数,而这个函数实际上是实例化一个Solver对象,初始化后调用了Solver中的Solve( )方法。而这个Solve( )函数主要就是在迭代运行下面这两个函数,就是刚才介绍的哪几个函数。

ComputeUpdateValue();
net_->Update();

==============================================================

至此,从底层到顶层对Caffe的主要结构都应该有了大致的概念。为了集中重点介绍Caffe的代码结构,文中略去了大量Caffe相关的实现细节和技巧,比如Layer和Net的参数如何初始化,proto文件的定义,基于cblas的卷积等操作的实现(cblas实现卷积这一点我的个人主页GanYuFei中的《Caffe学习笔记5-BLAS与boost::thread加速》有介绍)等等就不一一列举了。

整体来看Layer部分代码最多,也反映出Caffe比较重视丰富网络单元的类型,然而由于Caffe的代码结构高度层次化,使得某些研究以及应用(比如研究类似非逐层连接的神经网络这种复杂的网络连接方式)难以在该平台实现。这也就是一开始说的一个不足。

另外,Caffe基本数据单元都用Blob,使得数据在内存中的存储变得十分高效,紧凑,从而提升了整体训练能力,而同时带来的问题是我们看见的一些可读性上的不便,比如forward的参数也是直接用Blob而不是设计一个新类以增强可读性。所以说性能的提升是以可读性为代价的。
最后一点也是最重要的一点,我从Caffe学到了很多。第一次看的C++项目就看到这么好的代码,实在是受益匪浅,在这里也感谢作者贾扬清等人的贡献。

甘宇飞更新于2/13/2015

编辑于 2015-02-13 36 条评论 感谢

分享

收藏 • 没有帮助 • 举报 • 作者保留权利

更多回答
Gein Chen ,Deep Learning/Linux/Vimer
yuncheng lu、爱吃麻辣菠萝、张小璐 等人赞同
楼上的大神回答的都很好了,非常感谢。这里我想说一下我自己学习caffe的方式,限于时间篇幅,并不想深入到具体的实现细节,只是从大的方向上谈谈,因为讲清楚一个细节都是一篇博客的篇幅了。

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初级教程视频。

第二个回答,讲得还是一样的乱,谢谢观看。

发布于 2016-01-06 21 条评论 感谢

分享

收藏 • 没有帮助 • 举报 • 作者保留权利

41赞同 反对,不会显示你的姓名
米八 ,人脸检测/关键点定位/识别
吴昊、白世明、知乎用户 等人赞同
其实关于怎么用caffe,我一直想写篇文章,因为给太多人讲过怎么用了,也帮很多人基于caffe写过代码。14年初因为赶NIPS,开始用caffe,大概用了有一年半了。

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

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

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

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

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

就酱紫,有时间再写得详细一点。反正就这么一句话:按照神经网络的运行过程看Caffe就好了。


from: http://www.zhihu.com/question/27982282/answer/39350629

深度学习caffe的代码怎么读?相关推荐

  1. java编程石头剪刀布图片_石头、剪刀、布!10分钟带你打开深度学习大门,代码已开源...

    原标题:石头.剪刀.布!10分钟带你打开深度学习大门,代码已开源 沉沉 发自 宇宙中心 量子位 出品 | 公众号 QbitAI 深度学习技术的不断普及,越来越多的语言可以用来进行深度学习项目的开发,即 ...

  2. caffe模型文件解析_深度学习 Caffe 初始化流程理解(数据流建立)

    深度学习 Caffe 初始化流程理解(数据流建立) 之前在简书的文章,搬迁过来 ^-^ 本文是作者原创,如有理解错误,恳请大家指出,如需引用,请注明出处. #Caffe FeatureMap数据流的建 ...

  3. 【每周CV论文】初学深度学习图像修复应该要读的文章

    欢迎来到<每周CV论文>.在这个专栏里,还是本着有三AI一贯的原则,专注于让大家能够系统性完成学习,所以我们推荐的文章也必定是同一主题的. 图像修复(image inpainting)或补 ...

  4. 循环神经网络matlab程序设计,神经网络及深度学习(包含matlab代码).pdf

    神经网络及深度学习(包含matlab代码) 神经网络及深度学习 (包含 MATLAB 仿真) 人工神经网络(Artificial Neural Network,即 ANN ), 作为对人脑最简单的一种 ...

  5. 【HSI】高光谱的数据集分类深度学习实战及代码理解

    [HSI]高光谱的数据集分类深度学习实战及代码理解 文章目录 [HSI]高光谱的数据集分类深度学习实战及代码理解 一.配置文件编写 二.高光谱图像的处理 2.1图像数据变换 2.2 数据整合 2.3 ...

  6. 深度学习怎么跑代码?

    深度学习怎么跑代码?从事深度学习的研究者都知道,深度学习代码需要设计海量的数据,需要很大很大很大的计算量,以至于CPU算不过来,需要通过GPU帮忙,今天怎么教大家免费使用GPU跑深度学习代码. 深度学 ...

  7. 深度学习在恶意代码检测方面的应用简单调研

    随着互联网的繁荣,现阶段的恶意代码也呈现出快速发展的趋势,主要表现为变种数量多.传播速度快.影响范围广.在这样的形势下,传统的恶意代码检测方法已经无法满足人们对恶意代码检测的要求.比如基于签名特征码的 ...

  8. 六种人体姿态估计的深度学习模型和代码总结

    六种人体姿态估计的深度学习模型和代码总结 姿态估计的目标是在RGB图像或视频中描绘出人体的形状,这是一种多方面任务,其中包含了目标检测.姿态估计.分割等等.有些需要在非水平表面进行定位的应用可能也会用 ...

  9. 深度学习caffe(4)——caffe配置(GPU)

    电脑:win7  64位,NVIDIA GeForce GTX1080 Ti,visual studio 2013. 深度学习caffe(1)--windows配置caffe(vs2013+pytho ...

最新文章

  1. python uiautomation选择list内容_使用python UIAutomation从QQ2017(v8.9)群界面获取所有群成员详细资料,...
  2. TSNE 有代码实现有附3D 2D效果图
  3. 飙着车学「机器学习」?要不是380万人围观我都不敢相信
  4. 【热烈祝贺】俺们的S5PV210 工控板终于启动到wince桌面了!
  5. MDP 笔记:Value Iteration
  6. IOS atomic与nonatomic,assign,copy与retain的定义和区别
  7. sql服务找不到服务器,我找不到SQL服务管理器
  8. java 常量区存放 new_java常量池与对象存储
  9. python输入print跳到documentation-习题 48: 更复杂的用户输入
  10. 倒果汁c语言,水果榨汁补维生素C?这些补维生素的错误别再犯了
  11. python程序gpu运行时间表_python gpu任务及时调度
  12. Python开发环境Anaconda3使用指南(入门篇)
  13. android工具栏隐藏和显示不出来了,Android工具栏操作按钮未显示
  14. mysql econnreset_javascript - 节点Js mysql(和mysql2)ECONNRESET - 堆栈内存溢出
  15. oracle12c dba或者sys身份的账户和密码,怎么一次性安装好oracle 12c依赖包
  16. vs2008怎么创建c语言程序,VS2008的使用
  17. C#实现微信自动发送消息
  18. PHP发币,处理货币价值的PHP库
  19. android前置拍照镜像代码,在Android中镜像前置摄像头
  20. 基于eNSP华为模拟器的VRRP简单实验

热门文章

  1. 传统金融PK互联网 必须透过现象看本质
  2. 互联网思维与非摩擦经济
  3. c语言函数之间互相调用符号,如何实现C和C++函数互相调用
  4. 寻宝机器人科技竞赛_第19届广西青少年机器人竞赛组织工作筹备会暨广西青少年科技辅导员交流活动在贺州举行...
  5. APM - 零侵入监控JDBC服务
  6. 爬虫学习笔记(二十一)—— Appium
  7. java dataurl_java url参数去重
  8. python mql4_可以转发文章不?老外用随机森林预测外汇分钟涨跌准确率高达85%。求高手用python重写。...
  9. 解决IDEA2020.1版本不兼容Lombok的问题
  10. Halcon知识: XLD的概念