caffe 源码学习(4)_反向传播权重更新

原创 2017年01月02日 23:11:51

还是以mnist手写字符,lenet5为例。
这里写图片描述
如上图所示,左边为9个layer层,右边为每层的top blob 的输出(featrue map)的维度。

  • 那么问题来了,训练的参数wijbi存在哪些变量里呢?
    1)有需要用BP算法进行训练参数的层(layer),内部都会有一个成员变量layer::blobs_,其中blobs_[0]存放wijΔwij,blobs_[1]存放biΔbi.
    2) 网络层内部成员变量 Net::blobs_内部存放数据及δ值。

      在lenet5网络中,只有两个卷积层(conv1,conv2)和两个全链接层(ip1,ip2)内部有需要训练的参数。因此lenet5网络需要8个blobs_存放训练参数,Net内部变量 net::learnable_params_与此8个blobs_共享内存。且,各层的layer::blobs_变量在layer::setup()中分配内存和初始化,在layer::Backward_cpu()中计算 ΔwijΔbi ,后续准备编写自己网络的童鞋需要重写此函数。最后调用Net::update()函数,更新wij
      以全链接层为例:

///ip层 blobs_ 开辟内存及初始化
template <typename Dtype>
void InnerProductLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {
  const int num_output = this->layer_param_.inner_product_param().num_output();
  bias_term_ = this->layer_param_.inner_product_param().bias_term();
  transpose_ = this->layer_param_.inner_product_param().transpose();
  N_ = num_output;
  const int axis = bottom[0]->CanonicalAxisIndex(
      this->layer_param_.inner_product_param().axis());
  // Dimensions starting from "axis" are "flattened" into a single
  // length K_ vector. For example, if bottom[0]'s shape is (N, C, H, W),
  // and axis == 1, N inner products with dimension CHW are performed.
  K_ = bottom[0]->count(axis);
  // Check if we need to set up the weights
  if (this->blobs_.size() > 0) {
    LOG(INFO) << "Skipping parameter initialization";
  } else {
    if (bias_term_) {
      this->blobs_.resize(2);
    } else {
      this->blobs_.resize(1);
    }
    // Initialize the weights
    vector<int> weight_shape(2);
    if (transpose_) {
      weight_shape[0] = K_;
      weight_shape[1] = N_;
    } else {
      weight_shape[0] = N_;
      weight_shape[1] = K_;
    }
    this->blobs_[0].reset(new Blob<Dtype>(weight_shape));
    // fill the weights
    shared_ptr<Filler<Dtype> > weight_filler(GetFiller<Dtype>(  //// 权重w初始化
        this->layer_param_.inner_product_param().weight_filler()));
    weight_filler->Fill(this->blobs_[0].get());
    // If necessary, intiialize and fill the bias term
    if (bias_term_) {
      vector<int> bias_shape(1, N_);
      this->blobs_[1].reset(new Blob<Dtype>(bias_shape)); //偏置初始化
      shared_ptr<Filler<Dtype> > bias_filler(GetFiller<Dtype>(
          this->layer_param_.inner_product_param().bias_filler()));
      bias_filler->Fill(this->blobs_[1].get());
    }
  }  // parameter initialization
  this->param_propagate_down_.resize(this->blobs_.size(), true);
}

权重变化量计算:

template <typename Dtype>
void InnerProductLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
    const vector<bool>& propagate_down,
    const vector<Blob<Dtype>*>& bottom) {
  if (this->param_propagate_down_[0]) {
    const Dtype* top_diff = top[0]->cpu_diff();
    const Dtype* bottom_data = bottom[0]->cpu_data();
    // Gradient with respect to weight
    if (transpose_) {
      caffe_cpu_gemm<Dtype>(CblasTrans, CblasNoTrans,
          K_, N_, M_,
          (Dtype)1., bottom_data, top_diff,
          (Dtype)1., this->blobs_[0]->mutable_cpu_diff());
    } else {
      caffe_cpu_gemm<Dtype>(CblasTrans, CblasNoTrans,
          N_, K_, M_,
          (Dtype)1., top_diff, bottom_data,
          (Dtype)1., this->blobs_[0]->mutable_cpu_diff());  //计算本层参数更新量 △W
    }
  }
  if (bias_term_ && this->param_propagate_down_[1]) {
    const Dtype* top_diff = top[0]->cpu_diff();
    // Gradient with respect to bias
    caffe_cpu_gemv<Dtype>(CblasTrans, M_, N_, (Dtype)1., top_diff,
        bias_multiplier_.cpu_data(), (Dtype)1.,
        this->blobs_[1]->mutable_cpu_diff());           //计算本层参数更新量 △b
  }
  if (propagate_down[0]) {
    const Dtype* top_diff = top[0]->cpu_diff();
    // Gradient with respect to bottom data
    if (transpose_) {
      caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasTrans,
          M_, K_, N_,
          (Dtype)1., top_diff, this->blobs_[0]->cpu_data(),
          (Dtype)0., bottom[0]->mutable_cpu_diff());
    } else {
      caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans,   /// 计算下一层的deta
          M_, K_, N_,
          (Dtype)1., top_diff, this->blobs_[0]->cpu_data(),
          (Dtype)0., bottom[0]->mutable_cpu_diff());
    }
  }
}

总的训练流程在Solver::Step()函数实现中:

template <typename Dtype>
void Solver<Dtype>::Step(int iters) {
  const int start_iter = iter_;
  const int stop_iter = iter_ + iters;
  int average_loss = this->param_.average_loss();
  losses_.clear();
  smoothed_loss_ = 0;

  while (iter_ < stop_iter) {  //以一个batch为一个周期
    // zero-init the params
    net_->ClearParamDiffs();   /////将Net 的成员变量param_ 中的diff空间初始化为0 
    if (param_.test_interval() && iter_ % param_.test_interval() == 0
        && (iter_ > 0 || param_.test_initialization())
        && Caffe::root_solver()) {
      TestAll();
      if (requested_early_exit_) {
        // Break out of the while loop because stop was requested while testing.
        break;
      }
    }

    for (int i = 0; i < callbacks_.size(); ++i) {
      callbacks_[i]->on_start();
    }
    const bool display = param_.display() && iter_ % param_.display() == 0;
    net_->set_debug_info(display && param_.debug_info());
    // accumulate the loss and gradient
    Dtype loss = 0;
    for (int i = 0; i < param_.iter_size(); ++i) {
      loss += net_->ForwardBackward(); /////关键。 前向运算、反向运算
    }
    loss /= param_.iter_size();
    // average the loss across iterations for smoothed reporting
    UpdateSmoothedLoss(loss, start_iter, average_loss);
    if (display) {
      LOG_IF(INFO, Caffe::root_solver()) << "Iteration " << iter_   //////////////// Iteration, loss=
          << ", loss = " << smoothed_loss_;
      const vector<Blob<Dtype>*>& result = net_->output_blobs();
      int score_index = 0;
      for (int j = 0; j < result.size(); ++j) {
        const Dtype* result_vec = result[j]->cpu_data();
        const string& output_name =
            net_->blob_names()[net_->output_blob_indices()[j]];
        const Dtype loss_weight =
            net_->blob_loss_weights()[net_->output_blob_indices()[j]];
        for (int k = 0; k < result[j]->count(); ++k) {      //////Train net out///////////////
          ostringstream loss_msg_stream;
          if (loss_weight) {
            loss_msg_stream << " (* " << loss_weight
                            << " = " << loss_weight * result_vec[k] << " loss)";
          }
          LOG_IF(INFO, Caffe::root_solver()) << "    Train net output #"
              << score_index++ << ": " << output_name << " = "
              << result_vec[k] << loss_msg_stream.str();
        }
      }
    }//if(display)
    for (int i = 0; i < callbacks_.size(); ++i) {
      callbacks_[i]->on_gradients_ready();
    }
    ApplyUpdate();  /////////////// 反向运算完成,统一更新权重值w

    // Increment the internal iter_ counter -- its value should always indicate
    // the number of times the weights have been updated.
    ++iter_;

    SolverAction::Enum request = GetRequestedAction();

    // Save a snapshot if needed.
    if ((param_.snapshot()
         && iter_ % param_.snapshot() == 0
         && Caffe::root_solver()) ||
         (request == SolverAction::SNAPSHOT)) {
      Snapshot();
    }
    if (SolverAction::STOP == request) {
      requested_early_exit_ = true;
      // Break out of training loop.
      break;
    }// if()
  }// end while
} // end step()
版权声明:本文为博主原创文章,转载请指明出处。

相关文章推荐

caffe作者贾杨清关于caffe解答

selected 15年6月 2  注:本文转载自:http://suanfazu.com/t/caffe/9479 一、讲...

Caffe学习笔记—Caffe反向传播计算

一、反向传播的特点   CNN进行前向传播阶段点,依次调用每个Layer的Forward函数,得到逐层的输出,最后一层与目标函数比较得到损失函数,计算误差更新值,通过反向传播路径层达到第一层...

(Caffe,LeNet)反向传播(六)

本文地址: 本部分剖析Caffe中Net::Backward()函数,即反向传播计算过程。从LeNet网络角度出发,且调试网络为训练网络,共9层网络。具体网络层信息见 (Caffe,LeNet)初始化...

深度学习小白——神经网络5(参数更新)

一、参数更新 1.随机梯度下降及各种更新方法 【普通更新】:沿着负梯度方向改变参数 x+= -learning_rate * dx 其中学习率是一个超参数,它是一个固定的常量。 【动量更新】(Mo...

RNN循环神经网络中的权重更新算法-BPTT

参考: http://ir.hit.edu.cn/~jguo/docs/notes/bptt.pdf http://www.cnblogs.com/wacc/p/5341670.html http...

caffe源码深入学习5:超级详细的caffe卷积层代码解析

caffe实现的卷积层是一个功能强大完整,同时也是一个相对复杂的层,涉及conv_layer.hpp,conv_layer.cpp,base_conv_layer.hpp和base_conv_laye...

caffe学习笔记3.2--前向传播和反向传播

caffe学习笔记3从3.1开始主要翻译一下caffe的官方文档,写的非常好,忍不住要作一下。本篇翻译来自caffe官网的:http://caffe.berkeleyvision.org/tutori...
  • thystar
  • thystar
  • 2016年04月26日 09:42
  • 5821

【用Python学习Caffe】8. 网络结构的权重共享量化

8. 网络结构的权重共享量化网络权重共享量化也是一类重要的网络压缩方法,其本质在于先通过聚类方法得到该层权重的聚类中心,然后通过聚类中心值来表示原权重值。因此权重值并不是由32位的浮点数来表示,而是由...
  • tostq
  • tostq
  • 2017年06月22日 22:28
  • 886

caffe 更新权重的理解

1.caffe 在做train时,每一步Step当中,要做iter_size次forward_backward迭代计算,其中每一次foward_backward计算都会产生权重的残差diff,那么做i...

Caffe中权值是怎么更新的

网址:http://blog.csdn.net/mounty_fsc/article/details/51588773 (Caffe,LeNet)权值更新(七) 在Solver::Apply...
  • junmuzi
  • junmuzi
  • 2016年08月16日 17:21
  • 1599
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:caffe 源码学习(4)_反向传播权重更新
举报原因:
原因补充:

(最多只允许输入30个字)