(四个类复杂性由低到高): - Blob:是基础的数据结构,是用来保存学习到的参数以及网络传输过程中产生数据的类。 - Layer:是网络的基本单元,由此派生出了各种层类。修改这部分的人主要是研究特征表达方向的。 - Net:是网络的搭建,将Layer所派生出层类组合成网络。 - Solver:是Net的求解,修改这部分人主要会是研究DL求解方向的。
caffe支持CUDA,在数据级别上也做了一些优化,重要的是对protocolbuffer(Google 公司内部的混合语言数据标准,是一种轻便高效的结构化数据存储格式,简称Protobuf)所定义的数据结构的继承,Caffe因此可以在尽可能小的内存占用下获得很高的效率,但是牺牲了一些代码可读性 在更高一级的Layer中Blob用下面的形式表示学习到的参数: vector<shared_ptr<Blob<Dtype>>>blobs_; 这里使用的是一个Blob容器,某些Layer包含多组学习参数,比如多个卷积核的卷积层。以及Layer所传递的数据形式,后面还有涉及:
vector<Blob<Dtype>*> ⊥ vector<Blob<Dtype>*> *top;1、5大Layer派生类型 Caffe十分强调网络层次性,也就是说卷积操作,非线性变换(ReLU等),Pooling,权值连接等。全部都由某一种Layer来表示。
具体分为5大类Layer:
NeuronLayer类定义于neuron_layers.hpp中,其派生类主要是元素级别的运算(eg. Dropout运算,激活函数ReLU,Sigmoid等),运算均为同址计算(in-place computation,返回值覆盖原值而占用新的内存)LossLayer类定义于loss_layers.hpp中,其派生类会产生loss,只有这些层才能够产生loss数据层定义于data_layer.hpp中,作为网络的最底层,主要实现数据格式的转换数据表达层(转的作者自己分的类)定义于vision_layers.hpp,实现特征表达功能,更具体地说包含卷积操作,Pooling操作,他们基本都会产生新的内存占用(Pooling相对较小)网络连接层和激活函数(转的作者分类)定义于common_layers.hpp,Caffe提供了单个层与多个层的连接,并在这个头文件里声明。这里包含了常用的全连接层InnerProductLayer类。2、Layer的重要成员函数 在Layer内部,数据主要有两种传递方式,正向传导(Forward)和反向传导(Backward)。 Forward和Backward有CPU和GPU(部分有)两种实现。Caffe中所有的Layer都要用这两种方法传递数据。
vitural void Forward(const vector<Blob<Dtype>*> &bottom, vector<Blob<Dtype>*> *top) = 0; virtual void Backward(const vector<Blob<Dtype>*> &top, const vector<Blob> &propagate_down, vector<Blob<Dtype>*> *bottom) = 0;ps:Bottom来表示输入,Top表示输出。前一层Layer的top是后一层layer的bottom。
Layer类派生出来的层类通过这实现这两个虚函数,产生了各种各样功能的层类。Forward是从根据bottom计算top的过程,Backward则相反,根据top计算bottom。注意:这里为什么用一个包含Blob的容器(vector),对于大多数Layer来说输入和输出都个连接只有一个Layer,然而对于某些Layer存在一对多的情况,比如LossLayer和某些连接层,在网络结构定义文件(*.proto)中每一层的参数bootom和top数目就决定了vector中元素数目。
layers { bottom:"decode1neuron" //该层底下连接的第一个Layer bottom:"flatdata" //该层底下连接的第二个Layer top:"12_error" //该层顶上连接的一个Layer name:"loss" //该层的名字 type:EUCLIDEAN_LOSS //该层的类型 loss_weight:0 }caffe结构是自底向上 3、Layer的重要成员变量 loss vector<Dtype> loss_; 每一层又有一个loss_值,只不过大多数Layer都是0,只有LossLayer才可能产生非0的loss_。计算loss是会把所有层的loss_相加。 learnable parameters
vector<shared_ptr<Blob<Dtype>>>blobs_;前面提到过的,Layer学习到的参数。
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。
这个类中包含一个Net的指针,主要是实现了训练模型参数所采用的优化算法,它所派生的类就可以对整个网络进行训练
shared_ptr<Net<Dtype>> net_;不同的模型训练方法通过重载函数ComputeUpdateValue()实现计算update参数的核心功能
virtual void ComputeUpdateValue() = 0;最后当进行整个网络训练过程(也就是运行caffe训练某个模型时),实际上是在运行caffe.cpp中的train函数,而这个函数实际上是实例化一个Solver的对象,初始化后调用了Solver中的Solve()方法。而这个Solve()函数主要是在迭代运行下面的这两个函数,就是刚才介绍的那几个函数。
ComputeUpdateValue(); net_->Update();Layer部分代码最多,反映出caffe比较重视丰富网络单元的类型,但caffe的代码结构高度层次化,使得某些研究以及应用(比如研究类似非逐层连接的神经网络这种复杂的网络连接方式)难以在该平台实现,这也是caffe的不足。 caffe基本数据单元都用Blob,使得数据在内存的存储变得十分高效,紧凑,从而提升了整体训练能力,但可读性不便。如forward参数也是直接用Blob而不是设计一个新类来增加可读性。所以说性能的提升是以可读性为代价的。
作者:知乎用户 链接:https://www.zhihu.com/question/27982282/answer/39350629 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
