【撸码caffe 五】数据层搭建
caffe.cpp中的train函数内声明了一个类型为Solver类的智能指针solver:
// Train / Finetune a model.
int train() {
……
shared_ptr<caffe::Solver<float> >
solver(caffe::SolverRegistry<float>::CreateSolver(solver_param));
……
}
之后调用Solver类的构造函数,在构造函数内执行了 Init(param)函数:
template <typename Dtype>
Solver<Dtype>::Solver(const SolverParameter& param, const Solver* root_solver)
: net_(), callbacks_(), root_solver_(root_solver),
requested_early_exit_(false) {
Init(param);
}
param是一个SolverParameter类对象,SolverParameter类继承自google的protobuf类,在类内定义了网络模型的参数和对网络的各种操作。
在Init函数里,又分别执行了一个InitTrainNet和InitTestNet函数,功能分别是构建训练网络和测试网络:
template <typename Dtype>
void Solver<Dtype>::Init(const SolverParameter& param) {
……
InitTrainNet();
if (Caffe::root_solver()) {
InitTestNets();
LOG(INFO) << "Solver scaffolding done.";
}
……
}
InitTrainNet函数里执行了一些检查工作,接着判断是否是root_solver,之后在net_.reset函数的入参里,以net_param为参数实例化了一个Net类对象:
template <typename Dtype>
void Solver<Dtype>::InitTrainNet() {
……
if (Caffe::root_solver()) {
net_.reset(new Net<Dtype>(net_param));
} else {
net_.reset(new Net<Dtype>(net_param, root_solver_->net_.get()));
}
}
在Net的构造函数里,执行了Net类的Init函数,这个Init函数完成了网络模型各个层的构建工作:
template <typename Dtype>
Net<Dtype>::Net(const NetParameter& param, const Net* root_net)
: root_net_(root_net) {
Init(param);
}
param.layer_size()函数获取到传入的param模型的网络层数,通过for循环,逐个构建网络的每个层,在Lenet的训练网络中,一共有9层:
template <typename Dtype>
void Net<Dtype>::Init(const NetParameter& in_param) {
……
for (int layer_id = 0; layer_id < param.layer_size(); ++layer_id) {
……
layers_[layer_id]->SetUp(bottom_vecs_[layer_id], top_vecs_[layer_id]);
……
}
}
SetUp是在layer.hpp中定义的,用于构建网络层,修改输出数据维度,以及设置损失权重:
void SetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
InitMutex();
CheckBlobCounts(bottom, top);
//配置网络模型的每一层
LayerSetUp(bottom, top);
//修改输出数据的维度
Reshape(bottom, top);
//设置损失权重
SetLossWeights(top);
}
数据层是网络模型的最底层,用于把数据封装成blob送入到网络中执行训练,也是SetUp里LaverSetUp第一个配置的网络层,lenet_train_test.prototxt中定义的训练网络的数据层:
layer {
name: "mnist"
type: "Data"
top: "data"
top: "label"
include {
phase: TRAIN
}
transform_param {
scale: 0.00390625
}
data_param {
source: "D:/Software/Caffe/caffe-master/examples/mnist/mnist_train_lmdb"
batch_size: 64
backend: LMDB
}
}
具体的数据层构建是在base_data_layer.cpp和data_layer.cpp中完成的。
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"
#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.
*/
template <typename Dtype>
//BaseDataLayer 继承自Layer类
class BaseDataLayer : public Layer<Dtype> {
public:
//LayerParameter类型的参数param是传入的网络模型
explicit BaseDataLayer(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 except by the BasePrefetchingDataLayer.
virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
//数据层可以并行solvers共享
// 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(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:
TransformationParameter transform_param_;
shared_ptr<DataTransformer<Dtype> > data_transformer_;
bool output_labels_; //label标签
};
//Batch类包含数据和标签数据
template <typename Dtype>
class Batch {
public:
Blob<Dtype> data_, label_;
};
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.
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);
virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
//GPU预先读取的batches组
// 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数值,包含PREFETCH_COUNT个batch数据组
Batch<Dtype> prefetch_[PREFETCH_COUNT];
BlockingQueue<Batch<Dtype>*> prefetch_free_;
BlockingQueue<Batch<Dtype>*> prefetch_full_;
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 {
template <typename Dtype>
//BaseDataLayer 类继承自Layer类
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;
}
//数据预处理
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); //数据层设置
}
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);
// 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.
for (int i = 0; i < PREFETCH_COUNT; ++i) {
prefetch_[i].data_.mutable_cpu_data();
if (this->output_labels_) {
prefetch_[i].label_.mutable_cpu_data();
}
}
#ifndef CPU_ONLY
if (Caffe::mode() == Caffe::GPU) {
for (int i = 0; i < PREFETCH_COUNT; ++i) {
prefetch_[i].data_.mutable_gpu_data(); //依次给队列中每个batch的数据blob分配cpu内存
if (this->output_labels_) {
prefetch_[i].label_.mutable_gpu_data(); //依次给队列中每个batch的标签blob分配cpu内存
}
}
}
#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();
load_batch(batch);
#ifndef CPU_ONLY
if (Caffe::mode() == Caffe::GPU) {
batch->data_.data().get()->async_gpu_push(stream);
CUDA_CHECK(cudaStreamSynchronize(stream));
}
#endif
prefetch_full_.push(batch);
}
} 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");
// Reshape to loaded data.
//调整数据维度,一次读取一个batch大小的数据
top[0]->ReshapeLike(batch->data_);
// Copy the data
caffe_copy(batch->data_.count(), batch->data_.cpu_data(),
top[0]->mutable_cpu_data()); //拷贝数据到输出中
DLOG(INFO) << "Prefetch copied";
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);
}
#ifdef CPU_ONLY
STUB_GPU_FORWARD(BasePrefetchingDataLayer, Forward);
#endif
INSTANTIATE_CLASS(BaseDataLayer);
INSTANTIATE_CLASS(BasePrefetchingDataLayer);
} // namespace caffe
data_layer.cpp:
template <typename Dtype>
DataLayer<Dtype>::DataLayer(const LayerParameter& param)
: BasePrefetchingDataLayer<Dtype>(param),
reader_(param) {
}
template <typename Dtype>
DataLayer<Dtype>::~DataLayer() {
this->StopInternalThread();
}
//主要工作是:Reshape top blob 和 prefetch得到的batch的data_ blob、label_ blob
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);//transformed_data_只是存储一张图片的数据,所以'0'维度依旧保持默认值'1'
// Reshape top[0] and prefetch_data according to the batch_size.
top_shape[0] = batch_size;//InferBlobShape(datum)返回的top_shape[0]为1
top[0]->Reshape(top_shape);
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
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
// 经过load_batch后,batch所指的数据显然发生了变化——> 虽然是以&(this->transformed_data_作为实参传递给Transform但是该地址与batch的data_ blob中每张图片的地址是相吻合的。
// load_batch(Batch<Dtype>* batch)方法Reshape了其中的data_ Blob,并且更新了数据成员transformed_data_。
// 因为Batch<Dtype>* batch仅仅是个指针,对其Reshape已经为这个Blob分配了所需要的内存,做到这一点已经足够了,毕竟prefetch_free_成员里存储的也只是指针。
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;
//返回count_。count_表示Blob存储的元素个数(shape_所有元素乘积). 如果是默认构造函数构造Blob,count_ capacity_为0。
//但是,经过Datalayer::DataLayerSetup函数的调用后,btach中data_/label_ blob都已经Reshape了,所以cout_,capacity_就不再为0了。
CHECK(batch->data_.count());
CHECK(this->transformed_data_.count());
// 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);//从reader_中获取一个datum来猜测top_shape。
this->transformed_data_.Reshape(top_shape);
// Reshape batch according to the batch_size.
top_shape[0] = batch_size;
batch->data_.Reshape(top_shape);//reshape data_ blob的大小
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();
}
for (int item_id = 0; item_id < batch_size; ++item_id) {
timer.Start();
// get a datum
Datum& datum = *(reader_.full().pop("Waiting for data"));//从reader_获取一张图片的Datum.
read_time += timer.MicroSeconds();
timer.Start();
// Apply data transformations (mirror, scale, crop...)
int offset = batch->data_.offset(item_id);//获取一张图片的offset,然后transform
//设置this->transformed_data_这个Blob的data_成员所指向的SyncedMemory类型对象的CPU内存指针cpu_ptr_设置为"top_data + offset"。
this->transformed_data_.set_cpu_data(top_data + offset);//简言之,将cpu_ptr定位到batch的data_ blob的"top_data + offset"位置处,使其指向当前即将要处理的一张图片,其实真实的过程是拷贝datum中的数据(或经过处理)至this->transformed_data_所指处。通过for循环,处理每张图片,从而更新transformed_data_。
this->data_transformer_->Transform(datum, &(this->transformed_data_));//调用后,this->transformed_data_所指向的内存会发生变化,即经过变换后的数据。如此更新数据成员transformed_data_,该成员是BasePrefetchingDataLayer类及其子类的数据成员
// 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.";
}
【撸码caffe 五】数据层搭建的更多相关文章
- caffe(2) 数据层及参数
要运行caffe,需要先创建一个模型(model),如比较常用的Lenet,Alex等, 而一个模型由多个屋(layer)构成,每一屋又由许多参数组成.所有的参数都定义在caffe.proto这个文件 ...
- php yaf框架扩展实践五——数据层
从狭义角度上来理解数据层就是数据库,比较广义的理解来看数据库.远程数据.文件等都可以看做数据层.项目初期的时候一般单一的数据库就可以了,随着流量的增大就要对数据层做很多的改进,例如增加从库分散读压力, ...
- 【撸码caffe四】 solver.cpp&&sgd_solver.cpp
caffe中solver的作用就是交替低啊用前向(forward)算法和后向(backward)算法来更新参数,从而最小化loss,实际上就是一种迭代的优化算法. solver.cpp中的Solver ...
- 【撸码caffe 二】 blob.hpp
Blob类是caffe中对处理和传递的实际数据的封装,是caffe中基本的数据存储单元,包括前向传播中的图像数据,反向传播中的梯度数据以及网络层间的中间数据变量(包括权值,偏置等),训练模型的参数等等 ...
- 【撸码caffe 三】 caffe.cpp
caffe.cpp文件完成对网络模型以及模型配置参数的读入和提取,提供了网络模型训练的入口函数train和对模型的测试入口函数test.文件中使用了很多gflags和glog指令,gflags是goo ...
- 【撸码caffe 一】syncedmen.hpp
SyncedMemory类主要负责在主机(CPU)和设备(GPU)之间管理内存分配和数据同步工作,封装了CPU和GPU之间的数据交互操作. 补充一点GPU的相关知识: 对CUDA架构而言,主机端的内存 ...
- Caffe实现多标签输入,添加数据层(data layer)
因为之前遇到了sequence learning问题(CRNN),里面涉及到一张图对应多个标签.Caffe源码本身是不支持多类标签数据的输入的. 如果之前习惯调用脚本create_imagenet.s ...
- 微服务迁移记(五):WEB层搭建(5)-集成ueditor编辑器,伪分布式图片上传
一.redis搭建 二.WEB层主要依赖包 三.FeignClient通用接口 以上三项,参考<微服务迁移记(五):WEB层搭建(1)> 四.SpringSecurity集成 参考:< ...
- 微服务迁移记(五):WEB层搭建(4)-简单的权限管理
一.redis搭建 二.WEB层主要依赖包 三.FeignClient通用接口 以上三项,参考<微服务迁移记(五):WEB层搭建(1)> 四.SpringSecurity集成 参考:< ...
随机推荐
- 史上巨坑: vim的"set foldmethod=syntax"设置竟然是导致ctrl+p(ctrl+n)补全在文件稍大时光标位于中间位置补全效率变慢的元凶!
最近我的vim又让我闹心了. 问题出现在supertab的补全速度上, 有时候按下tab键半天才弹出补全列表, 即便是弹出了列表在列表上下移动也变得的相当缓慢, 这让我的很是蛋疼. 在完全无法接受这个 ...
- 9、scala面向对象编程之继承
1. extends 2.override 和super 3.override field 4.isInstanceOf和asInstanceOf 5.getClass和classOf 6.使用模式 ...
- python笔记之发送邮件
发送邮件前提:开启邮箱授权码 一.开启授权码(以163邮箱为例) 1.登录163邮箱,点击设置--POP3/SMTP/IMAP,出现设置界面 2. 开启SMTP服务且可以查询SMTP的host地址 ...
- C# 获取当年的周六周日
public void GetWMDay() { List<string> list = new List<string>(); "; DateTime counYe ...
- css nth-child 的应用
最近改视频监控页面,由于窗口比较多,以前是通过计算窗口大小位置来处理页面布局的,其实还是比较麻烦,而且偶尔会有页面位置错乱的现象,虽然只是及其偶尔的现象,但总归是不好. 计算窗口位置的代码: /*监控 ...
- 如何创建一个项目,让gitlab自动触发jenkins进行构建
前进是:你已经配置好jenkins+gitlab自动化布置了,这里只是常规构建新的项目时,需要做的配置,记录下来,以免忘了又着急 参考这篇博客: https://www.jianshu.com/p/e ...
- js案例分析
名字取的高大上,其实只是我平时上网浏览遇到的一些我感觉还不错的小题目,再加上我或者是我在网上找到的一些理解,就保存到这里了. 2019/4/2 最新开了个新坑,是一个javascipt30的一些案例 ...
- 零基础到精通Linux,从这篇文章开始
2018年想做Linux运维的人应该如何学习才能快速精通Linux? Linux入门这么简单,为什么很多人学不会? 想要成为一个合格的运维工程师,到底怎么才能从零开始精通Linux? 作为一个运维小白 ...
- git clone下载代码,中途断掉怎么办?
问题如下: 解决办法: 1)运行以下命令进行clone $ git clone --recursive https:xxxxxx 2)进入项目根目录,继续下载 $ cd eigen-git-mirro ...
- MySQL-----一对多
一对多: 用户表和部门表 用户: 用户id 用户名 部门 1 George 1 2 Elizabeth 1 3 Bruce 2 4 Catherine 3 部门: 部门id 部门名称 1 CEO ...