1. 程式人生 > >caffe原始碼分析-DataLayer

caffe原始碼分析-DataLayer

DataLayer作為caffe訓練時的資料層(以多執行緒的方式讀取資料加速solver的訓練過程),繼承自BaseDataLayer/BasePrefetchingDataLayer

template <typename Dtype>
class BaseDataLayer : public Layer<Dtype> {
public:
    explicit BaseDataLayer(const LayerParameter& param);
    // This method may not be overridden except by the BasePrefetchingDataLayer.
    virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
    const vector<Blob<Dtype>*>& top);
    // Data layers should be shared by multiple solvers in parallel
    virtual inline bool ShareInParallel() const { return true; }
    virtual void DataLayerSetUp(const vector<Blob<Dtype>*>& bottom,
    const vector<Blob<Dtype>*>& top) {}
    // Data layers have no bottoms, so reshaping is trivial.
    virtual void Reshape(....) {//do nothing}
    virtual void Backward_cpu(....) {//do nothing}

protected:
    TransformationParameter transform_param_;
    shared_ptr<DataTransformer<Dtype> > data_transformer_;
    bool output_labels_;
};

data_transformer_主要是對輸入的圖片做crop_size,do_mirror,mean等操作.

下面看起函式定義LayerSetUp(引數初始化):

template <typename Dtype>
void BaseDataLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
                                      const vector<Blob<Dtype>*>& top) {
    if (top.size() == 1) {
        output_labels_ = false;
    } else {
        output_labels_ = true;
    }
    data_transformer_.reset(
            new DataTransformer<Dtype>(transform_param_, this->phase_));
    data_transformer_->InitRand();
    // The subclasses should setup the size of bottom and top
    DataLayerSetUp(bottom, top);
}

BasePrefetchingDataLayer還繼承了InternalThread

template <typename Dtype>
class BasePrefetchingDataLayer :
        public BaseDataLayer<Dtype>, public InternalThread {
public:
    explicit BasePrefetchingDataLayer(const LayerParameter& param);

 
    void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
                    const vector<Blob<Dtype>*>& top);

    virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
                             const vector<Blob<Dtype>*>& top);

    // Prefetches batches 
    static const int PREFETCH_COUNT = 3;

protected:
    virtual void InternalThreadEntry();
    virtual void load_batch(Batch<Dtype>* batch) = 0;

    Batch<Dtype> prefetch_[PREFETCH_COUNT];
    BlockingQueue<Batch<Dtype>*> prefetch_free_;
    BlockingQueue<Batch<Dtype>*> prefetch_full_;

    Blob<Dtype> transformed_data_;
};

template <typename Dtype>
void BasePrefetchingDataLayer<Dtype>::LayerSetUp(
        const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
    BaseDataLayer<Dtype>::LayerSetUp(bottom, top);
    //.........
    DLOG(INFO) << "Initializing prefetch";
    this->data_transformer_->InitRand();
    StartInternalThread();
    DLOG(INFO) << "Prefetch initialized.";
}

其中load_batch函式被DataLayer過載.

template <typename Dtype>
void BasePrefetchingDataLayer<Dtype>::InternalThreadEntry() {
        while (!must_stop()) {
            Batch<Dtype> *batch = prefetch_free_.pop();
            load_batch(batch);

            prefetch_full_.push(batch);
        }
}

DataLayer中使用執行緒讀取Batch(image,label)push到佇列中,然後pop出來前向傳播:

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");
    // Reshape to loaded data.
    top[0]->ReshapeLike(batch->data_);

    // Copy the data
    caffe_copy(batch->data_.count(), batch->data_.cpu_data(),
               top[0]->mutable_cpu_data());

    if (this->output_labels_) {
        // 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);
}

下面看DataLayer:

template <typename Dtype>
class DataLayer : public BasePrefetchingDataLayer<Dtype> {
public:
    explicit DataLayer(const LayerParameter& param);
    virtual ~DataLayer();
    virtual void DataLayerSetUp(const vector<Blob<Dtype>*>& bottom,
                                const vector<Blob<Dtype>*>& top);
    // DataLayer uses DataReader instead for sharing for parallelism
    virtual inline bool ShareInParallel() const { return false; }
    virtual inline const char* type() const { return "Data"; }
    virtual inline int ExactNumBottomBlobs() const { return 0; }
    virtual inline int MinTopBlobs() const { return 1; }
    virtual inline int MaxTopBlobs() const { return 2; }

protected:
    virtual void load_batch(Batch<Dtype>* batch);
    DataReader reader_;

};

DataLayerSetUp初始化引數:

template <typename Dtype>
void DataLayer<Dtype>::DataLayerSetUp(const vector<Blob<Dtype>*>& bottom,
                                      const vector<Blob<Dtype>*>& top) {
    const int batch_size = this->layer_param_.data_param().batch_size();
    // Read a data point, and use it to initialize the top blob.
    Datum& datum = *(reader_.full().peek());

    // Use data_transformer to infer the expected blob shape from datum.
    vector<int> top_shape = this->data_transformer_->InferBlobShape(datum);
    this->transformed_data_.Reshape(top_shape);
    // Reshape top[0] and prefetch_data according to the batch_size.
    top_shape[0] = batch_size;
    top[0]->Reshape(top_shape);
    for (int i = 0; i < this->PREFETCH_COUNT; ++i) {
        this->prefetch_[i].data_.Reshape(top_shape);
    }
    
    if (this->output_labels_) {// label
        vector<int> label_shape(1, batch_size);
        top[1]->Reshape(label_shape);
        for (int i = 0; i < this->PREFETCH_COUNT; ++i) {
            this->prefetch_[i].label_.Reshape(label_shape);
        }
    }
}

load_batch線上程中輪訓讀取資料庫(如lmdb)中的資料,並做crop_size,do_mirror,mean等操作,然後通過Forward_cpu傳遞給下一層.

template<typename Dtype>
void DataLayer<Dtype>::load_batch(Batch<Dtype>* batch) {
    CPUTimer batch_timer;
    batch_timer.Start();
    double read_time = 0;nnndouble trans_time = 0;
    CPUTimer timer;

    const int batch_size = this->layer_param_.data_param().batch_size();
    Datum& datum = *(reader_.full().peek());
    // Use data_transformer to infer the expected blob shape from datum.
    vector<int> top_shape = this->data_transformer_->InferBlobShape(datum);
    this->transformed_data_.Reshape(top_shape);
    // Reshape batch according to the batch_size.
    top_shape[0] = batch_size;
    batch->data_.Reshape(top_shape);

    Dtype* top_data = batch->data_.mutable_cpu_data();
    Dtype* top_label = NULL;

    if (this->output_labels_) {
        top_label = batch->label_.mutable_cpu_data();
    }
    for (int item_id = 0; item_id < batch_size; ++item_id) {
        timer.Start();
        // get a datum
        Datum& datum = *(reader_.full().pop("Waiting for data"));
        read_time += timer.MicroSeconds();
        timer.Start();
        // Apply data transformations (mirror, scale, crop...)
        int offset = batch->data_.offset(item_id);
        this->transformed_data_.set_cpu_data(top_data + offset);
        this->data_transformer_->Transform(datum, &(this->transformed_data_));
        // Copy label.
        if (this->output_labels_) {
            top_label[item_id] = datum.label();
        }
        trans_time += timer.MicroSeconds();

        reader_.free().push(const_cast<Datum*>(&datum));
    }
    timer.Stop();
    batch_timer.Stop();
//  DLOG(INFO) << "Prefetch batch: " << batch_timer.MilliSeconds() << " ms.";
//  DLOG(INFO) << "     Read time: " << read_time / 1000 << " ms.";
//  DLOG(INFO) << "Transform time: " << trans_time / 1000 << " ms.";
}

使用示例如下:

layer {
  name: "mnist"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    scale: 0.00390625
  }
  data_param {
    source: "/home/xy/caffe-master/examples/mnist/mnist_train_lmdb"
    batch_size: 64
    backend: LMDB
  }
}

proto定義如下:

message DataParameter {
  enum DB {
    LEVELDB = 0;
    LMDB = 1;
  }
  // Specify the data source.
  optional string source = 1;
  // Specify the batch size.
  optional uint32 batch_size = 4;
  optional uint32 rand_skip = 7 [default = 0];
  optional DB backend = 8 [default = LEVELDB];
  // Prefetch queue (Number of batches to prefetch to host memory.
  optional uint32 prefetch = 10 [default = 4];
}

caffe系列原始碼分析介紹

本系列深度學習框架caffe 原始碼分析主要內容如下:

自己從頭構建一遍工程,這樣能讓我更好的瞭解大型的專案的構建。當然原始的caffe的構建感覺還是比較複雜(主要是cmake),我這裡僅僅使用cmake構建,而且簡化點,當然最重要的是支援CLion直接執行除錯(如果需要這個工程可以評論留下你的郵箱,我給你傳送過去)。

這裡寫圖片描述

2. caffe的資料記憶體分配類SyncedMemory, 以及類Blob資料傳輸的媒介.

主要內容:
caffe原始碼分析-SyncedMemory
caffe原始碼分析-Blob
其中Blob分析給出了其直接與opencv的圖片相互轉化以及操作,可以使得我們更好的理解Blob.

3. caffe layer的原始碼分析,包括從整體上說明了layer類別以及其proto定義與核心函式.

首先分析了最簡單的layer Relu,然後在是inner_product_layer全連線層, 最後是layer_factorycaffe中 以此工廠模式create各種Layer.

4. 資料輸入層,主要是多執行緒+BlockingQueue的方式讀取資料訓練:
5. IO處理例如讀取proto檔案轉化為網路,以及網路引數的序列化
6. 最後給出了使用純C++結合多層感知機網路訓練mnist的示例

內容如下:

類似與caffe一樣按照layer、solver、loss、net等模組構建的神經網路實現可以見下面這篇blog,相信看懂了這個python的程式碼理解caffe框架會更簡單點.

最後如果需要cmake + CLion直接執行除錯caffe的程式碼工程,可以評論留下你的郵箱,我給你傳送過去.

相關推薦

caffe原始碼分析-DataLayer

DataLayer作為caffe訓練時的資料層(以多執行緒的方式讀取資料加速solver的訓練過程),繼承自BaseDataLayer/BasePrefetchingDataLayer。 template <typename Dtype> class

caffe 原始碼分析【三】:Euclidean loss layer

以下是Euclidean loss layer的程式碼分析,轉自: https://blog.csdn.net/seashell_9/article/details/68064294 一. 前向函式 template <typename Dtype> void Euclide

caffe 原始碼分析【二】:Layer基類

建構函式 //標頭檔案 include/caffe/layer.hpp //實現檔案 src/caffe/layer.cpp // src/caffe/layer.cu /* * 建構函式 * 子類中修改建構函式,自定義設定在SetUp()中設定

caffe 原始碼分析【一】: Blob類

Blob類的:     //標頭檔案: include\caffe\blob.hpp //cpp檔案: src\caffe\blob.cpp //cu檔案: src/caffe/blob.cu //定義某layer的輸入blobs const ve

caffe原始碼分析-Blob

本文主要分析caffe原始碼分析-Blob,主要如下幾個方面: overview整體上了解caffe的Blob Blob 成員變數 Blob主要函式,核心在於Blob的使用例項以及其與opencv Mat的操作的相互轉化(附帶執行結果基於CLion) o

caffe原始碼分析-ReLULayer

啟用函式如:ReLu,Sigmoid等layer相對較為簡單,所以在分析InnerProductLayer前,我們先看下啟用函式層。 常見啟用層ReLU的使用示例如下: layer { name: "relu1" type: "ReLU" bott

caffe原始碼分析-layer_factory

caffe中有許多的layer,在net中建立連線layer是通過工廠模式的方式建立,而不是每一個new然後連線。在net.cpp中建立layer方式如下: layers_.push_back(Laye

caffe原始碼分析-InputLayer

對於輸入層,我們首先分析最簡單的InputLayer層,其常作為網路inference時的輸入,簡單的mnist使用示例如下: layer { name: "data" type: "Input

caffe原始碼分析-BlockingQueue

BlockingQueue執行緒安全的佇列, 作為caffe訓練時資料同步的重要資料結構,本文做簡要分析。 template<typename T> class BlockingQueue

caffe原始碼分析-DataTransformer

本文主要分析caffe中DataTransformer這個類, 主要作用是: 將Datum型別或者cv::Mat, 轉化為caffe的Blob<Dtype>,並按照Transformation``Parameter引數對影象做處理,例如scale

Caffe原始碼分析訓練過程

下面開始來具體進行介紹。 先從Caffe.cpp檔案中的train()函式開始說起。 1、建立一個SolverParameter solver_param用來儲存求解(優化)的一些引數,SolverParameter這個資料結構具體被定義在caffe.proto檔案中。 2、Caffe::readP

caffe原始碼分析-cmake 工程構建

本文主要說明下,caffe原始碼分析過程中的cmake(結合IDE CLion)工程構建問題。在分析caffe原始碼的過程中,我沒有僅僅只是看程式碼,而是: 自己從頭構建一遍工程,這樣能讓我更好的瞭解大型的專案的構建。當然原始的caffe的構建感覺還是比較複

caffe原始碼分析:blob.hpp分析

以下摘自網際網路: Blob作為Caffe的四大模組之一,負責完成CPU/GPU儲存申請、同步和資料持久化對映。Caffe內部資料儲存和通訊都是通過Blob來完成,Blob提供統一的儲存操作介面,可用來儲存訓練資料、模型引數等。Blob是一個高維連續陣

caffe原始碼分析 vector& bottom

Blob:4個維度 n x c x h x w; bottom[0] 、bottom[1]代表該層有幾個輸入。 bottom[0]->count(): 輸入中,元素的總維數(個數) bottom[0]->nums(): 輸入中,塊(block)的個數,該引數還對應batch_size,即同時輸

caffe原始碼分析-inner_product_layer

本文主要分析caffe inner_product_layer原始碼,主要內容如下: 結合使用以及proto定義介紹InnerProductLayer的引數; 簡要分析Filler初始化,caffe中的layer引數,例如constant, gaussi

[caffe] 原始碼分析

caffe 1), i. using namespace caffe; ii. class Caffe{ inline static SetDevice(int i); } Caffe::SetDevice(gpus[0]);

caffe原始碼分析-db, io

本文主要分析下caffe的原始碼,io操作,以及資料庫檔案(如lmdb)讀取。 例如,從prototxt讀取網路初始化引數: NetParameter param; ReadNetParamsFromTextFileOrDie(path, &param)

Caffe原始碼(四):math_functions 分析

轉自:https://blog.csdn.net/seven_first/article/details/47378697#1-caffecpugemm-%E5%87%BD%E6%95%B0 主要函式 math_function 定義了caffe 中用到的一些矩陣操作和數值計算的一些函式,這

caffe原始碼深入學習6:超級詳細的im2col繪圖解析,分析caffe卷積操作的底層實現

在先前的兩篇部落格中,筆者詳細解析了caffe卷積層的定義與實現,可是在conv_layer.cpp與base_conv_layer.cpp中,卷積操作的實現仍然被隱藏,通過im2col_cpu函式和caffe_cpu_gemm函式(後者實現矩陣乘法)實現,在此篇部落格中,筆者旨在向大家展示,caf

caffe Layer基類原始碼分析

建構函式 //標頭檔案 include/caffe/layer.hpp //實現檔案 src/caffe/layer.cpp // src/caffe/layer.cu /*