剖析Caffe源码之Layer

本文深入解析Caffe框架中的Layer模块,介绍了Layer的构造、关键函数如SetUp、Forward及Backward的作用,以及LayerParameter的配置。Layer是神经网络计算的基础单元,通过不同的派生类实现各种神经网络层的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

Layer介绍

Layer分析

LayerParameter

Class Layer

Layer派生类

Layer源码

Layer构造函数

SetUp函数

Forward函数

Backward函数

总结 

参考资料


Layer介绍

如果将caffe比作成一个大楼,那么Blob就是盖成大楼的每块砖瓦,而Layer就是用Blob组成的一层层楼房,layer是神经网络中模型中比较关键的部分,是构成整个计算过程的基础。在上节中,通过对Blob源码进行剖析,对Blob进行了一定了解,而Layer是以Blob作为输入和输出,Layer其本质就是根据输入计算输出,每个layer只完成一类特定的计算,例如convolution操作、pooling、非线性变换、内积运算,以及数据加载、归一化和损失计算等。

 上图是一个经典的卷积Layer,是以bottom blob为输入,输出为top blob。

 官方文档:http://caffe.berkeleyvision.org/tutorial/net_layer_blob.html

Layer分析

在看Layer源码之前首先需要对整个Layer参数进行了解,Layer的入参除了Blob之外还有很多其他参数,用于配置

LayerParameter

LayerParameter参数的定义在src\caffe\proto\caffe.proto,是采用的ProtoBuf格式,上节已经对ProtoBuf 基本语法进行了解析:

注意在LayerParameter结构之前有一段说明,记录了该message中的number ID使用到了哪里,以及最近新添加的参数,ProtoBuf规定ID不能重复。原代码中对每个参数都进行了详细描述

message LayerParameter {
  optional string name = 1; // the layer name: Layer 名称
  optional string type = 2; // the layer type: Layer type
  repeated string bottom = 3; // the name of each bottom blob: bottom blob入参名称
  repeated string top = 4; // the name of each top blob:top blob出参名称

  // The train / test phase for computation.
  optional Phase phase = 10; //是用于模型训练还是测试,TRAIN值为0:用于训练,TEST值为1:用于测试

  // The amount of weight to assign each top blob in the objective.
  // Each layer assigns a default value, usually of either 0 or 1,
  // to each top blob.
  repeated float loss_weight = 5;// 每个输出top blob的loss权重

  // Specifies training parameters (multipliers on global learning constants,
  // and the name and other settings used for weight sharing).
  repeated ParamSpec param = 6; //特定训练参数,可以查看ParamSpec结构

  // The blobs containing the numeric parameters of the layer.
  repeated BlobProto blobs = 7; // 每层的参数 blob

  // Specifies whether to backpropagate to each bottom. If unspecified,
  // Caffe will automatically infer whether each input needs backpropagation
  // to compute parameter gradients. If set to true for some inputs,
  // backpropagation to those inputs is forced; if set false for some inputs,
  // backpropagation to those inputs is skipped.
  //
  // The size must be either 0 or equal to the number of bottoms.
  repeated bool propagate_down = 11;

  // Rules controlling whether and when a layer is included in the network,
  // based on the current NetState.  You may specify a non-zero number of rules
  // to include OR exclude, but not both.  If no include or exclude rules are
  // specified, the layer is always included.  If the current NetState meets
  // ANY (i.e., one or more) of the specified rules, the layer is
  // included/excluded.
  repeated NetStateRule include = 8;
  repeated NetStateRule exclude = 9;

  // Parameters for data pre-processing.
  optional TransformationParameter transform_param = 100;

  // Parameters shared by loss layers.
  optional LossParameter loss_param = 101;

  // Layer type-specific parameters.
  //
  // Note: certain layers may have more than one computational engine
  // for their implementation. These layers include an Engine type and
  // engine parameter for selecting the implementation.
  // The default for the engine is set by the ENGINE switch at compile-time.
  optional AccuracyParameter accuracy_param = 102;
  optional ArgMaxParameter argmax_param = 103;
  optional BatchNormParameter batch_norm_param = 139;
  optional BiasParameter bias_param = 141;
  optional ClipParameter clip_param = 148;
  optional ConcatParameter concat_param = 104;
  optional ContrastiveLossParameter contrastive_loss_param = 105;
  optional ConvolutionParameter convolution_param = 106;
  optional CropParameter crop_param = 144;
  optional DataParameter data_param = 107;
  optional DropoutParameter dropout_param = 108;
  optional DummyDataParameter dummy_data_param = 109;
  optional EltwiseParameter eltwise_param = 110;
  optional ELUParameter elu_param = 140;
  optional EmbedParameter embed_param = 137;
  optional ExpParameter exp_param = 111;
  optional FlattenParameter flatten_param = 135;
  optional HDF5DataParameter hdf5_data_param = 112;
  optional HDF5OutputParameter hdf5_output_param = 113;
  optional HingeLossParameter hinge_loss_param = 114;
  optional ImageDataParameter image_data_param = 115;
  optional InfogainLossParameter infogain_loss_param = 116;
  optional InnerProductParameter inner_product_param = 117;
  optional InputParameter input_param = 143;
  optional LogParameter log_param = 134;
  optional LRNParameter lrn_param = 118;
  optional MemoryDataParameter memory_data_param = 119;
  optional MVNParameter mvn_param = 120;
  optional ParameterParameter parameter_param = 145;
  optional PoolingParameter pooling_param = 121;
  optional PowerParameter power_param = 122;
  optional PReLUParameter prelu_param = 131;
  optional PythonParameter python_param = 130;
  optional RecurrentParameter recurrent_param = 146;
  optional ReductionParameter reduction_param = 136;
  optional ReLUParameter relu_param = 123;
  optional ReshapeParameter reshape_param = 133;
  optional ScaleParameter scale_param = 142;
  optional SigmoidParameter sigmoid_param = 124;
  optional SoftmaxParameter softmax_param = 125;
  optional SPPParameter spp_param = 132;
  optional SliceParameter slice_param = 126;
  optional SwishParameter swish_param = 147;
  optional TanHParameter tanh_param = 127;
  optional ThresholdParameter threshold_param = 128;
  optional TileParameter tile_param = 138;
  optional WindowDataParameter window_data_param = 129;
}

Class Layer

Layer是caffe较为复杂模块,其中layer是所有layer基类,定义所有layer的基本接口,其头文件为caffe\include\caffe\layer.hpp,C++文件在\src\caffe\layer.cpp中

在layer.hpp文件中其大部分接口都是虚函数,具体实现是由各个派生类来实现,class layer中的接口成员主要由以下列表:

 

Layer方法类别Layer方法描述
构造与析构函数explicit Layer(const LayerParameter& param)带参数显示构造函数
virtual ~Layer() 析构函数,由具体派生类来实现
Setup以及LayerParameter相关函数

void SetUp

(const vector<Blob<Dtype>*>& bottom,  const vector<Blob<Dtype>*>& top)

bottom Blob为Layer输入参数

top  Blob为其输出参数

为Layer的环境安装函数,主要是参数设置

virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,

  const vector<Blob<Dtype>*>& top

虚函数,Layer安装函数,由具体的派生类来实现

virtual void Reshape

(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top)

虚函数,根据bottom的输入shape,设置其输出Blob shape,由具体的派生类来实现
const LayerParameter& layer_param()获取Layer的入参LayerParameter,将其存储在class layer中的私有变量layer_param_中
virtual void ToProto(LayerParameter* param, bool write_diff = false);获取Layer的入参LayerParameter,将其存储在class layer中的私有变量layer_param_中
virtual inline const char* type() 虚函数,返回Layer type,具体实现由派生类来实现
前向传播和后向传播inline Dtype Forward(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top)
前向传播实现函数,bottom为输入,top为输出
inline void Backward(const vector<Blob<Dtype>*>& top,
      const vector<bool>& propagate_down,
      const vector<Blob<Dtype>*>& bottom)

后向传播实现函数,bottom为输入,top为输出

propagate_down为与bottom 大小相对的标志位vector,主要用来标记是否做误差梯度

 virtual inline bool AllowForceBackward

(const int bottom_index)

虚函数,是否允许强制反向传播,如果 AllowForceBackward(i) == false,则会忽略force_backward设定,具体实现由派生类来实现
 inline bool param_propagate_down(const int param_id)指定该Layer是否计算相对权值和偏置项的梯度,具体相对谁由param_id指定
inline void set_param_propagate_down(const int param_id, const bool value)设置该Layer是否计算相对权值或偏置项的梯度,具体相对谁由param_id指定

virtual void Forward_cpu

(const vector<Blob<Dtype>*>& bottom,  const vector<Blob<Dtype>*>& top)

虚函数,CPU版本的前向传播函数,具体实现是由派生类来实现

virtual void Forward_gpu

(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top)

虚函数,GPU版本的前向传播函数,具体实现由派生类来实现
Loss相关inline Dtype loss(const int top_index) 返回某个top blob标量loss值
inline void set_loss(const int top_index, const Dtype value)根据给定的index,设置某个top blob标量loss值
virtual inline const char* type() 虚函数,返回Layer type,具体实现由派生类来实现
Blob相关virtual inline int ExactNumBottomBlobs()虚函数,返回Bottom Blob的数量,具体实现由派生类来实现
 virtual inline int MinBottomBlobs()虚函数,返回layer所需要的最小Bottom Blob数量,具体实现由派生类来实现
virtual inline int MaxBottomBlobs()虚函数,返回layer期望的最多Bottom Blob数量,具体实现由派生类来实现
virtual inline int ExactNumTopBlobs() 虚函数,返回layer的输出Top Bottom Blob数量,具体实现由派生类来实现
virtual inline int MinTopBlobs() 虚函数,返回期望最小的Tob Blob数量,具体实现由派生类来实现
virtual inline int MaxTopBlobs()虚函数,返回期望最大的Tob Blob数量,具体实现由派生类来实现
virtual inline bool EqualNumBottomTopBlobs()虚函数,输入bottom与输出top数量是否相同,如果相对返回true,否则返回false,具体实现由派生类来实现
virtual inline bool AutoTopBlobs() 虚函数,是否允许匿名Top Blob,即由该layer自动创建,如为真,在Net:Init()函数会创建足够多的匿名Top Blob来满足该Layer ExactNumTopBlobs()、MinTopBlobs()需求
Layer相关参数LayerParameter layer_param_用于存储LayerParameter参数
Phase phase_用于训练train还是test参数
vector<shared_ptr<Blob<Dtype> > > blobs_Layer内部权值或偏置项,以Blob方式组织
vector<bool> param_propagate_down_标志位,是否计算对应参数的误差梯度
vector<Dtype> loss_标志位,在目标函数中,是否每个Top Blob都有非零权重

最新的caffe版本中Layer类中去掉了一个Layer是否被多个Net共享相关函数,例如IsShare(),ShareInParallel()等相关函数。

Layer派生类

从上面列表中在Layer大部分关键函数实现都是用的虚函数,并没有实现,而是需要相关的派生类自己实现,由此可知Layer只是个基类,定义了大部分功能的接口。

Caffe最新代码中由此Layer派生出:Data Layer、Vision Layer、Recurrent Layers、Common Layers、Normalization Layers、Activation/Neuron Layers、Utility Layers等几大功能模块。

每次Caffe的更新,都会将添加大量的Layer,下面是最新版本的Layer的分布图,与之前版本已经有了很大不同,但是其基本架构没用多大变化

Layer源码

通过官网资料:http://caffe.berkeleyvision.org/tutorial/net_layer_blob.html,看到每个layer的主要是三个关键函数实现:

setup:初始化layer,在网络链接时要首先调用setup初始化

Forward:前向传播,根据输入bottom计算输出,并发送到top

Backword:反向传播,根据输出的tio 计算出梯度误差,并发送给输出bottom。

官方文档说明如下:

最新版本的caffe Layer CPP文件\src\caffe\layer.cpp如下:

基本上为一个空文件,这个主要是

  • Layer是个基类 大部分接口都是虚函数形式,需要派生类来实现
  • 需要Layer实现的接口较少,直接在 caffe\include\caffe\layer.hpp函数中实现,代码会更简洁

Layer构造函数

Layer显式带参数构造函数源码如下:

通过函数注释说明可知道,构造函数是不需要派生类来实现的,由基类来实现,如果由其他一些特殊参数可以通过Setup函数来实现

  • 首先将将传递的LayerParameter参数保存到layer_param_中,以便供后面进行从处理。
  • 其次将LayerParameter中的phase单独保存到phase中 是用于Train还是Test
  • 最后将LayerParameter中的blobs参数保存到blobs_中,为该层的数值参数的Blob

SetUp函数

SetUp()函数是Net调Layer首先需要做的第一步,对该Layer 一些变量环境进行安装:

该函数入参bottom是Layer的入参,top为Layer出参,Layer的shape在LayerSetup中进行设置。

 

首先调用CheckBlobCounts()对入参bottom和出参top进行检查,如果该检查不满足要求,可以在派生类中重新写该函数进行覆盖。

 

LayerSetup()函数由各个派生类来实现,主要是每个派生Layer类可能都有特殊的安装需求,需要在具体的Layer中实现。

 

 Reshape()同样也是由派生类来实现,主要是具体实现的layer,其输出格式每个都不一样。

为每个输出top分配loss权重,从 LayerParameter参数中读取每个出身top的loss_weight,并调用set_loss,将其保存到loss_中,其中0是不参与目标计算,1是参与目标计算:

 最后是将找到Blob 中diff_数据指针,调用caffe_set()将其保存到diff_中:

 SetUp函数主要完成几件事情

  • 调用派生类的LayerSetup,安装派生类的特殊要求
  • 设置输出top的shape
  • 设置Loss权重

Forward函数

Forward计算前向传播,其代码如下:

其处理首先要区分是在CPU上计算还是在GPU上进行计算,如果是在CPU上则调用Forward_cpu()函数,如果是在GPU上则调用Forward_gpu()函数,

上述两个函数为虚函数,需要在具体的Layer自己来实现。计算完成之后,根据所设置的权重是否计算输出权重,并输出最后的结果到top中。

Backward函数

Bakcward计算其后向传播,其函数源码为:

其处理流程同样区分CPU或者GPU,最后分别调用Backward_cpu或者Backward_gpu:

两个函数同样需要派生的Layer来实现

总结 

通过对Layer源码可以看到其layer只是个框架基类,实现功能较少,需要每个相应的派生类来自己实现,后面将会对派生出来的几个大类分别进行分析

参考资料

《21天实战caffe》

http://caffe.berkeleyvision.org/tutorial/net_layer_blob.html

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Huo的藏经阁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值