Caffe中的数据的存储交换以及操作都是以blob的形式进行的,layer是模型和计算的基础,net整和并连接layer, 其中Layer层是整个框架的基本计算模块.
所有的Pooling,Convolve,apply nonlinearities等操作都在这里实现。在Layer中input data
用bottom
表示, output data
用top
表示。每一层定义了三种操作setup(Layer初始化)
, forward(正向传导,根据input计算output)
, backward(反向传导计算,根据output计算input的梯度)
重要成员函数
前向传播
和后向传播
是caffe中layer层的核心计算单元, Caffe中所有的Layer都要用这两种方法传递数据, Forward和Backward有CPU和GPU(部分有)两种实现, 在layer.hpp
中,
1 | /** @brief Using the CPU device, compute the layer output. */ |
- Layer类派生出来的层类通过这实现两个虚函数(注释处),产生了各式各样功能的层类。Forward是从根据bottom计算top的过程,Backward则相反(根据top计算bottom)。
为什么用了一个包含Blob的容器(vector),对于大多数Layer来说输入和输出都各连接只有一个Layer,然而对于某些Layer存在一对多的情况,比如LossLayer和某些连接层。在网路结构定义文件(*.proto)中每一层的参数bottom和top数目就决定了vector中元素数目。
重要成员变量
1 | LayerParameter layer_param_; // 这个是protobuf文件中存储的layer参数 |
blobs_
是Layer学习到的参数每一层有一个loss值,只不多大多数Layer都是0,只有LossLayer才可能产生非0的loss。计算loss是会把所有层的
loss_
相加。
Layer派生类
NeuronLayer类
定义于neuron_layers.hpp中,其派生类主要是元素级别的运算(比如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类。
工厂方法模式
工厂方法模式包含如下角色:
- Product:抽象产品
- ConcreteProduct:具体产品
- Factory:抽象工厂
- ConcreteFactory:具体工厂
1 | /////////////////////////////////////////////////////////// |
1 |
|
在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责哪一个产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。
创建Layer
在caffe创建layer的时候即使用到的工厂方法模式, 下面我们先来看两个很重要的宏定义.
在宏定义中, #
是把参数字符串化,##
是连接两个参数成为一个整体
第一个宏定义REGISTER_LAYER_CLASS
实际上是为每一个layer创建一个creator函数:
1 |
|
REGISTER_LAYER_CREATOR负责将创建层的函数放入LayerRegistry
1 |
|
以EuclideanLossLayer为例, 在该类的最后, 调用 REGISTER_LAYER_CLASS(EuclideanLoss);来注册这一个类. 通过上面的两个宏定义, 实际上是”创建”了下面的函数.1
2
3
4
5
6
7
8template <typename Dtype>
// create一个EuclideanLossLayer对象, 并返回对象指针
shared_ptr<Layer<Dtype> > Creator_EuclideanLossLayer(const LayerParameter& param)
{
return shared_ptr<Layer<Dtype> >(new EuclideanLossLayer<Dtype>(param));
} \
static LayerRegisterer<float> g_creator_f_EuclideanLoss("EuclideanLoss", Creator_EuclideanLossLayer<float>);
static LayerRegisterer<double> g_creator_d_EuclideanLoss("EuclideanLoss", Creator_EuclideanLossLayer<double>);
LayerRegistry
1 | template <typename Dtype> |
LayerRegisterer
构造函数只做一件事: 在LayerRegistry的registry list中, 添加一个layer的creator
1 | template <typename Dtype> |
总结 : 创建一个新layer后, 先写一个静态函数创建并返回该函数的对象 (Creator), 然后创建对应的LayerRegisterer对象, 该对象在构造时会调用 LayerRegistry 中的 AddCreator, 将该layer 注册到 registy中去.