Caffe中的Net

Net在Caffe中代表一个完整的CNN模型,它包含若干Layer实例。前面我们已经在第5天内容中看到用ProtoBuffer文本文件(prototxt)描述的经典网络结构如LeNet、AlexNet,这些结构反映在Caffe代码实现上就是一个Net对象。Net其实是相对Blob、 Layer更为复杂的设计,需要沉住气

Net的基本用法

Net是一张图纸,对应的描述文件为*.prototxt,我们选择Caffe自带的CaffeNet模型描述文件,位于models/bvlc_reference_caffenet/deploy.prototxt。将该文件拷贝到当前工作目录下。

编写测试代码为:

#include <vector>
#include <iostream>
#include <caffe/net.hpp>
using namespace caffe;
using namespace std;

int main(void)
{
    std::string proto("deploy.prototxt");
    Net<float> nn(proto,caffe::TEST);
    vector<string> bn = nn.blob_names();    // 获取 Net 中所有 Blob 对象名
    vector<string> ln = nn.layer_names();   // 获取 Net 中所有 Layer 对象名
    for (int i = 0; i < bn.size(); i++)
    {
        cout<<"Blob #"<<i<<" : "<<bn[i]<<endl;
    }
    for (int i = 0; i < ln.size(); i++)
    {
        cout<<"layer #"<<i<<" : "<<ln[i]<<endl;
    }
    return 0;
}

编译(注意这里我们需要安装openblas,具体过程不再这里讲述):

g++ -o netapp net_demo.cpp -I /usr/local/Cellar/caffe/include -D CPU_ONLY -I /usr/local/Cellar/caffe/.build_release/src/ -L /usr/local/Cellar/caffe/build/lib -I /usr/local/Cellar/openblas/0.2.19_1/include  -lcaffe -lglog -lboost_system -lprotobuf

注意到这里有一段-I /usr/local/Cellar/openblas/0.2.19_1/include这是为了连接到本地的blas

运行 ./netapp :

发现又报错了:

运行install_name_tool -add_rpath '/usr/local/Cellar/caffe/build/lib/' /usr/local/Cellar/caffe/LZHcaffe/./netapp命令,连接库文件.

运行成功后,输出为:

...省略上面部分,之前已经见过了
I0622 11:03:24.688719 3012948928 net.cpp:255] Network initialization done.
Blob #0 : data
Blob #1 : conv1
Blob #2 : pool1
Blob #3 : norm1
Blob #4 : conv2
Blob #5 : pool2
Blob #6 : norm2
Blob #7 : conv3
Blob #8 : conv4
Blob #9 : conv5
Blob #10 : pool5
Blob #11 : fc6
Blob #12 : fc7
Blob #13 : fc8
Blob #14 : prob
layer #0 : data
layer #1 : conv1
layer #2 : relu1
layer #3 : pool1
layer #4 : norm1
layer #5 : conv2
layer #6 : relu2
layer #7 : pool2
layer #8 : norm2
layer #9 : conv3
layer #10 : relu3
layer #11 : conv4
layer #12 : relu4
layer #13 : conv5
layer #14 : relu5
layer #15 : pool5
layer #16 : fc6
layer #17 : relu6
layer #18 : drop6
layer #19 : fc7
layer #20 : relu7
layer #21 : drop7
layer #22 : fc8
layer #23 : prob

通过上面的简单例子,我们发现Net中既包括Layer对象,有包括Blob对象.其中Blob对象用于存放每个Layer输入/输出中间结果.Layer则根据Net描述对指定的输入Blob进行某些计算处理(卷积、下采样、全连接、非线性变换、计算代价函数等),输出结果放到指定的输出Blob中。输入Blob和输出Blob可能为同一个。所有的Layer和Blob对象都用名字区分,同名的Blob表示同一个Blob对象,同名的Layer表示同一个Layer对象。而Blob和Layer同名则不代表它们有任何直接关系

我们可以通过has_blob()、has_layer()函数来査询当前Net对象是否包含指定名字的Blob或Layer对象,如果返回值为真,则可以进-步调用blob_by_name()、layer_by_name()函数直接获取相应的Blob或Layer指针,进行些操作(如提取某层计算输出特征或某个Blob中的权值)。

数据结构描述

我们这里先了解下caffe.proto:


message NetParameter {
  optional string name = 1; // consider giving the network a name
  // DEPRECATED. See InputParameter. The input blobs to the network.
  repeated string input = 3;
  // DEPRECATED. See InputParameter. The shape of the input blobs.(网络的输入Blob名称,可以多个Blob)
  repeated BlobShape input_shape = 8;

  // 4D input dimensions -- deprecated.  Use "input_shape" instead.
  // If specified, for each input blob there should be four
  // values specifying the num, channels, height and width of the input blob.
  // Thus, there should be a total of (4 * #input) numbers.
  //(旧版的维度信息)
  repeated int32 input_dim = 4;

  // Whether the network will force every layer to carry out backward operation.
  // If set False, then whether to carry out backward is determined
  // automatically according to the net structure and learning rates.
  optional bool force_backward = 5 [default = false];
  // The current "state" of the network, including the phase, level, and stage.
  // Some layers may be included/excluded depending on this state and the states
  // specified in the layers' include and exclude fields.
  optional NetState state = 6;

  // Print debugging information about results while running Net::Forward,
  // Net::Backward, and Net::Update.
  optional bool debug_info = 7 [default = false];

  // The layers that make up the net.  Each of their configurations, including
  // connectivity and behavior, is specified as a LayerParameter.
  repeated LayerParameter layer = 100;  // ID 100 so layers are printed last.

  // DEPRECATED: use 'layer' instead.
  repeated V1LayerParameter layers = 2;
}

看似很短的proto描述,实际上对应的真实网络prototxt可以很长很长,关键在于可重复多次出现的LayerParameterlayer这个字段。其他字段的功能基本都是辅助网络运行的,在代码中会看到更多的细节。

Net的形成

我们将Blob比作Caffe砖石,Layer比作Caffe的墙面,那么Net更像是工匠手中的图纸,描述了每个墙面应当出现的位置,这样设计的房屋才足够牢固、抗震。为了达到这个目的,Net实现时必然有一套用于记录Layer、Blob的数据结构。在下表中公布一下这些数据结构的名字,错过与它们打交道的机会。

类对象 含义
layers_ 记录Net prototxt中出现的每个Layer
layer_names_ 记录Net prototxt中出现的每个Layer的名称
layer_names_index_ 记录Net prototxt中每个Layer名称与顺序索引的对应关系
layer_need_backward_ 记录Layer是否需要反向传播过程
blobs_ 记录Net中所有Blob
blob_names_ 记录每个Blob名称
blob_names_index_ 记录每个Blob名称与顺序索引的对应关系
blob_need_backward_ 记录每个Blob是否需要反向传播过程
bottom_vecs_ blobs_的影子,记录每个Layer的输入Blob
bottom_id_vecs_ 与bottom_vecs_关联,用于在blobs_中定位每个Layer的每个输入Blob
bottom_need_backward_ 与bottom_vecs_关联,标志每个Blob是否需要反向传播过程
top_vecs_ blobs_的影子,记录每个Layer的输出Blob
top_id_vecs_ 与top_vecs_关联,用于在blobs_中定位每个Layer的每个输出Blob
blob_loss_weights_ Net中每个Blob对损失函数的投票因子,一般损失层为1,其他层为0
net_input_blob_indices_ Net输入Blob在blobs_中的索引
net_output_blob_indices_ Net输出Blob在blobs_中的索引
net_input_blobs_ Net 输入 Blob
net_output_blobs_ Net 输出 Blob
params_ Net权值Blob,用于存储网络权值
param_display_names_ Net中权值Blob的名称
learnable_params_ Net中可训练的权值Blob
params_lr_ learnable_params_中每个元素是否有学习速率倍乘因子
has_params_lr_ 标志learnable_params_中每个元素是否有学习速率倍乘因子
params_weight_decay_ learnable_params_中每个元素的权值衰减倍乘因子
has_params_decay_ 标志learnable_params_中每个元素是否有权值衰减倍乘因子

看到上面有两类Blob:以param开头的权值Blob以blob开头的Layer输入/输出Blob它们虽然都是Blob类型,但在网络中的地位截然不同。权值Blob会随着学习过程而更新,归属于“模型”:Layer输入/输出Blob则只会随网络输入变化,归属于“数据”。深度学习的目的就是不断从“数据”中获取知识,存储到“模型”中,应用于后来的“数据”。

Net声明位于include/caffe/net.hpp中,内容如下:


template <typename Dtype>
class Net {
 public:
 //显示构造函数
  explicit Net(const NetParameter& param);
  explicit Net(const string& param_file, Phase phase,
>       const int level = 0, const vector<string>* stages = NULL);
//析构函数
  virtual ~Net() {}

  /// @brief Initialize a network with a NetParameter.
  void Init(const NetParameter& param);

  //运行前向传播,输入Blob已经领先填充
  const vector<Blob<Dtype>*>& Forward(Dtype* loss = NULL);
  /// @brief DEPRECATED; use Forward() instead.
  const vector<Blob<Dtype>*>& ForwardPrefilled(Dtype* loss = NULL) {
    LOG_EVERY_N(WARNING, 1000) << "DEPRECATED: ForwardPrefilled() "
        << "will be removed in a future version. Use Forward().";
    return Forward(loss);
  }
  
   /**
   * The From and To variants of Forward and Backward operate on the
   * (topological) ordering by which the net is specified. For general DAG
   * networks, note that (1) computing from one layer to another might entail
   * extra computation on unrelated branches, and (2) computation starting in
   * the middle may be incorrect if all of the layers of a fan-in are not
   * included.
   */
   //前向传播的几种形式
  Dtype ForwardFromTo(int start, int end);
  Dtype ForwardFrom(int start);
  Dtype ForwardTo(int end);
  /// @brief DEPRECATED; set input blobs then use Forward() instead.
  const vector<Blob<Dtype>*>& Forward(const vector<Blob<Dtype>* > & bottom,
      Dtype* loss = NULL);
    //清零所有权值的diff域,应在反向传播之前运行
    void ClearParamDiffs();
    //几种不同形式的Net反向传播,无须指定输入/输出Blob,因为在前向传播过程中已经建立连接
  void Backward();
  void BackwardFromTo(int start, int end);
  void BackwardFrom(int start);
  void BackwardTo(int end);
  //对Net中所有Layer自底向上进行变形,无须运行一次前向传播就可以计算各层所需的Blob尺寸
  void Reshape();
  //前向传播+反向传播,输入为Bottom Blob,输出为loss
  Dtype ForwardBackward() {
    Dtype loss;
    Forward(&loss);
    Backward();
    return loss;
  }
  //根据已经(由Solver)准备好的diff值更新网络权值
   void Update();
  /**
   * @brief Shares weight data of owner blobs with shared blobs.
   *
   * Note: this is called by Net::Init, and thus should normally not be
   * called manually.
   */
  void ShareWeights();

  /**
   * @brief For an already initialized net, implicitly copies (i.e., using no
   *        additional memory) the pre-trained layers from another Net.
   */
   //从1个已训练好的Net获取共享权值
  void ShareTrainedLayersWith(const Net* other);
  // For an already initialized net, CopyTrainedLayersFrom() copies the already
  // trained layers from another net parameter instance.
  /**
   * @brief For an already initialized net, copies the pre-trained layers from
   *        another Net.
   */
  void CopyTrainedLayersFrom(const NetParameter& param);
  void CopyTrainedLayersFrom(const string trained_filename);
  void CopyTrainedLayersFromBinaryProto(const string trained_filename);
  void CopyTrainedLayersFromHDF5(const string trained_filename);
  /// @brief Writes the net to a proto.
  // 序列化一个 Net 到 ProtoBuffer
  void ToProto(NetParameter* param, bool write_diff = false) const;
  /// @brief Writes the net to an HDF5 file.
  //序列化一个Net到HDF5
  void ToHDF5(const string& filename, bool write_diff = false) const;
  
  /// @brief returns the network name.
  inline const string& name() const { return name_; }
  /// @brief returns the layer names
  inline const vector<string>& layer_names() const { return layer_names_; }
  /// @brief returns the blob names
  inline const vector<string>& blob_names() const { return blob_names_; }
  /// @brief returns the blobs
  inline const vector<shared_ptr<Blob<Dtype> > >& blobs() const {
    return blobs_;
  }
  /// @brief returns the layers
  inline const vector<shared_ptr<Layer<Dtype> > >& layers() const {
    return layers_;
  }
  /// @brief returns the phase: TRAIN or TEST
  inline Phase phase() const { return phase_; }
  /**
   * @brief returns the bottom vecs for each layer -- usually you won't
   *        need this unless you do per-layer checks such as gradients.
   */
   //返回每个Layer的Bottom Blob
  inline const vector<vector<Blob<Dtype>*> >& bottom_vecs() const {
    return bottom_vecs_;
  }
  /**
   * @brief returns the top vecs for each layer -- usually you won't
   *        need this unless you do per-layer checks such as gradients.
   */
   //返回每个Layer的Top Blob
  inline const vector<vector<Blob<Dtype>*> >& top_vecs() const {
    return top_vecs_;
  }
  /// @brief returns the ids of the top blobs of layer i
  inline const vector<int> & top_ids(int i) const {
    CHECK_GE(i, 0) << "Invalid layer id";
    CHECK_LT(i, top_id_vecs_.size()) << "Invalid layer id";
    return top_id_vecs_[i];
  }
  /// @brief returns the ids of the bottom blobs of layer i
  inline const vector<int> & bottom_ids(int i) const {
    CHECK_GE(i, 0) << "Invalid layer id";
    CHECK_LT(i, bottom_id_vecs_.size()) << "Invalid layer id";
    return bottom_id_vecs_[i];
  }
  
  inline const vector<vector<bool> >& bottom_need_backward() const {
    return bottom_need_backward_;
  }
  inline const vector<Dtype>& blob_loss_weights() const {
    return blob_loss_weights_;
  }
  //返回每个Layer是否需要反向传播计算
  inline const vector<bool>& layer_need_backward() const {
    return layer_need_backward_;
  }
  /// @brief returns the parameters
  inline const vector<shared_ptr<Blob<Dtype> > >& params() const {
    return params_;
  }
  //返回所有可训练权值
  inline const vector<Blob<Dtype>*>& learnable_params() const {
    return learnable_params_;
  }
  /// @brief returns the learnable parameter learning rate multipliers(倍乘因子)
  inline const vector<float>& params_lr() const { return params_lr_; }
  inline const vector<bool>& has_params_lr() const { return has_params_lr_; }
  /// @brief returns the learnable parameter decay multipliers(返回可训练权值的衰减因子)
  inline const vector<float>& params_weight_decay() const {
    return params_weight_decay_;
  }
  inline const vector<bool>& has_params_decay() const {
    return has_params_decay_;
  }
  //返回Layer名称与向量下标映射对
  const map<string, int>& param_names_index() const {
    return param_names_index_;
  }
  //返回权值所有者
  inline const vector<int>& param_owners() const { return param_owners_; }
  inline const vector<string>& param_display_names() const {
    return param_display_names_;
  }
  /// @brief Input and output blob numbers
  inline int num_inputs() const { return net_input_blobs_.size(); }
  inline int num_outputs() const { return net_output_blobs_.size(); }
  //返回输入Blob
  inline const vector<Blob<Dtype>*>& input_blobs() const {
    return net_input_blobs_;
  }
  //返回输出Blob
  inline const vector<Blob<Dtype>*>& output_blobs() const {
    return net_output_blobs_;
  }
  //返回输入Blob下标
  inline const vector<int>& input_blob_indices() const {
    return net_input_blob_indices_;
  }
  //返回输出Blob下标
  inline const vector<int>& output_blob_indices() const {
    return net_output_blob_indices_;
  }
  //查找当前网络是否包含某一名称Blob
  bool has_blob(const string& blob_name) const;
  //如果包含,那么就把它找出来
  const shared_ptr<Blob<Dtype> > blob_by_name(const string& blob_name) const;
  //查找当前网络是都包含某一名称Layer
  bool has_layer(const string& layer_name) const;
  //如果包含,那么就把它找出来
  const shared_ptr<Layer<Dtype> > layer_by_name(const string& layer_name) const;
 //设置debug_info_
  void set_debug_info(const bool value) { debug_info_ = value; }

// Helpers for Init.(下面这些函数是Init的帮手)
  /**
   * @brief Remove layers that the user specified should be excluded given the current
   *        phase, level, and stage.
   */
   //过滤掉用户指定的在某个阶段、级别、状态下不应包含的Layer
  static void FilterNet(const NetParameter& param,
      NetParameter* param_filtered);
  /// @brief return whether NetState state meets NetStateRule rule
  //判断网络状态是否满足网络规则
  static bool StateMeetsRule(const NetState& state, const NetStateRule& rule,
      const string& layer_name);

  // Invoked at specific points during an iteration
  class Callback {
   protected:
    virtual void run(int layer) = 0;

    template <typename T>
    friend class Net;
  };
  const vector<Callback*>& before_forward() const { return before_forward_; }
  void add_before_forward(Callback* value) {
    before_forward_.push_back(value);
  }
  const vector<Callback*>& after_forward() const { return after_forward_; }
  void add_after_forward(Callback* value) {
    after_forward_.push_back(value);
  }
  const vector<Callback*>& before_backward() const { return before_backward_; }
  void add_before_backward(Callback* value) {
    before_backward_.push_back(value);
  }
  const vector<Callback*>& after_backward() const { return after_backward_; }
  void add_after_backward(Callback* value) {
    after_backward_.push_back(value);
  }
  
  protected:
  // Helpers for Init.
  /// @brief Append a new top blob to the net.
  //为网络追加一个Top Blob
  void AppendTop(const NetParameter& param, const int layer_id,
                 const int top_id, set<string>* available_blobs,
                 map<string, int>* blob_name_to_idx);
  /// @brief Append a new bottom blob to the net.
  //为网络追加一个Bottom Blob
  int AppendBottom(const NetParameter& param, const int layer_id,
                   const int bottom_id, set<string>* available_blobs,
                   map<string, int>* blob_name_to_idx);
  /// @brief Append a new parameter blob to the net.
  //为网络追加一个权值Blob
  void AppendParam(const NetParameter& param, const int layer_id,
                   const int param_id);

  /// @brief Helper for displaying debug info in Forward.
  void ForwardDebugInfo(const int layer_id);
  /// @brief Helper for displaying debug info in Backward.
  void BackwardDebugInfo(const int layer_id);
  /// @brief Helper for displaying debug info in Update.
  //显示权值更新调试信息
  void UpdateDebugInfo(const int param_id);
  
  /// @brief The network name
  string name_;
  /// @brief The phase: TRAIN or TEST
  Phase phase_;
  /// @brief Individual layers in the net(网络中的独立层)
  vector<shared_ptr<Layer<Dtype> > > layers_;
  vector<string> layer_names_; //层名称
  map<string, int> layer_names_index_;  //层名称与索引映射表
  vector<bool> layer_need_backward_;    //标记某个层是否需要BP
  /// @brief the blobs storing intermediate results between the layer.
  vector<shared_ptr<Blob<Dtype> > > blobs_; //层与层中间传递数据的管道
  vector<string> blob_names_;   //Blob名称
  map<string, int> blob_names_index_;   //Blob名称与索引映射表
  vector<bool> blob_need_backward_; //标记某个Blob是否需要BP
  /// bottom_vecs stores the vectors containing the input for each layer.
  /// They don't actually host the blobs (blobs_ does), so we simply store
  /// pointers.
  //bottom_vecs_存放每个层的输入Blob,实际上它并不是这些Blob的所有者(所有者为blobs_),只是存放了指针.
  vector<vector<Blob<Dtype>*> > bottom_vecs_;
  vector<vector<int> > bottom_id_vecs_;
  vector<vector<bool> > bottom_need_backward_;
  /// top_vecs stores the vectors containing the output for each layer
  //top_vecs_存放每个层的输入Blob,实际上它并不是这些Blob的所有者(所有者为blobs_),只是存放了指针.
  vector<vector<Blob<Dtype>*> > top_vecs_;
  vector<vector<int> > top_id_vecs_;
  /// Vector of weight in the loss (or objective) function of each net blob,
  /// indexed by blob_id.
  //每个Blob队全局损失函数的贡献权重
  vector<Dtype> blob_loss_weights_;
  vector<vector<int> > param_id_vecs_;
  vector<int> param_owners_;
  vector<string> param_display_names_;
  vector<pair<int, int> > param_layer_indices_;
  map<string, int> param_names_index_;
  /// blob indices for the input and the output of the net
  //网络输入/输出Blob的索引
  vector<int> net_input_blob_indices_;
  vector<int> net_output_blob_indices_;
  vector<Blob<Dtype>*> net_input_blobs_;
  vector<Blob<Dtype>*> net_output_blobs_;
  /// The parameters in the network.(网络权值)
  vector<shared_ptr<Blob<Dtype> > > params_;
  //可训练的网络权值
  vector<Blob<Dtype>*> learnable_params_;
  /**
   * The mapping from params_ -> learnable_params_: we have
   * learnable_param_ids_.size() == params_.size(),
   * and learnable_params_[learnable_param_ids_[i]] == params_[i].get()
   * if and only if params_[i] is an "owner"; otherwise, params_[i] is a sharer
   * and learnable_params_[learnable_param_ids_[i]] gives its owner.
   */
   //从params到learnable_params_的映射
   //当且仅当params_[i]为所有者时,learnable_param_ids_.size() == params__.size()以及 learnable_params_[learnable_parara_ids_[i]] == params_[i].get()成立
   //否则,params_[i]只是1个共享者,learnable_params_[learnable_param_ids_[i]]给出了它的所有者
  vector<int> learnable_param_ids_;
  /// the learning rate multipliers for learnable_params_
  vector<float> params_lr_;
  vector<bool> has_params_lr_;
  /// the weight decay multipliers for learnable_params_(权值衰减因子)
  vector<float> params_weight_decay_;
  vector<bool> has_params_decay_;
  /// The bytes of memory used by this net(记录网络占用的内存大小)
  size_t memory_used_;
  /// Whether to compute and display debug info for the net.(是否显示调试信息)
  bool debug_info_;
  // Callbacks
  vector<Callback*> before_forward_;
  vector<Callback*> after_forward_;
  vector<Callback*> before_backward_;
  vector<Callback*> after_backward_;
//禁用拷贝构造函数,赋值运算函数
DISABLE_COPY_AND_ASSIGN(Net);
};


}  // namespace caffe

#endif  // CAFFE_NET_HPP_

这里关于Net的头文件学习就到这里,后续学习相关的实现代码(cpp文件)

机制和策略

首先caffe中的Net/Layer/Blob是一种分层设计模式

在我们生活中普遍存在但又最容易被忽视的两个概念是:机制和策略.

一般来说,对于某客观事物.机制回答了“它能干啥”这个问题,策略则回答了“它怎么用”这个问题。

回到Caffe源码上,我们发现Blob提供了数据容器的机制;而Layer则通过不同的策略使用该数据容器,实现多元化的计算处理过程,同时又提供了深度学习各种基本算法(卷积、下采样、损失函数计算等)的机制;Net则利用Layer这些机制,组合为完整的深度学习模型,提供了更加丰富的学习策略。后面我们还会看到,Net也是一种机制。

在阅读源码时,时刻记得目标是希望看到高层策略,还是底层机制?

2017/6/21 posted in  Caffe 数据结构

Caffe中Layer的学习

Layer是Caffe的基本计算单元,至少有一个输入Blob (Bottom Blob)和一个输出Blob (Top Blob),部分Layer带有权值(Weight)和偏置项(Bias),有两个运算方向:前向传播(Forward)和反向传播(Backward),其中前向传播计算会对输入Blob进行某种处理(存权值和偏置项的Layer会利用这些对输入进行处理),得到输出Blob;而反向传播计算则对输出Blob的diff进行某种处理,得到输入Blob的diff(有权值和偏置项的Layer可能也会计算权值Blob、偏置项Blob的diff)。

layer中的数据结构描述

我们可以搜索caffe中关于message LayerParameter的类,来了解.
如果你一开始找不到这个类在那个文件描述,可以用下面这个命令去搜索:

➜  caffe git:(master) ✗ grep -n -H -R "message LayerParameter" *


得到它的路径.

我们发现是在src/caffe/proto/caffe.proto这个路径中.因为caffe使用google_protobuf数据类型来声明layer.关于google_protobuf的相关内容,之后可以研究一下.

这里我们看一下源码:

//注意:如果你增加了1个新的LayerParameter域,一定记得更新一个可用ID
// LayerParameter 下一个layer-specific ID: 147 (last added: recurrent_param)
message LayerParameter {
  optional string name = 1; // the layer name
  optional string type = 2; // the layer type
  repeated string bottom = 3; // 输入Blob(bottom Blob)的名称
  repeated string top = 4; // 输出Blob(Top Blob)的名称

  // 当前计算阶段(TRAIN 或 TEST)
    optional Phase phase = 10;

  // 为每个Top Blob分配对损失函数的权重,毎个Layer都有默认值,要么为0,表示不参与目标函数计算:要么为1,表示参与损失函数计算
  repeated float loss_weight = 5;

  // 指定训练参数(例如相对全局学习常数的缩放因子,以及用于权值共享 的名称或其他设置)
  repeated ParamSpec param = 6;

  // 承载了该层数值参数的Blob
  repeated BlobProto blobs = 7;
  //是否对Bottom Blob进行反向传播过程。该字段的维度应与 Bottom Blob个数一致
  // Specifies whether to backpropagate to each bottom. If unspecified,
  // Caffe will automatically infer whether each input needs backpropagation
  // to compute parameter gradients. If set to true for some inputs,
  // backpropagation to those inputs is forced; if set false for some inputs,
  // backpropagation to those inputs is skipped.
  //
  // The size must be either 0 or equal to the number of bottoms.
  repeated bool propagate_down = 11;

 //控制某个层在某个时刻是否包含在网络中,基于当前NetState。你可以为include或exclude(不要同时)指定非零值。如果没有任何规则,那么该层一直包含在网络中:如果当前NetState满足了任何1个指定规则,耶么该层会被包含或排斥
  // Rules controlling whether and when a layer is included in the network,
  // based on the current NetState.  You may specify a non-zero number of rules
  // to include OR exclude, but not both.  If no include or exclude rules are
  // specified, the layer is always included.  If the current NetState meets
  // ANY (i.e., one or more) of the specified rules, the layer is
  // included/excluded.
  repeated NetStateRule include = 8;
  repeated NetStateRule exclude = 9;

  // Parameters for data pre-processing.数据预处理参数
  optional TransformationParameter transform_param = 100;

  // Parameters shared by loss layers.所有损失层共享的参数
  optional LossParameter loss_param = 101;
  
  
  //特定类型层的参数。注意一些层实现时可能有多于一种的计算引擎,这些层包括一个引擎类型和引擎参数来选择实现.默认引擎是在编译阶段由引擎开关设置的
  // Layer type-specific parameters.
  //
  // Note: certain layers may have more than one computational engine
  // for their implementation. These layers include an Engine type and
  // engine parameter for selecting the implementation.
  // The default for the engine is set by the ENGINE switch at compile-time.
  optional AccuracyParameter accuracy_param = 102;
  optional ArgMaxParameter argmax_param = 103;
  optional BatchNormParameter batch_norm_param = 139;
  optional BiasParameter bias_param = 141;
  optional ConcatParameter concat_param = 104;
  optional ContrastiveLossParameter contrastive_loss_param = 105;
  optional ConvolutionParameter convolution_param = 106;
  optional CropParameter crop_param = 144;
  optional DataParameter data_param = 107;
  optional DropoutParameter dropout_param = 108;
  optional DummyDataParameter dummy_data_param = 109;
  optional EltwiseParameter eltwise_param = 110;
  optional ELUParameter elu_param = 140;
  optional EmbedParameter embed_param = 137;
  optional ExpParameter exp_param = 111;
  optional FlattenParameter flatten_param = 135;
  optional HDF5DataParameter hdf5_data_param = 112;
  optional HDF5OutputParameter hdf5_output_param = 113;
  optional HingeLossParameter hinge_loss_param = 114;
  optional ImageDataParameter image_data_param = 115;
  optional InfogainLossParameter infogain_loss_param = 116;
  optional InnerProductParameter inner_product_param = 117;
  optional InputParameter input_param = 143;
  optional LogParameter log_param = 134;
  optional LRNParameter lrn_param = 118;
  optional MemoryDataParameter memory_data_param = 119;
  optional MVNParameter mvn_param = 120;
  optional ParameterParameter parameter_param = 145;
  optional PoolingParameter pooling_param = 121;
  optional PowerParameter power_param = 122;
  optional PReLUParameter prelu_param = 131;
  optional PythonParameter python_param = 130;
  optional RecurrentParameter recurrent_param = 146;
  optional ReductionParameter reduction_param = 136;
  optional ReLUParameter relu_param = 123;
  optional ReshapeParameter reshape_param = 133;
  optional ScaleParameter scale_param = 142;
  optional SigmoidParameter sigmoid_param = 124;
  optional SoftmaxParameter softmax_param = 125;
  optional SPPParameter spp_param = 132;
  optional SliceParameter slice_param = 126;
  optional TanHParameter tanh_param = 127;
  optional ThresholdParameter threshold_param = 128;
  optional TileParameter tile_param = 138;
  optional WindowDataParameter window_data_param = 129;
}

Layer是怎么炼成的

Layer头文件位于include/caffe/layer.hpp中,我们来解析一下:

#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 {

/**
 * @brief An interface for the units of computation which can be composed into a
 *        Net.
 *
 * Layer%s must implement a Forward function, in which they take their input
 * (bottom) Blob%s (if any) and compute their output Blob%s (if any).
 * They may also implement a Backward function, in which they compute the error
 * gradients with respect to their input Blob%s, given the error gradients with
 * their output Blob%s.
 */
template <typename Dtype>
class Layer {
 public:
  /**
   * You should not implement your own constructor. Any set up code should go
   * to SetUp(), where the dimensions of the bottom blobs are provided to the
   * layer.
   */
   //显式构造函数,从LayerParameter对象中加载配置
  explicit Layer(const LayerParameter& param)
    : layer_param_(param) {
      // Set phase(训练/预测) and copy blobs (if there are any).
      phase_ = param.phase();
      if (layer_param_.blobs_size() > 0) {
        //按 layer_param_设置本身Blob对象个数,并依次将每个Blob对象尺寸调整为与layer_param_中的Blob尺寸一致
        blobs_.resize(layer_param_.blobs_size());
        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() {}

  /**
   * @brief Implements common layer setup functionality.
   *
   * @param bottom the preshaped input blobs
   * @param top
   *     the allocated but unshaped output blobs, to be shaped by Reshape
   *
   * Checks that the number of bottom and top blobs is correct.
   * Calls LayerSetUp to do special layer setup for individual layer types,
   * followed by Reshape to set up sizes of top blobs and internal buffers.
   * Sets up the loss weight multiplier blobs for any non-zero loss weights.
   * This method may not be overridden.
   */
   
   //配置函数,实现常用层配置接口,不可被覆盖
  void SetUp(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {
    CheckBlobCounts(bottom, top);   //检查Blob
    LayerSetUp(bottom, top);        //  与层类型相关的配置过程
    Reshape(bottom, top);       //对Top Blob变形
    SetLossWeights(top);        //设置损失权值因子Blob
  }

  /**
   * @brief Does layer-specific setup: your layer should implement this function
   *        as well as Reshape.
   *
   * @param bottom
   *     the preshaped input blobs, whose data fields store the input data for
   *     this layer
   * @param top
   *     the allocated but unshaped output blobs
   *
   * This method should do one-time layer specific setup. This includes reading
   * and processing relevent parameters from the <code>layer_param_</code>.
   * Setting up the shapes of top blobs and internal buffers should be done in
   * <code>Reshape</code>, which will be called before the forward pass to
   * adjust the top blob sizes.
   */
   
   //层配置(虚)函数,做特定类型层相关的配置,由该类型层自己实现
  virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) {}
   
   //变形(纯虚)函数,修改Top Blob以及内部Blob缓冲区的形状
  virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) = 0;

 
   //前向传播函数,给定Bottom Blob,计算Top Blob和loss,返回值为当前层loss
   //该函数会调用相应设裕包装闲数,如Forward_cpu或Forward_gpu来实现真正的计算过程。如果该层有任意非零loss_weights参数,那么包装函数会计算并返回loss
   //派生类应该实现Forward_cpu和Forward_gpu (可选〉
  inline Dtype Forward(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top);

  //反向传播函数,给定Top Blob误差梯度,汁算Bottom Blob误差梯度
  //参数说明:
  // top-Top Blob,其diff域包含来自上一层的误差梯度
  // propagate_down -- 多路幵关,与Bottom Blob矢量维度相问,每个值表示是否将误差梯度传递到对应的 Bottom Blob
  // bottom—Bottom Blob,其diff域需要由该函数计算得到
  // 该函数会调用相应设备包装函数,如Backward_cpu或Backward_gpu来实现真正的计算过程,由派生类负责实现
  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; }

  //返回是否允许匿名Top Blob,即由该Layer自动创建。若为真,在Net::Init()函数中会创建足够多的匿名Top Blob来满足该 Layer ExactNumTopBlobs()、MinTopBlobs()需求
  virtual inline bool AutoTopBlobs() const { return false; }

  //返回某些Bottom Blob足否允许强制反向传播,如果AllowForceBackward(i) === false,将会忽略 force_backward 设定
  virtual inline bool AllowForceBackward(const int bottom_index) const {
    return true;
  }

  //指定该Layer是否计算相对权值或偏置项的梯度,具体相对谁由param_id指定
  inline bool param_propagate_down(const int param_id) {
    return (param_propagate_down_.size() > param_id) ?
        param_propagate_down_[param_id] : false;
  }
  
  //设置该Layer是否计算相对权值或偏置项的梯度,具体相对谁由param_id指定
  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;
  }


 protected:
  /** The protobuf that stores the layer parameters */
  LayerParameter layer_param_;
  /** 当前所处阶段: TRAIN or TEST */
  Phase phase_;
  /** The vector that stores the learnable parameters as a set of blobs. */
  //Layer 内部权值或偏置项,以 Blob 方式组织
  vector<shared_ptr<Blob<Dtype> > > blobs_;
  /** Vector indicating whether to compute the diff of each param blob. */
  //标志位,是否计算对应参数的误差梯度
  vector<bool> param_propagate_down_;

  //标志位,在目标函数中,是否每个Top Blob都有非零权重
  vector<Dtype> loss_;

//下面4个函数,我们会在各个Layer派生类中经常看到

  /** @brief Using the CPU device, compute the layer output. */
  virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) = 0;
  /**
   * @brief Using the GPU device, compute the layer output.
   *        Fall back to Forward_cpu() if unavailable.
   */
  virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {
    // LOG(WARNING) << "Using CPU code as backup.";
    return Forward_cpu(bottom, top);
  }

  /**
   * @brief Using the CPU device, compute the gradients for any parameters and
   *        for the bottom blobs if propagate_down is true.
   */
  virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
      const vector<bool>& propagate_down,
      const vector<Blob<Dtype>*>& bottom) = 0;
  /**
   * @brief Using the GPU device, compute the gradients for any parameters and
   *        for the bottom blobs if propagate_down is true.
   *        Fall back to Backward_cpu() if unavailable.
   */
  virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
      const vector<bool>& propagate_down,
      const vector<Blob<Dtype>*>& bottom) {
    // LOG(WARNING) << "Using CPU code as backup.";
    Backward_cpu(top, propagate_down, bottom);
  }

  /**
   * Called by the parent Layer's SetUp to check that the number of bottom
   * and top Blobs provided as input match the expected numbers specified by
   * the {ExactNum,Min,Max}{Bottom,Top}Blobs() functions.
   */
   //校验输入/输出Blob数目是否满足Layer要求
  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.
   */
   //该函数在Layer的Setup函数中被调用,主要目的是初始化与Top Blob相关的loss权重,放到Top Blob的diff域,实际由Forward()计算loss函数
   //loss_weight == 0,表示当前层不参与loss函数汁算,大部分Layer属于这一类
   //loss_weight ==1,表示当前层参与loss函数汁算,损失层(LossLayer) 属于这一类
  inline void SetLossWeights(const vector<Blob<Dtype>*>& top) {
  //从ProtoBuffer对象中获得Layer参数,这里需要用loss_weight参数
    const int num_loss_weights = layer_param_.loss_weight_size();
    //如果 ProtoBuffer中存在至少一个loss_weight参数,loss_weight参数个数应当与Top Blob数目相同,或者不要loss_weight参数
    if (num_loss_weights) {
      CHECK_EQ(top.size(), num_loss_weights) << "loss_weight must be "
          "unspecified or specified once per top blob.";
    //遍历每个Top Blob
      for (int top_id = 0; top_id < top.size(); ++top_id) {
      // 从 ProtoBuffer 对象拿到 loss_weight 实际值(0 或者1)
        const Dtype loss_weight = layer_param_.loss_weight(top_id);
        //若为0,跳过
        if (loss_weight == Dtype(0)) { continue; }\
        //若不为0,则对网络进行相关设置
        this->set_loss(top_id, loss_weight);    //本地记录loss_weight值
        const int count = top[top_id]->count();
        Dtype* loss_multiplier = top[top_id]->mutable_cpu_diff();
        //将loss_weight值入 Top Blob 的diff域,传递到其他需耍使用的地一方,实现远程同步
        caffe_set(count, loss_weight, loss_multiplier);
      }
    }
  }

 private:
 //禁用拷贝构造函数和賦值运算函数 
  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.
//使用时只需在派生类中改写 Forward_cpu、Forward_gpu、Backward_cpu、Backward_gpu
template <typename Dtype>
inline Dtype Layer<Dtype>::Forward(const vector<Blob<Dtype>*>& bottom,
    const vector<Blob<Dtype>*>& top) {
  Dtype loss = 0;
  Reshape(bottom, top);
  switch (Caffe::mode()) {      //判断计算设备
  case Caffe::CPU:      //在CPU上执行Forward计算
    Forward_cpu(bottom, top);   //调用CPU版本的 Forward函数
    //还没完,要计算loss (如果有的话)
    for (int top_id = 0; top_id < top.size(); ++top_id) {
      if (!this->loss(top_id)) { continue; }
      const int count = top[top_id]->count();
      // 若为 LossLayer,则已经通过Forward函数计算出全局损失函数,放在Top Blob data域
      const Dtype* data = top[top_id]->cpu_data();
      // 若loss_weight不为0,则己经在SetLossWeights函数中将loss权重放在Top Blob diff域
      const Dtype* loss_weights = top[top_id]->cpu_diff();
      // 计算加权后的loss之和,得到标量loss值
      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.";
  }
  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.";
  }
}

//将层配置参数序列化为ProtoBuffer
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_

Layer源文件位于src/caffe/layer.cpp中:

#include "caffe/layer.hpp"

namespace caffe {

INSTANTIATE_CLASS(Layer);

}  // namespace caffe

可见Layer大部分函数并没有实现,只有虚函数,真正的实现都在派生类中。具体代码可以进一步阅读 src/caffe/丨ayers/*.cpp

在使用 Layer 之前,需要先包含头文件#include <caffe/layer.hpp>,再通过using namespace caffe;使用命名空间caffe。如果代码中试图创建Layer对象,编译时会报错:

error: cannot declare variable 'a^ to be of abstract type 'caffe::Layer<float>

这是因为Layer类是一个虚基类,不能直接创建对象。关于虚基类,这里不再过多说明.

2017/6/15 posted in  Caffe 数据结构

caffe数据结构描述

打开caffe目录下的src/caffe/proto/caffe.proto文件,首先讲的就是Blob的描述.

// 该结构描述了 Blob的形状信息
message BlobShape {
  repeated int64 dim = 1 [packed = true];  //只包括若干int64类型值,分别表示Blob每个维度的大小。packed表示这些值在内存中紧密排布,没有空洞
}

//该结构描述Blob在磁盘中序列化后的形态
message BlobProto {
  optional BlobShape shape = 7;    //可选,包括一个BlobShape对象
  repeated float data = 5 [packed = true]; // //包括若千浮点元素,存储数据或权值,元素数目由shape或(num, channels, height, width)确定,这些元素在内存中紧密排布.
  repeated float diff = 6 [packed = true];  ////包括若干浮点元素,用于存储增量信息,维度与data 数组一致
  repeated double double_data = 8 [packed = true];  // 与 data并列,只是类型为double
  repeated double double_diff = 9 [packed = true];  // 与 diff 并列,只是类型为 double

  // 4D dimensions -- deprecated.  Use "shape" instead.
  optional int32 num = 1 [default = 0];
  optional int32 channels = 2 [default = 0];
  optional int32 height = 3 [default = 0];
  optional int32 width = 4 [default = 0];
}

// The BlobProtoVector is simply a way to pass multiple blobproto instances
// around.
message BlobProtoVector {
  repeated BlobProto blobs = 1;
}

这里我们使用protobuffer主要是因为它具有很好的健壮性,将编程最容易出问题的地方加以隐藏,让机器自动处理.

Blob的构成

Blob是一个模板类,声明在include/caffe/blob.hpp中,里面封装了一些基本的Layer,Net,Solver等,还有syncedmem类:


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

#include "caffe/common.hpp"
#include "caffe/proto/caffe.pb.h"//由protoc生成的头文件,声明了 BlobProto、BlobShape等遵循caffe.proto协议的数据结构 可以在src/caffe/proto文件下运行protoc caffe.proto --cpp_out=./命令生成该头文件.
#include "caffe/syncedmem.hpp"  //CPU/GPU共享内存类,用于数据同步

const int kMaxBlobAxes = 32;    //Blob最大维数目
template <typename Dtype>
class Blob {    //类声明
 public:
    //默认构造函数
  Blob()
       : data_(), diff_(), count_(0), capacity_(0) {}
    //显式构造函数
  explicit Blob(const int num, const int channels, const int height, const int width);
  explicit Blob(const vector<int>& shape);

 //变形函数,报据输入参数重新设置当前Blob形状,必要时重新分配内存
  void Reshape(const int num, const int channels, const int height,
      const int width);
  
  void Reshape(const vector<int>& shape);
  void Reshape(const BlobShape& shape);
  void ReshapeLike(const Blob& other);
  //得到Blob形状字符串用于打印log,见Caffe运行log,类似"Top shape: 100 1 28 28 (78400)"
  inline string shape_string() const {
    ostringstream stream;
    for (int i = 0; i < shape_.size(); ++i) {
      stream << shape_[i] << " ";
    }
    stream << "(" << count_ << ")";
    return stream.str();
  }
  //返回Blob形状
  inline const vector<int>& shape() const { return shape_; }
    //返回某1维度的尺寸
  inline int shape(int index) const {
    return shape_[CanonicalAxisIndex(index)];
  }
  //返回维度数目
  inline int num_axes() const { return shape_.size(); }
  //返回Blob中元素总数
  inline int count() const { return count_; }
    //返回Blob中某几维子集的元素总数
    inline int count(int start_axis, int end_axis) const {
    CHECK_LE(start_axis, end_axis); //保证 start_axis <= end_axis
    CHECK_GE(start_axis, 0);  // 保证 start_axis >= 0
    CHECK_GE(end_axis, 0);      // 保证 end_axis >= 0
    CHECK_LE(start_axis, num_axes()); //保证start_axis    <=总的维度数目
    CHECK_LE(end_axis, num_axes()); //保证end_axis <=总的维度数目
    int count = 1;
    for (int i = start_axis; i < end_axis; ++i) {
      count *= shape(i);
    }
    return count;
  }
  //计算从某一维度开始的元素总数
  inline int count(int start_axis) const {
    return count(start_axis, num_axes());
  }
  //转换坐标轴索引[-N,N)为普通索引[0,N)
  inline int CanonicalAxisIndex(int axis_index) const {
    CHECK_GE(axis_index, -num_axes())
        << "axis " << axis_index << " out of range for " << num_axes()
        << "-D Blob with shape " << shape_string();
    CHECK_LT(axis_index, num_axes())
        << "axis " << axis_index << " out of range for " << num_axes()
        << "-D Blob with shape " << shape_string();
    if (axis_index < 0) {
    //负索引表示从后向前访问,-1表示最后一个个元素,普通索引值为 N-1:同理,-2 => N-2, -3 => N-3,…
      return axis_index + num_axes();
    }
    return axis_index;
  }
  //获取某一维的尺寸
  /// @brief Deprecated legacy shape accessor num: use shape(0) instead.
  inline int num() const { return LegacyShape(0); }
  /// @brief Deprecated legacy shape accessor channels: use shape(1) instead.
  inline int channels() const { return LegacyShape(1); }
  /// @brief Deprecated legacy shape accessor height: use shape(2) instead.
  inline int height() const { return LegacyShape(2); }
  /// @brief Deprecated legacy shape accessor width: use shape(3) instead.
  inline int width() const { return LegacyShape(3); }
  inline int LegacyShape(int index) const {
    CHECK_LE(num_axes(), 4)
        << "Cannot use legacy accessors on Blobs with > 4 axes.";
    CHECK_LT(index, 4);
    CHECK_GE(index, -4);
    if (index >= num_axes() || index < -num_axes()) {
      // Axis is out of range, but still in [0, 3] (or [-4, -1] for reverse
      // indexing) -- this special case simulates the one-padding used to fill
      // extraneous axes of legacy blobs.
      return 1;
    }
    return shape(index);
  }
  //下面的是计算偏移量的函数
  inline int offset(const int n, const int c = 0, const int h = 0,
      const int w = 0) const {
    CHECK_GE(n, 0);
    CHECK_LE(n, num());
    CHECK_GE(channels(), 0);
    CHECK_LE(c, channels());
    CHECK_GE(height(), 0);
    CHECK_LE(h, height());
    CHECK_GE(width(), 0);
    CHECK_LE(w, width());
    return ((n * channels() + c) * height() + h) * width() + w;
  }

  inline int offset(const vector<int>& indices) const {
    CHECK_LE(indices.size(), num_axes());
    int offset = 0;
    for (int i = 0; i < num_axes(); ++i) {
      offset *= shape(i);
      if (indices.size() > i) {
        CHECK_GE(indices[i], 0);
        CHECK_LT(indices[i], shape(i));
        offset += indices[i];
      }
    }
    return offset;
  }
  //按值拷贝Blob到当前Blob
  void CopyFrom(const Blob<Dtype>& source, bool copy_diff = false, bool reshape = false);
  
  //下面几个函数是存取器(getter/setter)
  inline Dtype data_at(const int n, const int c, const int h,
      const int w) const {
    return cpu_data()[offset(n, c, h, w)];
  }

  inline Dtype diff_at(const int n, const int c, const int h,
      const int w) const {
    return cpu_diff()[offset(n, c, h, w)];
  }

  inline Dtype data_at(const vector<int>& index) const {
    return cpu_data()[offset(index)];
  }

  inline Dtype diff_at(const vector<int>& index) const {
    return cpu_diff()[offset(index)];
  }

  inline const shared_ptr<SyncedMemory>& data() const {
    CHECK(data_);
    return data_;
  }

  inline const shared_ptr<SyncedMemory>& diff() const {
    CHECK(diff_);
    return diff_;
  }
  
  //只读访问cpu_date
  const Dtype* cpu_data() const;
  //设置cpu_date
  void set_cpu_data(Dtype* data);
  const int* gpu_shape() const;
  //只读访问gpu_date
  const Dtype* gpu_data() const;
  //设置gpu_date
  void set_gpu_data(Dtype* data);
  //只读访问cpu_diff
  const Dtype* cpu_diff() const;
  //只读访问gpu_diff
  const Dtype* gpu_diff() const;
  //下面四个是读写访问数据
  Dtype* mutable_cpu_data();
  Dtype* mutable_gpu_data();
  Dtype* mutable_cpu_diff();
  Dtype* mutable_gpu_diff();
  void Update();    //Blob更新运算,可简单理解为data与diff的merge过程
  //反序列化函数,从BlobProto中恢复个Blob对象
  void FromProto(const BlobProto& proto, bool reshape = true);
  //序列化函数,将内存中的Blob对象保存到BlobProto中
  void ToProto(BlobProto* proto, bool write_diff = false) const;

  /// @brief Compute the sum of absolute values (L1 norm) of the data.
  Dtype asum_data() const;
  /// @brief Compute the sum of absolute values (L1 norm) of the diff.
  Dtype asum_diff() const;
  /// @brief Compute the sum of squares (L2 norm squared) of the data.
  Dtype sumsq_data() const;
  /// @brief Compute the sum of squares (L2 norm squared) of the diff.
  Dtype sumsq_diff() const;

/// @brief Scale the blob data by a constant factor.
  void scale_data(Dtype scale_factor);
  /// @brief Scale the blob diff by a constant factor.
  void scale_diff(Dtype scale_factor);
 // 共享另一个 Blob 的 diff
  void ShareData(const Blob& other);
  void ShareDiff(const Blob& other);
  
  protected:
  shared_ptr<SyncedMemory> data_;   //存放指向data的指针
  shared_ptr<SyncedMemory> diff_;   //存放指向diff的指针
  shared_ptr<SyncedMemory> shape_data_; 
  vector<int> shape_;   //形状信息
  int count_;   //存放有效元素数目信息
  int capacity_;    //存放Blob容器的容量信息

  DISABLE_COPY_AND_ASSIGN(Blob);    //禁用拷贝构造函数、陚值运算符重载
};  // class Blob

注意到Caffe类中成员变量名都带有后缀,这样在函数实现中容易区分临时变量和类成员变量。

打幵include/caffe/syncedmem.hpp,査看该类的用法:

#ifndef CAFFE_SYNCEDMEM_HPP_
#define CAFFE_SYNCEDMEM_HPP_

#include <cstdlib>

#ifdef USE_MKL
  #include "mkl.h"
#endif

#include "caffe/common.hpp"

namespace caffe {

//如果在GPU模式,且CUDA使能,那么主机内存会以页锁定内存方式分配(使用cudaMallocHostU函数。对f-单GPU的性能提升不明显,但多GPU会非常明显)
inline void CaffeMallocHost(void** ptr, size_t size, bool* use_cuda) {
#ifndef CPU_ONLY
  if (Caffe::mode() == Caffe::GPU) {
    CUDA_CHECK(cudaMallocHost(ptr, size));
    *use_cuda = true;
    return;
  }
#endif
#ifdef USE_MKL
  *ptr = mkl_malloc(size ? size:1, 64);
#else
  *ptr = malloc(size);
#endif
  *use_cuda = false;
  CHECK(*ptr) << "host allocation of size " << size << " failed";
}
// 与CaffeMallocHost对应
inline void CaffeFreeHost(void* ptr, bool use_cuda) {
#ifndef CPU_ONLY
  if (use_cuda) {
    CUDA_CHECK(cudaFreeHost(ptr));
    return;
  }
#endif
#ifdef USE_MKL
  mkl_free(ptr);
#else
  free(ptr);
#endif
}

//该类负责存储分配以及主机和设备间同步
class SyncedMemory {
 public:
 //构造函数
  SyncedMemory();
  //显式构造函数
  explicit SyncedMemory(size_t size);
  //析构函数
  ~SyncedMemory();
  const void* cpu_data();       //只读获取cpu data
  void set_cpu_data(void* data);    //设置cpu data
  const void* gpu_data();       //只读获取gpu data
  void set_gpu_data(void* data);    //设置gpu data
  void* mutable_cpu_data();     // 读写获取 cpu data
  void* mutable_gpu_data();     // 读写获取 gpu data
  //状态机变量,表示4种状态:术初始化、CPU数据奋效、GPU数据有效、己同步
  enum SyncedHead { UNINITIALIZED, HEAD_AT_CPU, HEAD_AT_GPU, SYNCED };
  //获得当前状态机变量值
  SyncedHead head() { return head_; }
  //获得当前存储空间尺寸
  size_t size() { return size_; }

#ifndef CPU_ONLY
  void async_gpu_push(const cudaStream_t& stream);
#endif

 private:
  void check_device();

  void to_cpu();    //数据同步至CPU
  void to_gpu();    //数据同步至GPU
  void* cpu_ptr_;   //位于CPU的数据指针
  void* gpu_ptr_;   //位于GPU的数据指针
  size_t size_;     //存储空间大小
  SyncedHead head_; //状态机变量
  bool own_cpu_data_;   //标志是否拥有CPU数据所有权(否,即从别的对象共享)
  bool cpu_malloc_use_cuda_;
  bool own_gpu_data_;   ////标志是否拥有GPU数据所有权
  int device_;      //设备号

  DISABLE_COPY_AND_ASSIGN(SyncedMemory);
};  // class SyncedMemory

}  // namespace caffe

#endif  // CAFFE_SYNCEDMEM_HPP_

Blob类实现的源码位于src/caffe/blob.cpp中,内容如下:


#include <climits>
#include <vector>

#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/syncedmem.hpp"
#include "caffe/util/math_functions.hpp"

namespace caffe {
//变维函数,将(num, channels, height, width}参数转换为vector<int>,然后调用重载的变维函数void Blob<Dtype>::Reshape(const BlobShape& shape)
template <typename Dtype>
void Blob<Dtype>::Reshape(const int num, const int channels, const int height,
    const int width) {
  vector<int> shape(4);
  shape[0] = num;
  shape[1] = channels;
  shape[2] = height;
  shape[3] = width;
  Reshape(shape);
}
//真正变维函数
template <typename Dtype>
void Blob<Dtype>::Reshape(const vector<int>& shape) {
  CHECK_LE(shape.size(), kMaxBlobAxes); //保证vector维度<=kMaxBlobAxes
  count_ = 1;   //用于计算元素总数=num * channels * height * width 
  shape_.resize(shape.size());  //成员变量维度也被重罝
  if (!shape_data_ || shape_data_->size() < shape.size() * sizeof(int)) {
    shape_data_.reset(new SyncedMemory(shape.size() * sizeof(int)));
  }
  int* shape_data = static_cast<int*>(shape_data_->mutable_cpu_data());
  for (int i = 0; i < shape.size(); ++i) {
    CHECK_GE(shape[i], 0);  // 保证每维度尺寸都>=0
    if (count_ != 0) {
    //证count_不溢出
      CHECK_LE(shape[i], INT_MAX / count_) << "blob size exceeds INT_MAX";
    }
    count_ *= shape[i];     //count_累乘
    shape_[i] = shape[i];   //为成员变量赋值
    shape_data[i] = shape[i];
  }
  if (count_ > capacity_) {     //如果新的count_大于当前己分f配空间容量
    capacity_ = count_;         //扩容,重新分配data_和dif f_空间
    data_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
    diff_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
  }
}

//void Blob<Dtype>::Reshape(const BlobShape& shape) 和void Blob<Dtype>::ReshapeLike(const Blob<Dtype>& other)与上面类似. 

//构造函数
template <typename Dtype>
Blob<Dtype>::Blob(const int num, const int channels, const int height,
    const int width)
  // 调用Reshape之前必须初始化capacity_,否则会导致不可预期结果
  : capacity_(0) {
  Reshape(num, channels, height, width);
}

template <typename Dtype>
Blob<Dtype>::Blob(const vector<int>& shape)
  // capacity_ must be initialized before calling Reshape
  : capacity_(0) {
  Reshape(shape);
}

template <typename Dtype>
const int* Blob<Dtype>::gpu_shape() const {
  CHECK(shape_data_);
  return (const int*)shape_data_->gpu_data();
}
//只读获取cpu date指针
template <typename Dtype>
const Dtype* Blob<Dtype>::cpu_data() const {
  CHECK(data_);     //保证data_不为 NULL
  return (const Dtype*)data_->cpu_data();
}
//修改cpu data指针
template <typename Dtype>
void Blob<Dtype>::set_cpu_data(Dtype* data) {
  CHECK(data);
  // Make sure CPU and GPU sizes remain equal
  size_t size = count_ * sizeof(Dtype);
  if (data_->size() != size) {
    data_.reset(new SyncedMemory(size));
    diff_.reset(new SyncedMemory(size));
  }
  data_->set_cpu_data(data);
}

template <typename Dtype>
const Dtype* Blob<Dtype>::gpu_data() const {
  CHECK(data_);
  return (const Dtype*)data_->gpu_data();
}

template <typename Dtype>
void Blob<Dtype>::set_gpu_data(Dtype* data) {
  CHECK(data);
  // Make sure CPU and GPU sizes remain equal
  size_t size = count_ * sizeof(Dtype);
  if (data_->size() != size) {
    data_.reset(new SyncedMemory(size));
    diff_.reset(new SyncedMemory(size));
  }
  data_->set_gpu_data(data);
}
//只读获取cpu_diff指针
template <typename Dtype>
const Dtype* Blob<Dtype>::cpu_diff() const {
  CHECK(diff_);
  return (const Dtype*)diff_->cpu_data();
}
//只读获取gpu_diff指针
template <typename Dtype>
const Dtype* Blob<Dtype>::gpu_diff() const {
  CHECK(diff_);
  return (const Dtype*)diff_->gpu_data();
}
//读写访问cpu data指针
template <typename Dtype>
Dtype* Blob<Dtype>::mutable_cpu_data() {
  CHECK(data_);
  return static_cast<Dtype*>(data_->mutable_cpu_data());
}
//读写访问gpu data指针
template <typename Dtype>
Dtype* Blob<Dtype>::mutable_gpu_data() {
  CHECK(data_);
  return static_cast<Dtype*>(data_->mutable_gpu_data());
}
//与上面相同
template <typename Dtype>
Dtype* Blob<Dtype>::mutable_cpu_diff() {
  CHECK(diff_);
  return static_cast<Dtype*>(diff_->mutable_cpu_data());
}

template <typename Dtype>
Dtype* Blob<Dtype>::mutable_gpu_diff() {
  CHECK(diff_);
  return static_cast<Dtype*>(diff_->mutable_gpu_data());
}
//共享另一个Blob的data指针
template <typename Dtype>
void Blob<Dtype>::ShareData(const Blob& other) {
  CHECK_EQ(count_, other.count());
  data_ = other.data();
}
//共享另一个Blob的diff指针
template <typename Dtype>
void Blob<Dtype>::ShareDiff(const Blob& other) {
  CHECK_EQ(count_, other.count());
  diff_ = other.diff();
}
//Update()函数用于网络参数Blob的更新。其中int和unsigned int类型处理并未实现
template <> void Blob<unsigned int>::Update() { NOT_IMPLEMENTED; }
template <> void Blob<int>::Update() { NOT_IMPLEMENTED; }
template <typename Dtype>
void Blob<Dtype>::Update() {
  // We will perform update based on where the data is located.data在哪里我们就在那里更新
  switch (data_->head()) {
  case SyncedMemory::HEAD_AT_CPU:       //data位于cpu端
    // 执行CPU计算
        caffe_axpy<Dtype>(count_, Dtype(-1),
        static_cast<const Dtype*>(diff_->cpu_data()),
        static_cast<Dtype*>(data_->mutable_cpu_data()));
    break;
  case SyncedMemory::HEAD_AT_GPU:   //data位于GPU端,或者CPU/GPU已经同步
  case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
    // 执行 CPU 上的计算,data_[i】=data_[i] - diff_[i], i = 0,1,2,…,count_-1
    caffe_gpu_axpy<Dtype>(count_, Dtype(-1),
        static_cast<const Dtype*>(diff_->gpu_data()),
        static_cast<Dtype*>(data_->mutable_gpu_data()));
#else
    NO_GPU;     //编泽时打开了CPU_ONLY选项,那么GPU模式禁用
#endif
    break;
  default:
    LOG(FATAL) << "Syncedmem not initialized.";
  }
}
//计算data_的L1-范数,其中int和unsigned int类型处理并未实现
template <> unsigned int Blob<unsigned int>::asum_data() const {
  NOT_IMPLEMENTED;
  return 0;
}
template <> int Blob<int>::asum_data() const {
  NOT_IMPLEMENTED;
  return 0;
}

template <typename Dtype>
Dtype Blob<Dtype>::asum_data() const {
  if (!data_) { return 0; }
  switch (data_->head()) {
  case SyncedMemory::HEAD_AT_CPU:
    return caffe_cpu_asum(count_, cpu_data());  //执行CPU上的asum计算
  case SyncedMemory::HEAD_AT_GPU:
  case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
  {
    Dtype asum;
    caffe_gpu_asum(count_, gpu_data(), &asum);
    return asum;
  }
#else
    NO_GPU;
#endif
  case SyncedMemory::UNINITIALIZED:
    return 0;
  default:
    LOG(FATAL) << "Unknown SyncedMemory head state: " << data_->head();
  }
  return 0;
}

template <> unsigned int Blob<unsigned int>::asum_diff() const {
  NOT_IMPLEMENTED;
  return 0;
}

template <> int Blob<int>::asum_diff() const {
  NOT_IMPLEMENTED;
  return 0;
}
//同上,计算diff_的L1范数
template <typename Dtype>
Dtype Blob<Dtype>::asum_diff() const {
  if (!diff_) { return 0; }
  switch (diff_->head()) {
  case SyncedMemory::HEAD_AT_CPU:
    return caffe_cpu_asum(count_, cpu_diff());
  case SyncedMemory::HEAD_AT_GPU:
  case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
  {
    Dtype asum;
    caffe_gpu_asum(count_, gpu_diff(), &asum);
    return asum;
  }
#else
    NO_GPU;
#endif
  case SyncedMemory::UNINITIALIZED:
    return 0;
  default:
    LOG(FATAL) << "Unknown SyncedMemory head state: " << diff_->head();
  }
  return 0;
}
//计算data_的L2-范数
template <> unsigned int Blob<unsigned int>::sumsq_data() const {
  NOT_IMPLEMENTED;
  return 0;
}
template <> int Blob<int>::sumsq_data() const {
  NOT_IMPLEMENTED;
  return 0;
}

template <typename Dtype>
Dtype Blob<Dtype>::sumsq_data() const {
  Dtype sumsq;
  const Dtype* data;
  if (!data_) { return 0; }
  switch (data_->head()) {
  case SyncedMemory::HEAD_AT_CPU:
    data = cpu_data();
    sumsq = caffe_cpu_dot(count_, data, data);  //执行 CPU上的dot计算
    break;
  case SyncedMemory::HEAD_AT_GPU:
  case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
    data = gpu_data();
    caffe_gpu_dot(count_, data, data, &sumsq);
#else
    NO_GPU;
#endif
    break;
  case SyncedMemory::UNINITIALIZED:
    return 0;
  default:
    LOG(FATAL) << "Unknown SyncedMemory head state: " << data_->head();
  }
  return sumsq;
}
//同上,计算diff_的L2-范数
template <> unsigned int Blob<unsigned int>::sumsq_diff() const {
  NOT_IMPLEMENTED;
  return 0;
}
template <> int Blob<int>::sumsq_diff() const {
  NOT_IMPLEMENTED;
  return 0;
}
template <typename Dtype>
Dtype Blob<Dtype>::sumsq_diff() const {
  Dtype sumsq;
  const Dtype* diff;
  if (!diff_) { return 0; }
  switch (diff_->head()) {
  case SyncedMemory::HEAD_AT_CPU:
    diff = cpu_diff();
    sumsq = caffe_cpu_dot(count_, diff, diff);
    break;
  case SyncedMemory::HEAD_AT_GPU:
  case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
    diff = gpu_diff();
    caffe_gpu_dot(count_, diff, diff, &sumsq);
    break;
#else
    NO_GPU;
#endif
  case SyncedMemory::UNINITIALIZED:
    return 0;
  default:
    LOG(FATAL) << "Unknown SyncedMemory head state: " << data_->head();
  }
  return sumsq;
}
//对data_进行幅度缩放
template <> void Blob<unsigned int>::scale_data(unsigned int scale_factor) {
  NOT_IMPLEMENTED;
}

template <> void Blob<int>::scale_data(int scale_factor) {
  NOT_IMPLEMENTED;
}

template <typename Dtype>
void Blob<Dtype>::scale_data(Dtype scale_factor) {
  Dtype* data;
  if (!data_) { return; }
  switch (data_->head()) {
  case SyncedMemory::HEAD_AT_CPU:   //执行CPU上的计算
    data = mutable_cpu_data();
    caffe_scal(count_, scale_factor, data);
    return;
  case SyncedMemory::HEAD_AT_GPU:
  case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
    data = mutable_gpu_data();
    caffe_gpu_scal(count_, scale_factor, data);
    return;
#else
    NO_GPU;
#endif
  case SyncedMemory::UNINITIALIZED:
    return;
  default:
    LOG(FATAL) << "Unknown SyncedMemory head state: " << data_->head();
  }
}

template <> void Blob<unsigned int>::scale_diff(unsigned int scale_factor) {
  NOT_IMPLEMENTED;
}

template <> void Blob<int>::scale_diff(int scale_factor) {
  NOT_IMPLEMENTED;
}
//对diff_进行缩放,同理
template <typename Dtype>
void Blob<Dtype>::scale_diff(Dtype scale_factor) {
  Dtype* diff;
  if (!diff_) { return; }
  switch (diff_->head()) {
  case SyncedMemory::HEAD_AT_CPU:
    diff = mutable_cpu_diff();
    caffe_scal(count_, scale_factor, diff);
    return;
  case SyncedMemory::HEAD_AT_GPU:
  case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
    diff = mutable_gpu_diff();
    caffe_gpu_scal(count_, scale_factor, diff);
    return;
#else
    NO_GPU;
#endif
  case SyncedMemory::UNINITIALIZED:
    return;
  default:
    LOG(FATAL) << "Unknown SyncedMemory head state: " << diff_->head();
  }
}
//判断形状是否相同
template <typename Dtype>
bool Blob<Dtype>::ShapeEquals(const BlobProto& other) {
  if (other.has_num() || other.has_channels() ||
      other.has_height() || other.has_width()) {
    // Using deprecated 4D Blob dimensions --
    // shape is (num, channels, height, width).
    // Note: we do not use the normal Blob::num(), Blob::channels(), etc.
    // methods as these index from the beginning of the blob shape, where legacy parameter blobs were indexed from the end of the blob shape (e.g., bias Blob shape (1 x 1 x 1 x N), IP layer weight Blob shape (1 x 1 x M x N)).
    //输入的维度若使用过时的维度信息(num, channels,height, width),则需要转换为新的vector参数,代码使用了C++中的“懒”逻辑
    return shape_.size() <= 4 &&
           LegacyShape(-4) == other.num() &&
           LegacyShape(-3) == other.channels() &&
           LegacyShape(-2) == other.height() &&
           LegacyShape(-1) == other.width();
  }
  //直接对比
  vector<int> other_shape(other.shape().dim_size());
  for (int i = 0; i < other.shape().dim_size(); ++i) {
    other_shape[i] = other.shape().dim(i);
  }
  return shape_ == other_shape;
}
//从另一个Blob对象拷贝data (可选diff),必要时进行变维
template <typename Dtype>
void Blob<Dtype>::CopyFrom(const Blob& source, bool copy_diff, bool reshape) {
  if (source.count() != count_ || source.shape() != shape_) {
    if (reshape) {
      ReshapeLike(source);      //如果要变维,则执行这个
    } else {    //两个blob形状不同,则报错
      LOG(FATAL) << "Trying to copy blobs of different sizes.";
    }
  }
  switch (Caffe::mode()) {
  case Caffe::GPU:      //GPU模式
    if (copy_diff) {
      caffe_copy(count_, source.gpu_diff(),
          static_cast<Dtype*>(diff_->mutable_gpu_data()));
    } else {
      caffe_copy(count_, source.gpu_data(),
          static_cast<Dtype*>(data_->mutable_gpu_data()));
    }
    break;
  case Caffe::CPU:      //CPU模式
    if (copy_diff) {
      caffe_copy(count_, source.cpu_diff(),
          static_cast<Dtype*>(diff_->mutable_cpu_data()));
    } else {
      caffe_copy(count_, source.cpu_data(),
          static_cast<Dtype*>(data_->mutable_cpu_data()));
    }
    break;
  default:
    LOG(FATAL) << "Unknown caffe mode.";
  }
}

//从BlobProto中加载一个Blob,适用于从磁盘载入之前导出的Blob
template <typename Dtype>
void Blob<Dtype>::FromProto(const BlobProto& proto, bool reshape) {
  if (reshape) {        //从BlobProto对象中获得所需各个维度信息
    vector<int> shape;
    if (proto.has_num() || proto.has_channels() ||
        proto.has_height() || proto.has_width()) {
      // Using deprecated 4D Blob dimensions --
      // shape is (num, channels, height, width).
      shape.resize(4);
      shape[0] = proto.num();
      shape[1] = proto.channels();
      shape[2] = proto.height();
      shape[3] = proto.width();
    } else {
      shape.resize(proto.shape().dim_size());
      for (int i = 0; i < proto.shape().dim_size(); ++i) {
        shape[i] = proto.shape().dim(i);
      }
    }
    Reshape(shape);     //Blob按照维度信息进行变维
  } else {
    CHECK(ShapeEquals(proto)) << "shape mismatch (reshape not set)";
  }
  // copy data 加载数据
  Dtype* data_vec = mutable_cpu_data();
  if (proto.double_data_size() > 0) {   // 如果之前保存的是double类型 data
    CHECK_EQ(count_, proto.double_data_size());
    for (int i = 0; i < count_; ++i) {
      data_vec[i] = proto.double_data(i);   //加载double date
    }
  } else {
    CHECK_EQ(count_, proto.data_size());
    for (int i = 0; i < count_; ++i) {
      data_vec[i] = proto.data(i);  //否则加载float data
    }
  }
  if (proto.double_diff_size() > 0) {   // 如果之前保存的是 double 类型 diff
    CHECK_EQ(count_, proto.double_diff_size());
    Dtype* diff_vec = mutable_cpu_diff();
    for (int i = 0; i < count_; ++i) {
      diff_vec[i] = proto.double_diff(i);
    }
  } else if (proto.diff_size() > 0) {
    CHECK_EQ(count_, proto.diff_size());
    Dtype* diff_vec = mutable_cpu_diff();
    for (int i = 0; i < count_; ++i) {
      diff_vec[i] = proto.diff(i);
    }
  }
}
//将Blob中的data(可选diff)导出到BlobProto结构体.便于存储到磁盘文件中
template <>
void Blob<double>::ToProto(BlobProto* proto, bool write_diff) const {
  proto->clear_shape();     //重置proto的维度,保证与blob相同
  for (int i = 0; i < shape_.size(); ++i) {
    proto->mutable_shape()->add_dim(shape_[i]);
  }
  proto->clear_double_data();   //清除data
  proto->clear_double_diff();   //清除diff
  const double* data_vec = cpu_data();  //将data导出到proto
  for (int i = 0; i < count_; ++i) {
    proto->add_double_data(data_vec[i]);
  }
  if (write_diff) {         //  若有write_diff的需求
    const double* diff_vec = cpu_diff();    //将diff导出到proto
    for (int i = 0; i < count_; ++i) {
      proto->add_double_diff(diff_vec[i]);
    }
  }
}
//同上,只不过类型为float
template <>
void Blob<float>::ToProto(BlobProto* proto, bool write_diff) const {
  proto->clear_shape();
  for (int i = 0; i < shape_.size(); ++i) {
    proto->mutable_shape()->add_dim(shape_[i]);
  }
  proto->clear_data();
  proto->clear_diff();
  const float* data_vec = cpu_data();
  for (int i = 0; i < count_; ++i) {
    proto->add_data(data_vec[i]);
  }
  if (write_diff) {
    const float* diff_vec = cpu_diff();
    for (int i = 0; i < count_; ++i) {
      proto->add_diff(diff_vec[i]);
    }
  }
}
//实例化Blob   类模板(float, double)
INSTANTIATE_CLASS(Blob);
template class Blob<int>;
template class Blob<unsigned int>;

}  // namespace caffe

到此,我们就了解了Caffe一些基本的数据结构.后面就应该学习Layer层中对数据的一些处理.

2017/6/10 posted in  Caffe 数据结构