深度学习caffe数据结构(四)—— blob数据结构blob.hpp文件详细解读

释放双眼,带上耳机,听听看~!

        blob是caffe中的基本数据结构,它声明在blob.hpp中,这个文件位于caffe根目录的include/caffe/路径下,在这篇文章中,我们对blob.hpp文件进行详细解读,以便对blob数据结构有深刻的认识。


1
2
3
4
5
6
7
8
9
10
11
1#ifndef CAFFE_BLOB_HPP_
2#define CAFFE_BLOB_HPP_
3
4#include <algorithm>
5#include <string>
6#include <vector>
7
8#include "caffe/common.hpp"
9#include "caffe/proto/caffe.pb.h"
10#include "caffe/syncedmem.hpp"
11

        这几行代码是blob.hpp文件的开头,主要是该文件包含的头文件。


1
2
1const int kMaxBlobAxes = 32;
2

这一行定义了一个int型变量,它表示的是blob的最大维数,老版本的caffe包含的维数是num、channels、height、width共四个维度,新版本的caffe最多可以支持32个维度。


1
2
1namespace caffe {
2

这一行定义caffe命名空间


1
2
1template <typename Dtype>
2

这一行表明这个类是模板类,Dtype为类型名,在c++中,通过这种方法可以实现多种数据类型的处理,比如Dtype可以float、double等。


1
2
3
4
5
1class Blob {
2 public:
3  Blob()
4       : data_(), diff_(), count_(0), capacity_(0) {}
5

这几行定义了Blob类,并声明了Blob的默认构造函数。


1
2
3
1  explicit Blob(const int num, const int channels, const int height, const int width);
2  explicit Blob(const vector<int>& shape);
3

这两行声明了两个Blob的显式构造函数,分别采用两种不同的数据类型来构造Blob类,第一个函数是使用num、channels、height、width这四个维度信息来构造Blob类,它是用来兼容老版本caffe的,第二个函数是使用int型的向量shape来构造Blob, shape最大维度为32。


1
2
1  void Reshape(const int num, const int channels, const int height, const int width);
2

这一行定义了一个变形函数,它的作用是通过num、channels、height、width这四个维度信息来改变原有的Blob的形状,这个函数主要是用来兼容老版本caffe的。


1
2
1  void Reshape(const vector<int>& shape);
2

这一行定义了另一个变形函数,它是通过shape向量来改变Blob的形状。


1
2
1  void Reshape(const BlobShape& shape);
2

这一行定义的变形函数是根据Blob描述文件中的形状信息来变形的函数。


1
2
1  void ReshapeLike(const Blob& other);
2

这一行依然是一个变形函数,它把本类的形状变成与other类形状相同。


1
2
3
4
5
6
7
8
9
1  inline string shape_string() const {
2    ostringstream stream;
3    for (int i = 0; i < shape_.size(); ++i) {
4      stream << shape_[i] << " ";
5    }
6    stream << "(" << count_ << ")";
7    return stream.str();
8  }
9

上面定义的是一个函数,它的作用是将Blob的形状信息变成字符串并返回。在函数中首先定义了一个流输出变量stream,然后在for循环中,把shape_的每一个维度转换为字符串挂接在stream后边,最后把元素数目count_挂接在stream后边,形成一个字符串输出。其中,shape_和count_是Blob了的成员变量,shape_是表示Blob形状的向量,count_是表示Blob元素数量的int型变量。caffe中成员变量是以下划线结尾的,比较号区分。


1
2
1  inline const vector<int>& shape() const { return shape_; }
2

这一行定义的是读取Blob形状的的函数,直接返回成员变量shape_。


1
2
3
4
1  inline int shape(int index) const {
2    return shape_[CanonicalAxisIndex(index)];
3  }
4

这一行定义的函数是读取某一个维度上的尺寸,在函数中调用了CanonicalAxisIndex()函数,它的作用是确认维度数index是否可用,在下面有详细定义。


1
2
3
1  inline int num_axes() const { return shape_.size(); }
2
3

这个函数用来读取Blob的维度数,直接返回成员变量shape_的尺寸。


1
2
1  inline int count() const { return count_; }
2

这个函数用来读取Blob的元素数目,直接返回成员变量count_。


1
2
3
4
5
6
7
8
9
10
11
12
13
1  inline int count(int start_axis, int end_axis) const {
2    CHECK_LE(start_axis, end_axis);
3    CHECK_GE(start_axis, 0);
4    CHECK_GE(end_axis, 0);
5    CHECK_LE(start_axis, num_axes());
6    CHECK_LE(end_axis, num_axes());
7    int count = 1;
8    for (int i = start_axis; i < end_axis; ++i) {
9      count *= shape(i);
10    }
11    return count;
12  }
13

上面这个函数用来计算从起始维度start_axis到结束维度end_axis的子集的元素数目,函数的前几行用来检测维度数是否合规,其中CHECK_LE(start_axis, end_axis);用来检测start_axis要小于或等于end_axis。CHECK_LE是检测函数,LE表示,检测的变量前面的变量要小于或等于后面的变量。GE表示大于或等于。LT表示小于,GT表示大于。其他几行读者可以自行分析所起的作用。下面通过一个for循环来计算元素数,计算方法为用count乘以所指定维度的尺寸。最后返回元素数目。


1
2
3
4
1  inline int count(int start_axis) const {
2    return count(start_axis, num_axes());
3  }
4

这个函数是计算从起始维度start_axis到最后维度的子集元素数目,调用第一个count函数来计算。


1
2
3
4
5
6
7
8
9
10
11
12
13
1  inline int CanonicalAxisIndex(int axis_index) const {
2    CHECK_GE(axis_index, -num_axes())
3        << "axis " << axis_index << " out of range for " << num_axes()
4        << "-D Blob with shape " << shape_string();
5    CHECK_LT(axis_index, num_axes())
6        << "axis " << axis_index << " out of range for " << num_axes()
7        << "-D Blob with shape " << shape_string();
8    if (axis_index < 0) {
9      return axis_index + num_axes();
10    }
11    return axis_index;
12  }
13

这个函数的作用是确认维度数是否合规,为了兼容老版本caffe,维度索引的取值范围为[-num_axes(),num_axes()),这个函数首先判断被判断的维度值axis_index是否在这个范围内,不在则退出,如果满足条件,则将axis_index转换到普通索引[0,num_axes() )范围内,负数的转换方法为axis_index + num_axes()。最后返回转换后的维度数。


1
2
3
4
5
1  inline int num() const { return LegacyShape(0); }
2  inline int channels() const { return LegacyShape(1); }
3  inline int height() const { return LegacyShape(2); }
4  inline int width() const { return LegacyShape(3); }
5

这几个函数返回兼容老版本的num、channels、height、width这四个维度的尺寸。函数中调用了LegacyShape()函数,下面进行解释。


1
2
3
4
5
6
7
8
9
10
11
1  inline int LegacyShape(int index) const {
2    CHECK_LE(num_axes(), 4)
3        << "Cannot use legacy accessors on Blobs with > 4 axes.";
4    CHECK_LT(index, 4);
5    CHECK_GE(index, -4);
6    if (index >= num_axes() || index < -num_axes()) {
7      return 1;
8    }
9    return shape(index);
10  }
11

这个函数是支持老版本的返回某个维度形状的函数,首先是判断维度索引index是否合规,合规则调用上面的shape函数返回第index维度的尺寸。


1
2
3
4
5
6
7
8
9
10
11
12
13
1  inline int offset(const int n, const int c = 0, const int h = 0,
2      const int w = 0) const {
3    CHECK_GE(n, 0);
4    CHECK_LE(n, num());
5    CHECK_GE(channels(), 0);
6    CHECK_LE(c, channels());
7    CHECK_GE(height(), 0);
8    CHECK_LE(h, height());
9    CHECK_GE(width(), 0);
10    CHECK_LE(w, width());
11    return ((n * channels() + c) * height() + h) * width() + w;
12  }
13

这个函数的作用是返回Blob中某个元素在内存中的偏移值。虽然Blob中的数据是多维数组,但是数据在内存中依然是顺序排列的。这个函数的输入为num、channels、height、width这四个维度的取值,输出为在[n][c][h][w]位置上的元素的偏移量。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
1  inline int offset(const vector<int>& indices) const {
2    CHECK_LE(indices.size(), num_axes());
3    int offset = 0;
4    for (int i = 0; i < num_axes(); ++i) {
5      offset *= shape(i);
6      if (indices.size() > i) {
7        CHECK_GE(indices[i], 0);
8        CHECK_LT(indices[i], shape(i));
9        offset += indices[i];
10      }
11    }
12    return offset;
13  }
14

上面的代码定义了另一个offset函数,它的输入是indices向量,indices中包含的是每个维度上的索引值。


1
2
3
1  void CopyFrom(const Blob<Dtype>& source, bool copy_diff = false,
2      bool reshape = false);
3

这个函数的作用是从source对象中复制数据到当前类中。copy_diff标志位指定是复制data还是复制diff,reshape标志位用来指定是否允许改变Blob形状。


1
2
3
4
5
1  inline Dtype data_at(const int n, const int c, const int h,
2      const int w) const {
3    return cpu_data()[offset(n, c, h, w)];
4  }
5

这个函数的作用是返回在num、channels、height、width这四个维度的n、c、h、w索引处的data元素值。调用offset函数返回内存中的data值。


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

这个函数的作用是返回在num、channels、height、width这四个维度的n、c、h、w索引处的diff元素值。调用offset函数返回内存中的diff值。


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

这个函数的作用与上面的第一个data_at()函数类似,返回的是内存中的data元素值,只不过它的输入为索引的向量。


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

这个函数与上一个函数类似,是通过索引的向量来返回内存中的diff值。


1
2
3
4
5
1  inline const shared_ptr<SyncedMemory>& data() const {
2    CHECK(data_);
3    return data_;
4  }
5

上面的函数用来返回内存中的data的地址,其中SyncedMemory是一个用来同步CPU和GPU中的数据的类,它在caffe根目录下的include/caffe/syncedmem.hpp文件中定义,有兴趣可以对照着这个文件对SyncedMemory的作用进行研究。


1
2
3
4
5
1  inline const shared_ptr<SyncedMemory>& diff() const {
2    CHECK(diff_);
3    return diff_;
4  }
5

上面的函数用来返回内存中的diff的地址。


1
2
1  const Dtype* cpu_data() const;
2

这一行声明了一个返回cpu中的data的地址的函数,对cpu_data的访问为只读模式。


1
2
1  void set_cpu_data(Dtype* data);
2

这一行声明的函数的作用是设置cpu_data,数据源为data。


1
2
1  const int* gpu_shape() const;
2

这一行的作用是声明一个返回gpu_shape的函数


1
2
1  const Dtype* gpu_data() const;
2

这一行的作用是返回gpu_data的地址,访问方式为只读。


1
2
1  void set_gpu_data(Dtype* data);
2

这一行声明的函数的作用是设置gpu_data,数据源为data。


1
2
1  const Dtype* cpu_diff() const;
2

这一行的作用是返回cpu_diff的地址,访问方式为只读。


1
2
1  const Dtype* gpu_diff() const;
2

这一行的作用是返回gpu_diff的地址,访问方式为只读。


1
2
3
4
5
1  Dtype* mutable_cpu_data();
2  Dtype* mutable_gpu_data();
3  Dtype* mutable_cpu_diff();
4  Dtype* mutable_gpu_diff();
5

这四行声明的是以读写方式访问cpu_data、gpu_data、cpu_diff、gpu_diff这四种数据的函数。


1
2
1  void Update();
2

Blob的更新函数,作用是计算data与diff的对应元素的和。


1
2
1  void FromProto(const BlobProto& proto, bool reshape = true);
2

上面这个函数的作用是从BlobProto中读取出一个Blob到本类。


1
2
1  void ToProto(BlobProto* proto, bool write_diff = false) const;
2

将本类写入到BlobProto中。


1
2
3
4
1  Dtype asum_data() const;
2  Dtype asum_diff() const;
3
4

这两个函数的作用是计算data和diff中的元素的的绝对值之和(L1范数)。


1
2
3
1  Dtype sumsq_data() const;
2  Dtype sumsq_diff() const;
3

这两个函数的作用是计算data和diff中的元素的的平方和(L2范数)。


1
2
3
1  void scale_data(Dtype scale_factor);
2  void scale_diff(Dtype scale_factor);
3

这两个函数的作用是将data和diff的各元素乘以一个标量系数。


1
2
3
1  void ShareData(const Blob& other);
2  void ShareDiff(const Blob& other);
3

这两个函数的作用是共享另一个Blob的data和diff。


1
2
1  bool ShapeEquals(const BlobProto& other);
2

这个函数的作用是判断本类的Blob与other的形状是否相同。


1
2
3
4
5
6
7
8
9
10
11
12
13
1 protected:
2  shared_ptr<SyncedMemory> data_;
3  shared_ptr<SyncedMemory> diff_;
4  shared_ptr<SyncedMemory> shape_data_;
5  vector<int> shape_;
6  int count_;
7  int capacity_;
8
9  DISABLE_COPY_AND_ASSIGN(Blob);
10};  // class Blob
11
12}  // namespace caffe
13

这些代码是blob.hpp的结尾,定义的是Blob的若干个成员变量。data_为data的内存地址,diff_为diff的内存地址,shape_data_为shape数据的地址,shape_为shape的向量,count_为Blob的元素数。capacity_为blob的容量信息。DISABLE_COPY_AND_ASSIGN(Blob);表示禁用拷贝构造函数、赋值运算符重载。

        至此,我们完成了blob.hpp文件的解读。

给TA打赏
共{{data.count}}人
人已打赏
安全运维

redis缓存

2021-12-11 11:36:11

安全运维

Ubuntu上NFS的安装配置

2021-12-19 17:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索