Caffe | 教你如何读懂caffe代码

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qiu931110/article/details/86560958

0.简介

要读懂caffe,首先要熟悉Blob,Layer,Net,Solver这几个大类。这四个大类紧密相连,贯穿了整个caffe的结构,下面先分别简单地介绍一下这四个类的主要作用。

  • Blob:作为数据传输的媒介,无论是网络权重参数,还是输入数据,都是转化为Blob数据结构来存储
  • Layer:作为网络的基础单元,神经网络中层与层间的数据节点、前后传递都在该数据结构中被实现,层类种类丰富,比如常用的卷积层、全连接层、pooling层等等,大大地增加了网络的多样性
  • Net:作为网络的整体骨架,决定了网络中的层次数目以及各个层的类别等信息
  • Solver:作为网络的求解策略,涉及到求解优化问题的策略选择以及参数确定方面,修改这个模块的话一般都会是研究DL的优化求解的方向。

所以利用caffe作深度学习的过程总结起来一句话:定义好整个网络结构Net,该结构是由一块块Layer组合起来的。最后用Solver解决方案来训练这个网络结构。这些过程中数据的传输均利用Blob。

1.caffe中用到的结构化数据

虽然我们了解了caffe在深度学习过程中的整体流程,但若是想要看懂这些结构在caffe中是如何定义的。那么我们首先要知道什么是proto文件。因为caffe中利用caffe.proto这个文件对其内部用到的数据作了解释,所以想要看懂caffe究竟是什么,了解caffe.proto就是第一步。proto文件的底层为Protocol Buffers,这是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化(使得数据传输变得高效),好了真想大白了,这就是为什么caffe要用这种结构来定义数据了。

在Caffe中,首先编写一个 proto 文件,定义我们程序中需要处理的结构化数据,在 protobuf 的术语中,结构化数据被称为 Message。proto 文件非常类似 java 或者 C 语言的数据定义。

Package caffe;
Message BlobProto{...}

定义一个命名空间,在该命名空间下定义一个类BlobProto(每一个Message都会生成一个类)。下面对Message做一个简单的介绍:

Message中的field:
//而且这些field有三种形式:
//1. Required是必须有值的,
//2. optional是可选项,
//3. repeated表示后面单元为相同类型的一组向量。

Message的tag:
//每个message里面的每个field都对应一个tag,
//分别是1~15或者以上,比如required string number=1;
//这个数字就是用来在生成的二进制文件中搜索查询的标签(怪不得会快)。
//关于这个数字,1到15会花费1byte的编码空间,16到2047花费2byte。
//所以一般建议把那些频繁使用的名字的标签设为1到15之间的值~

Message的enum:
//enum枚举类型调用方法:caffe::BlobProto::枚举类型里面的变量。

然后通过编译这个.proto文件之后就会生成一个.pb.cc和一个.pb.h文件。编译完成后caffe就可以基于C++运行了。

protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto
Message类别:
属于blob的:BlobProto, BlobProtoVector, Datum。

属于layer的:FillerParameter, LayerParameter,
ArgMaxParameter,TransformationParameter, LossParameter, AccuracyParameter,
ConcatParameter, ContrastiveLossParameter, ConvolutionParameter,
DataParameter, DropoutParameter, DummyDataParameter, EltwiseParameter,
ExpParameter, HDF5DataParameter, HDF5OutputParameter, HingeLossParameter,
ImageDataParameter, InfogainLossParameter, InnerProductParameter,
LRNParameter, MemoryDataParameter, MVNParameter, PoolingParameter,
PowerParameter, PythonParameter, ReLUParameter, SigmoidParameter,
SliceParameter, SoftmaxParameter, TanHParameter, ThresholdParameter等。

属于net的:NetParameter, SolverParameter, SolverState, NetState, NetStateRule,
ParamSpec。

NetParameter弄清楚NetParameter类的组成,也就明白了.Caffemodel的具体数据构成;
SolverState类记录的是当前迭代状态和参数设置,与.solverstate文件有关系;

caffe中的一些主要的数据类型如下(主要就是我们最开始提到的Net网络,Layer模块,Solver模块,Blob模块):

message BlobProto {//blob的属性以及blob中的数据(data\diff)
  optional int32 num = 1 [default = 0];
  optional int32 channels = 2 [default = 0];
  optional int32 height = 3 [default = 0];
  optional int32 width = 4 [default = 0];
  repeated float data = 5 [packed = true];
  repeated float diff = 6 [packed = true];
}
message LayerParameter {
  repeated string bottom = 2; //输入的blob的名字(string)
  repeated string top = 3; //输出的blob的名字(string)
  optional string name = 4; //层的名字
  enum LayerType { //层的枚举(enum,和c++中的enum一样)
    NONE = 0;
    ACCURACY = 1;
    BNLL = 2;
    CONCAT = 3;
    CONVOLUTION = 4;
    DATA = 5;
    DROPOUT = 6;
    EUCLIDEAN_LOSS = 7;
    ELTWISE_PRODUCT = 25;
    FLATTEN = 8;
    HDF5_DATA = 9;
    HDF5_OUTPUT = 10;
    HINGE_LOSS = 28;
    IM2COL = 11;
    IMAGE_DATA = 12;
    INFOGAIN_LOSS = 13;
    INNER_PRODUCT = 14;
    LRN = 15;
    MEMORY_DATA = 29;
    MULTINOMIAL_LOGISTIC_LOSS = 16;
    POOLING = 17;
    POWER = 26;
    RELU = 18;
    SIGMOID = 19;
    SIGMOID_CROSS_ENTROPY_LOSS = 27;
    SOFTMAX = 20;
    SOFTMAX_LOSS = 21;
    SPLIT = 22;
    TANH = 23;
    WINDOW_DATA = 24;
  }
  optional LayerType type = 5; // 层的类型
  repeated BlobProto blobs = 6; //blobs的数值参数
  repeated float blobs_lr = 7; //学习速率
  repeated float weight_decay = 8; //权值衰减(repeated)

  // 相对于某一特定层的参数(optional)
  optional ConcatParameter concat_param = 9;
  optional ConvolutionParameter convolution_param = 10;
  optional DataParameter data_param = 11;
  optional DropoutParameter dropout_param = 12;
  optional HDF5DataParameter hdf5_data_param = 13;
  optional HDF5OutputParameter hdf5_output_param = 14;
  optional ImageDataParameter image_data_param = 15;
  optional InfogainLossParameter infogain_loss_param = 16;
  optional InnerProductParameter inner_product_param = 17;
  optional LRNParameter lrn_param = 18;
  optional MemoryDataParameter memory_data_param = 22;
  optional PoolingParameter pooling_param = 19;
  optional PowerParameter power_param = 21;
  optional WindowDataParameter window_data_param = 20;
  optional V0LayerParameter layer = 1;
}
message NetParameter {
  optional string name = 1;//网络的名字
  repeated LayerParameter layers = 2; //基础单位layer
  repeated string input = 3;//输入层blob的名字
  repeated int32 input_dim = 4;//输入层blob的维度,应该等于(4*input)
  optional bool force_backward = 5 [default = false];//网络是否进行反向传播。
//如果设置为否,则由网络的结构和学习速率来决定是否进行反向传播。
}
message SolverParameter {
  optional string train_net = 1; // 训练网络的proto file
  optional string test_net = 2; // 测试网络的proto file
  optional int32 test_iter = 3 [default = 0]; // 每次测试时的迭代次数
  optional int32 test_interval = 4 [default = 0]; // 两次测试的间隔迭代次数
  optional bool test_compute_loss = 19 [default = false];
  optional float base_lr = 5; // 基本学习率
  optional int32 display = 6; // 两次显示的间隔迭代次数
  optional int32 max_iter = 7; // 最大迭代次数
  optional string lr_policy = 8; // 学习速率衰减方式
  optional float gamma = 9; // 关于梯度下降的一个参数
  optional float power = 10; // 计算学习率的一个参数
  optional float momentum = 11; // 动量
  optional float weight_decay = 12; // 权值衰减
  optional int32 stepsize = 13; // 学习速率的衰减步长
  optional int32 snapshot = 14 [default = 0]; // snapshot的间隔
  optional string snapshot_prefix = 15; // snapshot的前缀
  optional bool snapshot_diff = 16 [default = false]; 
// 是否对于 diff 进行 snapshot
  enum SolverMode {
    CPU = 0;
    GPU = 1;
  }
  optional SolverMode solver_mode = 17 [default = GPU]; // solver的模式,默认为GPU
  optional int32 device_id = 18 [default = 0]; // GPU的ID
  optional int64 random_seed = 20 [default = -1]; // 随机数种子
}

2.Bolb简介

Blob:是基础的数据结构,是用来保存学习到的参数以及网络传输过程中产生数据的类。是Caffe作为数据传输的媒介,无论是网络权重参数,还是输入数据,都是转化为Blob数据结构来存储,网络,求解器等都是直接与此结构打交道的。其直观的可以把它看成一个有4维度的结构体(包含数据和梯度),而实际上,它们只是一维的指针而已,其4维结构通过shape属性得以计算出来(根据C语言的数据顺序),因为Blob是row-major(行优先)保存的,比如对于输入(n, c, h, w)位置的数据位置为((n * channels_+c) * height_+h) * width_+w。
Blob是用以存储数据的四维数组,分别由下面组成:

  • 对于数据:num(输入数据量,比如sgd时,mini-batch的大小),channels(通道数量),height(图片的高度),width(图片的宽度)。
  • 对于卷积权重:outputinputheight*width
  • 对于卷积偏置:output11*1
  • 对于卷积层输出:输入图片数量对应feature maps数量输出图片的高度*输出图片的宽度;

对于BlobProto,可以看到定义了四个optional的int32类型的名字(name)num、channels、height和width,optional意味着Blob可以有一个或者没有这个参数,而后面的repeated意味着float类型的data和diff可以重复任意次,而加上[packed = true]是为了更高效的编码。

message BlobProto {
   optional int32 num = 1 [default = 0];
   optional int32 channels = 2 [default = 0];
   optional int32 height = 3 [default = 0];
   optional int32 width = 4 [default = 0];
   repeated float data = 5 [packed = true];
   repeated float diff = 6 [packed = true];
}

所以总结来说Blob中,主要数据有两个data和diff(data表示流动数据(输出数据),而diff则存储BP的梯度)。用num、channels、height和width这四个维度来确定数据的具体位置,做一些数据查询和Blobreshape的操作。

3.Net简介

Net:是网络的搭建,将Layer所派生出层类组合成网络。Net用容器的形式将多个Layer有序地放在一起,其自身实现的功能主要是对逐层Layer进行初始化,以及提供Update( )的接口(更新网络参数),本身不能对参数进行有效地学习过程。Net也有它自己的Forward()和Backward(),他们是对整个网络的前向和反向传导,各调用一次就可以计算出网络的loss了,下面就是Net一些参数。

message NetParameter {
  optional string name = 1;//网络的名字
  repeated LayerParameter layers = 2; //基础单位layer
  repeated string input = 3;//输入层blob的名字
  repeated int32 input_dim = 4;//输入层blob的维度,应该等于(4*input)
  optional bool force_backward = 5 [default = false];//网络是否进行反向传播。
//如果设置为否,则由网络的结构和学习速率来决定是否进行反向传播。
}

4.Layer简介

Layer:是网络的基本单元,由此派生出了各种层类。这才是本文重点想要讲述的。Layer类派生出来的层类通过这实现这两个虚函数Forward()和Backward(),产生了各式各样功能的层类。Forward是从根据bottom计算top的过程,Backward则相反(根据top计算bottom)。Layer中可写的东西太多了,之后再单独写一篇~。

猜你喜欢

转载自blog.csdn.net/qiu931110/article/details/86560958
今日推荐