常思考->有目標->重實踐->善反思
本文主要分析caffe
layer
層,主要內容如下:
-
從整體上說明下
caffe
的layer層的類別,以及作用 -
通過proto定義與類Layer簡要說明下Layer的核心成員變數;
-
Layer類的核心成員函式
1. 類Layer overview
caffe中的Layer主要分為如下幾個模組:
- 輸入層Data Layers
Data Layers定義了caffe中網路的輸入,依賴於高效的資料庫,例如(LevelDB or LMDB)。並且可以對資料做預處理,例如mean subtraction, scaling, random cropping, mirroring。 常用的有:Input, ImageData.
-
Vision Layers層(卷積相關)例如,卷積層Convolution Layer, 池化層Pooling Layer等
-
迴圈網路層Recurrent Layers 例如,LSTM, RNN等。
-
Common Layers例如,Inner Product 全連線層,Dropout棄權層,等。
-
Normalization Layers(歸一化層)例如Local Response Normalization (LRN), Batch Normalization 。
-
Activation / Neuron Layers(啟用層),例如ReLU, Sigmoid等。
-
Utility Layers, 例如Flatten, Reshape等。
-
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
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;
}