0.Layer简介
Layer:是网络的基本单元,由此派生出了各种层类。修改这部分的人主要是研究特征表达方向的。Layer类派生出来的层类通过这实现这两个虚函数Forward()和Backward(),产生了各式各样功能的层类。Forward是从根据bottom计算top的过程,Backward则相反(根据top计算bottom)。
1.Layer.hpp派生出来的类
Layer.hpp是抽象出来的基类,其他的xxx_Layers.hpp都是在其基础上的继承。在Layer.hpp的基础上衍生出来的5种Layers:
- data_layer (data_layer.hpp/cpp)
- neuron_layer (neuron_layer.hpp/cpp)
- loss_layer (loss_layer.hpp/cpp)
- common_layer (common_layer.hpp/cpp)
- vision_layer (vision_layer.hpp/cpp)
具体作用看下面的番外
而且还有更多的派生类继承自上面的这五大类别。举个例子描述几个,比如:
<0> data_layer派生出的五种LayerType
- DATA ([data_layer.hpp])
- MEMORY_DATA dummy_data_layer.hpp
- HDF5_DATA hdf5_data_layer.hpp
- HDF5_OUTPUT hdf5_output_layer.hpp
- IMAGE_DATA image_data_layer.hpp
<1> neuron_layer派生出很多种激活函数层
同样是数据的操作层,neuron_layer实现里大量激活函数,主要是元素级别的操作,具有同的bottom,topsize。Caffe中实现了大量激活函数GPU和CPU的都有很多。它们的父类都是NeuronLayer。
<2> loss_layer派生出很多种网络误差函数层
这个头文件包含了neuron_layers.hpp,估计是需要调用里面的函数计算Loss,一般来说Loss放在最后一层。caffe实现了大量loss function,它们的父类都是LossLayer。
<3> common_layer
这个头文件包含了前面提到的data_layers.hpp, loss_layers.hpp, neuron_layers.hpp说明这一层肯定开始有复杂的操作了。这一层主要进行的是vision_layer的连接。声明了几个类型的common_layer,部分有GPU实现:
- InnerProductLayer
- SplitLayer
- FlattenLayer
- ConcatLayer
- SilenceLayer
- ReshapeLayer
- EltwiseLayer
- ReduceLayer
- SliceLayer
- SoftmaxLayer
- ArgMaxLayer
- MVNLayer
<4> vision_layer
他包含了前面所有的头文件,说明是最复杂的操作,它主要是实现Convolution和Pooling操作。
#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/common_layers.hpp"
#include "caffe/data_layers.hpp"
#include "caffe/layer.hpp"
#include "caffe/loss_layers.hpp"
#include "caffe/neuron_layers.hpp"
#include "caffe/proto/caffe.pb.h"
主要有以下几个类。:
- ConvolutionLayer
- Im2colLayer
- LRNLayer
- PoolingLayer
3.layer.hpp中包含的头文件和主要参数
layer.hpp是所有的网络层的基类,其中,定义了一些通用的接口,比如前馈,反馈,reshape,setup等。
在layer.hpp头文件里,包含了这几个头文件:
#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/layer_factory.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/util/device_alternate.hpp"
在device_alternate.hpp中,通过#ifdef CPU_ONLY定义了一些宏来取消GPU的调用:
#define STUB_GPU(classname)
#define STUB_GPU_FORWARD(classname, funcname)
#define STUB_GPU_BACKWARD(classname, funcname)
layer中有这三个主要参数:
LayerParameter layer_param_; // 这个是protobuf文件中存储的layer参数
vector>> blobs_; // 这个存储的是layer的参数,在程序中用的
vector param_propagate_down_; // 这个bool表示是否计算各个blob参数的diff,即传播误差
Layer类的构建函数explicit Layer(const LayerParameter& param) : layer_param_(param)会尝试从protobuf文件读取参数。其三个主要接口:
virtual void SetUp(const vector*>& bottom, vector*>* top)
inline Dtype Forward(const vector*>& bottom, vector*>* top);
inline void Backward(const vector*>& top, const vector& propagate_down, const *>* bottom);
Layer()尝试从protobuf读取参数;
SetUp()根据实际的参数设置进行实现,对各种类型的参数初始化;
Forward()和Backward()对应前向计算和反向更新,输入统一都是
bottom,输出为top,其中Backward里面有个propagate_down参数,
用来表示该Layer是否反向传播参数。
Caffe::mode()具体选择使用CPU或GPU操作。
SetUp函数需要根据实际的参数设置进行实现,对各种类型的参数初始化;Forward和Backward对应前向计算和反向更新,输入统一都是bottom,输出为top,其中Backward里面有个propagate_down参数,用来表示该Layer是否反向传播参数。
在Forward和Backward的具体实现里,会根据Caffe::mode()进行对应的操作,即使用cpu或者gpu进行计算,两个都实现了对应的接口Forward_cpu、Forward_gpu和Backward_cpu、Backward_gpu,这些接口都是virtual,具体还是要根据layer的类型进行对应的计算(注意:有些layer并没有GPU计算的实现,所以封装时加入了CPU的计算作为后备)。另外,还实现了ToProto的接口,将Layer的参数写入到protocol buffer文件中。
3.番外篇
data_layers.hpp
原始数据的输入层,处于整个网络的最底层,它可以从数据库leveldb、lmdb中读取数据,也可以直接从内存中读取,还可以从hdf5,甚至是原始的图像读入数据。作为网络的最底层,主要实现数据格式的转换。
neuron_layers.hpp
输入了data后,就要计算了,比如常见的sigmoid、tanh等等。这些都计算操作被抽象成了neuron_layers.hpp里面的类NeuronLayer,这个层只负责具体的计算,因此明确定义了输入ExactNumBottomBlobs()和ExactNumTopBlobs()都是常量1,即输入一个blob,输出一个blob。其派生类主要是元素级别的运算(比如Dropout运算,激活函数ReLu,Sigmoid等),运算均为同址计算(inplacecomputation,返回值覆盖原值而占用新的内存)。
common_layers.hpp
网络连接层和激活函数定义在这。剩下的那些复杂的计算则通通放在了common_layers.hpp中。像ArgMaxLayer、ConcatLayer、FlattenLayer、SoftmaxLayer、SplitLayer和SliceLayer等各种对blob增减修改的操作。
loss_layers.hpp
前面的data_layer和common_layer都是中间计算层,虽然会涉及到反向传播,但传播的源头来自于loss_layer,即网络的最终端。这一层因为要计算误差,所以输入都是2个blob,输出1个blob。其派生类会产生loss,只有这些层能够产生loss。
vision_layers.hpp
特征表达类主要是图像卷积的操作,像convolusion、pooling、LRN都在里面,按官方文档的说法,是可以输出图像的。实现特征表达功能,更具体地说包含卷积操作,Pooling操作,他们基本都会产生新的内存占用(Pooling相对较小)。
对整个layer层有了个基本认识:data负责输入,vision负责卷积相关的计算,neuron和common负责中间部分的数据计算,而loss是最后一部分,负责计算反向传播的误差。具体的实现都在src/caffe/layers里面,慢慢再研究研究。
参考文献:
各个layer
参考文献2
参考文献3源码解析