1. 程式人生 > >Caffe 程式碼解讀之全連線層concat layer

Caffe 程式碼解讀之全連線層concat layer

今天,我們看一下caffe的拼接層,即將兩個或多個layer進行拼接。
首先,看一下caffe官方文件。
concat

同其他layer一樣,分為setup、reshape、Forward_cpu、Backward_cpu。

//concat_layer 用來實現兩個或者多個blob的連結,即多輸入一輸出
//支援在num 維度上的連結(concat_dim = 0 : (n1+n2+...+nk)∗c∗h∗w )
//和channel維度上的連結(concat_dim = 1 : n∗(c1+c2+...+ck)∗h∗w)。

//axis ,dim :0 為 num 維度連結,1 為 channel 維度連結
//這裡需要給出axis或concat_dim template <typename Dtype> void ConcatLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { const ConcatParameter& concat_param = this->layer_param_.concat_param(); CHECK(!(concat_param.has_axis() && concat_param.has_concat_dim())) << "Either axis or concat_dim should be specified; not both."
; } template <typename Dtype> void ConcatLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { //獲取axis,確定拼接哪一維度 const int num_axes = bottom[0]->num_axes(); const ConcatParameter& concat_param = this->layer_param_.concat_param(); //以下都在獲取、判斷axis的維度
if (concat_param.has_concat_dim()) { concat_axis_ = static_cast<int>(concat_param.concat_dim()); // Don't allow negative indexing for concat_dim, a uint32 -- almost // certainly unintended. CHECK_GE(concat_axis_, 0) << "casting concat_dim from uint32 to int32 " << "produced negative result; concat_dim must satisfy " << "0 <= concat_dim < " << kMaxBlobAxes; CHECK_LT(concat_axis_, num_axes) << "concat_dim out of range."; } else { concat_axis_ = bottom[0]->CanonicalAxisIndex(concat_param.axis()); } // Initialize with the first blob. //這裡有一點需要解釋,可以看到,bottom型別為 vector<Blob<Dtype>*>,這裡只需要使用bottom[0] //給shape賦值就好,其實botom本身就是一個Blob的vector //比如:我要將兩個layer拼接,那麼久有bottom[0]以及bottom[1] vector<int> top_shape = bottom[0]->shape(); //concat_axis_ = 0 : num_concats_=num;concat_axis_ = 1 : num_concats_=num x channel; num_concats_ = bottom[0]->count(0, concat_axis_); //concat_axis_ = 0 : concat_input_size_=channel x height x width; //concat_axis_ = 1 : concat_input_size_=height x width; concat_input_size_ = bottom[0]->count(concat_axis_ + 1); int bottom_count_sum = bottom[0]->count(); //檢測num_axes拼接的層是否相同,num_axes為維度資訊 for (int i = 1; i < bottom.size(); ++i) { CHECK_EQ(num_axes, bottom[i]->num_axes()) << "All inputs must have the same #axes."; for (int j = 0; j < num_axes; ++j) { if (j == concat_axis_) { continue; } CHECK_EQ(top_shape[j], bottom[i]->shape(j)) << "All inputs must have the same shape, except at concat_axis."; } bottom_count_sum += bottom[i]->count(); top_shape[concat_axis_] += bottom[i]->shape(concat_axis_); } top[0]->Reshape(top_shape); CHECK_EQ(bottom_count_sum, top[0]->count()); }

1、這裡有一點需要解釋,可以看到,bottom型別為 vector blob,這裡只需要使用bottom[0]給shape賦值就好,其實bottom本身就是一個Blob的vector。
2、CHECK_**,這裡給小白們解釋一下,就是判斷是否相等、小於、大於
這裡寫圖片描述
3、 count,這看到有好多的count函式,這些函式在blob層實現,解釋如下:

inline int count() const { return count_; }

  /**
   * @brief Compute the volume of a slice; i.e., the product of dimensions
   *        among a range of axes.
   *
   * @param start_axis The first axis to include in the slice.
   *
   * @param end_axis The first axis to exclude from the slice.
   */
  inline int count(int start_axis, int end_axis) const {
    CHECK_LE(start_axis, end_axis);
    CHECK_GE(start_axis, 0);
    CHECK_GE(end_axis, 0);
    CHECK_LE(start_axis, num_axes());
    CHECK_LE(end_axis, num_axes());
    int count = 1;
    for (int i = start_axis; i < end_axis; ++i) {
      count *= shape(i);
    }
    return count;
  }
  /**
   * @brief Compute the volume of a slice spanning from a particular first
   *        axis to the final axis.
   *
   * @param start_axis The first axis to include in the slice.
   */
  inline int count(int start_axis) const {
    return count(start_axis, num_axes());
  }

前向傳播就是layer的拼接

template <typename Dtype>
void ConcatLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {
  Dtype* top_data = top[0]->mutable_cpu_data();
  int offset_concat_axis = 0;
  const int top_concat_axis = top[0]->shape(concat_axis_);
  //遍歷所有輸入bottom
  for (int i = 0; i < bottom.size(); ++i) {
    const Dtype* bottom_data = bottom[i]->cpu_data();
    const int bottom_concat_axis = bottom[i]->shape(concat_axis_);
    //把 各個bottom data 拷貝到輸出 top data 的對應位置
    for (int n = 0; n < num_concats_; ++n) {
      //case 0:num x channel x h x w;case 1: channel x h x w
      //case 0:bottom_data + n x num x channel x h x w ;
      //case 1:bottom_data + n x channel x h x w ;
      caffe_copy(bottom_concat_axis * concat_input_size_,
          bottom_data + n * bottom_concat_axis * concat_input_size_,
          top_data + (n * top_concat_axis + offset_concat_axis)
              * concat_input_size_);
    }
    offset_concat_axis += bottom_concat_axis;
  }
}

反向傳播,就是layer層之間diff和data的傳播

//反向傳播就是對每一個bottom的 diff 做和 data 相同的連結
template <typename Dtype>
void ConcatLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
      const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
  const Dtype* top_diff = top[0]->cpu_diff();
  int offset_concat_axis = 0;
  const int top_concat_axis = top[0]->shape(concat_axis_);
  for (int i = 0; i < bottom.size(); ++i) {
    if (!propagate_down[i]) { continue; }
    Dtype* bottom_diff = bottom[i]->mutable_cpu_diff();
    const int bottom_concat_axis = bottom[i]->shape(concat_axis_);
    for (int n = 0; n < num_concats_; ++n) {
      caffe_copy(bottom_concat_axis * concat_input_size_, top_diff +
          (n * top_concat_axis + offset_concat_axis) * concat_input_size_,
          bottom_diff + n * bottom_concat_axis * concat_input_size_);
    }
    offset_concat_axis += bottom_concat_axis;
  }
}