Caffe(一)之 Blob

Posted by Hux Blog on September 6, 2017

blob.hpp & blob.cpp

  • 定义全局变量
    1
    
    const int kMaxBlobAxes = 32;
    

    就是说caffe最多支持32维,一般四维(num, channel, width, height)

  • 定义 Blob 类
    • 其中private数据
      • shared_ptr 类型的 data_、diff_、shape_data_
      • vector shape_
      • int count_, capacity_;
    • 一个blob的要素,数据、梯度、数据shape(因为是c++,需要寻址);count_为各维度乘积,即所需要的空间;capacity_为当前分配的空间,如果count_超过capacity_,需要重新分配资源。
    • DISABLE_COPY_AND_ASSIGN(Blob); 禁止拷贝构造和赋值构造Blob,有可能很大,用指针传递。
    • Blob构造函数两种形式,一种4维(num, channel, width, height)(Deprecated), 一种一个vector作为shape
  • Blob成员函数
    • 多个重载Reshape(构造函数会调用Reshape)。Reshape作用是根据输入shape改变当前这个blob对象的维度(只是改变shape_里面的值,没有动data_),然后如果有必要(count_>capacity_)重新分配空间(若已有空间大于所需空间,永远不会释放)。Reshape可以用于初始化分配空间,也可以用于Layer::Reshape或者Layer::Forward需要改变blob维度的时候。其中最终实现的用的是这个版本的重载
      1
      
        void Blob<Dtype>::Reshape(const vector<int>& shape)
      
      • Note that reshaping an input blob and immediately calling Net::Backward is an error; either Net::Forward or Net::Reshape need to be called to propagate the new input shape to higher layers. 就是Reshape后,应该先forward或者网络整体reshape以下,才可以调backward。
    • count()的多个重载,计算[start_axis, end_axis)对应维度的乘积
    • offset根据给定的num, channel, width, height)计算偏移量,用于定位数据
    • CopyFrom 即Copy from a source Blob. 从source拷贝到当前blob中(拷贝data或者diff)
    • data() 返回data_(指针)
    • diff() 返回diff_(指针)

    • set_cpu_data,似乎是设定cpu数据的指针指向 HEAD_AT_CPU

    • asum_data、asum_diff、sumsq_data、sumsq_diff、scale_data、scale_diff 均是调用cblas计算。MKL是intel针对其GPU优化的线性代数库,blas是另外一个线性代数库。
    • ShareData 让data_指向SyncedMemory holding the data_ of Blob
    • Update():更新Net中Blob的参数,由于是浮点型且用模板实现,故用 NOT_IMPLEMENTED 标志声明 int和unsigned int版本不定义。 也即实现的的是 y = y - gradien
    • (TODO)enum SyncedHead { UNINITIALIZED, HEAD_AT_CPU, HEAD_AT_GPU, SYNCED };
    • (TODO)BlobProto 类
  • c++新知识
    • 原则上应该在所有的构造函数前加explicit关键字,当你有心利用隐式转换的时候再去解除explicit,这样可以大大减少错误的发生。
    • 关键字inline必须与函数定义放在一起才能使函数成为内联,仅仅将inline放在函数声明前面不起任何作用。建议把inline函数的定义放到头文件中。在每个调用该inline函数的文件中包含该头文件。这种方法保证对每个inline函数只有一个定义,且程序员无需复制代码,并且不可能在程序的生命期中引起无意的不匹配的事情。引入内联函数的目的是为了解决程序中函数调用的效率问题。
    • vector.resize(): 更改这个vector容器的size,小于当前vector.size()直接截断,大于则用val填补
      1
      2
      
        void resize (size_type n);
        void resize (size_type n, const value_type& val);
      
    • static_cast(TODO):简单的说就是转换数据类型。static_cast不仅可以用在指针和引用上,还可以用在基础数据和对象上;前面提到过reinterpret_cast可以用在”没有关系”的类型之间,而用static_cast来处理的转换就需要两者具有”一定的关系”了。

    • #define INSTANTIATE_CLASS(classname);非常非常非常重要的宏,重要的事说三遍。由于Caffe采用分离式模板编程方法(据说也是Google倡导的),模板未类型实例化的定义空间和实例化的定义空间是不同的。实际上,编译器并不会理睬分离在cpp里的未实例化的定义代码,而是将它放置在一个虚拟的空间。一旦一段明确类型的代码,访问这段虚拟代码空间,就会被编译器拦截。如果你想要让模板的声明和定义分离编写,就需要在cpp定义文件里,将定义指定明确的类型,实例化。这个宏的作用正是如此。(Google编程习惯的宏吧)。