<轉載>conv_layer和deconv_layer解讀

卷積計算是caffe代碼裏的核心部分之一,有必要理解其內部原理及實現過程。


首先,要知道caffe裏的卷積核都是三維的

在caffe中卷積核是三維的還是二維的?


下面分割線之間的內容來自http://blog.csdn.NET/u014114990/article/details/51125776

/******************************************************************************************/

下面講一下,caffe中的實現。


Caffe中的卷積計算是將卷積核矩陣和輸入圖像矩陣變換爲兩個大的矩陣A與B,然後A與B進行矩陣相乘得到結果C(利用GPU進行矩陣相乘的高效性),三個矩陣的說明如下:

(1)在矩陣A中

        M爲卷積核個數,K=k*k,等於卷積核大小,即第一個矩陣每行爲一個卷積核向量(是將二維的卷積核轉化爲一維),總共有M行,表示有M個卷積核。

(2)在矩陣B中

        N=((image_h + 2*pad_h – kernel_h)/stride_h+ 1)*((image_w +2*pad_w – kernel_w)/stride_w + 1)

        image_h:輸入圖像的高度

        image_w:輸入圖像的寬度

        pad_h:在輸入圖像的高度方向兩邊各增加pad_h個單位長度(因爲有兩邊,所以乘以2)

        pad_w:在輸入圖像的寬度方向兩邊各增加pad_w個單位長度(因爲有兩邊,所以乘以2)

        kernel_h:卷積核的高度

        kernel_w:卷積核的寬度

        stride_h:高度方向的滑動步長;

        stride_w:寬度方向的滑動步長。

        因此,N爲輸出圖像大小的長寬乘積,也是卷積核在輸入圖像上滑動可截取的最大特徵數。

        K=k*k,表示利用卷積核大小的框在輸入圖像上滑動所截取的數據大小,與卷積核大小一樣大。

(3)在矩陣C中

        矩陣C爲矩陣A和矩陣B相乘的結果,得到一個M*N的矩陣,其中每行表示一個輸出圖像即feature map,共有M個輸出圖像(輸出圖像數目等於卷積核數目)


 (在Caffe中是使用src/caffe/util/im2col.cu中的im2col和col2im來完成矩陣的變形和還原操作)

 

 舉個例子(方便理解):

     假設有兩個卷積核爲,因此M=2,kernel_h=2,kernel_w=2,K= kernel_h * kernel_w=4

     輸入圖像矩陣爲,因此image_h=3,image_w=3,令邊界擴展爲0即pad_h=0,pad_w=0,滑動步長爲1,即stride_h=1,stride_w=1

     故N=[(3+2*0-2)/1+1]*[ (3+2*0-2)/1+1]=2*2=4

 

    A矩陣(M*K)爲(一行爲一個卷積核),B矩陣(K*N)爲(B矩陣的每一列爲一個卷積核要卷積的大小)

    A 矩陣的由來:::    

    B矩陣的由來:(caffe 有 imtocol.cpp代碼,專門用於實現) 



    C=A*B=*=

    C中的分別爲兩個輸出特徵圖像即feature map。驗證了 有幾個卷積核就有幾個feature map

 

    在Caffe源碼中,src/caffe/util/math_functions.cu(如果使用CPU則是src/util/math_functions.cpp)中的caffe_gpu_gemm()函數,其中有兩個矩陣A(M*K)

    與矩陣    B(K*N),大家可以通過輸出M、K、N的值即相應的矩陣內容來驗證上述的原理,代碼中的C矩陣與上述的C矩陣不一樣,代碼中的C矩陣存儲的是偏置bias,

    是A  與B相乘後得到M*N大小的矩陣,然後再跟這個存儲偏置的矩陣C相加完成卷積過程。如果是跑Mnist訓練網絡的話,可以看到第一個卷積層卷積過程中,

    M=20,K=25,N=24*24=576。

  (caffe中涉及卷積具體過程的文件主要有:src/caffe/layers/conv_layer.cu、src/caffe/layers/base_conv_layer.cpp、                src/caffe/util/math_functions.cu、src/caffe/util/im2col.cu)

    另外大家也可以參考知乎上賈揚清大神的回答,幫助理解http://www.zhihu.com/question/28385679

  (對於他給出的ppt上的C表示圖像通道個數,如果是RGB圖像則通道數爲3,對應於caffe代碼中的變量爲src/caffe/layers/base_conv_layer.cpp中

     函數forward_gpu_gemm中的group_)

/********************************************************************************************************/


梳理caffe代碼im2col(十七)

caffe中卷積計算詳解

Caffe源碼解析5:Conv_Layer

Caffe 代碼閱讀-卷積

Caffe Convolutional Layer 記錄

Caffe源碼學習系列二—-卷積層

caffe卷積層代碼閱讀筆記

卷積運算轉換爲矩陣乘法

github上關於卷積操作的可視化介紹

A guide to convolution arithmetic for deep learning

在 Caffe 中如何計算卷積?


Caffe源碼(四):base_conv_layer 分析

梳理caffe代碼base_conv_layer(十八)

其中caffe_cpu_gemm是對cblas_dgemm函數的封裝


Caffe Convolutional Layer 記錄






先看base_conv_layer

成員數據如下

/// @brief The spatial dimensions of a filter kernel.

Blob<int> kernel_shape_;
  /// @brief The spatial dimensions of the stride.
  Blob<int> stride_;
  /// @brief The spatial dimensions of the padding.
  Blob<int> pad_;
  /// @brief The spatial dimensions of the dilation.
  Blob<int> dilation_;
  /// @brief The spatial dimensions of the convolution input.
  Blob<int> conv_input_shape_;
  /// @brief The spatial dimensions of the col_buffer.
  vector<int> col_buffer_shape_;
  /// @brief The spatial dimensions of the output.
  vector<int> output_shape_;
  const vector<int>* bottom_shape_;


  int num_spatial_axes_;
  int bottom_dim_;
  int top_dim_;


  int channel_axis_;
  int num_;
  int channels_;
  int group_;
  int out_spatial_dim_;
  int weight_offset_;
  int num_output_;
  bool bias_term_;
  bool is_1x1_;
  bool force_nd_im2col_;



對於卷積層中的卷積操作,還有一個group的概念要說明一下,groups是代表filter 組的個數。引入gruop主要是爲了選擇性的連接卷基層的輸入端和輸出端的channels,否則參數會太多。每一個group 和1/ group的input 通道和 1/group 的output通道進行卷積操作。比如有4個input, 8個output,那麼1-4屬於第一組,5-8屬於第二個gruop。



主要函數

1. LayerSetUp 函數:

template <typename Dtype>
void BaseConvolutionLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {
  CHECK_EQ(4, bottom[0]->num_axes()) << "Input must have 4 axes, "
      << "corresponding to (num, channels, height, width)";
  // Configure the kernel size, padding, stride, and inputs.
  ConvolutionParameter conv_param = this->layer_param_.convolution_param();
  CHECK(!conv_param.has_kernel_size() !=
      !(conv_param.has_kernel_h() && conv_param.has_kernel_w()))
      << "Filter size is kernel_size OR kernel_h and kernel_w; not both";
  CHECK(conv_param.has_kernel_size() ||
      (conv_param.has_kernel_h() && conv_param.has_kernel_w()))
      << "For non-square filters both kernel_h and kernel_w are required.";
  CHECK((!conv_param.has_pad() && conv_param.has_pad_h()
      && conv_param.has_pad_w())
      || (!conv_param.has_pad_h() && !conv_param.has_pad_w()))
      << "pad is pad OR pad_h and pad_w are required.";
  CHECK((!conv_param.has_stride() && conv_param.has_stride_h()
      && conv_param.has_stride_w())
      || (!conv_param.has_stride_h() && !conv_param.has_stride_w()))
      << "Stride is stride OR stride_h and stride_w are required.";
  if (conv_param.has_kernel_size()) {
    kernel_h_ = kernel_w_ = conv_param.kernel_size();
  } else {
    kernel_h_ = conv_param.kernel_h();
    kernel_w_ = conv_param.kernel_w();
  }//用戶自定義kernel size 的兩種方式
  CHECK_GT(kernel_h_, 0) << "Filter dimensions cannot be zero.";
  CHECK_GT(kernel_w_, 0) << "Filter dimensions cannot be zero.";
  if (!conv_param.has_pad_h()) {
    pad_h_ = pad_w_ = conv_param.pad();
  } else {
    pad_h_ = conv_param.pad_h();
    pad_w_ = conv_param.pad_w();
  }
  if (!conv_param.has_stride_h()) {
    stride_h_ = stride_w_ = conv_param.stride();
  } else {
    stride_h_ = conv_param.stride_h();
    stride_w_ = conv_param.stride_w();
  }
  // Special case: im2col is the identity for 1x1 convolution with stride 1
  // and no padding, so flag for skipping the buffer and transformation.
  is_1x1_ = kernel_w_ == 1 && kernel_h_ == 1
      && stride_h_ == 1 && stride_w_ == 1 && pad_h_ == 0 && pad_w_ == 0;
  // Configure output channels and groups.
  channels_ = bottom[0]->channels();
  num_output_ = this->layer_param_.convolution_param().num_output();
  CHECK_GT(num_output_, 0);
  group_ = this->layer_param_.convolution_param().group();
  CHECK_EQ(channels_ % group_, 0);
  CHECK_EQ(num_output_ % group_, 0)
      << "Number of output should be multiples of group.";
      //channel 和 輸出 feature map 個數必須爲group的整數倍,每個group中只用本group的featrue map
  if (reverse_dimensions()) {
    conv_out_channels_ = channels_;
    conv_in_channels_ = num_output_;
  } else {
    conv_out_channels_ = num_output_;//用戶指定輸出feature map的數量
    conv_in_channels_ = channels_;
  }
  // Handle the parameters: weights and biases.
  // - blobs_[0] holds the filter weights
  // - blobs_[1] holds the biases (optional)
  bias_term_ = this->layer_param_.convolution_param().bias_term();//默認爲 true
  if (this->blobs_.size() > 0) {
    LOG(INFO) << "Skipping parameter initialization";
  } else {
    if (bias_term_) {
      this->blobs_.resize(2);
    } else {
      this->blobs_.resize(1);
    }
    // Initialize and fill the weights:
    // output channels x input channels per-group x kernel height x kernel width
    this->blobs_[0].reset(new Blob<Dtype>(
        conv_out_channels_, conv_in_channels_ / group_, kernel_h_, kernel_w_));
    shared_ptr<Filler<Dtype> > weight_filler(GetFiller<Dtype>(
        this->layer_param_.convolution_param().weight_filler()));
    weight_filler->Fill(this->blobs_[0].get()); //用weight_filler初始化
    // If necessary, initialize and fill the biases.
    if (bias_term_) {
      vector<int> bias_shape(1, num_output_);
      this->blobs_[1].reset(new Blob<Dtype>(bias_shape));
      shared_ptr<Filler<Dtype> > bias_filler(GetFiller<Dtype>(
          this->layer_param_.convolution_param().bias_filler()));
      bias_filler->Fill(this->blobs_[1].get());
    }
  }
  // Propagate gradients to the parameters (as directed by backward pass).
  this->param_propagate_down_.resize(this->blobs_.size(), true);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93

2.Reshape 函數:

template <typename Dtype>
void BaseConvolutionLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {
  CHECK_EQ(4, bottom[0]->num_axes()) << "Input must have 4 axes, "
      << "corresponding to (num, channels, height, width)";//blob是四維數組
  num_ = bottom[0]->num();
  height_ = bottom[0]->height();
  width_ = bottom[0]->width();
  CHECK_EQ(bottom[0]->channels(), channels_) << "Input size incompatible with"
    " convolution kernel.";
  // TODO: generalize to handle inputs of different shapes.
  for (int bottom_id = 1; bottom_id < bottom.size(); ++bottom_id) {
    CHECK_EQ(num_, bottom[bottom_id]->num()) << "Inputs must have same num.";
    CHECK_EQ(channels_, bottom[bottom_id]->channels())
        << "Inputs must have same channels.";
    CHECK_EQ(height_, bottom[bottom_id]->height())
        << "Inputs must have same height.";
    CHECK_EQ(width_, bottom[bottom_id]->width())
        << "Inputs must have same width.";
  }//有多少個bottom 就有多少個top輸出,要求每個bottom有相同的shape,因爲用的是同一組filter
  // Shape the tops.
  compute_output_shape();//在conv_layer中定義,計算輸出feature map 的shape
  for (int top_id = 0; top_id < top.size(); ++top_id) {
    top[top_id]->Reshape(num_, num_output_, height_out_, width_out_);
  }
  if (reverse_dimensions()) {
    conv_in_height_ = height_out_;//根據pad情況計算所得輸出top 的height,具體如何計算在conv_layer的compute_output_shape()中定義
    conv_in_width_ = width_out_;
    conv_out_spatial_dim_ = height_ * width_;
  } else {
    conv_in_height_ = height_; //輸入bottom 的height
    conv_in_width_ = width_;   //輸入bottom 的width
    conv_out_spatial_dim_ = height_out_ * width_out_;
  }
  kernel_dim_ = conv_in_channels_ * kernel_h_ * kernel_w_;//對應一個輸出的feature map
  weight_offset_ = conv_out_channels_ * kernel_dim_ / group_ / group_;
  col_offset_ = kernel_dim_ * conv_out_spatial_dim_ / group_;
  output_offset_ = conv_out_channels_ * conv_out_spatial_dim_ / group_;
  // The im2col result buffer will only hold one image at a time to avoid
  // overly large memory usage. In the special case of 1x1 convolution
  // it goes lazily unused to save memory.
  if (reverse_dimensions()) {
    col_buffer_.Reshape(1, kernel_dim_, height_, width_);
  } else {
    col_buffer_.Reshape(1, kernel_dim_, height_out_, width_out_);
  }
  // Set up the all ones "bias multiplier" for adding biases by BLAS
  if (bias_term_) {
    vector<int> bias_multiplier_shape(1, height_out_ * width_out_);
    bias_multiplier_.Reshape(bias_multiplier_shape);
    caffe_set(bias_multiplier_.count(), Dtype(1),
        bias_multiplier_.mutable_cpu_data());
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

3.forward_cpu_gemm 函數:


template <typename Dtype>
void BaseConvolutionLayer<Dtype>::forward_cpu_gemm(const Dtype* input,
    const Dtype* weights, Dtype* output, bool skip_im2col) {
  const Dtype* col_buff = input;
  if (!is_1x1_) {
    if (!skip_im2col) {
      conv_im2col_cpu(input, col_buffer_.mutable_cpu_data());
    }
    col_buff = col_buffer_.cpu_data();
  }
  for (int g = 0; g < group_; ++g) {
    caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, conv_out_channels_ /
        group_, conv_out_spatial_dim_, kernel_dim_ / group_,
        (Dtype)1., weights + weight_offset_ * g, col_buff + col_offset_ * g,
        (Dtype)0., output + output_offset_ * g);
  }
}// 實現卷積操作

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

4.forward_cpu_bias 函數:

template <typename Dtype>
void BaseConvolutionLayer<Dtype>::forward_cpu_bias(Dtype* output,
    const Dtype* bicas) {
  caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, num_output_,
      height_out_ * width_out_, 1, (Dtype)1., bias, bias_multiplier_.cpu_data(),
      (Dtype)1., output);
}//卷積後加bias
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

4.backward_cpu_gemm函數:

template <typename Dtype>
void BaseConvolutionLayer<Dtype>::backward_cpu_gemm(const Dtype* output,
    const Dtype* weights, Dtype* input) {
  Dtype* col_buff = col_buffer_.mutable_cpu_data();
  if (is_1x1_) {
    col_buff = input;
  }
  for (int g = 0; g < group_; ++g) {
    caffe_cpu_gemm<Dtype>(CblasTrans, CblasNoTrans, kernel_dim_ / group_,
        conv_out_spatial_dim_, conv_out_channels_ / group_,
        (Dtype)1., weights + weight_offset_ * g, output + output_offset_ * g,
        (Dtype)0., col_buff + col_offset_ * g);
  }
  if (!is_1x1_) {
    conv_col2im_cpu(col_buff, input);
  }計算關於bottom data的導數以便傳給下一層
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

5.weight_cpu_gemm 函數:

template <typename Dtype>
void BaseConvolutionLayer<Dtype>::weight_cpu_gemm(const Dtype* input,
    const Dtype* output, Dtype* weights) {
  const Dtype* col_buff = input;
  if (!is_1x1_) {
    conv_im2col_cpu(input, col_buffer_.mutable_cpu_data());
    col_buff = col_buffer_.cpu_data();
  }
  for (int g = 0; g < group_; ++g) {
    caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasTrans, conv_out_channels_ / group_,
        kernel_dim_ / group_, conv_out_spatial_dim_,
        (Dtype)1., output + output_offset_ * g, col_buff + col_offset_ * g,
        (Dtype)1., weights + weight_offset_ * g);
  }
}//計算關於weight的導數用於更新。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

6.backward_cpu_bias 函數:

template <typename Dtype> 

void BaseConvolutionLayer<Dtype>::backward_cpu_bias(Dtype* bias, 

    const Dtype* input) { 

  caffe_cpu_gemv<Dtype>(CblasNoTrans, num_output_, height_out_ * width_out_, 1., 

      input, bias_multiplier_.cpu_data(), 1., bias); 

} 計算關於bias的導數
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6


其中用到的一些矩陣運算函數在math_functions.cpp裏實現


主要函數

math_function 定義了caffe 中用到的一些矩陣操作和數值計算的一些函數,這裏以float類型爲例做簡單的分析

1. caffe_cpu_gemm 函數:

template<>
void caffe_cpu_gemm<float>(const CBLAS_TRANSPOSE TransA,
    const CBLAS_TRANSPOSE TransB, const int M, const int N, const int K,
    const float alpha, const float* A, const float* B, const float beta,
    float* C) {
  int lda = (TransA == CblasNoTrans) ? K : M;
  int ldb = (TransB == CblasNoTrans) ? N : K;
  cblas_sgemm(CblasRowMajor, TransA, TransB, M, N, K, alpha, A, lda, B,
      ldb, beta, C, N);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

功能: C=alpha*A*B+beta*C 
A,B,C 是輸入矩陣(一維數組格式) 
CblasRowMajor :數據是行主序的(二維數據也是用一維數組儲存的) 
TransA, TransB:是否要對A和B做轉置操作(CblasTrans CblasNoTrans) 
M: A、C 的行數 
N: B、C 的列數 
K: A 的列數, B 的行數 
lda : A的列數(不做轉置)行數(做轉置) 
ldb: B的列數(不做轉置)行數(做轉置)

2. caffe_cpu_gemv 函數:

template <>
void caffe_cpu_gemv<float>(const CBLAS_TRANSPOSE TransA, const int M,
    const int N, const float alpha, const float* A, const float* x,
    const float beta, float* y) {
  cblas_sgemv(CblasRowMajor, TransA, M, N, alpha, A, N, x, 1, beta, y, 1);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

功能: y=alpha*A*x+beta*y 
其中X和Y是向量,A 是矩陣 
M:A 的行數 
N:A 的列數 
cblas_sgemv 中的 參數1 表示對X和Y的每個元素都進行操作

3.caffe_axpy 函數:

template <>
void caffe_axpy<float>(const int N, const float alpha, const float* X,
    float* Y) { cblas_saxpy(N, alpha, X, 1, Y, 1); }
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

功能: Y=alpha*X+Y 
N:爲X和Y中element的個數

4.caffe_set 函數:

template <typename Dtype>
void caffe_set(const int N, const Dtype alpha, Dtype* Y) {
  if (alpha == 0) {
    memset(Y, 0, sizeof(Dtype) * N);  // NOLINT(caffe/alt_fn)
    return;
  }
  for (int i = 0; i < N; ++i) {
    Y[i] = alpha; 
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

功能:用常數 alpha 對 Y 進行初始化 
函數 void *memset(void *buffer, char c, unsigned count) 一般爲新申請的內存做初始化,功能是將buffer所指向內存中的每個字節的內容全部設置爲c指定的ASCII值, count爲塊的大小

5.caffe_add_scalar 函數:

template <>
void caffe_add_scalar(const int N, const float alpha, float* Y) {
  for (int i = 0; i < N; ++i) {
    Y[i] += alpha;
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

功能: 給 Y 的每個 element 加上常數 alpha

6.caffe_copy 函數:

template <typename Dtype>
void caffe_copy(const int N, const Dtype* X, Dtype* Y) {
  if (X != Y) {
    if (Caffe::mode() == Caffe::GPU) {
#ifndef CPU_ONLY
      // NOLINT_NEXT_LINE(caffe/alt_fn)
      CUDA_CHECK(cudaMemcpy(Y, X, sizeof(Dtype) * N, cudaMemcpyDefault));
#else
      NO_GPU;
#endif
    } else {
      memcpy(Y, X, sizeof(Dtype) * N);  // NOLINT(caffe/alt_fn)
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

函數 void *memcpy(void *dest, void *src, unsigned int count) 把src所指向的內存區域 copy到dest所指向的內存區域, count爲塊的大小

7.caffe_scal 函數:

template <>
void caffe_scal<float>(const int N, const float alpha, float *X) {
  cblas_sscal(N, alpha, X, 1);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

功能:X = alpha*X 
N: X中element的個數

8.caffeine_cup_axpby 函數:

template <>
void caffe_cpu_axpby<float>(const int N, const float alpha, const float* X,
                            const float beta, float* Y) {
  cblas_saxpby(N, alpha, X, 1, beta, Y, 1);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

功能:Y= alpha*X+beta*Y

9.caffe_add、 caffe_sub、 caffe_mul、 caffe_div 函數:

template <>
void caffe_add<float>(const int n, const float* a, const float* b,
    float* y) {
  vsAdd(n, a, b, y);
}
template <>
void caffe_sub<float>(const int n, const float* a, const float* b,
    float* y) {
  vsSub(n, a, b, y);
}

template <>
void caffe_mul<float>(const int n, const float* a, const float* b,
    float* y) {
  vsMul(n, a, b, y);
}

template <>
void caffe_div<float>(const int n, const float* a, const float* b,
    float* y) {
  vsDiv(n, a, b, y);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

功能:這四個函數分別實現element-wise的加減乘除(y[i] = a[i] + - * \ b[i])

10.caffe_powx、 caffe_sqr、 caffe_exp、 caffe_abs 函數:

template <>
void caffe_powx<float>(const int n, const float* a, const float b,
    float* y) {
  vsPowx(n, a, b, y);
}

template <>
void caffe_sqr<float>(const int n, const float* a, float* y) {
  vsSqr(n, a, y);
}


template <>
void caffe_exp<float>(const int n, const float* a, float* y) {
  vsExp(n, a, y);
}

template <>
void caffe_abs<float>(const int n, const float* a, float* y) {
    vsAbs(n, a, y);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

功能 : 同樣是element-wise操作,分別是y[i] = a[i] ^ b, y[i] = a[i]^2,y[i] = exp(a[i] ),y[i] = |a[i] |

11.int caffe_rng_rand 函數:

unsigned int caffe_rng_rand() {
  return (*caffe_rng())();
}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

功能:返回一個隨機數

12.caffe_nextafer 函數:

template <typename Dtype>
Dtype caffe_nextafter(const Dtype b) {
  return boost::math::nextafter<Dtype>(
      b, std::numeric_limits<Dtype>::max());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

功能 : 返回 b 最大方向上可以表示的最接近的數值。

13.caffe_cpu_strided_dot 函數:

template <>
double caffe_cpu_strided_dot<double>(const int n, const double* x,
    const int incx, const double* y, const int incy) {
  return cblas_ddot(n, x, incx, y, incy);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

功能: 返回 vector X 和 vector Y 的內積。 
incx, incy : 步長,即每隔incx 或 incy 個element 進行操作。

14.caffe_cpu_hamming_distance 函數:

template <>
int caffe_cpu_hamming_distance<float>(const int n, const float* x,
                                  const float* y) {
  int dist = 0;
  for (int i = 0; i < n; ++i) {
    dist += __builtin_popcount(static_cast<uint32_t>(x[i]) ^
                               static_cast<uint32_t>(y[i]));
  }
  return dist;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

功能:返回 x 和 y 之間的海明距離。(兩個等長字符串之間的海明距離是兩個字符串對應位置的不同字符的個數。)

15. caffe_cpu_asum 函數:

template <>
float caffe_cpu_asum<float>(const int n, const float* x) {
  return cblas_sasum(n, x, 1);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

功能:計算 vector x 的所有element的絕對值之和。

16.caffe_cpu_scale 函數:

template <>
void caffe_cpu_scale<float>(const int n, const float alpha, const float *x,
                            float* y) {
  cblas_scopy(n, x, 1, y, 1);
  cblas_sscal(n, alpha, y, 1);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

功能:y = alpha*x




關於deconv

這個概念很混亂,沒有統一的定義,在不同的地方出現,意義卻不一樣。

上採樣的卷積層有很多名字:全卷積(full convolution),網絡內上採樣( in-network upsampling),微步幅卷積(fractionally-strided convolution),反向卷積(backwards convolution),去卷積(deconvolution),上卷積(upconvolution),以及轉置卷積(transposed convolution)。用「去卷積」這個術語是非常不推薦的,因爲這是一個過載的術語:在數學運算或計算機視覺中的其他應用有着完全不同的含義。



神經網絡中,怎樣計算caffe中反捲積層(deconv)的感受野(receptive field)

What are deconvolutional layers?



deconv_layer.cpp

  1. #include <vector>  
  2.   
  3. #include “caffe/layers/deconv_layer.hpp”  
  4.   
  5. namespace caffe {  
  6.   
  7. template <typename Dtype>  
  8. void DeconvolutionLayer<Dtype>::compute_output_shape() {  
  9.   const int* kernel_shape_data = this->kernel_shape_.cpu_data();  
  10.   const int* stride_data = this->stride_.cpu_data();  
  11.   const int* pad_data = this->pad_.cpu_data();  
  12.   const int* dilation_data = this->dilation_.cpu_data();  
  13.   this->output_shape_.clear();  
  14.   for (int i = 0; i < this->num_spatial_axes_; ++i) {  
  15.     // i + 1 to skip channel axis  
  16.     const int input_dim = this->input_shape(i + 1);  
  17.     const int kernel_extent = dilation_data[i] * (kernel_shape_data[i] - 1) + 1;  
  18.     const int output_dim = stride_data[i] * (input_dim - 1)  
  19.         + kernel_extent - 2 * pad_data[i];  
  20.     this->output_shape_.push_back(output_dim);  
  21.   }  
  22. }  
  23.   
  24. template <typename Dtype>  
  25. void DeconvolutionLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,  
  26.       const vector<Blob<Dtype>*>& top) {  
  27.   const Dtype* weight = this->blobs_[0]->cpu_data();  
  28.   for (int i = 0; i < bottom.size(); ++i) {  
  29.     const Dtype* bottom_data = bottom[i]->cpu_data();  
  30.     Dtype* top_data = top[i]->mutable_cpu_data();  
  31.     for (int n = 0; n < this->num_; ++n) {  
  32.       this->backward_cpu_gemm(bottom_data + n * this->bottom_dim_, weight,  
  33.           top_data + n * this->top_dim_);  
  34.       if (this->bias_term_) {  
  35.         const Dtype* bias = this->blobs_[1]->cpu_data();  
  36.         this->forward_cpu_bias(top_data + n * this->top_dim_, bias);  
  37.       }  
  38.     }  
  39.   }  
  40. }  
  41.   
  42. template <typename Dtype>  
  43. void DeconvolutionLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,  
  44.       const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {  
  45.   const Dtype* weight = this->blobs_[0]->cpu_data();  
  46.   Dtype* weight_diff = this->blobs_[0]->mutable_cpu_diff();  
  47.   for (int i = 0; i < top.size(); ++i) {  
  48.     const Dtype* top_diff = top[i]->cpu_diff();  
  49.     const Dtype* bottom_data = bottom[i]->cpu_data();  
  50.     Dtype* bottom_diff = bottom[i]->mutable_cpu_diff();  
  51.     // Bias gradient, if necessary.  
  52.     if (this->bias_term_ && this->param_propagate_down_[1]) {  
  53.       Dtype* bias_diff = this->blobs_[1]->mutable_cpu_diff();  
  54.       for (int n = 0; n < this->num_; ++n) {  
  55.         this->backward_cpu_bias(bias_diff, top_diff + n * this->top_dim_);  
  56.       }  
  57.     }  
  58.     if (this->param_propagate_down_[0] || propagate_down[i]) {  
  59.       for (int n = 0; n < this->num_; ++n) {  
  60.         // Gradient w.r.t. weight. Note that we will accumulate diffs.  
  61.         if (this->param_propagate_down_[0]) {  
  62.           this->weight_cpu_gemm(top_diff + n * this->top_dim_,  
  63.               bottom_data + n * this->bottom_dim_, weight_diff);  
  64.         }  
  65.         // Gradient w.r.t. bottom data, if necessary, reusing the column buffer  
  66.         // we might have just computed above.  
  67.         if (propagate_down[i]) {  
  68.           this->forward_cpu_gemm(top_diff + n * this->top_dim_, weight,  
  69.               bottom_diff + n * this->bottom_dim_,  
  70.               this->param_propagate_down_[0]);  
  71.         }  
  72.       }  
  73.     }  
  74.   }  
  75. }  
  76.   
  77. #ifdef CPU_ONLY  
  78. STUB_GPU(DeconvolutionLayer);  
  79. #endif  
  80.   
  81. INSTANTIATE_CLASS(DeconvolutionLayer);  
  82. REGISTER_LAYER_CLASS(Deconvolution);  
  83.   
  84. }  // namespace caffe  
#include <vector>





#include "caffe/layers/deconv_layer.hpp" namespace caffe { template <typename Dtype> void DeconvolutionLayer<Dtype>::compute_output_shape() { const int* kernel_shape_data = this->kernel_shape_.cpu_data(); const int* stride_data = this->stride_.cpu_data(); const int* pad_data = this->pad_.cpu_data(); const int* dilation_data = this->dilation_.cpu_data(); this->output_shape_.clear(); for (int i = 0; i < this->num_spatial_axes_; ++i) { // i + 1 to skip channel axis const int input_dim = this->input_shape(i + 1); const int kernel_extent = dilation_data[i] * (kernel_shape_data[i] - 1) + 1; const int output_dim = stride_data[i] * (input_dim - 1) + kernel_extent - 2 * pad_data[i]; this->output_shape_.push_back(output_dim); } } template <typename Dtype> void DeconvolutionLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { const Dtype* weight = this->blobs_[0]->cpu_data(); for (int i = 0; i < bottom.size(); ++i) { const Dtype* bottom_data = bottom[i]->cpu_data(); Dtype* top_data = top[i]->mutable_cpu_data(); for (int n = 0; n < this->num_; ++n) { this->backward_cpu_gemm(bottom_data + n * this->bottom_dim_, weight, top_data + n * this->top_dim_); if (this->bias_term_) { const Dtype* bias = this->blobs_[1]->cpu_data(); this->forward_cpu_bias(top_data + n * this->top_dim_, bias); } } } } template <typename Dtype> void DeconvolutionLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top, const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) { const Dtype* weight = this->blobs_[0]->cpu_data(); Dtype* weight_diff = this->blobs_[0]->mutable_cpu_diff(); for (int i = 0; i < top.size(); ++i) { const Dtype* top_diff = top[i]->cpu_diff(); const Dtype* bottom_data = bottom[i]->cpu_data(); Dtype* bottom_diff = bottom[i]->mutable_cpu_diff(); // Bias gradient, if necessary. if (this->bias_term_ && this->param_propagate_down_[1]) { Dtype* bias_diff = this->blobs_[1]->mutable_cpu_diff(); for (int n = 0; n < this->num_; ++n) { this->backward_cpu_bias(bias_diff, top_diff + n * this->top_dim_); } } if (this->param_propagate_down_[0] || propagate_down[i]) { for (int n = 0; n < this->num_; ++n) { // Gradient w.r.t. weight. Note that we will accumulate diffs. if (this->param_propagate_down_[0]) { this->weight_cpu_gemm(top_diff + n * this->top_dim_, bottom_data + n * this->bottom_dim_, weight_diff); } // Gradient w.r.t. bottom data, if necessary, reusing the column buffer // we might have just computed above. if (propagate_down[i]) { this->forward_cpu_gemm(top_diff + n * this->top_dim_, weight, bottom_diff + n * this->bottom_dim_, this->param_propagate_down_[0]); } } } } } #ifdef CPU_ONLY STUB_GPU(DeconvolutionLayer); #endif INSTANTIATE_CLASS(DeconvolutionLayer); REGISTER_LAYER_CLASS(Deconvolution); } // namespace caffe







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章