caffe源碼閱讀(3)-Datalayer


DataLayer是把數據從文件導入到網絡的層,從網絡定義prototxt文件可以看一下數據層定義

layer {
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    mirror: true
    crop_size: 224
    mean_value: 104
    mean_value: 117
    mean_value: 123
  }
  data_param {
    source: "examples/imagenet/ilsvrc12_train_lmdb"
    batch_size: 32
    backend: LMDB
  }
}
l

數據層包括了文件位置、文件類型、bath_size大小、圖片變換等一些參數。可以看書,datalayer之后有top,沒有bottom,即它是最底層的,它的forward運算只是負責把數據填充到top即可,並不使用bottom。

在caffe中數據層不僅僅限於DataLayer,因為常常使用DataLayer導入數據,這里只是閱讀DataLayer部分。

數據層相關代碼定義在data_layers.hpp中,DataLayer是從其他類派生出來的,一層一層來閱讀。除了用到了datalayer相關的類,還用到了InternalThread,用來封裝了線程,使用線程函數來取數據;類BlockingQueue是一個阻塞隊列,用來輔助取數據;類DataReader,用來從文件讀數據。

BaseDataLayer

BaseDataLayer直接從Layer派生出來。其成員變量有

  TransformationParameter transform_param_;//具體在protobuf中
  shared_ptr<DataTransformer<Dtype> > data_transformer_;//和輸入數據轉換相關。流入scale,crop,mirror等
  bool output_labels_;//是否有標簽,無標簽可以是無監督學習

TransformationParameter是圖像變換一些相關的參數,例如圖像縮放、鏡像變換、crop、減去均值等操作。
DataTransformer類實現了圖像變換的函數。

Batch

Batch是和批相關的類,只是把2個數據結構封裝為一個,把數據和標簽對應起來。

template <typename Dtype>
class Batch {
 public:
  Blob<Dtype> data_, label_;
};

BasePrefetchingDataLayer

BasePrefetchingDataLayer派生自BaseDataLayerInternalThread。其中InternalThread是封裝了線程,通過虛函數InternalThreadEntry來執行線程函數,用一個單獨的線程函數來取數據。
成員變量為:

  Batch<Dtype> prefetch_[PREFETCH_COUNT];
  BlockingQueue<Batch<Dtype>*> prefetch_free_;
  BlockingQueue<Batch<Dtype>*> prefetch_full_;
  Blob<Dtype> transformed_data_;//用來輔助實現圖片變換操作

PREFETCH_COUNT的大小,程序設為3,為了提前填充free隊列。兩個阻塞隊列,邏輯功能比較簡單:從free隊列取數據結構,填充數據結構放到full隊列;從full隊列取數據,使用數據,清空數據結構,放到free隊列。還有一個Blob結構,用來當做中間變量輔助圖像變換。
虛函數InternalThreadEntry是線程執行的函數,用來取數據

//這里是取數據的線程
template <typename Dtype>
void BasePrefetchingDataLayer<Dtype>::InternalThreadEntry() {
#ifndef CPU_ONLY
  cudaStream_t stream;
  if (Caffe::mode() == Caffe::GPU) {
    CUDA_CHECK(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));
  }
#endif

  try {
    while (!must_stop()) {
      Batch<Dtype>* batch = prefetch_free_.pop();//從free_隊列去數據結構
      load_batch(batch);//取數據,填充數據結構。在其派生類實現的
#ifndef CPU_ONLY
      if (Caffe::mode() == Caffe::GPU) {
        batch->data_.data().get()->async_gpu_push(stream);//異步,把數據同步到GPU,使用Syncedmem->async_gpu_push
        CUDA_CHECK(cudaStreamSynchronize(stream));
      }
#endif
      prefetch_full_.push(batch);//把數據放到full_隊列
    }
  } catch (boost::thread_interrupted&) {
    // Interrupted exception is expected on shutdown
  }
#ifndef CPU_ONLY
  if (Caffe::mode() == Caffe::GPU) {
    CUDA_CHECK(cudaStreamDestroy(stream));
  }
#endif
}

數據層的forward函數不進行計算,不使用bottom,只是准備數據,填充到top

template <typename Dtype>
void BasePrefetchingDataLayer<Dtype>::Forward_cpu(
    const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
  Batch<Dtype>* batch = prefetch_full_.pop("Data layer prefetch queue empty");//從full隊列取數據
  // Reshape to loaded data.
  top[0]->ReshapeLike(batch->data_);//調整top大小,一次讀取一個batch大小的數據
  // Copy the data。把數據拷貝到top中
  caffe_copy(batch->data_.count(), batch->data_.cpu_data(),
             top[0]->mutable_cpu_data());
  DLOG(INFO) << "Prefetch copied";
  if (this->output_labels_) {//如果有標簽,也要把標簽拷貝到top中
    // Reshape to loaded labels.
    top[1]->ReshapeLike(batch->label_);
    // Copy the labels.
    caffe_copy(batch->label_.count(), batch->label_.cpu_data(),
        top[1]->mutable_cpu_data());
  }

  prefetch_free_.push(batch);//用過的數據結構,放回free隊列
}

DataLayer

DataLayer是真正在網絡中使用的類,派生自BasePrefetchingDataLayer。成員變量為:

DataReader reader_;

DataReader負責從硬盤讀數據到一個隊列,之后提供給data_layer使用.即使並行運行多個solver,也只有一個線程來讀數據,這樣可以確保'順序'取數據,不同的solver取到的數據不同.
DataReader的沒有bottom,top中,如果沒有標簽,blob數量為1;有標簽blob數量為2。
虛函數load_batch,一次導入一個batch_size大小的數據;之后進行DataTransformer變換。


免責聲明!

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



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