caffe源码 之 数据层

本文主要解析caffe源码文件/src/caffe/layers/Data_layer.cpp和Base_Data_layer.cpp,这两个文件主要实现caffe数据层的定义。

data_layer应该是网络的最底层,主要是将数据送给blob进入到net中。能过代码可以看到Data_Layer类与Layer类之间存在着如下的继承关系:::
这里写图片描述
所以要看懂Data_Layer类构造,要先了解Layer类的构造:http://blog.csdn.net/lanxuecc/article/details/53023211

其次了解Base_data_layer.cpp中的BaseDataLayer类与BasePrefetchingDataLayer类,InternalThread类是Caffe中的多线程接口虚类。

Base_data_layer.hpp::::::::

#ifndef CAFFE_DATA_LAYERS_HPP_
#define CAFFE_DATA_LAYERS_HPP_

#include <vector>

#include "caffe/blob.hpp"
#include "caffe/data_transformer.hpp"  //data_transformer文件中实现了常用的数据预处理操作,如尺度变换,减均值,镜像变换等
#include "caffe/internal_thread.hpp"   //处理多线程的代码文件
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"      
#include "caffe/util/blocking_queue.hpp"  //线程队列的相关文件

namespace caffe {

/**
 * @brief Provides base for data layers that feed blobs to the Net.
 *
 * TODO(dox): thorough documentation for Forward and proto params.
 */
 /*Layer的子类,data_layer的基类负责将Blobs数据送入网络*/
template <typename Dtype>
class BaseDataLayer : public Layer<Dtype> {
 public:
  explicit BaseDataLayer(const LayerParameter& param);  //构造函数,传入的参数就是solover.prototxt文件中定义的每层的参数
  // LayerSetUp: implements common data layer setup functionality, and calls
  // DataLayerSetUp to do special data layer setup for individual layer types.
  // This method may not be overridden except by the BasePrefetchingDataLayer.
  // 该虚函数实现了一般data_layer的功能,能够调用DataLayerSetUp来完成具体的data_layer的设置
  // 只能被BasePrefetchingDataLayer类来重载
  virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top);

  // Data layers should be shared by multiple solvers in parallel
  // 数据层可以被其他的solver共享
  virtual inline bool ShareInParallel() const { return true; }

  // 层数据设置,具体要求的data_layer要重载这个函数来具体实现
  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(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {}

  //虚函数由子类具体实现具体的cpu与gpu的后向传播
  virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
      const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {}
  virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
      const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {}

 protected:
  // 在caffe.proto中定义的参数类
  TransformationParameter transform_param_;

  //DataTransformer类的智能指针,DataTransformer类主要负责对数据进行预处理
  shared_ptr<DataTransformer<Dtype> > data_transformer_;

  //是否有labels
  bool output_labels_;
};

//两个blob类的对象,数据与标签
template <typename Dtype>
class Batch {
 public:
  Blob<Dtype> data_, label_;
};

/*派生自类BaseDataLayer和类InternalThread*/
template <typename Dtype>
class BasePrefetchingDataLayer :
    public BaseDataLayer<Dtype>, public InternalThread {
 public:
   //构造函数
  explicit BasePrefetchingDataLayer(const LayerParameter& param);

  // LayerSetUp: implements common data layer setup functionality, and calls
  // DataLayerSetUp to do special data layer setup for individual layer types.
  // This method may not be overridden.
  // 该虚函数实现了一般data_layer的功能,能够调用DataLayerSetUp来完成具体的data_layer的设置
  // 该函数不能被重载
  void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top);

  //具体的data_layer具体的实现这两个函数
  virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top);
  virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top);

  // Prefetches batches (asynchronously if to GPU memory)
  static const int PREFETCH_COUNT = 3;

 protected:
  //通过这个函数执行线程函数
  virtual void InternalThreadEntry();
  //加载batch
  virtual void load_batch(Batch<Dtype>* batch) = 0;

  /*batch数组*/
  Batch<Dtype> prefetch_[PREFETCH_COUNT];

  /*两个阻塞队列*/
  BlockingQueue<Batch<Dtype>*> prefetch_free_;  /*从prefetch_free_队列取数据结构,填充数据结构放到prefetch_full_队列*/
  BlockingQueue<Batch<Dtype>*> prefetch_full_;  /*从prefetch_full_队列取数据,使用数据,清空数据结构,放到prefetch_free_队列*/

  /*转换过的blob数据,中间变量用来辅助图像变换*/
  Blob<Dtype> transformed_data_;
};

}  // namespace caffe

#endif  // CAFFE_DATA_LAYERS_HPP_

Base_data_layer.cpp::::::::

#include <boost/thread.hpp>
#include <vector>

#include "caffe/blob.hpp"
#include "caffe/data_transformer.hpp"
#include "caffe/internal_thread.hpp"
#include "caffe/layer.hpp"
#include "caffe/layers/base_data_layer.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/util/blocking_queue.hpp"

namespace caffe {

// 构造函数初始化,先用param初始化父类Layer
// 再用param.transform_param()初始化transform_param_s
template <typename Dtype>
BaseDataLayer<Dtype>::BaseDataLayer(const LayerParameter& param)
    : Layer<Dtype>(param),
      transform_param_(param.transform_param()) {
}

template <typename Dtype>
void BaseDataLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {
  if (top.size() == 1) {  //获得是否有label
    output_labels_ = false;
  } else {
    output_labels_ = true;
  }
  /*创建DataTransformer类的智能指针,用来预处理数据*/
  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构造函数
template <typename Dtype>
BasePrefetchingDataLayer<Dtype>::BasePrefetchingDataLayer(
    const LayerParameter& param)
    : BaseDataLayer<Dtype>(param),
      prefetch_free_(), prefetch_full_() {
  for (int i = 0; i < PREFETCH_COUNT; ++i) {
    prefetch_free_.push(&prefetch_[i]);
  }
}

template <typename Dtype>
void BasePrefetchingDataLayer<Dtype>::LayerSetUp(
    const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
  BaseDataLayer<Dtype>::LayerSetUp(bottom, top);// 先调用父类BaseDataLayer的LayerSetUp
  // Before starting the prefetch thread, we make cpu_data and gpu_data
  // calls so that the prefetch thread does not accidentally make simultaneous
  // cudaMalloc calls when the main thread is running. In some GPUs this
  // seems to cause failures if we do not so.
  // 在开启prefetch线程之前,调用cpu_data和gpu_data,
  // 这样主线程正在运行时,prefetch线程避免同时调用cudaMalloc,
  // 这样做避免了某些gpu上出现错误
  for (int i = 0; i < PREFETCH_COUNT; ++i) {
    prefetch_[i].data_.mutable_cpu_data();      /*依次给队列中每个batch的数据blob分配cpu内存*/
    if (this->output_labels_) {
      prefetch_[i].label_.mutable_cpu_data();   /*依次分配每个每个batch的标签blob分配cpu内存*/
    }
  }

  #ifndef CPU_ONLY
  if (Caffe::mode() == Caffe::GPU) {
    for (int i = 0; i < PREFETCH_COUNT; ++i) {
      prefetch_[i].data_.mutable_gpu_data();/*依次给队列中每个batch的数据blob分配gpu内存*/
      if (this->output_labels_) {
        prefetch_[i].label_.mutable_gpu_data();/*依次分配每个每个batch的标签blob分配gpu内存*/
      }
    }
  }
  #endif

  DLOG(INFO) << "Initializing prefetch";
  this->data_transformer_->InitRand();//生成随机数据种子
  StartInternalThread();//启动内部读取数据线程
  DLOG(INFO) << "Prefetch initialized.";
}

// 如果有空闲线程,让该线程去取数据
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
}

// 将预处理过的batch,送到top
// 数据层的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
  caffe_copy(batch->data_.count(), batch->data_.cpu_data(),
             top[0]->mutable_cpu_data());// Copy the data。把数据拷贝到top中
  DLOG(INFO) << "Prefetch copied";
  if (this->output_labels_) {//如果有标签,也要把标签拷贝到top中
    // Reshape to loaded labels.
    top[1]->ReshapeLike(batch->label_);//调整top标签形状大小
    // Copy the labels.
    caffe_copy(batch->label_.count(), batch->label_.cpu_data(),
        top[1]->mutable_cpu_data());  //拷贝标签到top中
  }

  prefetch_free_.push(batch);//用过的数据结构,放回free队列
}

#ifdef CPU_ONLY
STUB_GPU_FORWARD(BasePrefetchingDataLayer, Forward);
#endif

INSTANTIATE_CLASS(BaseDataLayer);
INSTANTIATE_CLASS(BasePrefetchingDataLayer);

}  // namespace caffe

再来看看Data_layer.cpp中定义的Data_layer类。

Data_layer.hpp

#include "caffe/data_reader.hpp"
#include "caffe/data_transformer.hpp"
#include "caffe/internal_thread.hpp"
#include "caffe/layer.hpp"
#include "caffe/layers/base_data_layer.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/util/db.hpp"

namespace caffe {

/*datalayer继承了类BasePrefetchingDataLayer*/
template <typename Dtype>
class DataLayer : public BasePrefetchingDataLayer<Dtype> {
 public:
  //构造函数
  explicit DataLayer(const LayerParameter& param);  /*传入protobuf的网络的层的参数*/
  //析构函数
  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"; }

  //返回输入的blobs数量,因为数据层最底层,所以为0
  virtual inline int ExactNumBottomBlobs() const { return 0; }
  //返回最小的输出blobs数量
  virtual inline int MinTopBlobs() const { return 1; }
  //返回最大的输出blobs数量 
  virtual inline int MaxTopBlobs() const { return 2; }

 protected:
  virtual void load_batch(Batch<Dtype>* batch);  //加载数据

  DataReader reader_;  /*其作用是添加读取数据任务至,一个专门读取数据库(examples/mnist/mnist_train_lmdb)的线程(若还不存在该线程,则创建该线程)*/
};

}  // namespace caffe

#endif  // CAFFE_DATA_LAYER_HPP_

Data_layer.cpp

#ifdef USE_OPENCV
#include <opencv2/core/core.hpp>
#endif  // USE_OPENCV
#include <stdint.h>

#include <vector>

#include "caffe/data_transformer.hpp"
#include "caffe/layers/data_layer.hpp"
#include "caffe/util/benchmark.hpp"

namespace caffe {

template <typename Dtype>
DataLayer<Dtype>::DataLayer(const LayerParameter& param)
  : BasePrefetchingDataLayer<Dtype>(param),   /*调用基类构造函数BasePrefetchingDataLayer()之后,对 DataReader reader_ 进行赋值*/
    reader_(param) {
}

template <typename Dtype>
DataLayer<Dtype>::~DataLayer() {
  this->StopInternalThread();   //终止线程
}

/*Data_layer用该函数来完成具体的层设置*/
template <typename Dtype>
void DataLayer<Dtype>::DataLayerSetUp(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top) {   /*这里batch_size就是solver.prototxt中传入的*/
  const int batch_size = this->layer_param_.data_param().batch_size();  /*layer_param_在父类layer中定义*/

  // Read a data point, and use it to initialize the top blob.
  // 获取读的数据指针,然后用它初始化top blob
  // Datum是在caffe.prototxt中定义的,DataReader用LayerParameter初始化后(内含有DataParameter),
  // 可以获取要读的数据信息,并返回Datum,后面在根据Datum来reshape
  Datum& datum = *(reader_.full().peek());

  // Use data_transformer to infer the expected blob shape from datum.
  // 从datum中判断top blob的形状
  vector<int> top_shape = this->data_transformer_->InferBlobShape(datum);

  //转换成top_blob需要的形状
  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);  /*reshape top[0]中数据*/

  // reshape每个线程的prefetch 数据,并且分配内存
  for (int i = 0; i < this->PREFETCH_COUNT; ++i) {
    this->prefetch_[i].data_.Reshape(top_shape);
  }
  LOG(INFO) << "output data size: " << top[0]->num() << ","
      << top[0]->channels() << "," << top[0]->height() << ","
      << top[0]->width();
  // label
  // 如果有标签,每个线程的标签也要reshape,并且分配内存
  if (this->output_labels_) {
    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);
    }
  }
}

// This function is called on prefetch thread
// 这个函数被prefetch线程所调用 
template<typename Dtype>
void DataLayer<Dtype>::load_batch(Batch<Dtype>* batch) {
  CPUTimer batch_timer;
  batch_timer.Start();
  double read_time = 0;
  double trans_time = 0;
  CPUTimer timer;
  CHECK(batch->data_.count());
  CHECK(this->transformed_data_.count());

  // 读取一个dataum,用来初始化top blob维度
  // Reshape according to the first datum of each batch
  // on single input batches allows for inputs of varying dimension.
  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;  // suppress warnings about uninitialized variables

  if (this->output_labels_) {
    top_label = batch->label_.mutable_cpu_data();
  }

  //循环加载batch  
  for (int item_id = 0; item_id < batch_size; ++item_id) {
    timer.Start();
    // get a datum
    // 读取数据datum  
    Datum& datum = *(reader_.full().pop("Waiting for data"));
    // 统计读取时间  
    read_time += timer.MicroSeconds();
    timer.Start();
    // 计算指针offset  
    // Apply data transformations (mirror, scale, crop...)
    int offset = batch->data_.offset(item_id);
    this->transformed_data_.set_cpu_data(top_data + offset);
    // 将datum数据拷贝到batch中  
    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总时间  
  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.";
}

INSTANTIATE_CLASS(DataLayer);
REGISTER_LAYER_CLASS(Data);

}  // namespace caffe

感谢::::http://blog.csdn.net/iamzhangzhuping/article/details/50582503

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值