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 更新权重的理解

1.caffe 在做train时,每一步Step当中,要做iter_size次forward_backward迭代计算,其中每一次foward_backward计算都会产生权重的残差diff,那么做i...
  • u010935076
  • u010935076
  • 2017年06月06日 11:31
  • 585

Caffe中权值是怎么更新的

网址:http://blog.csdn.net/mounty_fsc/article/details/51588773 (Caffe,LeNet)权值更新(七) 在Solver::Apply...
  • junmuzi
  • junmuzi
  • 2016年08月16日 17:21
  • 1884

(Caffe,LeNet)权值更新(七)

本文地址: 在Solver::ApplyUpdate()函数中,根据反向传播阶段计算的loss关于网络权值的偏导,使用配置的学习策略,更新网络权值从而完成本轮学习。...
  • mounty_fsc
  • mounty_fsc
  • 2016年06月05日 11:40
  • 3937

深度学习笔记9:权值更新的实现

权值更新 在前面的反向传播中我们计算出每一层的权值W和偏置b的偏导数之后,最后一步就是对权值和偏置进行更新了。 在之前的BP算法的介绍中我们给出了如下公式: 其中的α为学习速率,一般学习率并不是...
  • l691899397
  • l691899397
  • 2016年08月24日 20:09
  • 4280

CNN的权重更新方法-训练过程

CNN权重的更新即是对损失函数()求最小误差,采用最小二乘法求导,或者牛顿迭代法后者BP都可以http://blog.csdn.net/zouxy09/article/details/9993371/...
  • xudong_98
  • xudong_98
  • 2016年11月28日 22:39
  • 1673

caffe 的权值更新

Solver 优化model的步骤:     1 调用 forward 产生 输出和loss,              2 调用backward 生成模型的梯度,           ...
  • stone_linclon
  • stone_linclon
  • 2016年06月17日 16:05
  • 468

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

还是以mnist手写字符,lenet5为例。 如上图所示,左边为9个layer层,右边为每层的top blob 的输出(featrue map)的维度。 那么问题来了,训练的参数wij和biw_...
  • dataningwei
  • dataningwei
  • 2017年01月02日 23:11
  • 1085

反向传播算法(BackPropagation,BP)

本文参考《神经网络和深度学习》,旨在说明BP算法是怎样的一个过程。在一个多层的神经网络中,反向传播算法就是不断的学习这个网络的权值和偏值,采用梯度下降法使得该神经网络的输出值与真实的目标值之间的误差最...
  • Hearthougan
  • Hearthougan
  • 2017年02月20日 11:45
  • 873

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

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

caffe finetune微调固定权重的方法

结论:若不加param参数的resnet-50进行finetune,所有层还是会进行微调,若不想微调需要在层中加入param{lr_mult: 0}。 注意防坑:在train.sh文件中,训练命...
  • Eclipsesy
  • Eclipsesy
  • 2018年01月29日 22:21
  • 57
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:caffe 源码学习(4)_反向传播权重更新
举报原因:
原因补充:

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