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
派生自BaseDataLayer
和InternalThread
。其中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變換。