【caffe源码研究】第四章:完整案例源码篇(2) :LeNet初始化训练网络
来源:互联网 发布:松竹梅清酒 知乎 编辑:程序博客网 时间:2024/04/30 05:07
一、Solver到Net
SGDSolver的构造函数中主要执行了其父类Solver的构造函数,接着执行Solver::Init()
函数,在Init()中,有两个函数值得注意:InitTrainNet()
和InitTestNets()
分别初始化训练网络和测试网络。
(1). InitTrainNet
首先,ReadNetParamsFromTextFileOrDie(param_.NET(), &net_param)
把param_.Net()
(即examples/mnist/lenet_train_test.prototxt
)中的信息读入net_param
。
其次,net_.reset(new Net<Dtype>(net_param))
重新构建网络,调用Net的构造方法。
然后,在构造方法中执行Net::init()
,开始正式创建网络。其主要代码如下:
template <typename Dtype> void Net<Dtype>::Init(const NetParameter& in_param) { ... for (int layer_id = 0; layer_id < param.layer_size(); ++layer_id) { // Setup layer. const LayerParameter& layer_param = param.layer(layer_id); // 在这里创建网络层 layers_.push_back(LayerRegistry<Dtype>::CreateLayer(layer_param)); // Figure out this layer's input and output for (int bottom_id = 0; bottom_id < layer_param.bottom_size(); ++bottom_id) { const int blob_id = AppendBottom(param, layer_id, bottom_id, &available_blobs, &blob_name_to_idx); // If a blob needs backward, this layer should provide it. need_backward |= blob_need_backward_[blob_id]; } int num_top = layer_param.top_size(); for (int top_id = 0; top_id < num_top; ++top_id) { AppendTop(param, layer_id, top_id, &available_blobs, &blob_name_to_idx); } ... // 在这里配置网络层 layers_[layer_id]->SetUp(bottom_vecs_[layer_id], top_vecs_[layer_id]); ... } for (int param_id = 0; param_id < num_param_blobs; ++param_id) { AppendParam(param, layer_id, param_id); } ... }
说明:
- Lenet5在caffe中共有9层,即param.layer_size()==9,以上代码每一次for循环创建一个网络层
- 每层网络是通过LayerRegistry::CreateLayer()创建的,类似与Solver的创建
- 14行Net::AppendBottom(),对于layer_id这层,从Net::blob_中取出blob放入该层对应的bottom_vecs_[layer_id]中
- 20行Net::AppendTop(),对于layer_id这层,创建blob(未包含数据)并放入Net::blob_中
- AppendParam中把每层网络的训练参数与网络变量learnable_params_绑定,在lenet中,只有conv1,conv2,ip1,ip2四层有参数,每层分别有参数与偏置参数两项参数,因而learnable_params_的size为8.
(2). LayerRegistry::CreateLayer
工厂模式new出网络层对象
(3). Layer::SetUp
void SetUp(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { InitMutex(); CheckBlobCounts(bottom, top); //每层进行配置 LayerSetUp(bottom, top); //修改输出数据的维数(即top_blob的维数)等 //关注数据维数的应关注此函数 Reshape(bottom, top); //设置损失权重 SetLossWeights(top);}
其中,Reshape函数中通过compute_output_shape计算输出blob的函数,
二、训练网络结构
注:Top Blob Shape格式为:BatchSize,ChannelSize,Height,Width(Total Count)
网络结构如图所示:
三、 第一层:Data Layer
(1). protobuff定义
训练网络的第一层protobuff定义为:
layer { name: "mnist" type: "Data" top: "data" top: "label" include { phase: TRAIN } transform_param { scale: 0.00390625 } data_param { source: "examples/mnist/mnist_train_lmdb" batch_size: 64 backend: LMDB }}
(2). 函数LayerRegistry::CreateLayer
第1节中代码第一次通过调用LayerRegistry::CreateLayer()创建了DataLayer类.
调用DataLayer()的构造函数,依次执行的顺序为其基类构造函数:Layer()、BaseDataLayer()、InternalThread()、BasePrefetchingDataLayer()、及DataLayer()。
其中,值得注意的是DataLayer(),在调用基类构造函数BasePrefetchingDataLayer()之后,对 DataReader reader_ 进行赋值,在该DataLayer对象中维护了一个DataReader对象reader_,其作用是添加读取数据任务至,一个专门读取数据库(examples/mnist/mnist_train_lmdb
)的线程(若还不存在该线程,则创建该线程),此处一共取出了4*64个样本至BlockingQueue<Datum*> DataReader::QueuePair::full_
。
template <typename Dtype>DataLayer<Dtype>::DataLayer(const LayerParameter& param) : BasePrefetchingDataLayer<Dtype>(param), reader_(param) {}
(3). 函数Layer::SetUp
此处按程序执行顺序值得关注的有:
在DataLayer::DataLayerSetUp
中根据DataReader
中介绍的读取的数据中取出一个样本推测blob的形状 BasePrefetchingDataLayer::LayerSetUp
如下代码prefetch_[i].data_.mutable_cpu_data()
用到了涉及到gpu、cpu
间复制数据的问题.
// 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(); } }
BasePrefetchingDataLayer
类继承了InternalThread,BasePrefetchingDataLayer<Dtype>::LayerSetUp
中通过调用StartInternalThread()
开启了一个新线程,从而执行BasePrefetchingDataLayer::InternalThreadEntry
BasePrefetchingDataLayer::InternalThreadEntry
关键代码如下,其中load_batch(batch)
为,从BlockingQueue<Datum*> DataReader::QueuePair::full_
(包含从数据库读出的数据)中读取一个batch_size的数据到BlockingQueue<Batch<Dtype>*> BasePrefetchingDataLayer::prefetch_full_
中。由于该线程在prefetch_free_
为空时将挂起等待(PREFETCH_COUNT=3
),prefetch_full_
中用完的Batch将放回prefetch_free_
中。该线程何时停止?
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); }
关于线程的总结:
- 此外一共涉及到两个线程,分别为都是继承了InnerThread的BasePrefetchingDataLayer(DataLayer)类和DataReader中的Body类
- Body为面向数据库的线程,不断从某个数据库中读出数据,存放至缓存为队列+
DataReader::QueuePair::BlockingQueue<Datum*>
,一般保存4*64个单位数据,单位为Datum - BasePrefetchingDataLayer为面向网络的线程,从Body的缓存中不断读取数据。
BasePrefetchingDataLayer
的缓存为队列BlockingQueue<Batch*>
,一般存放3个单位的数据,单位为Batch
static const int PREFETCH_COUNT = 3;Batch<Dtype> prefetch_[PREFETCH_COUNT];BlockingQueue<Batch<Dtype>*> prefetch_free_;BlockingQueue<Batch<Dtype>*> prefetch_full_;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]); }}prefetch_full_与prefetch_free_中的元素由prefetch_提供
四、第二层:Convolution Layer
(1). protobuff定义
layer { name: "conv1" type: "Convolution" bottom: "data" top: "conv1" param { lr_mult: 1 } param { lr_mult: 2 } convolution_param { num_output: 20 kernel_size: 5 stride: 1 weight_filler { type: "xavier" } bias_filler { type: "constant" } }}
不像DataLayer 直接执行的是构造函数,此时执行的是GetConvolutuionLayer()
,然后调用ConvolutionLayer()
.
(2). LayerSetUp
在Layer::SetUp
中,调用了ConvolutionLayer
的基类BaseConvolutionLayer
的LayerSetUp及Reshape
函数,该类的主要成员变量如下:
/** * @brief Abstract base class that factors out the BLAS code common to * ConvolutionLayer and DeconvolutionLayer. */template <typename Dtype>class BaseConvolutionLayer : public Layer<Dtype> { public: explicit BaseConvolutionLayer(const LayerParameter& param) : Layer<Dtype>(param) {} virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top); virtual void Reshape(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top); ... /// @brief The spatial dimensions of a filter kernel. Blob<int> kernel_shape_; /// @brief The spatial dimensions of the stride. Blob<int> stride_; /// @brief The spatial dimensions of the padding. Blob<int> pad_; /// @brief The spatial dimensions of the dilation. Blob<int> dilation_; /// @brief The spatial dimensions of the convolution input. Blob<int> conv_input_shape_; /// @brief The spatial dimensions of the col_buffer. vector<int> col_buffer_shape_; /// @brief The spatial dimensions of the output. vector<int> output_shape_; const vector<int>* bottom_shape_;...};
说明:
- LayerSetUp函数中,主要是初始化了kernel_shape_、stride_、pad_、dilation_以及初始化网络参数,并存放与Layer::blobs_中。
- Reshape函数中,conv_input_shape_、bottom_shape_等
五、第三层:Pooling Layer
(2). protobuff定义
layer { name: "pool1" type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { pool: MAX kernel_size: 2 stride: 2 }}
(2). Layer::SetUp
通过调用虚函数LayerSetUp及Reshape对以下成员变量进行初始化
/** * @brief Pools the input image by taking the max, average, etc. within regions. * * TODO(dox): thorough documentation for Forward, Backward, and proto params. */template <typename Dtype>class PoolingLayer : public Layer<Dtype> { .... int kernel_h_, kernel_w_; int stride_h_, stride_w_; int pad_h_, pad_w_; int channels_; int height_, width_; int pooled_height_, pooled_width_; bool global_pooling_; Blob<Dtype> rand_idx_; Blob<int> max_idx_;};
六、 第四层、第五层
基本同第二层、第三层
七、 第六层:InnerProduct Layer
(1). protobuff定义
layer { name: "ip1" type: "InnerProduct" bottom: "pool2" top: "ip1" param { lr_mult: 1 } param { lr_mult: 2 } inner_product_param { num_output: 500 weight_filler { type: "xavier" } bias_filler { type: "constant" } }}
(2). Layer::SetUp
/** * @brief Also known as a "fully-connected" layer, computes an inner product * with a set of learned weights, and (optionally) adds biases. * * TODO(dox): thorough documentation for Forward, Backward, and proto params. */template <typename Dtype>class InnerProductLayer : public Layer<Dtype> { ... int M_; int K_; int N_; bool bias_term_; Blob<Dtype> bias_multiplier_;};
说明:
- N_为输出大小,即等于protobuff中定义的num_output
- K_为输入大小,对于该层Bottom Blob形状为(N, C, H, W),N为batch_size,K_=C*H*W,M_=N。其中只有C、H、W跟内积相关
八、SoftmaxWithLoss Layer
(1). protobuff定义
layer { name: "loss" type: "SoftmaxWithLoss" bottom: "ip2" bottom: "label" top: "loss"}
九、SoftmaxWithLoss Layer
(1). protobuff定义
layer { name: "loss" type: "SoftmaxWithLoss" bottom: "ip2" bottom: "label" top: "loss"}
(2). Layer::SetUp
值得注意的是:
- 类SoftmaxWithLossLayer包含类SoftmaxLayer的实例
shared_ptr<Layer<Dtype> > softmax_layer_
- softmax_layer_在LayerSetUp中赋值。
- 此函数内调用
Layer::SetLossWeights
初始化了该层的Top Blob(loss) - 成员变量prob_作为Softmaxlayer的top blob
- bottom blob[0]作为softmaxlayer的bottom blob
- 所以经过softmaxlayer计算之后,得出64*10(每个样本的每个类别上的概率)存放在prob_中
两个类间的关系如下图:
- 【caffe源码研究】第四章:完整案例源码篇(2) :LeNet初始化训练网络
- 【caffe源码研究】第四章:完整案例源码篇(3) :LeNet初始化测试网络
- 【caffe源码研究】第四章:完整案例源码篇(4) :LeNet前向过程
- 【caffe源码研究】第四章:完整案例源码篇(5) :LeNet反向过程
- 【caffe源码研究】第四章:完整案例源码篇(1) :LeNetSolver初始化
- (Caffe,LeNet)初始化训练网络(三)
- Caffe MNIST训练lenet网络
- 【caffe源码研究】第三章:源码篇(6) :caffe.proto
- 【caffe源码研究】第二章:使用篇(6) : 训练过程分析工具
- Caffe框架源码剖析(2)—训练网络
- Caffe框架源码剖析(2)—训练网络
- Caffe源码阅读(粗读)--网络训练
- Caffe源码阅读(粗读)--网络初始化
- 【caffe源码研究】第二章:使用篇(2):tools
- 【caffe源码研究】第二章:实战篇(2) : ImageNet分类
- 【caffe源码研究】第三章:源码篇(3) :工厂模式
- 【caffe源码研究】第三章:源码篇(4) :Solver
- 【caffe源码研究】第三章:源码篇(5) :Net
- 35. Search Insert Position
- scapy:网络数据包操作
- 嵌入式系统的电气隔离
- python调用vlc显示视频实例
- java中不常见的关键字:strictfp,transient
- 【caffe源码研究】第四章:完整案例源码篇(2) :LeNet初始化训练网络
- Spring boot中使用log4j记录日志
- JS之定义指定时间的Date对象
- continue 和 break
- 存储过程 编译错误:PLS-00103: Encountered the symbol "TABLE" when expecting one of the following:
- url当前目录与根目录
- mysql 拼接字符串已达到预期效果
- QT更改窗口名称
- java.lang.UnsatisfiedLinkErro...nativeLibraryDirectories...couldn't find "libluajava.so"