前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >caffe源码分析-layer

caffe源码分析-layer

作者头像
bear_fish
发布2019-02-25 11:50:39
1K0
发布2019-02-25 11:50:39
举报

本文主要分析caffe layer层,主要内容如下:

  1. 从整体上说明下caffe的layer层的类别,以及作用
  2. 通过proto定义与类Layer简要说明下Layer的核心成员变量;
  3. Layer类的核心成员函数

1. 类Layer overview

caffe中的Layer主要分为如下几个模块:

  1. 输入层Data Layers

Data Layers定义了caffe中网络的输入,依赖于高效的数据库,例如(LevelDB or LMDB)。并且可以对数据做预处理,例如mean subtraction, scaling, random cropping, mirroring。

常用的有:Input, ImageData.

  1. Vision Layers层(卷积相关)例如,卷积层Convolution Layer, 池化层Pooling Layer等
  2. 循环网络层Recurrent Layers 例如,LSTM, RNN等。
  3. Common Layers例如,Inner Product 全连接层,Dropout弃权层,等。
  4. Normalization Layers(归一化层)例如Local Response Normalization (LRN), Batch Normalization 。
  5. Activation / Neuron Layers(激活层),例如ReLU, Sigmoid等。
  6. Utility Layers, 例如Flatten, Reshape等。
  7. Loss Layers, 例如Sigmoid Cross-Entropy Loss, Sum-of-Squares / Euclidean等。

layer.hpp: 父类Layer,定义所有layer的基本接口。

data_layers.hpp: 继承自父类Layer,定义与输入数据操作相关的子Layer,例如DataLayer,HDF5DataLayer和ImageDataLayer等。

vision_layers.hpp: 继承自父类Layer,定义与特征表达相关的子Layer,例如ConvolutionLayer,PoolingLayer和LRNLayer等。

neuron_layers.hpp: 继承自父类Layer,定义与非线性变换相关的子Layer,例如ReLULayer,TanHLayer和SigmoidLayer等。

loss_layers.hpp: 继承自父类Layer,定义与输出误差计算相关的子Layer,例如EuclideanLossLayer,SoftmaxWithLossLayer和HingeLossLayer等。

common_layers.hpp: 继承自父类Layer,定义与中间结果数据变形、逐元素操作相关的子Layer,例如ConcatLayer,InnerProductLayer和SoftmaxLayer等。

layer_factory.hpp: Layer工厂模式类,负责维护现有可用layer和相应layer构造方法的映射表。

每个Layer根据自身需求的不同,会定义CPU或GPU版本的实现,例如ConvolutionLayer的CPU和GPU实现就定义在了两个文件中conv_layer.cpp, conv_layer.cu.


2. 通过proto定义与类Layer简要说明下Layer的核心成员变量

proto的LayerParameter核心参数如下,除了基础的参数外还有其他的继承类如:ConvolutionParameter额外参数。

代码语言:javascript
复制
// LayerParameter next available layer-specific ID: 145 (last added: crop_param)
message LayerParameter {
  optional string name = 1; // the layer name
  optional string type = 2; // the layer type
  repeated string bottom = 3; // the name of each bottom blob
  repeated string top = 4; // the name of each top blob

  // The train / test phase for computation.
  optional Phase phase = 10;

  repeated float loss_weight = 5;
  // The blobs containing the numeric parameters of the layer.
// Specifies training parameters (multipliers on global learning constants,
// and the name and other settings used for weight sharing).
  repeated ParamSpec param = 6;

  repeated BlobProto blobs = 7;

  // The size must be either 0 or equal to the number of bottoms.
  repeated bool propagate_down = 11;

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

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

  // Layer type-specific parameters.
  optional ConvolutionParameter convolution_param = 106;
  optional InnerProductParameter inner_product_param = 117;
}

上面的参数,我们重点关注下,ParamSpec,定义如下:

代码语言:javascript
复制
// Specifies training parameters (multipliers on global learning constants,
// and the name and other settings used for weight sharing).
message ParamSpec {
  // The names of the parameter blobs -- useful for sharing parameters among
  // layers, but never required otherwise.  To share a parameter between two
  // layers, give it a (non-empty) name.
  optional string name = 1;
  //......
  // The multiplier on the global learning rate for this parameter.
  optional float lr_mult = 3 [default = 1.0];

  // The multiplier on the global weight decay for this parameter.
  optional float decay_mult = 4 [default = 1.0];
}

此参数定义了反向传播过程的参数更新的学习率(结合solve中的base_lr),由于参数有weight和bias因此是repeated,使用的示例如下:

代码语言:javascript
复制
layer {
  name: "ip1"
  type: "InnerProduct"
  param { lr_mult: 1 }  // for weight
  param { lr_mult: 2 }  // for bias
  inner_product_param {
    num_output: 500
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
  bottom: "pool2"
  top: "ip1"
}

在solver.prototxt中我们定义了base_lr: 0.01,因此weight的学习率是0.01, bias的学习率是0.01*2.


3. Layer类的核心成员函数

代码语言:javascript
复制
template<typename Dtype>
class Layer{
protected:
  //protobuf文件中存储的layer参数,从protocal buffers格式的网络结构说明文件中读取
    //protected类成员,构造函数中初始化
    LayerParameter layer_param_;
    //层状态,参与网络的训练还是测试
    Phase phase_;
    // 可学习参数层权值和偏置参数,使用向量是因为权值参数和偏置是分开保存在两个blob中的
    // 在基类layer中初始化(只是在描述文件定义了的情况下)
    vector<shared_ptr<Blob<Dtype> > > blobs_;
    // 标志每个可学习参数blob是否需要计算反向传递的梯度值
    vector<bool> param_propagate_down_;
    // 非LossLayer为零,LossLayer中表示每个top blob计算的loss的权重
    vector<Dtype> loss_;
private:
    /** Whether this layer is actually shared by other nets*/
    bool is_shared_;
    // 若该layer被shared,则需要这个mutex序列保持forward过程的正常运行
    shared_ptr<boost::mutex> forward_mutex_;
}

Layer的核心函数在于Forward,Backward,这两个函数调用Forward_cpu、Forward_gpu以及Backward_cpu,Backward_gpu(这四个函数子类需要实现)。

代码语言:javascript
复制
inline Dtype Forward(const vector<Blob<Dtype>*>& bottom,
                     const vector<Blob<Dtype>*>& top);
//给定相对于 top 层输出的梯度,计算其相对于输入的梯度,并传递到 bottom
 层。一个有参数的 layer 需要计算相对于各个参数的梯度值并存储在内部。
inline void Backward(const vector<Blob<Dtype>*>& top,
                     const vector<bool>& propagate_down,
                     const vector<Blob<Dtype>*>& bottom);

protected:
//纯虚函数,子类必须实现,使用cpu经行前向计算
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
                         const vector<Blob<Dtype>*>& top) = 0;
//使用gpu经行前向计算, 如果gpu没有实现则使用默认的CPU版本
virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
                         const vector<Blob<Dtype>*>& top) {
    // LOG(WARNING) << "Using CPU code as backup.";
    return Forward_cpu(bottom, top);
}
//纯虚函数,派生类必须实现
virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
                          const vector<bool>& propagate_down,
                          const vector<Blob<Dtype>*>& bottom) = 0;
virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
                          const vector<bool>& propagate_down,
                          const vector<Blob<Dtype>*>& bottom) {
    // LOG(WARNING) << "Using CPU code as backup.";
    Backward_cpu(top, propagate_down, bottom);
}

例如Backward仅仅在Backward_cpu,Backward_gpu做了一层包装:

代码语言:javascript
复制
template <typename Dtype>
inline void Layer<Dtype>::Backward(const vector<Blob<Dtype>*>& top,
                                   const vector<bool>& propagate_down,
                                   const vector<Blob<Dtype>*>& bottom) {
    switch (Caffe::mode()) {
        case Caffe::CPU:
            Backward_cpu(top, propagate_down, bottom);
            break;
        case Caffe::GPU:
            Backward_gpu(top, propagate_down, bottom);
            break;
        default:
            LOG(FATAL) << "Unknown caffe mode.";
    }
}

Forward同理:

代码语言:javascript
复制
// 前向传播和反向传播接口。 每个Layer的派生类都应该实现Forward_cpu()
template <typename Dtype>
inline Dtype Layer<Dtype>::Forward(const vector<Blob<Dtype>*>& bottom,
                                   const vector<Blob<Dtype>*>& top) {
    // Lock during forward to ensure sequential forward
    Lock();
    Dtype loss = 0;
    Reshape(bottom, top);// we may change input data size(num)
    switch (Caffe::mode()) {
        case Caffe::CPU:
            Forward_cpu(bottom, top);
            // .......
            break;
        case Caffe::GPU:
            Forward_gpu(bottom, top);
#ifndef CPU_ONLY
            // gpu realize, omitted
#endif
            break;
        default:
            LOG(FATAL) << "Unknown caffe mode.";
    }
    Unlock();
    return loss;
}

caffe系列源码分析介绍

本系列深度学习框架caffe 源码分析主要内容如下:

1. caffe源码分析-cmake 工程构建:

caffe源码分析-cmake 工程构建主要内容:

自己从头构建一遍工程,这样能让我更好的了解大型的项目的构建。当然原始的caffe的构建感觉还是比较复杂(主要是cmake),我这里仅仅使用cmake构建,而且简化点,当然最重要的是支持CLion直接运行调试(如果需要这个工程可以评论留下你的邮箱,我给你发送过去)。

2. caffe的数据内存分配类SyncedMemory, 以及类Blob数据传输的媒介.

主要内容:

caffe源码分析-SyncedMemory

caffe源码分析-Blob

其中Blob分析给出了其直接与opencv的图片相互转化以及操作,可以使得我们更好的理解Blob.

3. caffe layer的源码分析,包括从整体上说明了layer类别以及其proto定义与核心函数.

内容如下:

caffe源码分析-layer

caffe源码分析-ReLULayer

caffe源码分析-inner_product_layer

caffe源码分析-layer_factory

首先分析了最简单的layer Relu,然后在是inner_product_layer全连接层, 最后是layer_factorycaffe中 以此工厂模式create各种Layer.

4. 数据输入层,主要是多线程+BlockingQueue的方式读取数据训练:

内容如下:

caffe源码分析-BlockingQueue

caffe源码分析-InternalThread

caffe源码分析-DataReader

5. IO处理例如读取proto文件转化为网络,以及网络参数的序列化

内容如下:

caffe源码分析-DataTransformer

caffe源码分析-db, io

6. 最后给出了使用纯C++结合多层感知机网络训练mnist的示例

内容如下:

caffe c++示例(mnist 多层感知机c++训练,测试)

类似与caffe一样按照layer、solver、loss、net等模块构建的神经网络实现可以见下面这篇blog,相信看懂了这个python的代码理解caffe框架会更简单点.

神经网络python实现


最后如果需要**cmake** + CLion**直接运行调试**caffe**的代码工程,可以评论留下你的邮箱,我给你发送过去.**

参考:

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

https://imbinwang.github.io/research/inside-caffe-code-layer

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年10月07日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 类Layer overview
  • 2. 通过proto定义与类Layer简要说明下Layer的核心成员变量
  • 3. Layer类的核心成员函数
  • caffe系列源码分析介绍
    • 1. caffe源码分析-cmake 工程构建:
      • 2. caffe的数据内存分配类SyncedMemory, 以及类Blob数据传输的媒介.
        • 3. caffe layer的源码分析,包括从整体上说明了layer类别以及其proto定义与核心函数.
          • 4. 数据输入层,主要是多线程+BlockingQueue的方式读取数据训练:
            • 5. IO处理例如读取proto文件转化为网络,以及网络参数的序列化
              • 6. 最后给出了使用纯C++结合多层感知机网络训练mnist的示例
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档