1. 程式人生 > >Caffe原始碼解析3:Layer

Caffe原始碼解析3:Layer

layer這個類可以說是裡面最終的一個基本類了,深度網路呢就是一層一層的layer,相互之間通過blob傳輸資料連線起來。首先layer必須要實現一個forward function,前遞函式當然功能可以自己定義啦,在forward中呢他會從input也就是Layer的bottom,對了caffe裡面網路的前一層是叫bottom的,從bottom中獲取blob,並且計算輸出的Blob,當然他們也會實現一個反向傳播,根據他們的input的blob以及output blob的error gradient 梯度誤差計算得到該層的梯度誤差。從公式中也可以看到:
\[\delta^l=((w^{l+1})^T\delta^{l+1}) \sigma'(z^l)\]

首先來看layer類的構造部分,以及Public部分的函式

template <typename Dtype>
class Layer {
 public:
  explicit Layer(const LayerParameter& param)
    : layer_param_(param), is_shared_(false) {
      // 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<Dtype>());
          blobs_[i]->FromProto(layer_param_.blobs(i));
        }
      }
    }
  virtual ~Layer() {}

首先獲得當前網路的Phase,是train還是test,在初始化列表初始化LayerParameter,之後blobs_這裡存放的是一個指向blob類的shared_ptr指標的一個vector,在這裡是申請空間,然後將傳入的layer_param中的blob拷貝過來。

void SetUp(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {
    InitMutex();
    CheckBlobCounts(bottom, top);
    LayerSetUp(bottom, top);
    Reshape(bottom, top);
    SetLossWeights(top);
  }

這裡是Setup函式,首先check 這個bottom和top的blob是否正確,再呼叫Layersetup對每一具體的層做進一步設定,之後再做reshape來設定top blobs和internal buffer。最後再設定loss weight multiplier 的blob對每一個非零的loss和weight,一般這個方法被繼承之後是不會被重寫的。

virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top)
virtual inline bool ShareInParallel() 
inline bool IsShared() const
inline void SetShared(bool is_shared)

LayerSetup就是對具體某一個layer的setup,被上面的那個函式所呼叫,ShareInParallel和IsShared和SetShared分別是用來返回並行狀態和獲得這一layer是否被多個nets所共享,預設是除了data layer都是關閉的。在多個GPU下的Train階段以及share是true的情況下,is_shared將會被置成true。

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

這個reshape主要是layer用來根據輸入的blob調節Internal buffer以及輸出的Blob的

注意

接下來是幾個最重要的函式,首先是Forward.這其實是一個裝飾器,繼承之後在呼叫的呼叫其相應的forward_cpu或者forward_gpu,根據輸入的input data blob計算相應的output data blob,同時會反應這一層layer的total loss.

inline Dtype Forward(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top);

這裡是BackWard,實現的是反向傳播,也就是給定top blob額error gradient 計算得到bottom的error gradient。其輸入時 output blobs ,在Ouput blobs裡面的diff儲存的就是其相應的error gradients。其中propagate_down這個引數跟Bottom的長度是一樣的,每一個Index用來指定是否需要反向傳播error gradients 到對應的bottom blob。而bottom 這裡面的diff 區域存放的就是BackWard計算出來相應的gradient error.

inline void Backward(const vector<Blob<Dtype>*>& top,
      const vector<bool>& propagate_down,
      const vector<Blob<Dtype>*>& bottom);

如果自己你要實現一個Layer的話,那麼Forward_cpu和Backward_cpu 以及gpu(可選),應該要有自己的實現。

傑下來幾個函式比較簡單,統一做說明

vector<shared_ptr<Blob<Dtype> > >& blobs()\\返回blobs
const LayerParameter& layer_param() \\返回layer 的引數parameter
virtual void ToProto(LayerParameter* param, bool write_diff = false)\\將層引數寫到Protobuffer裡
inline Dtype loss(const int top_index) const \\給定index返回相應的scalar loss
inline void set_loss(const int top_index, const Dtype value)\\給定Index設定loss
virtual inline const char* type()\\返回layer的type

一下幾個函式主要獲得bottom或者top blob的數量狀態,比較簡單,看名字即可

virtual inline int ExactNumBottomBlobs() 
virtual inline int MinBottomBlobs() 
virtual inline int MaxBottomBlobs() 
virtual inline int ExactNumTopBlobs() 
virtual inline int MinTopBlobs() 
virtual inline int MaxTopBlobs() 
virtual inline bool EqualNumBottomTopBlobs()
virtual inline bool AutoTopBlobs() 

AllowforceBackward用來設定是否強制梯度返回,因為有些層其實不需要梯度資訊 ,後面兩個函式分別檢視以及設定是是否需要計算梯度。

virtual inline bool AllowForceBackward(const int bottom_index)
inline bool param_propagate_down(const int param_id)
inline void set_param_propagate_down(const int param_id, const bool value)

好,我們再後下面看,幾個變數和函式都是保護變數

LayerParameter layer_param_; \\儲存layer的引數 parameter
Phase phase_; \\標定階段是train還是test
vector<shared_ptr<Blob<Dtype> > > blobs_; \\是Blob的一個集合,儲存了learnbale引數
vector<bool> param_propagate_down_;\\標誌位是否要計算param blob的梯度
vector<Dtype> loss_;\\用來表明那個top blob 有非零的權重

下面這幾個函式,分別是計算cpu和gpu模式下的正向和反向傳播

virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top)
virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& 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)

這個函式被setup呼叫,主要是check bottom和top 的blob是否match,這裡面用了上面提到的ExactBottomBlobs()等函式

virtual void CheckBlobCounts(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top)

SetLoss是非常重要的一個步驟,是被SetUp呼叫來初始化top bottom的weights,並且儲存非零的loss weights 在diff blob裡面

inline void SetLossWeights(const vector<Blob<Dtype>*>& top)

私有變數和函式如下,東西比較少,主要是對並行中的鎖進行控制

bool is_shared_; //標記該layer是否被其他nets所共享
shared_ptr<boost::mutex> forward_mutex_;//若該layer被shared,則需要這個mutex序列保持forward過程的正常執行
void InitMutex();//初始化forward 的 mutex
void Lock();//locak mutex
void Unlock();//unlock mutex這一看就明白了

最後其實proto裡面有個layer_parameter是挺重要的一個結構,儲存了layer的大量資訊,這個具體可以到proto資料夾下檢視,這裡就暫時不列出了。