1. 程式人生 > >常思考->有目標->重實踐->善反思

常思考->有目標->重實踐->善反思

本文主要分析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額外引數。


// 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,定義如下:


// 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,使用的示例如下:

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類的核心成員函式


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(這四個函式子類需要實現)。


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做了一層包裝:

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同理:

// 前向傳播和反向傳播介面。 每個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;
}