Caffe解读(二)


  • 路线图

http://blog.csdn.net/kkk584520/article/details/41681085

  • (1)从CAFFE_ROOT/src/caffe/proto/caffe.proto开始,了解各类数据结构,主要是内存对象和序列化磁盘文件的一一对应关系,知道如何从磁盘Load一个对象到内存,以及如何将内存对象Save到磁盘,中间的过程实现都是由Protobuf自动完成的。
  • (2)看头文件,先理解整个框架。Caffe中类数目众多,但脉络十分清晰。在Testing时,最外层的类是Caffe::Net,包含了多个Caffe::Layer对象,而Layer对象派生出神经网络多种不同层的类(DataLayer,ConvolutionLayer,InnerProductionLayer,AccurancyLayer等),每层会有相应的输入输出(Blob对象)以及层的参数(可选,Blob对象);Blob中包括了SyncedMemory对象,统一了CPU和GPU存储器。自顶向下去看这些类,结合理论知识很容易掌握使用方法。
  • (3)有针对性地去看cpp和cu文件。一般而言,Caffe框架不需要修改,只需要增加新的层实现即可。例如想自己实现卷积层,只需从ConvolutionLayer派生一个新类MyConvolutionLayer,然后将几个虚函数改成自己的实现即可。所以这一阶段关注点在算法上,而不是源码本身。
  • (4)可以自己尝试编写各类工具,集成到Caffe内部。在CAFFE_ROOT/tools/下面有很多实用工具,可以根据需要修改。例如从训练好的模型中抽取参数进行可视化可以用Python结合matplot实现。

Caffe 数据结构

protobuf详解

参考 http://blog.csdn.net/kkk584520/article/details/41046827
http://www.cnblogs.com/yymn/p/5167013.html

  • protobuf用于caffe中的参数管理。在实际使用的时候主要是作为一个代码自动生成工具来使用,通过生成对所定义的数据结构的标准读写代码,用户可以通过标准的读写接口从文件中进行数据的读取,解析和存储。
  • protobuf使用前,先编写proto文件,这是描述我们需要配置参数的数据结构。
  • src/caffe/proto/caffe.proto:(以NetParameter为例)

message NetParameter {
    optional string name = 1; // consider giving the network a name
    // DEPRECATED. See InputParameter. The input blobs to the network.
    repeated string input = 3;
    // DEPRECATED. See InputParameter. The shape of the input blobs.
    repeated BlobShape input_shape = 8;
    // 4D input dimensions -- deprecated.  Use "input_shape" instead.
    // If specified, for each input blob there should be four
    // values specifying the num, channels, height and width of the input blob.
    // Thus, there should be a total of (4 * #input) numbers.
    repeated int32 input_dim = 4;

    // Whether the network will force every layer to carry out backward operation.
    // If set False, then whether to carry out backward is determined
    // automatically according to the net structure and learning rates.
    optional bool force_backward = 5 [default = false];
    // The current "state" of the network, including the phase, level, and stage.
    // Some layers may be included/excluded depending on this state and the states
    // specified in the layers' include and exclude fields.
    optional NetState state = 6;

    // Print debugging information about results while running Net::Forward,
    // Net::Backward, and Net::Update.
    optional bool debug_info = 7 [default = false];

    // The layers that make up the net.  Each of their configurations, including
    // connectivity and behavior, is specified as a LayerParameter.
    repeated LayerParameter layer = 100;  // ID 100 so layers are printed last.
    // DEPRECATED: use 'layer' instead.
    repeated V1LayerParameter layers = 2;
  • 解析

    • 一个message称为一个消息,由至少一个字段组合而成,字段格式是:

    限定修饰符① | 数据类型② | 字段名称③ | = | 字段编码值④ | [字段默认值⑤]
    
    • 限定修饰符包含 required\optional\repeated:
      • Required: 表示是一个必须字段,必须相对于发送方,在发送消息之前必须设置该字段的值,对于接收方,必须能够识别该字段的意思。发送之前没有设置required字段或者无法识别required字段都会引发编解码异常,导致消息被丢弃。
      • Optional:表示是一个可选字段,可选对于发送方,在发送消息时,可以有选择性的设置或者不设置该字段的值。对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段,消息中的其它字段正常处理。—因为optional字段的特性,很多接口在升级版本中都把后来添加的字段都统一的设置为optional字段,这样老的版本无需升级程序也可以正常的与新的软件进行通信,只不过新的字段无法识别而已,因为并不是每个节点都需要新的功能,因此可以做到按需升级和平滑过渡。
      • Repeated:表示该字段可以包含0~N个元素。其特性和optional一样,但是每一次可以包含多个值。可以看作是在传递一个数组的值。
    • 字段编码值用于使通信双方互相能识别对方的字段。相同的编码值,其限定修饰符和数据类型必须相同。编码值取值范围:1~2^32.编码值越大,其编码的时间和空间效率就越低。protobuf建议把经常要传递的值的字段编码设置为1~15.消息中的字段编码的值无需连续,只要是合法的,并且不能在同一个消息中有字段包含相同的编码值。
    • 默认值:对required数据类型,如果用户没有设置值,则使用默认值传递;对optional数据类型,如果没有接收到optional字段,则设置为默认值。
    • 在实际程序中只需要调用类中的接口如:set_***(), add_***(), mutable_***(), ***_size()等就能够操作相应的参数,最后将内存中的参数序列化为文件只需要执行SerializeToOstream()。相应的读取参数文件的操作为ParseFromIstream()。
    • message支持嵌套消息,消息可以包含另一个消息作为其字段。也可以在消息内定义一个新的消息。
    • NetParameter中的BlobShape、NetState、LayerParameter 、V1LayerParameter也是一个message,同样包含多个字段,只需要通过接口函数进行调用。

认识protobuf对后续学习源码很有好处,在源码里经常会出现一些参数操作,如果不了解protobuf的存在就会对这些操作无所适从不知出处。

核心类

  • Blob类
    • blob作为统一的内存接口在不同应用场景具有不同的含义,如可以是:batches of images, model parameters, derivatives for optimization等
    • 源代码见以下:
      • data_ 为原始数据
      • diff_ 为梯度信息
      • shape_data_ 为blob数据
      • shape_ 为blob维度:
        1.对于图像数据(n, k, h, w),blob数据维度为n*k*h*w,blob是row-major保存,因此在(n, k, h, w)图像位置的数据的物理位置为((n * K + k) * H + h) * W + w。注意这个保存方式,在用opencv读图传入caffe中时由于存储方式不同需要对数据进行转换
        2.对于参数,维度根据该层的类型和配置来决定。如对于有3个输入96个输出的卷积层,Filter核 11*11,则blob为96*3*11*11;对于全连接层,1000个输出,1024个输入,则blob为1000*1024
      • count_ 为该blob总容量(size)
    • blob类源代码:

template 
class Blob {
  public:
    Blob(): data_(), diff_(), count_(0), capacity_(0) {}
    /// @brief Deprecated; use Blob(const vector& shape).
    explicit Blob(const int num, const int channels, const int height, const int width);
    explicit Blob(const vector& shape);
    /// @brief Deprecated; use Reshape(const vector& shape).
    void Reshape(const int num, const int channels, const int height, const int width);   
    ......

    protected:
      shared_ptr data_;
      shared_ptr diff_;
      shared_ptr shape_data_; 
      vector shape_;
      int count_;
      int capacity_;

      DISABLE_COPY_AND_ASSIGN(Blob);
    };  // class Blob
  • Layer类

    • 主要定义了layer的初始化、前向传播、反向传播操作。
      • layer_param_ 存储该层的参数(protobuf)
      • phase_ Train or Test阶段
      • blobs_存储每层网络的训练参数,通常有blobs_[0]存储该层网络的weight,blobs_[1]存储该层网络的bias。不管是weight还是bias都具有blob的结构
      • param_propagate_down_ 标记该层网络做不做反向传播
      • loss_ 存储每层网络的loss,但是除了loss layer外,基本上其他类型的层的loss都是0
      • Forward:前向传播,根据bottom计算top,调用了Forward_cpu(必须实现)和Forward_gpu(可选,若未实现,则调用cpu的)
      • Backward:反向传播,根据top计算bottom的梯度,其他同上
    • 五大派生类

      • Data Layers -data_layer,memory_data_layer,hdf5_data_layer,hdf5_output_layer,image_data_layer,window_data_layer,dummy_data_layer等等
      • Vision Layers
        - conv_layer,pooling_layer,lrn_layer等等
      • Activation Neuron Layers -relu_layer,sigmoid_layer,tanh_layer,prelu_layer,power_layer,absval_layer,bnll_layer等等
      • Loss Layers
        -softmax_loss_layer,euclidean_loss_layer(EuclideanLoss),hinge_loss_layer,infogain_loss_layer,sigmoid_cross_entropy_loss_layer,infogain_loss_layer等等
      • Common Layers
        - 单个层与多个层的连接,concat_layer,eltwise_layer,mvn_layer(Mean-Variance Normalization),slice_layer,split_layer等等
      • 每个layer都有对应的.cpp,.cu和.hpp文件声明和实现了各个类的接口
    • layer类源代码


template 
  class Layer {
    public:
      explicit Layer(const LayerParameter& param) : layer_param_(param) {
      // Set phase and copy blobs (if there are any).
        phase_ = param.phase();
        if (layer_param_.blobs_size() > 0) {
          blobs_.resize(layer_param_.blobs_size());
            for (int i = 0; i < layer_param_.blobs_size(); ++i) {
              blobs_[i].reset(new Blob());
              blobs_[i]->FromProto(layer_param_.blobs(i));
            }
          }
        }
       virtual ~Layer() {}

       void SetUp(const vector*>& bottom, const vector*>& top) {
         CheckBlobCounts(bottom, top);
         LayerSetUp(bottom, top);
         Reshape(bottom, top);
         SetLossWeights(top);
       }
       inline Dtype Forward(const vector*>& bottom, const vector*>& top);
       inline void Backward(const vector*>& top, const vector& propagate_down, const vector*>& bottom);
       ......
    protected:
      /** The protobuf that stores the layer parameters */
      LayerParameter layer_param_;
      /** The phase: TRAIN or TEST */
      Phase phase_;
      /** The vector that stores the learnable parameters as a set of blobs. */
      vector > > blobs_;
      /** Vector indicating whether to compute the diff of each param blob. */
      vector param_propagate_down_;

      /** The vector that indicates whether each top blob has a non-zero weight in  the objective function. */
      vector loss_;
      /** @brief Using the CPU device, compute the layer output. */
      virtual void Forward_cpu(const vector*>& bottom, const vector*>& top) = 0;
      /**@brief Using the GPU device, compute the layer output. Fall back to Forward_cpu() if unavailable. */
      virtual void Forward_gpu(const vector*>& bottom, const vector*>& top) {
      // LOG(WARNING) << "Using CPU code as backup.";
      return Forward_cpu(bottom, top);
      }
      /**@brief Using the CPU device, compute the gradients for any parameters and for the bottom blobs if propagate_down is true.*/
      virtual void Backward_cpu(const vector*>& top, const vector& propagate_down, const vector*>& bottom) = 0;
      /** @brief Using the GPU device, compute the gradients for any parameters and for the bottom blobs if propagate_down is true. Fall back to Backward_cpu() if unavailable. */
      virtual void Backward_gpu(const vector*>& top, const vector& propagate_down, const vector*>& bottom) {
      // LOG(WARNING) << "Using CPU code as backup.";
      Backward_cpu(top, propagate_down, bottom);
      }
      ......

    private:
      DISABLE_COPY_AND_ASSIGN(Layer);
    };  // class Layer
  • Net类
    • Init,通过创建blob和layer搭建了整个网络框架,以及调用各层的SetUp函数
    • blobs_ 存放这每一层产生的blobls的中间结果
    • top_vecs_ 存放每一层的top blobs
    • Net类源代码

template 
  class Net {
    public:
      explicit Net(const NetParameter& param);
      explicit Net(const string& param_file, Phase phase, const int level = 0, const vector* stages = NULL);
      virtual ~Net() {}

      /// @brief Initialize a network with a NetParameter.
      void Init(const NetParameter& param);
      ......

      const vector*>& Forward(const vector* > & bottom, Dtype* loss = NULL);
      ......

      void Backward();
      Dtype ForwardBackward() {
            Dtype loss;
            Forward(&loss);
            Backward();
            return loss;
          }
    protected:
      /// @brief The network name
      string name_;
      /// @brief The phase: TRAIN or TEST
      Phase phase_;
      /// @brief Individual layers in the net
      vector > > layers_;
      vector layer_names_;
      map layer_names_index_;
      vector layer_need_backward_;
      /// @brief the blobs storing intermediate results between the layer.
      vector > > blobs_;
      ......
      vector*> > bottom_vecs_;
      ......
      vector*> > top_vecs_;
      ......
      DISABLE_COPY_AND_ASSIGN(Net);
    };
    }  // namespace caffe
  • Solver类
    • 优化方法

参考
http://blog.csdn.net/mounty_fsc/article/details/51085654
http://blog.csdn.net/mounty_fsc/article/details/51088173

你可能感兴趣的:(caffe学习)