caffe源码解析之Blob 及Python和C++接口调用

来源:互联网 发布:大人认字软件 编辑:程序博客网 时间:2024/06/05 11:58

一、Blob到底是什么东西?

        可以理解Blob就是个4维数组,n*c*h*w;每个维度分别表示批处理数量,通道个数,高度和宽度;

        这个4维数组的存在形式是以类的形式存在,可以理解为blob就是个数据存储容器。

二、Blob的数据成员

 protected:  shared_ptr<SyncedMemory> data_;  shared_ptr<SyncedMemory> diff_;  shared_ptr<SyncedMemory> shape_data_;  vector<int> shape_;  int count_;  int capacity_;
         Blob的数据成员很简单,就上面的6个。

     其中:

      SyncedMemory是内存管理的类,现在先不管它。
               data_是存储内容的指针(boost库的shared_ptr共享智能指针),比如存储前向传播的图像数据;

                diff_也是存储数据的指针, 但它存储的是反向传播的梯度数据;

                 shape_data_和shape_都是表示blob的形状, 即n,c,h,w的大小;

                 count_表示blob的元素数量,即:n*c*h*w;

                 capacity_表示当前元素容量, 因为blob可能会reshape,当count_比capacity大时,capacity会更新成count_;

三/Bolb的成员函数

      3.1 构造函数:         

  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);
3个构造函数,都是操作这个blob的4维元素的大小;

   3.2 Reshape函数:

  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);
  Reshape函数,用来重新指定Blob的4维元素大小,将每一维度的大小存放在成员变量shape_中,count_和capacity_也都赋值了,

                             创建SyncedMemory类, (data_和diff_在这并没有赋值,也未分配内存)

3.3 与矩阵结构有关的方法

  inline const vector<int>& shape() const { return shape_; }   //返回4维数组的结构  inline int shape(int index) const {    return shape_[CanonicalAxisIndex(index)];      // 返回某一维度的大小;  }  inline int num_axes() const { return shape_.size(); }  //返回维度值  inline int count() const { return count_; }            //返回元素数量                   inline int count(int start_axis, int end_axis) const {     //返回某些维度的元素数量  inline int count(int start_axis) const {            //返回从某一维度到结束的元素数量    return count(start_axis, num_axes());  }  inline int CanonicalAxisIndex(int axis_index) const { }   //返回数组的下标   inline int num() const { return LegacyShape(0); }             //返回n维度的元素个数  inline int channels() const { return LegacyShape(1); }           //返回c维度的元素个数  inline int height() const { return LegacyShape(2); }  inline int width() const { return LegacyShape(3); }  inline int LegacyShape(int index) const {                //返回某一维度的个数  inline int offset(const int n, const int c = 0, const int h = 0)    //返回指定元素相对首地址的偏移量  inline int offset(const vector<int>& indices) const {  }
可以发现 这些方法都是获得blob结构, 元素个数, 及维度信息等方法的。

3.4 与数据访问相关的方法

  void CopyFrom(const Blob<Dtype>& source, bool copy_diff = false,  //从一个Blob中复制数据到另一个blob      bool reshape = false);  inline Dtype data_at(const int n, const int c, const int h,   //通过offset的偏移量,访问指定位置的数据      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 {      //返回data_地址    CHECK(data_);    return data_;  }  inline const shared_ptr<SyncedMemory>& diff() const {      //返回diff_地址    CHECK(diff_);    return diff_;  }  const Dtype* cpu_data() const;    //分配内存,并返回首地址  void set_cpu_data(Dtype* data);   //设置数据在cpu上  const int* gpu_shape() const;      //内存分配,反回在gpu上的地址,share_ptr调用  const Dtype* gpu_data() const;    //内存分配,反回在gpu上的地址  ,data_调用   void set_gpu_data(Dtype* data);  const Dtype* cpu_diff() const;  const Dtype* gpu_diff() const;  Dtype* mutable_cpu_data();         //返回可以改写的data地址  Dtype* mutable_gpu_data();  Dtype* mutable_cpu_diff();  Dtype* mutable_gpu_diff();  void Update();             //data-diff
这一部分是Blob的核心, 包括cpu和gpu的内存分配及访问。

   3.5 Blob的其它数学计算

 Dtype asum_data() const;         //求data的l1范数      Dtype asum_diff() const;        //求diff的l1范数    Dtype sumsq_data() const;       //求data的l2范数    Dtype sumsq_diff() const;        //求diff的l2范数  void scale_data(Dtype scale_factor);    //缩放    void scale_diff(Dtype scale_factor);  void ShareData(const Blob& other);     //data共享   void ShareDiff(const Blob& other);  bool ShapeEquals(const BlobProto& other);    //判断是否相等。
这部分的方法设计到部分数学计算。


四、总结

        Blob仅仅是一个数据结构,4维矩阵, 它还同时提供了本身结构、 内存分配、访问数据、及其它一些方法。 这部分仅仅是程序相关,与算法还没有多大关系。


五、Blob在Python接口中的调用

       以VGG16的模型来分析,假设预测的图片为1张,则最后输出的Blob结构为(1,1000),1 表示第一张图,而1000表示1000个分类的概率值。

 

output=net.forward()output_prob=output['prob'][0]
output 表示前向传播结束后最后一层返回的Blob, 它是python的字典结构, 通过关键字可以访问。

   即: output["prob"][0] 注意关键字prob 是网络最后一层softmax层的 名称, 而【0】 表示第一张图片的各个概率值, 从而output_prob变成了一维结构,表示第一张图片的1000个概率分布。


六、Blob在C++接口中的调用

         

  net->Forward();  const float *out_probs = net->output_blobs()[0]->cpu_data();
net是深度学习的网络, 前向传播计算,结束后, 调用output_blobs()方法, 返回net网络中的 net_output_blobs_成员变量,这是一个vector<Blob<Dtype> *>类型的, 返回的是所有网络输出的blob动态数组, 通过下标【0】 找到输出的第一个blob, 然后调用blob的cpu_data()方法(上面讲过),返回blob中data数据的指针, 将此指针赋值给out_probs, 然后利用out_probs就可以对输出的结果进行操作了。

      关于其中Net网络的细节部分,等解析玩layer网络后, 在解析net网络, 请持续关注。。。