深度學習caffe數據結構(四)—— blob數據結構blob.hpp文件詳細解讀

        blob是caffe中的基本數據結構,它聲明在blob.hpp中,這個文件位於caffe根目錄的include/caffe/路徑下,在這篇文章中,我們對blob.hpp文件進行詳細解讀,以便對blob數據結構有深刻的認識。

#ifndef CAFFE_BLOB_HPP_
#define CAFFE_BLOB_HPP_

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

#include "caffe/common.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/syncedmem.hpp"

        這幾行代碼是blob.hpp文件的開頭,主要是該文件包含的頭文件。

const int kMaxBlobAxes = 32;

這一行定義了一個int型變量,它表示的是blob的最大維數,老版本的caffe包含的維數是num、channels、height、width共四個維度,新版本的caffe最多可以支持32個維度。

namespace caffe {

這一行定義caffe命名空間

template <typename Dtype>

這一行表明這個類是模板類,Dtype爲類型名,在c++中,通過這種方法可以實現多種數據類型的處理,比如Dtype可以float、double等。

class Blob {
 public:
  Blob()
       : data_(), diff_(), count_(0), capacity_(0) {}

這幾行定義了Blob類,並聲明瞭Blob的默認構造函數。

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

這兩行聲明瞭兩個Blob的顯式構造函數,分別採用兩種不同的數據類型來構造Blob類,第一個函數是使用num、channels、height、width這四個維度信息來構造Blob類,它是用來兼容老版本caffe的,第二個函數是使用int型的向量shape來構造Blob, shape最大維度爲32。

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

這一行定義了一個變形函數,它的作用是通過num、channels、height、width這四個維度信息來改變原有的Blob的形狀,這個函數主要是用來兼容老版本caffe的。

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

這一行定義了另一個變形函數,它是通過shape向量來改變Blob的形狀。

  void Reshape(const BlobShape& shape);

這一行定義的變形函數是根據Blob描述文件中的形狀信息來變形的函數。

  void ReshapeLike(const Blob& other);

這一行依然是一個變形函數,它把本類的形狀變成與other類形狀相同。

  inline string shape_string() const {
    ostringstream stream;
    for (int i = 0; i < shape_.size(); ++i) {
      stream << shape_[i] << " ";
    }
    stream << "(" << count_ << ")";
    return stream.str();
  }

上面定義的是一個函數,它的作用是將Blob的形狀信息變成字符串並返回。在函數中首先定義了一個流輸出變量stream,然後在for循環中,把shape_的每一個維度轉換爲字符串掛接在stream後邊,最後把元素數目count_掛接在stream後邊,形成一個字符串輸出。其中,shape_和count_是Blob了的成員變量,shape_是表示Blob形狀的向量,count_是表示Blob元素數量的int型變量。caffe中成員變量是以下劃線結尾的,比較號區分。

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

這一行定義的是讀取Blob形狀的的函數,直接返回成員變量shape_。

  inline int shape(int index) const {
    return shape_[CanonicalAxisIndex(index)];
  }

這一行定義的函數是讀取某一個維度上的尺寸,在函數中調用了CanonicalAxisIndex()函數,它的作用是確認維度數index是否可用,在下面有詳細定義。

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

這個函數用來讀取Blob的維度數,直接返回成員變量shape_的尺寸。

  inline int count() const { return count_; }

這個函數用來讀取Blob的元素數目,直接返回成員變量count_。

  inline int count(int start_axis, int end_axis) const {
    CHECK_LE(start_axis, end_axis);
    CHECK_GE(start_axis, 0);
    CHECK_GE(end_axis, 0);
    CHECK_LE(start_axis, num_axes());
    CHECK_LE(end_axis, num_axes());
    int count = 1;
    for (int i = start_axis; i < end_axis; ++i) {
      count *= shape(i);
    }
    return count;
  }

上面這個函數用來計算從起始維度start_axis到結束維度end_axis的子集的元素數目,函數的前幾行用來檢測維度數是否合規,其中CHECK_LE(start_axis, end_axis);用來檢測start_axis要小於或等於end_axis。CHECK_LE是檢測函數,LE表示,檢測的變量前面的變量要小於或等於後面的變量。GE表示大於或等於。LT表示小於,GT表示大於。其他幾行讀者可以自行分析所起的作用。下面通過一個for循環來計算元素數,計算方法爲用count乘以所指定維度的尺寸。最後返回元素數目。

  inline int count(int start_axis) const {
    return count(start_axis, num_axes());
  }

這個函數是計算從起始維度start_axis到最後維度的子集元素數目,調用第一個count函數來計算。

  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) {
      return axis_index + num_axes();
    }
    return axis_index;
  }

這個函數的作用是確認維度數是否合規,爲了兼容老版本caffe,維度索引的取值範圍爲[-num_axes(),num_axes()),這個函數首先判斷被判斷的維度值axis_index是否在這個範圍內,不在則退出,如果滿足條件,則將axis_index轉換到普通索引[0,num_axes() )範圍內,負數的轉換方法爲axis_index + num_axes()。最後返回轉換後的維度數。

  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); }

這幾個函數返回兼容老版本的num、channels、height、width這四個維度的尺寸。函數中調用了LegacyShape()函數,下面進行解釋。

  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()) {
      return 1;
    }
    return shape(index);
  }

這個函數是支持老版本的返回某個維度形狀的函數,首先是判斷維度索引index是否合規,合規則調用上面的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;
  }

這個函數的作用是返回Blob中某個元素在內存中的偏移值。雖然Blob中的數據是多維數組,但是數據在內存中依然是順序排列的。這個函數的輸入爲num、channels、height、width這四個維度的取值,輸出爲在[n][c][h][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;
  }

上面的代碼定義了另一個offset函數,它的輸入是indices向量,indices中包含的是每個維度上的索引值。

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

這個函數的作用是從source對象中複製數據到當前類中。copy_diff標誌位指定是複製data還是複製diff,reshape標誌位用來指定是否允許改變Blob形狀。

  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)];
  }

這個函數的作用是返回在num、channels、height、width這四個維度的n、c、h、w索引處的data元素值。調用offset函數返回內存中的data值。

  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)];
  }

這個函數的作用是返回在num、channels、height、width這四個維度的n、c、h、w索引處的diff元素值。調用offset函數返回內存中的diff值。

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

這個函數的作用與上面的第一個data_at()函數類似,返回的是內存中的data元素值,只不過它的輸入爲索引的向量。

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

這個函數與上一個函數類似,是通過索引的向量來返回內存中的diff值。

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

上面的函數用來返回內存中的data的地址,其中SyncedMemory是一個用來同步CPU和GPU中的數據的類,它在caffe根目錄下的include/caffe/syncedmem.hpp文件中定義,有興趣可以對照着這個文件對SyncedMemory的作用進行研究。

  inline const shared_ptr<SyncedMemory>& diff() const {
    CHECK(diff_);
    return diff_;
  }

上面的函數用來返回內存中的diff的地址。

  const Dtype* cpu_data() const;

這一行聲明瞭一個返回cpu中的data的地址的函數,對cpu_data的訪問爲只讀模式。

  void set_cpu_data(Dtype* data);

這一行聲明的函數的作用是設置cpu_data,數據源爲data。

  const int* gpu_shape() const;

這一行的作用是聲明一個返回gpu_shape的函數

  const Dtype* gpu_data() const;

這一行的作用是返回gpu_data的地址,訪問方式爲只讀。

  void set_gpu_data(Dtype* data);

這一行聲明的函數的作用是設置gpu_data,數據源爲data。

  const Dtype* cpu_diff() const;

這一行的作用是返回cpu_diff的地址,訪問方式爲只讀。

  const Dtype* gpu_diff() const;

這一行的作用是返回gpu_diff的地址,訪問方式爲只讀。

  Dtype* mutable_cpu_data();
  Dtype* mutable_gpu_data();
  Dtype* mutable_cpu_diff();
  Dtype* mutable_gpu_diff();

這四行聲明的是以讀寫方式訪問cpu_data、gpu_data、cpu_diff、gpu_diff這四種數據的函數。

  void Update();

Blob的更新函數,作用是計算data與diff的對應元素的和。

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

上面這個函數的作用是從BlobProto中讀取出一個Blob到本類。

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

將本類寫入到BlobProto中。

  Dtype asum_data() const;
  Dtype asum_diff() const;

這兩個函數的作用是計算data和diff中的元素的的絕對值之和(L1範數)。

  Dtype sumsq_data() const;
  Dtype sumsq_diff() const;

這兩個函數的作用是計算data和diff中的元素的的平方和(L2範數)。

  void scale_data(Dtype scale_factor);
  void scale_diff(Dtype scale_factor);

這兩個函數的作用是將data和diff的各元素乘以一個標量係數。

  void ShareData(const Blob& other);
  void ShareDiff(const Blob& other);

這兩個函數的作用是共享另一個Blob的data和diff。

  bool ShapeEquals(const BlobProto& other);

這個函數的作用是判斷本類的Blob與other的形狀是否相同。

 protected:
  shared_ptr<SyncedMemory> data_;
  shared_ptr<SyncedMemory> diff_;
  shared_ptr<SyncedMemory> shape_data_;
  vector<int> shape_;
  int count_;
  int capacity_;

  DISABLE_COPY_AND_ASSIGN(Blob);
};  // class Blob

}  // namespace caffe

這些代碼是blob.hpp的結尾,定義的是Blob的若干個成員變量。data_爲data的內存地址,diff_爲diff的內存地址,shape_data_爲shape數據的地址,shape_爲shape的向量,count_爲Blob的元素數。capacity_爲blob的容量信息。DISABLE_COPY_AND_ASSIGN(Blob);表示禁用拷貝構造函數、賦值運算符重載。

        至此,我們完成了blob.hpp文件的解讀。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章