caffe源码解析之blob.hpp或blob.cpp

作者:wjmishuai
出处:http://blog.csdn.net/wjmishuai/article/details/50961471 声明:版权所有,转载请注明出处
caffe可以分为三层:Blob、Layer、Net
Blob是一个四维的数组,用于存储数据,包括输入数据、输出数据、权值; Layer层则是神经网络中具体的各层结构,主要用于计算,在根据配置文件初始化网络结构后,前向计算结果,反向更新参数,而它的输入和输出都是Blob数据; Net的层就是多个Layer组合而成的有向无环图结构,也就是具体的网络了。 Layer和Net的代码有待深入,尤其是Layer的代码,caffe实现了差不多40种不同的Layer层,里面有不同的激活函数,这个要好好研究下。
【caffe源码解析之blob.hpp或blob.cpp】关于Blob就这么多内容,毕竟就是一个统一的数据存取接口,学到了一些封装的手法,可以看看CPU和GPU一些接口的封装;另一方面是对于Protocol Buffer有了一些了解。

#ifndef CAFFE_BLOB_HPP_//防止头文件重复引用 #define CAFFE_BLOB_HPP_#include #include #include /*common.hpp主要用来单例化Caffe类, *并封装了boost和CUDA随机数生成的函数, *提供了统一的接口 */ #include "common.hpp" /*caffe.pb.h是google protocol buffer根据caffe.proto自动生成的。 *使用protocol buffer有这些好处,一方面可以用文本文件定义结构化的数据类型, *另一方面可以生成查询效率更高、占空间更小的二进制文件 */ #include "caffe.pb.h" //syncedmem主要用于分配内存和释放内存 #include "syncedmem.hpp" //math_functions里面封装了很多cblas矩阵运算 #include "util/math_functions.hpp"const int kMaxBlobAxes = INT_MAX; namespace caffe {//命名空间为caffe/* *主要数据有两个data和diff,用num、channels、height和width *这四个维度来确定数据的具体位置,做一些数据查询和Blobreshape的操作 */ template class Blob { public: Blob()//blob的构造函数 : data_(), diff_(), count_(0), capacity_(0) {}//data_(), diff_()是用于存放数据的指针,/*num_, channel_, height_, width_主要用来做定位offset和reshape处理。 *对于输入(n, c, h, w)位置的数据位置为((n*channels_+c)*height_+h)*width_+w, *可以依据位置取data_()或diff_()中的数据。 */ explicit Blob(const int num, const int channels, const int height, const int width); explicit Blob(const vector& shape); /*Reshape函数的作用是改变一个blob的大小 *1.读入num_,channels_,height_,width_的大小 *2.计算count_:count_ = num_ * channels_ * height_ * width_; *3.如果count_不为0,则重新为data_和diff_分配一块空间 *如果count为0,则都初始化为NULL */ void Reshape(const int num, const int channels, const int height, const int width); void Reshape(const vector& shape); void Reshape(const BlobShape& shape); //ReshapeLike的作用是为data_和diff_ 重新分配一块空间,大小和另一个blob的一样 void ReshapeLike(const Blob& other); inline string shape_string() const { ostringstream stream; for (int i = 0; i < shape_.size(); ++i) { stream << shape_[i] << " "; } stream << "(" << count_ << ")"; return stream.str(); } inline const vector& shape() const { return shape_; }//返回shape//返回第i个索引的shape,index可以是负数, inline int shape(int index) const { return shape_[CanonicalAxisIndex(index)]; } inline int num_axes() const { return shape_.size(); }//返回shape的大小 inline int count() const { return count_; }//返回参数count//计算一个slice的体积 inline int count(int start_axis, int end_axis) const {int count = 1; for (int i = start_axis; i < end_axis; ++i) { count *= shape(i); } return count; } //计算从从一个特定的axis到最后一个axis的slice的体积。 inline int count(int start_axis) const { return count(start_axis, num_axes()); }//对负数(index可能是负数)规范化的一个函数 inline int CanonicalAxisIndex(int axis_index) const {if (axis_index < 0) { return axis_index + num_axes(); } return axis_index; }/// 功能是返回一些成员变量,比如,num,channels,height,width等 inline int num() const { return LegacyShape(0); } inline int channels() const { return LegacyShape(1); } inline int height() const { return LegacyShape(2); } inline int width() const { return LegacyShape(3); } inline int LegacyShape(int index) const {if (index >= num_axes() || index < -num_axes()) { /*如果index超出索引范围,但是在范围 [0, 3] 或[-4, -1]内, *这种特殊的情况下,模拟一个填充值,用来填补axes 。 */ return 1; } return shape(index); } //计算偏移量,因为数据在内存是以一维数组形式的,所以需要计算偏移量来访问 inline int offset(const int n, const int c = 0, const int h = 0, const int w = 0) const {return ((n * channels() + c) * height() + h) * width() + w; }inline int offset(const vector& indices) const {int offset = 0; for (int i = 0; i < num_axes(); ++i) { offset *= shape(i); if (indices.size() > i) {offset += indices[i]; } } return offset; } /** *从source拷贝数据。copy_diff作为标志来区分是拷贝data还是拷贝diff *1.如果是GPU: 如果是拷贝diff:调用cudaMemcpy函数将source的diff拷贝过来,否则拷贝data *2.如果是CPU: 如果是拷贝diff:调用memcpy函数将source的diff拷贝过来 否则拷贝data */ void CopyFrom(const Blob& source, bool copy_diff = false, bool reshape = false); //从cpu访问数据data 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)]; } //从cpu访问数据diff 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)]; } //从cpu访问数据data inline Dtype data_at(const vector& index) const { return cpu_data()[offset(index)]; } //从cpu访问数据diff inline Dtype diff_at(const vector& index) const { return cpu_diff()[offset(index)]; } //从cpu访问数据data inline const shared_ptr& data() const { return data_; } //从cpu访问数据diff inline const shared_ptr& diff() const { return diff_; } /**调用SyncedMemory的函数,来返回数据的指针; 前两个调用to_cpu(),返回cpu_ptr; *第一个对于data对象,第二个对于diff对象 *后两个调用to_gpu(),返回gpu_ptr;第一个对于data对象,第二个对于diff对象 */ void set_cpu_data(Dtype* data); const Dtype* cpu_data() const; const Dtype* gpu_data() const; const Dtype* cpu_diff() const; const Dtype* gpu_diff() const; Dtype* mutable_cpu_data(); Dtype* mutable_gpu_data(); Dtype* mutable_cpu_diff(); Dtype* mutable_gpu_diff(); /**更新data_的数据,就是减去diff_的数据。 *1.判断blob的位置 *2.调用caffe_axpy:在math_functions.cpp可以找到该函数的实现,其实这函数也是封装了mkl的函数。这里调用是为了实现了两个向量的减法。 *3.调用caffe_gpu_axpy:在math_functions.cpp可以找到该函数的实现,其实这函数也是封装了cublas的函数。这里调用是为了实现了两个向量的减法。 */ void Update(); /**功能:从proto读数据进来,其实就是反序列化 *1.先把blob的大小改变一下 *2.得到cpu中数据的地址 *3.用proto中的data覆盖blob中的data *4.用proto中的diff覆盖blob中的diff */ void FromProto(const BlobProto& proto, bool reshape = true); //把blob数据保存到proto中 void ToProto(BlobProto* proto, bool write_diff = false) const; //计算绝对值的data总和(L1范数)。 Dtype asum_data() const; //计算绝对值的diff总和(L1范数)。 Dtype asum_diff() const; //计算绝对值的data总和(L2范数)。 Dtype sumsq_data() const; //计算绝对值的diff总和(L2范数)。 Dtype sumsq_diff() const; //通过常量因子测量blob data void scale_data(Dtype scale_factor); 通过常量因子测量blob diff void scale_diff(Dtype scale_factor); //从other的blob复制data和diff的值 void ShareData(const Blob& other); void ShareDiff(const Blob& other); bool ShapeEquals(const BlobProto& other); protected: shared_ptr data_; // 存放数据 shared_ptr diff_; //存放梯度 vector shape_; //存放形状 int count_; //数据个数 int capacity_; //数据容量DISABLE_COPY_AND_ASSIGN(Blob); }; // class Blob}// namespace caffe#endif// CAFFE_BLOB_HPP_



    推荐阅读