【深度学习】【Caffe源代码解读2】笔记20 Caffe的基本数据结构之Layer

本文详细介绍了Caffe框架中Layer的实现原理与工作机制,包括Layer的配置、初始化、前向传播、反向传播等关键环节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/************************************************************************************************************************
文件说明:
		【1】blob.hpp文件位于../libcaffe/include.hpp下
		【2】Blob是caffe中实际处理和传递数据的数据封装包,并且在CPU和GPU之间有数据同步能力
		【3】Blob是一个模板类,所以在实例化Blob对象时,需要传递对象的类型参数
Layer的计算和连接:
        【1】Layer是caffe模型的【本质内容】和【执行计算】的基本单元。
		【2】Layer可以进行很多运算,如:convolve(卷积)、pool(池化)、inner product(内积)、sigmod等非线性运算,元素级别的数
		     据变换,normalize(归一化)、load data(数据加载)、softmax和loss
		【3】一个Layer通过底部bottom(底部)连接层接收数据,通过top(顶部)连接层输出数据
		【4】每一个Layer层都定义了3中重要的运算:
		         【1】setup(初始化设置):在模型初始化时,重置Layers及其相互之间的连接
				 【2】forward(前向传播):从bottom层中接收数据,进行计算后,将输出送入到top层中
				 【3】backforward(反向传播):给定相对于top层输出的梯度,计算其相对于输入的梯度,并传递到bottom层。一个有
				      参数的layer需要计算相对各个参数的梯度值,并存储在内部。
		【5】需要特别指出的,Forward和Backward函数分别有CPU和GPU两种实现方式。如果没有GPU版本,那么layer将转向作为备用选
		     型的CPU方式。尽管这样会增加额外的数据传递成本(输入数据由GPU上复制到CPU,之后输出数据从CPU又复制回到GPU),但是
			 对于做一些快速实验,这样的操作还是很方便的。
		【6】总的来说,layer承担了网络的【两个核心操作】:
		         【1】forward pass(前向传播):接收输入,并计算输出。
				 【2】backward pass(反向传播):接收关于输出的梯度,计算相对于参数和输入的梯度并反向传播给在它前面的层。由此
				      组成每个layer的前向和反向通道
		【7】由于caffe网络的组合性和其代码的模块化,自定义layer是很容易的。只要定义好layer的:
		         【1】setup
				 【2】forward
				 【3】backward
				 就可以将layer纳入到网络。
开发环境:
        Win10+caffe+cuda7.5+opencv+vs2013
时间地点:
        陕西师范大学 文津楼 2017.8.7
作    者:
        九 月
*************************************************************************************************************************/
#ifndef CAFFE_LAYER_H_
#define CAFFE_LAYER_H_

#include <algorithm>
#include <string>
#include <vector>

#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/layer_factory.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/util/math_functions.hpp"

/**
 Forward declare boost::thread instead of including boost/thread.hpp
 to avoid a boost/NVCC issues (#1009, #1010) on OSX.
 */
namespace boost { class mutex; }

namespace caffe 
{
template <typename Dtype>
class Layer 
{
 public:
  /****************************************************************************************************************
  函数说明:
          显式的构造函数,从LayerParameter对象中加载配置
  C++语言:
         【1】一般地,除非有明显理由想要定义隐式转换,否则,单形参构造函数应该为explicit
		 【2】C++关键字explicit用来修饰类的构造函数,指明该构造函数是显式的。explicit关键字只能用于类内部的构造函数
		      声明上,在类定义外部不能重复它。
		 【3】只有单个形参,而且形参是本类类型对象的引用常量,这样的构造函数称为复制构造函数。
  ****************************************************************************************************************/
  explicit Layer(const LayerParameter& param): layer_param_(param), is_shared_(false) 
  {
	  phase_ = param.phase();                            //【1】设置当前阶段(TRAIN/TEST)
      if (layer_param_.blobs_size() > 0) 
	  {
		  blobs_.resize(layer_param_.blobs_size());      //【2】按照layer_param_设置本身Blob对象个数。并依次将每个Blob
		                                                 //     对象尺寸调整为layer_param中的Blob尺寸。
		  for (int i = 0; i < layer_param_.blobs_size(); ++i)
		  {
			  blobs_[i].reset(new Blob<Dtype>());
			  blobs_[i]->FromProto(layer_param_.blobs(i));
		  }
	  }
  }
  virtual ~Layer() {}                                   //【3】虚虚构函数

  //【4】配置函数,实现常用层配置接口,不可被覆盖
  void SetUp(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) 
  {
    InitMutex();
    CheckBlobCounts(bottom, top);
    LayerSetUp(bottom, top);
    Reshape(bottom, top);
    SetLossWeights(top);
  }
  //【5】层配置虚函数,做特定类型层的相关配置,该类型层自己实现
  virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) {}
  //【6】在数据并行中,一个Layer是否被多个Net共享
  virtual inline bool ShareInParallel() const 
  { 
	  return false; 
  }
  inline bool IsShared() const 
  { 
	  return is_shared_;
  }

  inline void SetShared(bool is_shared) 
  {
    CHECK(ShareInParallel() || !is_shared)<< type() << "Layer does not support sharing.";
    is_shared_ = is_shared;
  }
  //【7】纯虚函数
  virtual void Reshape(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) = 0;
  /****************************************************************************************************************
  函数原型:
          inline Dtype Forward(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top);
  函数功能:
          前向传播函数,给定Bottom Blob,计算Top Blob和los,返回值为当前层Loss
  函数参数:
          【1】const vector<Blob<Dtype>*>& bottom------输入参数-----给定的Bottom Blob
		  【2】const vector<Blob<Dtype>*>& top---------输出参数-----计算出来的Top Blob
  函数返回值:
          Dtype------返回当前层的loss
  ****************************************************************************************************************/
  inline Dtype Forward(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top);
  /****************************************************************************************************************
  函数原型:
          inline void Backward(const vector<Blob<Dtype>*>& top,
		                       const vector<bool>& propagate_down,
		                       const vector<Blob<Dtype>*>& bottom);
  函数功能:
         【1】反向传播函数,给定Top Blob误差梯度,计算Bottom Blob误差梯度
		 【2】该函数会调用相应【设备包装函数】,如Backward_cpu或Backward_gpu来实现真正的计算过程,由派生类负责实现
  函数参数:
         【1】const vector<Blob<Dtype>*>&  top--------------其diff域来自上一层的误差梯度
         【2】const vector<bool>&          propagate_down---多路开关,与Bottom Blob矢量维度相同,每个值表示是否将
		                                                    误差梯度传递到对应的Bottom Blob
		 【3】const vector<Blob<Dtype>*>&     bottom--------其diff域由函数计算得到
  函数返回值:
          Dtype------返回当前层的loss
  ****************************************************************************************************************/
  inline void Backward(const vector<Blob<Dtype>*>&    top,
	                    const vector<bool>&            propagate_down,
                        const vector<Blob<Dtype>*>&     bottom);
  /****************************************************************************************************************
  函数说明:
          返回Layer内部可训练的权值、偏置项Blob向量
  *****************************************************************************************************************/
  vector<shared_ptr<Blob<Dtype> > >& blobs() 
  {
    return blobs_;
  }
  /****************************************************************************************************************
  函数说明:
          返回Layer初始化参数,由ProtoBuffer提供
  *****************************************************************************************************************/
  const LayerParameter& layer_param() const { return layer_param_; }
  /****************************************************************************************************************
  函数说明:
          将Layer初始化参数写入ProtoBuffer的缓冲区
  *****************************************************************************************************************/
  virtual void ToProto(LayerParameter* param, bool write_diff = false);
  /****************************************************************************************************************
  函数说明:
          返回与某个Top Blob相关的标量Loss
  *****************************************************************************************************************/
  inline Dtype loss(const int top_index) const 
  {
    return (loss_.size() > top_index) ? loss_[top_index] : Dtype(0);
  }
  /****************************************************************************************************************
  函数说明:
          设置与某个Top Blob相关的标量loss值
  *****************************************************************************************************************/
  inline void set_loss(const int top_index, const Dtype value) 
  {
    if (loss_.size() <= top_index) 
	{
		loss_.resize(top_index + 1, Dtype(0));
    }
    loss_[top_index] = value;
  }
  /****************************************************************************************************************
  函数说明:
          返回层类型字符串,便于识别,由派生类负责实现
  *****************************************************************************************************************/
  virtual inline const char* type() const { return ""; }
  /****************************************************************************************************************
  函数说明:
          返回该Layer需要的输入Blob数目,-1表示不关心,由派生类负责实现
  *****************************************************************************************************************/
  virtual inline int ExactNumBottomBlobs() const { return -1; }
  virtual inline int MinBottomBlobs() const { return -1; }
  virtual inline int MaxBottomBlobs() const { return -1; }
  /****************************************************************************************************************
  函数说明:
          返回该Layer需要的输出的Blob数目,-1表示不关心,由派生类负责实现
  *****************************************************************************************************************/
  virtual inline int ExactNumTopBlobs() const { return -1; }
  virtual inline int MinTopBlobs() const { return -1; }
  virtual inline int MaxTopBlobs() const { return -1; }
  /****************************************************************************************************************
  函数说明:
          返回该Layer是否具有相同的输入/输出Blob,由派生类负责实现
  *****************************************************************************************************************/
  virtual inline bool EqualNumBottomTopBlobs() const { return false; }
  virtual inline bool AutoTopBlobs() const { return false; }
  virtual inline bool AllowForceBackward(const int bottom_index) const 
  {
    return true;
  }
  inline bool param_propagate_down(const int param_id) 
  {
    return (param_propagate_down_.size() > param_id) ?
        param_propagate_down_[param_id] : false;
  }
  inline void set_param_propagate_down(const int param_id, const bool value) 
  {
    if (param_propagate_down_.size() <= param_id) 
	{
      param_propagate_down_.resize(param_id + 1, true);
    }
    param_propagate_down_[param_id] = value;
  }
  inline Phase phase() { return phase_; }
  virtual inline void set_phase(Phase phase) 
  {
    phase_ = phase;
  }

 protected:
  LayerParameter                    layer_param_;                //【1】保存Layer参数的ProtoBuffer对象
  Phase                             phase_;                      //【2】枚举类新,Layer当前所处阶段,可选TRAIN或TEST
  vector<shared_ptr<Blob<Dtype> > > blobs_;                      //【3】Layer内部权值或偏置项,以Blob方式组织
  vector<bool>                     param_propagate_down_;       //【4】标志位,是否计算对应参数的误差梯度
  vector<Dtype>                     loss_;                       //【5】标志位,在目标函数中,是否每个Top Blob都有非零权重
  //【注意】:下面四个函数,我们在各个Layer派生类中会经常看到
  virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) = 0;
  virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) 
  {
    return Forward_cpu(bottom, top);
  }
  virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,const vector<bool>& propagate_down,const vector<Blob<Dtype>*>& bottom) = 0;
  virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,const vector<bool>& propagate_down,const vector<Blob<Dtype>*>& bottom) 
  {

    Backward_cpu(top, propagate_down, bottom);
  }
  virtual void CheckBlobCounts(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) 
  {
    if (ExactNumBottomBlobs() >= 0) {
      CHECK_EQ(ExactNumBottomBlobs(), bottom.size())<< type() << " Layer takes " << ExactNumBottomBlobs()<< " bottom blob(s) as input.";
    }
    if (MinBottomBlobs() >= 0) 
	{
      CHECK_LE(MinBottomBlobs(), bottom.size())<< type() << " Layer takes at least " << MinBottomBlobs()<< " bottom blob(s) as input.";
    }
    if (MaxBottomBlobs() >= 0) 
	{
      CHECK_GE(MaxBottomBlobs(), bottom.size())<< type() << " Layer takes at most " << MaxBottomBlobs()<< " bottom blob(s) as input.";
    }
    if (ExactNumTopBlobs() >= 0) 
	{
      CHECK_EQ(ExactNumTopBlobs(), top.size())<< type() << " Layer produces " << ExactNumTopBlobs()<< " top blob(s) as output.";
    }
    if (MinTopBlobs() >= 0) 
	{
      CHECK_LE(MinTopBlobs(), top.size())<< type() << " Layer produces at least " << MinTopBlobs()<< " top blob(s) as output.";
    }
    if (MaxTopBlobs() >= 0) 
	{
      CHECK_GE(MaxTopBlobs(), top.size())<< type() << " Layer produces at most " << MaxTopBlobs()<< " top blob(s) as output.";
    }
    if (EqualNumBottomTopBlobs()) 
	{
      CHECK_EQ(bottom.size(), top.size())<< type() << " Layer produces one top blob as output for each "<< "bottom blob input.";
    }
  }

  /**
   * Called by SetUp to initialize the weights associated with any top blobs in
   * the loss function. Store non-zero loss weights in the diff blob.
   */
  inline void SetLossWeights(const vector<Blob<Dtype>*>& top) 
  {
    const int num_loss_weights = layer_param_.loss_weight_size();
    if (num_loss_weights) 
	{
      CHECK_EQ(top.size(), num_loss_weights) << "loss_weight must be ""unspecified or specified once per top blob.";
      for (int top_id = 0; top_id < top.size(); ++top_id) 
	  {
        const Dtype loss_weight = layer_param_.loss_weight(top_id);
        if (loss_weight == Dtype(0)) { continue; }
        this->set_loss(top_id, loss_weight);
        const int count = top[top_id]->count();
        Dtype* loss_multiplier = top[top_id]->mutable_cpu_diff();
        caffe_set(count, loss_weight, loss_multiplier);
      }
    }
  }

 private:
  bool                       is_shared_;                 //【1】标志位,表明该Layer是否被其他Net共享
  shared_ptr<boost::mutex>   forward_mutex_;             //【2】如果该Layer被共享,则需要该信号量保证顺序执行
  void InitMutex();                                      //【3】初始化forward_mutex_
  void Lock();                                           //【4】加锁
  void Unlock();                                         //【5】解锁

  DISABLE_COPY_AND_ASSIGN(Layer);                          
};  // class Layer

// Forward and backward wrappers. You should implement the cpu and
// gpu specific implementations instead, and should not change these
// functions.
template <typename Dtype>
inline Dtype Layer<Dtype>::Forward(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) 
{
  // Lock during forward to ensure sequential forward
  Lock();
  Dtype loss = 0;
  Reshape(bottom, top);
  switch (Caffe::mode()) 
  {
  case Caffe::CPU:
    Forward_cpu(bottom, top);
    for (int top_id = 0; top_id < top.size(); ++top_id) 
	{
      if (!this->loss(top_id)) { continue; }
      const int count = top[top_id]->count();
      const Dtype* data = top[top_id]->cpu_data();
      const Dtype* loss_weights = top[top_id]->cpu_diff();
      loss += caffe_cpu_dot(count, data, loss_weights);
    }
    break;
  case Caffe::GPU:
    Forward_gpu(bottom, top);
#ifndef CPU_ONLY
    for (int top_id = 0; top_id < top.size(); ++top_id) 
	{
      if (!this->loss(top_id)) { continue; }
      const int count = top[top_id]->count();
      const Dtype* data = top[top_id]->gpu_data();
      const Dtype* loss_weights = top[top_id]->gpu_diff();
      Dtype blob_loss = 0;
      caffe_gpu_dot(count, data, loss_weights, &blob_loss);
      loss += blob_loss;
    }
#endif
    break;
  default:
    LOG(FATAL) << "Unknown caffe mode.";
  }
  Unlock();
  return loss;
}

template <typename Dtype>
inline void Layer<Dtype>::Backward(const vector<Blob<Dtype>*>& top,const vector<bool>& propagate_down,const vector<Blob<Dtype>*>& bottom) 
{
  switch (Caffe::mode()) 
  {
  case Caffe::CPU:
    Backward_cpu(top, propagate_down, bottom);
    break;
  case Caffe::GPU:
    Backward_gpu(top, propagate_down, bottom);
    break;
  default:
    LOG(FATAL) << "Unknown caffe mode.";
  }
}

// Serialize LayerParameter to protocol buffer
template <typename Dtype>
void Layer<Dtype>::ToProto(LayerParameter* param, bool write_diff) 
{
  param->Clear();
  param->CopyFrom(layer_param_);
  param->clear_blobs();
  for (int i = 0; i < blobs_.size(); ++i) {
    blobs_[i]->ToProto(param->add_blobs(), write_diff);
  }
}

}  // namespace caffe

#endif  // CAFFE_LAYER_H_

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值