(Caffe)基本類Blob,Layer,Net(一)

本文地址:http://blog.csdn.net/mounty_fsc/article/details/51085654css

Caffe中,Blob。Layer,Net,Solver是最爲核心的類,下面介紹這幾個類,Solver將在下一節介紹。html

1 Blob

1.1 簡單介紹

Blob是:c++

  1. 對待處理數據帶一層封裝,用於在Caffe中通訊傳遞。
  2. 也爲CPU和GPU間提供同步能力
  3. 數學上,是一個N維的C風格的存儲數組
    總的來講。Caffe使用Blob來交流數據,其是Caffe中標準的數組與統一的內存接口,它是多功能的。在不一樣的應用場景具備不一樣的含義,如可以是:batches of images, model parameters, and derivatives for optimization等。

1.2 源碼

/** * @brief A wrapper around SyncedMemory holders serving as the basic * computational unit through which Layer%s, Net%s, and Solver%s * interact. * * TODO(dox): more thorough description. */  
template <typename Dtype>  
class Blob {  
 public:  
  Blob()  
       : data_(), diff_(), count_(0), capacity_(0) {}  

  /// @brief Deprecated; use <code>Blob(const vector<int>& shape)</code>. 
  explicit Blob(const int num, const int channels, const int height,  
      const int width);  
  explicit Blob(const vector<int>& shape);  

  .....  

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

  DISABLE_COPY_AND_ASSIGN(Blob);  
};  // class Blob 

注:此處僅僅保留了構造函數與成員變量。git

說明:github

  1. Blob在實現上是對SyncedMemory(見1.5部分)進行了一層封裝。

  2. shape_爲blob維度,見1.3部分
  3. data_爲原始數據
  4. diff_爲梯度信息
  5. count_爲該blob的總容量(即數據的size)。函數count(x,y)(或count(x))返回某個切片[x,y]([x,end])內容量,本質上就是shape[x]shape[x+1]….*shape[y]的值

1.3 Blob的shape

由源碼中可以注意到Blob有個成員變量:vector shape_
其做用:數組

  1. 對於圖像數據,shape可以定義爲4維的數組(Num, Channels, Height, Width)或(n, k, h, w)。因此Blob數據維度爲n*k*h*w。Blob是row-major保存的,所以在(n, k, h, w)位置的值物理位置爲((n * K + k) * H + h) * W + w。當中Number是數據的batch size,對於256張圖片爲一個training batch的ImageNet來講n = 256;Channel是特徵維度,如RGB圖像k = 3
  2. 對於全鏈接網絡,使用2D blobs (shape (N, D))。而後調用InnerProductLayer
  3. 對於參數,維度依據該層的類型和配置來肯定。對於有3個輸入96個輸出的卷積層,Filter核 11 x 11,則blob爲96 x 3 x 11 x 11. 對於全鏈接層,1000個輸出。1024個輸入。則blob爲1000 x 1024.

1.4 Blob的行優先的存儲方式

以Blob中二維矩陣爲例(如全鏈接網絡shape (N, D))。如圖所看到的。一樣的存儲方式可以推廣到多維。markdown

這裏寫圖片描寫敘述

1.5 SyncedMemory

由1.2知。Blob本質是對SyncedMemory的再封裝。網絡

其核心代碼例如如下:app

/** 
 * @brief Manages memory allocation and synchronization between the host (CPU) 
 *        and device (GPU). 
 * 
 * TODO(dox): more thorough description. 
 */  
class SyncedMemory {  
 public:  
...  
 const void* cpu_data();  
  const void* gpu_data();  
  void* mutable_cpu_data();  
  void* mutable_gpu_data();  
...  
 private:  
...  
  void* cpu_ptr_;  
  void* gpu_ptr_;  
...  
};  // class SyncedMemory

Blob同一時候保存了data_和diff_,其類型爲SyncedMemory的指針。
對於data_(diff_一樣),事實上際值要麼存儲在CPU(cpu_ptr_)要麼存儲在GPU(gpu_ptr_),有兩種方式訪問CPU數據(GPU一樣):框架

  1. 常量方式,void* cpu_data(),其不改變cpu_ptr_指向存儲區域的值。
  2. 可變方式,void* mutable_cpu_data(),其可改變cpu_ptr_指向存儲區值。
    以mutable_cpu_data()爲例

    void* SyncedMemory::mutable_cpu_data() {
      to_cpu();
      head_ = HEAD_AT_CPU;
      return cpu_ptr_;
    }
    
    inline void SyncedMemory::to_cpu() {
      switch (head_) {
      case UNINITIALIZED:
        CaffeMallocHost(&cpu_ptr_, size_, &cpu_malloc_use_cuda_);
        caffe_memset(size_, 0, cpu_ptr_);
        head_ = HEAD_AT_CPU;
        own_cpu_data_ = true;
        break;
      case HEAD_AT_GPU:
    
    #ifndef CPU_ONLY
    
        if (cpu_ptr_ == NULL) {
          CaffeMallocHost(&cpu_ptr_, size_, &cpu_malloc_use_cuda_);
          own_cpu_data_ = true;
        }
        caffe_gpu_memcpy(size_, gpu_ptr_, cpu_ptr_);
        head_ = SYNCED;
    
    #else
    
        NO_GPU;
    
    #endif
    
        break;
      case HEAD_AT_CPU:
      case SYNCED:
        break;
      }
    }

說明

  1. 經驗上來講,假設不需要改變其值,則使用常量調用的方式,並且,不要在你對象中保存其指針。爲什麼要這樣設計呢。因爲這樣涉及可以隱藏CPU到GPU的同步細節,以及下降數據傳遞從而提升效率。當你調用它們的時候。SyncedMem會決定什麼時候去複製數據,一般狀況是僅當gnu或cpu改動後有複製操做,引用1官方文檔中有一個樣例說明什麼時候進行復制操做。
  2. 調用mutable_cpu_data()可以讓head轉移到cpu上
  3. 第一次調用mutable_cpu_data()是UNINITIALIZED將運行9到14行。將爲cpu_ptr_分配host內存
  4. 若head從gpu轉移到cpu。將把數據從gpu拷貝到cpu中

2 Layer

2.1 簡單介紹

Layer是Caffe的基礎以及基本計算單元。Caffe十分強調網絡的層次性,可以說。一個網絡的大部分功能都是以Layer的形式去展開的,如convolute,pooling,loss等等。
在建立一個Caffe模型的時候,也是以Layer爲基礎進行的,需依照src/caffe/proto/caffe.proto中定義的網絡及參數格式定義網絡 prototxt文件(需瞭解google protocol buffer)

2.2 Layer與Blob的關係

如圖,名爲conv1的Layer 的輸入是名爲data的bottom blob,其輸出是名爲conv1的top blob。

其protobuff定義例如如下,一個layer有一個到多個的top和bottom,其相應於blob

layer { name: "conv1" type: "Convolution" bottom: "data" top: "conv1" .... }

2.3 源碼

/** * Layer%s must implement a Forward function, in which they take their input * (bottom) Blob%s (if any) and compute their output Blob%s (if any). * They may also implement a Backward function, in which they compute the error * gradients with respect to their input Blob%s, given the error gradients with * their output Blob%s. */  
    template <typename Dtype>  
    class Layer {  
     public:  
      /** * You should not implement your own constructor. Any set up code should go * to SetUp(), where the dimensions of the bottom blobs are provided to the * layer. */  
      explicit Layer(const LayerParameter& param)  
        : layer_param_(param), is_shared_(false) {  
    ...  
        }  
      virtual ~Layer() {}  

      /** * @brief Implements common layer setup functionality. * @param bottom the preshaped input blobs * @param top * the allocated but unshaped output blobs, to be shaped by Reshape */  
      void SetUp(const vector<Blob<Dtype>*>& bottom,  
          const vector<Blob<Dtype>*>& top) {  
    ...  
      }  

      ...  

      /** * @brief Given the bottom blobs, compute the top blobs and the loss. * \return The total loss from the layer. * * The Forward wrapper calls the relevant device wrapper function * (Forward_cpu or Forward_gpu) to compute the top blob values given the * bottom blobs. If the layer has any non-zero loss_weights, the wrapper * then computes and returns the loss. * * Your layer should implement Forward_cpu and (optionally) Forward_gpu. */  
      inline Dtype Forward(const vector<Blob<Dtype>*>& bottom,  
          const vector<Blob<Dtype>*>& top);  

      /** * @brief Given the top blob error gradients, compute the bottom blob error * gradients. * * @param top * the output blobs, whose diff fields store the gradient of the error * with respect to themselves * @param propagate_down * a vector with equal length to bottom, with each index indicating * whether to propagate the error gradients down to the bottom blob at * the corresponding index * @param bottom * the input blobs, whose diff fields will store the gradient of the error * with respect to themselves after Backward is run * * The Backward wrapper calls the relevant device wrapper function * (Backward_cpu or Backward_gpu) to compute the bottom blob diffs given the * top blob diffs. * * Your layer should implement Backward_cpu and (optionally) Backward_gpu. */  
      inline void Backward(const vector<Blob<Dtype>*>& top,  
          const vector<bool>& propagate_down,  
          const vector<Blob<Dtype>*>& 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<shared_ptr<Blob<Dtype> > > blobs_;  
      /** Vector indicating whether to compute the diff of each param blob. */  
      vector<bool> param_propagate_down_;  

      /** The vector that indicates whether each top blob has a non-zero weight in * the objective function. */  
      vector<Dtype> loss_;  

      virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,  
          const vector<Blob<Dtype>*>& top) = 0;  

      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);  
      }  

    ...  

     };  // class Layer 

說明:每一層定義了三種操做

  1. Setup:Layer的初始化
  2. Forward:前向傳導計算。依據bottom計算top,調用了Forward_cpu(必須實現)和Forward_gpu(可選,若未實現,則調用cpu的)
  3. Backward:反向傳導計算。依據top計算bottom的梯度。其它同上

2.4 派生類分類

在Layer的派生類中,主要可以分爲Vision Layers

  • Vision Layers
    Vison 層主要用於處理視覺圖像相關的層。以圖像做爲輸入,產生其它的圖像。其主要特色是具備空間結構。
    包括Convolution(conv_layer.hpp)、Pooling(pooling_layer.hpp)、Local Response Normalization(LRN)(lrn_layer.hpp)、im2col等。注:老版本號的Caffe有頭文件include/caffe/vision_layers.hpp,新版本號中用include/caffe/layer/conv_layer.hpp等代替
  • Loss Layers
    這些層產生loss,如Softmax(SoftmaxWithLoss)、Sum-of-Squares / Euclidean(EuclideanLoss)、Hinge / Margin(HingeLoss)、Sigmoid Cross-Entropy(SigmoidCrossEntropyLoss)、Infogain(InfogainLoss)、Accuracy and Top-k等
  • Activation / Neuron Layers
    元素級別的運算,運算均爲同址計算(in-place computation。返回值覆蓋原值而佔用新的內存)。如:ReLU / Rectified-Linear and Leaky-ReLU(ReLU)、Sigmoid(Sigmoid)、TanH / Hyperbolic Tangent(TanH)、Absolute Value(AbsVal)、Power(Power)、BNLL(BNLL)等
  • Data Layers
    網絡的最底層,主要實現數據格式的轉換,如:Database(Data)、In-Memory(MemoryData)、HDF5 Input(HDF5Data)、HDF5 Output(HDF5Output)、Images(ImageData)、Windows(WindowData)、Dummy(DummyData)等
  • Common Layers
    Caffe提供了單個層與多個層的鏈接。

    如:Inner Product(InnerProduct)、Splitting(Split)、Flattening(Flatten)、Reshape(Reshape)、Concatenation(Concat)、Slicing(Slice)、Elementwise(Eltwise)、Argmax(ArgMax)、Softmax(Softmax)、Mean-Variance Normalization(MVN)等

注,括號內爲Layer Type,沒有括號暫缺信息。具體咱見引用2

3 Net

3.1 簡單介紹

一個Net由多個Layer組成。

一個典型的網絡從data layer(從磁盤中加載數據)出發到loss layer結束。如圖是一個簡單的邏輯迴歸分類器。

例如如下定義:

name: "LogReg"
layer { name: "mnist" type: "Data" top: "data" top: "label" data_param { source: "input_leveldb" batch_size: 64 }
}
layer { name: "ip" type: "InnerProduct" bottom: "data" top: "ip" inner_product_param { num_output: 2 }
}
layer { name: "loss" type: "SoftmaxWithLoss" bottom: "ip" bottom: "label" top: "loss" }

3.2 源碼

/** * @brief Connects Layer%s together into a directed acyclic graph (DAG) * specified by a NetParameter. * * TODO(dox): more thorough description. */
template <typename Dtype>
class Net {
 public:
...
  /// @brief Initialize a network with a NetParameter.
  void Init(const NetParameter& param);
...

  const vector<Blob<Dtype>*>& Forward(const vector<Blob<Dtype>* > & bottom,
      Dtype* loss = NULL);
...
  /** * The network backward should take no input and output, since it solely * computes the gradient w.r.t the parameters, and the data has already been * provided during the forward pass. */
  void Backward();
  ...
  Dtype ForwardBackward(const vector<Blob<Dtype>* > & bottom) {
    Dtype loss;
    Forward(bottom, &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<shared_ptr<Layer<Dtype> > > layers_;
  /// @brief the blobs storing intermediate results between the layer.
  vector<shared_ptr<Blob<Dtype> > > blobs_;
  vector<vector<Blob<Dtype>*> > bottom_vecs_;
  vector<vector<Blob<Dtype>*> > top_vecs_;
  ...
  /// The root net that actually holds the shared layers in data parallelism
  const Net* const root_net_;
};
}  // namespace caffe

說明:

  1. Init中,經過建立blob和layer搭建了整個網絡框架,以及調用各層的SetUp函數。
  2. blobs_存放這每一層產生的blobls的中間結果。bottom_vecs_存放每一層的bottom blobs,top_vecs_存放每一層的top blobs

參考文獻:
[1].http://caffe.berkeleyvision.org/tutorial/net_layer_blob.html
[2].http://caffe.berkeleyvision.org/tutorial/layers.html
[3].https://yufeigan.github.io
[4].https://www.zhihu.com/question/27982282

相關文章
相關標籤/搜索