caffe源碼閱讀(1)-數據流Blob


Blob是Caffe中層之間數據流通的單位,各個layer之間的數據通過Blob傳遞。在看Blob源碼之前,先看一下CPU和GPU內存之間的數據同步類SyncedMemory;使用GPU運算時,數據要在GPU顯存中,但是一開始數據是通過CPU讀到內存,通過類SyncedMemory來實現顯存和內存之間的數據的同步。

SyncedMemory

先看一下成員變量

  //數據在cpu或gpu,指向數據的指針
  void* cpu_ptr_;
  void* gpu_ptr_;
  size_t size_;//數據大小
  SyncedHead head_;//數據狀態,有四種:UNINITIALIZED, HEAD_AT_CPU, HEAD_AT_GPU, SYNCED
  bool own_cpu_data_;
  bool cpu_malloc_use_cuda_;//是否使用cuda標記
  bool own_gpu_data_;
  int gpu_device_;

兩個指針分別指向在內存和顯存的數據,size_記錄數據大小,head_是枚舉變量,記錄數據狀態。gpu_device_指出使用哪塊顯卡。
成員函數根據名字能看出大概意思,其中

void async_gpu_push(const cudaStream_t& stream);

是異步同步數據到GPU,這里的"異步“是指把數據同步到GPU,在同步未完成時就返回,不需要等待完成同步。

CaffeMallocHost/CaffeFreeHost

這是一個功能和malloc/free相同的分配/釋放內存/顯存的函數。如果使用了GPU,則在在GPU上分配和釋放,否則在內存上分配和釋放。

Blob

Blob類的成員變量很少

protected:
  shared_ptr<SyncedMemory> data_;//存放數據
  shared_ptr<SyncedMemory> diff_;//存放梯度
  shared_ptr<SyncedMemory> shape_data_;//Blob形狀,N K H W
  vector<int> shape_;//保存 N K H W
  int count_;//元素個數
  int capacity_;//當前元素個數

Blob存儲着圖像數據,以及偏差。圖像數據大小由channel、height、width判斷,一個Blob可能存儲多幅圖像,所以多了一個num。即Blob大小有Num,K(channel),Height,Weight決定。

Blob成員函數很多:
Reshape函數用來調整Blob形狀,最終調用的函數如下

 template <typename Dtype>
void Blob<Dtype>::Reshape(const vector<int>& shape) {
  CHECK_LE(shape.size(), kMaxBlobAxes);//維數不能超過kMaxBlobAxes
  count_ = 1;//賦值為1,為了相乘
  shape_.resize(shape.size());
  if (!shape_data_ || shape_data_->size() < shape.size() * sizeof(int)) {
    shape_data_.reset(new SyncedMemory(shape.size() * sizeof(int)));
  }
  int* shape_data = static_cast<int*>(shape_data_->mutable_cpu_data());
  for (int i = 0; i < shape.size(); ++i) {
    CHECK_GE(shape[i], 0);
    CHECK_LE(shape[i], INT_MAX / count_) << "blob size exceeds INT_MAX";
    count_ *= shape[i];//記錄數據大小
    shape_[i] = shape[i];
    shape_data[i] = shape[i];
  }
  if (count_ > capacity_) {//capactity不小於count
    capacity_ = count_;
    data_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
    diff_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
  }
}

可以看出,如果Reshape時,如果大小不夠時,會重新分配內存/顯存,釋放原有內存/顯存。

Update()函數用來更新數據,根據數據所在位置進行更新

 template <typename Dtype>
void Blob<Dtype>::Update() {
  // We will perform update based on where the data is located.
  switch (data_->head()) {
  case SyncedMemory::HEAD_AT_CPU:
    // perform computation on CPU
    //data_ = data_ - diff_
    caffe_axpy<Dtype>(count_, Dtype(-1),
        static_cast<const Dtype*>(diff_->cpu_data()),
        static_cast<Dtype*>(data_->mutable_cpu_data()));
    break;
  case SyncedMemory::HEAD_AT_GPU:
  case SyncedMemory::SYNCED:
#ifndef CPU_ONLY
    // perform computation on GPU
    //data_ = data_ - diff_
    caffe_gpu_axpy<Dtype>(count_, Dtype(-1),
        static_cast<const Dtype*>(diff_->gpu_data()),
        static_cast<Dtype*>(data_->mutable_gpu_data()));
#else
    NO_GPU;
#endif
    break;
  default:
    LOG(FATAL) << "Syncedmem not initialized.";
  }
}

Update()函數,實際上進行的運算時data_ = data_ - diff_。

計算范數的函數是特化實現的:
函數asum_data()asum_diff()是計算data_或diff_的L1范數。
函數sumsq_data()sumsq_diff()是計算data_或diff_的L1范數。

其他函數根據名字都可以大概理解了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM