caffe base_conv_layers.cpp 学习

来源:互联网 发布:淘宝最热门关键词 编辑:程序博客网 时间:2024/06/05 09:00
##################### In vision_layers.hpp #######################
%%%%%%%%%%%%%%%%%%%%%class BaseConvolutionLayer%%%%%%%%%%%%%%%%%%%%%%protected数据成员:/// @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 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_;private数据成员:int num_kernels_im2col_;int num_kernels_col2im_;int conv_out_channels_;int conv_in_channels_;int conv_out_spatial_dim_;int kernel_dim_;int col_offset_;int output_offset_;Blob<Dtype> col_buffer_;Blob<Dtype> bias_multiplier_;


##################### In im2col.cpp #######################
template <typename Dtype>void im2col_cpu(const Dtype* data_im, const int channels,    const int height, const int width, const int kernel_h, const int kernel_w,    const int pad_h, const int pad_w,    const int stride_h, const int stride_w,    Dtype* data_col) {  const int height_col = (height + 2 * pad_h - kernel_h) / stride_h + 1;  const int width_col = (width + 2 * pad_w - kernel_w) / stride_w + 1;  const int channels_col = channels * kernel_h * kernel_w;  for (int c_col = 0; c_col < channels_col; ++c_col) {    int w_offset = c_col % kernel_w;    int h_offset = (c_col / kernel_w) % kernel_h;    int c_im = c_col / kernel_h / kernel_w;    for (int h_col = 0; h_col < height_col; ++h_col) {      for (int w_col = 0; w_col < width_col; ++w_col) {        int h_im = h_col * stride_h - pad_h + h_offset;        int w_im = w_col * stride_w - pad_w + w_offset;        data_col[(c_col * height_col + h_col) * width_col + w_col] =            (h_im >= 0 && w_im >= 0 && h_im < height && w_im < width) ?            data_im[(c_im * height + h_im) * width + w_im] : 0;      }    }  }}该函数的功能就是将整张图片按照卷积的窗口大小切成子图(按照stride来切,可以有重叠),最后全部元素拉成一列。为啥要怎样做,因为对于这个小窗口内拉成一列的神经元来说来说,它们跟下一层神经元就是全连接了,所以这个小窗口里面的梯度计算就可以按照全连接来计算就可以了。可以这么想:将图片的通道数与kernel_h、kernel_w的乘积作为子图的channel,即channels_col。由于卷积窗口在输入上按照stride移动,所以总共有height_col×width_col个子图,而每个子图的通道数为channels_col,所以最终拉成的向量的维度是channels_col×height_col×width_col。而c_im, h_im, w_im则用来计算卷积层输入相应元素的位置信息。template <typename Dtype>inline void im2col_nd_core_cpu(const Dtype* data_input, const bool im2col,    const int num_spatial_axes, const int* im_shape, const int* col_shape,    const int* kernel_shape, const int* pad, const int* stride,    Dtype* data_output)该方法是针对输入的spatial dimension 不是二维的情况,但是在一般情况下处理的数据是图像时,其spatial dimension 是二维的,所以这里不细究。template <typename Dtype>void col2im_cpu(const Dtype* data_col, const int channels,    const int height, const int width, const int kernel_h, const int kernel_w,    const int pad_h, const int pad_w,    const int stride_h, const int stride_w,    Dtype* data_im) {  caffe_set(height * width * channels, Dtype(0), data_im);  const int height_col = (height + 2 * pad_h - kernel_h) / stride_h + 1;  const int width_col = (width + 2 * pad_w - kernel_w) / stride_w + 1;  const int channels_col = channels * kernel_h * kernel_w;  for (int c_col = 0; c_col < channels_col; ++c_col) {    int w_offset = c_col % kernel_w;    int h_offset = (c_col / kernel_w) % kernel_h;    int c_im = c_col / kernel_h / kernel_w;    for (int h_col = 0; h_col < height_col; ++h_col) {      for (int w_col = 0; w_col < width_col; ++w_col) {        int h_im = h_col * stride_h - pad_h + h_offset;        int w_im = w_col * stride_w - pad_w + w_offset;        if (h_im >= 0 && h_im < height && w_im >= 0 && w_im < width)          data_im[(c_im * height + h_im) * width + w_im] +=              data_col[(c_col * height_col + h_col) * width_col + w_col];      }    }  }}im2col_cpu()的相反过程


####################In caffe.proto#####################
在此文件中,有一段关于axis的注释:// The axis to interpret as "channels" when performing convolution.  // Preceding dimensions are treated as independent inputs;  // succeeding dimensions are treated as "spatial".  // With (N, C, H, W) inputs, and axis == 1 (the default), we perform  // N independent 2D convolutions, sliding C-channel (or (C/g)-channels, for  // groups g>1) filters across the spatial axes (H, W) of the input.  // With (N, C, D, H, W) inputs, and axis == 1, we perform  // N independent 3D convolutions, sliding (C/g)-channels  // filters across the spatial axes (D, H, W) of the input.  optional int32 axis = 16 [default = 1];


######################In base_conv_layer.cpp###################
template <typename Dtype>void BaseConvolutionLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,      const vector<Blob<Dtype>*>& top) {  // Configure the kernel size, padding, stride, and inputs.  ConvolutionParameter conv_param = this->layer_param_.convolution_param();  force_nd_im2col_ = conv_param.force_nd_im2col();//im2col,一般情况下num_spatial_axes_==2,即将2维图像拉成向量,但force_nd_im2col_针对的是更general的情况n-d“图像”  channel_axis_ = bottom[0]->CanonicalAxisIndex(conv_param.axis());  const int first_spatial_axis = channel_axis_ + 1;  const int num_axes = bottom[0]->num_axes();  num_spatial_axes_ = num_axes - first_spatial_axis;  CHECK_GE(num_spatial_axes_, 0);  vector<int> bottom_dim_blob_shape(1, num_spatial_axes_ + 1);  vector<int> spatial_dim_blob_shape(1, std::max(num_spatial_axes_, 1));//当num_spatial_axes_==2时,spatial_dim_blob_shape这个vector只包含一个元素且值为2  // Setup filter kernel dimensions (kernel_shape_).  kernel_shape_.Reshape(spatial_dim_blob_shape);//以spatial_dim_blob_shape为参数来构造一个Blob,即kernel_shape_,则这个Blob的维度信息只包含一个维度,值为2,也就是说这个Blob的count_==2。尽管这个Blob的维度信息只包含一个维度,因为在后续的计算(Im2col)中,我只关心这个Blob中的数据的值,而不关心这个Blob的shape信息,例如在Im2col()中,只要取出相应数值即可kernel_shape_.cpu_data()[0], kernel_shape_.cpu_data()[1],pad_.cpu_data()[0], pad_.cpu_data()[1]。  int* kernel_shape_data = kernel_shape_.mutable_cpu_data();  if (conv_param.has_kernel_h() || conv_param.has_kernel_w()) {    CHECK_EQ(num_spatial_axes_, 2)        << "kernel_h & kernel_w can only be used for 2D convolution.";    CHECK_EQ(0, conv_param.kernel_size_size())        << "Either kernel_size or kernel_h/w should be specified; not both.";    kernel_shape_data[0] = conv_param.kernel_h();//kernel_shape_data是一个二维数组    kernel_shape_data[1] = conv_param.kernel_w();  } else {    const int num_kernel_dims = conv_param.kernel_size_size();    CHECK(num_kernel_dims == 1 || num_kernel_dims == num_spatial_axes_)        << "kernel_size must be specified once, or once per spatial dimension "        << "(kernel_size specified " << num_kernel_dims << " times; "        << num_spatial_axes_ << " spatial dims);";      for (int i = 0; i < num_spatial_axes_; ++i) {        kernel_shape_data[i] =            conv_param.kernel_size((num_kernel_dims == 1) ? 0 : i);      }  }  for (int i = 0; i < num_spatial_axes_; ++i) {    CHECK_GT(kernel_shape_data[i], 0) << "Filter dimensions must be nonzero.";  }  // Setup stride dimensions (stride_).  stride_.Reshape(spatial_dim_blob_shape);  int* stride_data = stride_.mutable_cpu_data();  if (conv_param.has_stride_h() || conv_param.has_stride_w()) {    CHECK_EQ(num_spatial_axes_, 2)        << "stride_h & stride_w can only be used for 2D convolution.";    CHECK_EQ(0, conv_param.stride_size())        << "Either stride or stride_h/w should be specified; not both.";    stride_data[0] = conv_param.stride_h();    stride_data[1] = conv_param.stride_w();  } else {    const int num_stride_dims = conv_param.stride_size();    CHECK(num_stride_dims == 0 || num_stride_dims == 1 ||          num_stride_dims == num_spatial_axes_)        << "stride must be specified once, or once per spatial dimension "        << "(stride specified " << num_stride_dims << " times; "        << num_spatial_axes_ << " spatial dims);";    const int kDefaultStride = 1;    for (int i = 0; i < num_spatial_axes_; ++i) {      stride_data[i] = (num_stride_dims == 0) ? kDefaultStride :          conv_param.stride((num_stride_dims == 1) ? 0 : i);      CHECK_GT(stride_data[i], 0) << "Stride dimensions must be nonzero.";    }  }  // Setup pad dimensions (pad_).  pad_.Reshape(spatial_dim_blob_shape);  int* pad_data = pad_.mutable_cpu_data();  if (conv_param.has_pad_h() || conv_param.has_pad_w()) {    CHECK_EQ(num_spatial_axes_, 2)        << "pad_h & pad_w can only be used for 2D convolution.";    CHECK_EQ(0, conv_param.pad_size())        << "Either pad or pad_h/w should be specified; not both.";    pad_data[0] = conv_param.pad_h();    pad_data[1] = conv_param.pad_w();  } else {    const int num_pad_dims = conv_param.pad_size();    CHECK(num_pad_dims == 0 || num_pad_dims == 1 ||          num_pad_dims == num_spatial_axes_)        << "pad must be specified once, or once per spatial dimension "        << "(pad specified " << num_pad_dims << " times; "        << num_spatial_axes_ << " spatial dims);";    const int kDefaultPad = 0;    for (int i = 0; i < num_spatial_axes_; ++i) {      pad_data[i] = (num_pad_dims == 0) ? kDefaultPad :          conv_param.pad((num_pad_dims == 1) ? 0 : i);    }  }  // 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_ = true;  for (int i = 0; i < num_spatial_axes_; ++i) {    is_1x1_ &=        kernel_shape_data[i] == 1 && stride_data[i] == 1 && pad_data[i] == 0;    if (!is_1x1_) { break; }  }  // Configure output channels and groups.  channels_ = bottom[0]->shape(channel_axis_);  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.";  if (reverse_dimensions()) {    conv_out_channels_ = channels_;    conv_in_channels_ = num_output_;  } else {    conv_out_channels_ = num_output_;    conv_in_channels_ = channels_;  }  // Handle the parameters: weights and biases.  // - blobs_[0] holds the filter weights  // - blobs_[1] holds the biases (optional)  vector<int> weight_shape(2);  weight_shape[0] = conv_out_channels_;  weight_shape[1] = conv_in_channels_ / group_;  for (int i = 0; i < num_spatial_axes_; ++i) {    weight_shape.push_back(kernel_shape_data[i]);  }  bias_term_ = this->layer_param_.convolution_param().bias_term();  vector<int> bias_shape(bias_term_, num_output_);  if (this->blobs_.size() > 0) {    CHECK_EQ(1 + bias_term_, this->blobs_.size())        << "Incorrect number of weight blobs.";    if (weight_shape != this->blobs_[0]->shape()) {      Blob<Dtype> weight_shaped_blob(weight_shape);      LOG(FATAL) << "Incorrect weight shape: expected shape "          << weight_shaped_blob.shape_string() << "; instead, shape was "          << this->blobs_[0]->shape_string();    }    if (bias_term_ && bias_shape != this->blobs_[1]->shape()) {      Blob<Dtype> bias_shaped_blob(bias_shape);      LOG(FATAL) << "Incorrect bias shape: expected shape "          << bias_shaped_blob.shape_string() << "; instead, shape was "          << this->blobs_[1]->shape_string();    }    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>(weight_shape));//blobs_[0]的维度信息是四个维度,count_为四个维度的值相乘    shared_ptr<Filler<Dtype> > weight_filler(GetFiller<Dtype>(        this->layer_param_.convolution_param().weight_filler()));    weight_filler->Fill(this->blobs_[0].get());    // If necessary, initialize and fill the biases.    if (bias_term_) {      this->blobs_[1].reset(new Blob<Dtype>(bias_shape));//blobs_[1]的维度信息是1个维度,count_为num_output_      shared_ptr<Filler<Dtype> > bias_filler(GetFiller<Dtype>(          this->layer_param_.convolution_param().bias_filler()));      bias_filler->Fill(this->blobs_[1].get());    }  }  kernel_dim_ = this->blobs_[0]->count(1);//是一个三维维度的乘积  weight_offset_ = conv_out_channels_ * kernel_dim_ / group_;//写成(conv_out_channels_ / group_) * kernel_dim_更直观。这个offset是相对group分组来讲的。  // Propagate gradients to the parameters (as directed by backward pass).  this->param_propagate_down_.resize(this->blobs_.size(), true);}


template <typename Dtype>void BaseConvolutionLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,      const vector<Blob<Dtype>*>& top) {  const int first_spatial_axis = channel_axis_ + 1;  CHECK_EQ(bottom[0]->num_axes(), first_spatial_axis + num_spatial_axes_)      << "bottom num_axes may not change.";  num_ = bottom[0]->count(0, channel_axis_);  CHECK_EQ(bottom[0]->shape(channel_axis_), channels_)      << "Input size incompatible with convolution kernel.";  // TODO: generalize to handle inputs of different shapes.所有的输入bottom都必须有相同的shape  for (int bottom_id = 1; bottom_id < bottom.size(); ++bottom_id) {    CHECK(bottom[0]->shape() == bottom[bottom_id]->shape())        << "All inputs must have the same shape.";  }  // Shape the tops.卷积层应该默认有多少个bottom 就有多少个top输出  bottom_shape_ = &bottom[0]->shape();  compute_output_shape();  vector<int> top_shape(bottom[0]->shape().begin(),      bottom[0]->shape().begin() + channel_axis_);  top_shape.push_back(num_output_);  for (int i = 0; i < num_spatial_axes_; ++i) {    top_shape.push_back(output_shape_[i]);  }  for (int top_id = 0; top_id < top.size(); ++top_id) {    top[top_id]->Reshape(top_shape);  }  if (reverse_dimensions()) {    conv_out_spatial_dim_ = bottom[0]->count(first_spatial_axis);  } else {    conv_out_spatial_dim_ = top[0]->count(first_spatial_axis);  }  //group分,对conv_in_channels分组; 卷积窗口在输入“图像”上按步长滑动,(可以想象)形成了多个子图;然后将所有子图拉成一列,列的长度就是col_offset_。  col_offset_ = kernel_dim_ * conv_out_spatial_dim_;//col_offset_与im2col_cpu()函数中channels_col的计算是相似的,但是值并不相等,原因在于:channels_col是将卷积层输入的通道数conv_in_channels_用于相乘,但kernel_dim_只用到了一部分channel,即conv_in_channels_/group_ 。  output_offset_ = conv_out_channels_ * conv_out_spatial_dim_ / group_;//卷积层的输出特征图也要分组,当然group_默认为1。写成(conv_out_channels_ / group_) * conv_out_spatial_dim_更直观  // Setup input dimensions (conv_input_shape_).  vector<int> bottom_dim_blob_shape(1, num_spatial_axes_ + 1);  conv_input_shape_.Reshape(bottom_dim_blob_shape);//与pad_、 stride_类似  int* conv_input_shape_data = conv_input_shape_.mutable_cpu_data();  for (int i = 0; i < num_spatial_axes_ + 1; ++i) {    if (reverse_dimensions()) {      conv_input_shape_data[i] = top[0]->shape(channel_axis_ + i);    } else {      conv_input_shape_data[i] = bottom[0]->shape(channel_axis_ + i);    }  }  // 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.  col_buffer_shape_.clear();//col_buffer_shape_是一个vector  col_buffer_shape_.push_back(kernel_dim_ * group_);//所有conv_in_channels_个输入的channels都包含其中。  for (int i = 0; i < num_spatial_axes_; ++i) {    if (reverse_dimensions()) {      col_buffer_shape_.push_back(input_shape(i + 1));    } else {      col_buffer_shape_.push_back(output_shape_[i]);    }  }  col_buffer_.Reshape(col_buffer_shape_);//一般情况下,col_buffer_的维度信息为三个维度。col_buffer_shape_的存储的元素为:kernel_dim_ * group_, 输出特征图的H, 输出特征图的W。可以认为col_buffer_内所存储的数据的维度为:(kernel_dim_ * group_) × H × W,且与kernel_dim_ x conv_out_spatial_dim_有密切关系.  bottom_dim_ = bottom[0]->count(channel_axis_);  top_dim_ = top[0]->count(channel_axis_);  num_kernels_im2col_ = conv_in_channels_ * conv_out_spatial_dim_;  num_kernels_col2im_ = reverse_dimensions() ? top_dim_ : bottom_dim_;  // Set up the all ones "bias multiplier" for adding biases by BLAS  out_spatial_dim_ = top[0]->count(first_spatial_axis);//out_spatial_dim_ == conv_out_spatial_dim_  if (bias_term_) {    vector<int> bias_multiplier_shape(1, out_spatial_dim_);    bias_multiplier_.Reshape(bias_multiplier_shape);//bias_multiplier_这个Blob的count_为out_spatial_dim_,是输出特征图的H×W    caffe_set(bias_multiplier_.count(), Dtype(1),        bias_multiplier_.mutable_cpu_data());  }}//////////////////////注意: col_offset_ output_offset_ weight_offet 都是因为group_分组而存在的\\\\\\\\\\\\\\\\\\\\\\\\\\\\\


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_,        (Dtype)1., weights + weight_offset_ * g, col_buff + col_offset_ * g,        (Dtype)0., output + output_offset_ * g);//weights <--- blobs_[0]->cpu_data()。类比全连接层,weights为权重,col_buff相当与数据,矩阵相乘weights×col_buff. 其中,weights的维度为(conv_out_channels_ /group_) x kernel_dim_, col_buff的维度为kernel_dim_ x conv_out_spatial_dim_, output的维度为(conv_out_channels_ /group_) x conv_out_spatial_dim_.  }}//只是对一张图像进行前向传播!与全连接层类比,conv_out_channels_ / group_相当与全连接层的输出神经元个数;conv_out_spatial_dim_相当于全连接层中的样本个数;kernel_dim_相当与全连接层中每个样本特征向量的维数。


template <typename Dtype>void BaseConvolutionLayer<Dtype>::forward_cpu_bias(Dtype* output,    const Dtype* bias) {  caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, num_output_,      out_spatial_dim_, 1, (Dtype)1., bias, bias_multiplier_.cpu_data(),      (Dtype)1., output);}//卷积后加bias


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_,        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的导数以便传给下一层


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_, conv_out_spatial_dim_,        (Dtype)1., output + output_offset_ * g, col_buff + col_offset_ * g,        (Dtype)1., weights + weight_offset_ * g);  }}//计算关于weight的导数用于更新。


template <typename Dtype>void BaseConvolutionLayer<Dtype>::backward_cpu_bias(Dtype* bias,    const Dtype* input) {  caffe_cpu_gemv<Dtype>(CblasNoTrans, num_output_, out_spatial_dim_, 1.,      input, bias_multiplier_.cpu_data(), 1., bias);}//计算关于bias的导数

##################In conv_layer.cpp#######################
template <typename Dtype>void ConvolutionLayer<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();  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 output_dim = (input_dim + 2 * pad_data[i] - kernel_shape_data[i])        / stride_data[i] + 1;    this->output_shape_.push_back(output_dim);  }}//计算输出feature map 的shape


template <typename Dtype>void ConvolutionLayer<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();//卷积层应该默认有多少个bottom 就有多少个top输出    Dtype* top_data = top[i]->mutable_cpu_data();    for (int n = 0; n < this->num_; ++n) {      this->forward_cpu_gemm(bottom_data + n * this->bottom_dim_, weight,          top_data + n * this->top_dim_);//说明是一张图像一张图像地进行前向传播,因为:1.num_ 2.top_dim的值等于top blob中一张图像拉成一列的列长。      if (this->bias_term_) {        const Dtype* bias = this->blobs_[1]->cpu_data();        this->forward_cpu_bias(top_data + n * this->top_dim_, bias);      }    }  }}//卷积层的Forward_cpu方法。

template <typename Dtype>void ConvolutionLayer<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(bottom_data + n * this->bottom_dim_,              top_diff + n * this->top_dim_, weight_diff);        }        // gradient w.r.t. bottom data, if necessary.        if (propagate_down[i]) {          this->backward_cpu_gemm(top_diff + n * this->top_dim_, weight,              bottom_diff + n * this->bottom_dim_);        }      }    }  }}//卷积层的Backward_cpu。



0 0
原创粉丝点击