本文從CSDN上轉(zhuǎn)移過來地址:
http://blog.csdn.net/mounty_fsc/article/details/51090306
1. Solver到Net
在SGDSolver
的構(gòu)造函數(shù)中詳見本系列博文(二)陪白,主要執(zhí)行了其父類Solver
的構(gòu)造函數(shù)夷野,接著執(zhí)行Solver::Init()
函數(shù)入客,在Init()
中,有兩個(gè)函數(shù)值得注意:InitTrainNet()
和InitTestNets()
分別初始化訓(xùn)練網(wǎng)絡(luò)和測試網(wǎng)絡(luò)背桐。
-
InitTrainNet
- 首先,
ReadNetParamsFromTextFileOrDie(param_.net(), &net_param)
把param_.net()
(即examples/mnist/lenet_train_test.prototxt
)中的信息讀入net_param
蝉揍。 - 其次链峭,
net_.reset(new Net<Dtype>(net_param))
重新構(gòu)建網(wǎng)絡(luò),調(diào)用Net
的構(gòu)造方法又沾。 - 然后弊仪,在構(gòu)造方法中執(zhí)行
Net::init()
,開始正式創(chuàng)建網(wǎng)絡(luò)杖刷。其主要代碼如下:
- 首先,
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);
}
...
}
```
**說明:**
1. Lenet5在caffe中共有9層励饵,即`param.layer_size() == 5`,以上代碼每一次for循環(huán)創(chuàng)建一個(gè)網(wǎng)絡(luò)層
2. 每層網(wǎng)絡(luò)是通過`LayerRegistry::CreateLayer()`創(chuàng)建的挺勿,類似與Solver的創(chuàng)建(詳見本系列博文(二))
3. 14行`Net::AppendBottom()`曲横,對(duì)于`layer_id`這層,從`Net::blob_`中取出blob放入該層對(duì)應(yīng)的`bottom_vecs_[layer_id]`中
4. 20行`Net::AppendTop()`不瓶,對(duì)于`layer_id`這層禾嫉,創(chuàng)建`blob`(未包含數(shù)據(jù))并放入`Net::blob_`中
5. `Layer::SetUp()`
6. `AppendParam`中把每層網(wǎng)絡(luò)的訓(xùn)練參數(shù)與網(wǎng)絡(luò)變量`learnable_params_`綁定,在lenet中蚊丐,只有`conv1`,`conv2`,`ip1`,`ip2`四層有參數(shù)熙参,每層分別有參數(shù)與偏置參數(shù)兩項(xiàng)參數(shù),因而`learnable_params_`的size為8.
-
InitTestNets
<font color="red">該部分內(nèi)容見本系列博文:(Caffe麦备,Lenet5)初始化測試網(wǎng)絡(luò)(四)孽椰。</font>
2 訓(xùn)練網(wǎng)絡(luò)結(jié)構(gòu)
序 | Layer | layer Type Bottom | Blob Top | Blob Top | Blob Shape |
---|---|---|---|---|---|
1 | minst | Data | data&&label | 64 1 28 28 (50176) && 64 (64) | |
2 | conv1 | Convolution | data | conv1 | 64 20 24 24 (737280) |
3 | pool1 | Pooling | conv1 | pool1 | 64 20 12 12 (184320) |
4 | conv2 | Convolution | pool1 | conv2 | 64 50 8 8 (204800) |
5 | pool2 | Pooling | conv2 | pool2 | 64 50 4 4 (51200) |
6 | ip1 | InnerProduct | pool2 | ip1 | 64 500 (32000) |
7 | relu1 | ReLU | ip1 | ip1(in-place) | 64 500 (32000) |
8 | ip2 | InnerProduct | ip1 | ip2 | 64 10 (640) |
9 | loss | SoftmaxWithLoss | ip2&&label | loss | (1) |
注:Top Blob Shape格式為:BatchSize,ChannelSize凛篙,Height黍匾,Width(Total Count)
3 第一層:Data Layer
3.1 protobuff定義
訓(xùn)練網(wǎng)絡(luò)的第一層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
}
}
3.2 函數(shù)LayerRegistry::CreateLayer
第1節(jié)中代碼第一次通過調(diào)用LayerRegistry::CreateLayer()
創(chuàng)建了DataLayer
類,DataLayer
類的繼承關(guān)系如下圖所示呛梆,詳見[1]:
由繼承圖可知锐涯,調(diào)用DataLayer()
的構(gòu)造函數(shù),依次執(zhí)行的順序?yàn)槠浠悩?gòu)造函數(shù):Layer()填物、BaseDataLayer()纹腌、InternalThread()
(詳見(Caffe)基本類InternalThread(三) )、BasePrefetchingDataLayer()滞磺、及DataLayer()
升薯。
其中,值得注意的是DataLayer()
击困,在調(diào)用基類構(gòu)造函數(shù)BasePrefetchingDataLayer()
之后涎劈,對(duì) DataReader reader_
進(jìn)行賦值,在該DataLayer對(duì)象中維護(hù)了一個(gè)DataReader
對(duì)象reader_
,其作用是添加讀取數(shù)據(jù)任務(wù)至炮障,一個(gè)專門讀取數(shù)據(jù)庫(examples/mnist/mnist_train_lmdb)的線程(若還不存在該線程目派,則創(chuàng)建該線程)坤候,此處一共取出了4*64個(gè)樣本至BlockingQueue<Datum*> DataReader::QueuePair::full_
。詳見(Caffe)基本類DataReader企蹭、QueuePair白筹、Body(四)
template <typename Dtype>
DataLayer<Dtype>::DataLayer(const LayerParameter& param)
: BasePrefetchingDataLayer<Dtype>(param),
reader_(param) {
}
3.3 函數(shù)Layer::SetUp
此處按程序執(zhí)行順序值得關(guān)注的有:
在DataLayer::DataLayerSetUp
中根據(jù)3.2DataReader中介紹的讀取的數(shù)據(jù)中取出一個(gè)樣本推測blob
的形狀-
BasePrefetchingDataLayer::LayerSetUp
如下代碼prefetch_[i].data_.mutable_cpu_data()
用到了涉及到gpu、cpu間復(fù)制數(shù)據(jù)的問題谅摄,見(Caffe)基本類Blob徒河,Layer,Net(一)1.4SyncedMemory及引用[2]// 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
中通過調(diào)用StartInternalThread()
開啟了一個(gè)新線程顽照,從而執(zhí)行BasePrefetchingDataLayer::InternalThreadEntry
-
BasePrefetchingDataLayer::InternalThreadEntry
關(guān)鍵代碼如下,其中load_batch(batch)
為闽寡,從2.2介紹的BlockingQueue<Datum*> DataReader::QueuePair::full_
(包含從數(shù)據(jù)庫讀出的數(shù)據(jù))中讀取一個(gè)batch_size
的數(shù)據(jù)到BlockingQueue<Batch<Dtype>*> BasePrefetchingDataLayer::prefetch_full_
中代兵。由于該線程在prefetch_free_
為空時(shí)將掛起等待(PREFETCH_COUNT=3
),prefetch_full_中用完的Batch
將放回prefetch_free_
中爷狈。<u>該線程何時(shí)停止植影?</u>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); }
關(guān)于線程的總結(jié):
- 此外一共涉及到兩個(gè)線程,分別為都是繼承了
InnerThread
的BasePrefetchingDataLayer(DataLayer)
類和DataReader
中的Body
類 -
Body
為面向數(shù)據(jù)庫的線程涎永,不斷從某個(gè)數(shù)據(jù)庫中讀出數(shù)據(jù)思币,存放至緩存為隊(duì)列DataReader::QueuePair::BlockingQueue<Datum*>
,一般保存4*64個(gè)單位數(shù)據(jù)羡微,單位為Datum
-
BasePrefetchingDataLayer
為面向網(wǎng)絡(luò)的線程谷饿,從Body
的緩存中不斷讀取數(shù)據(jù)。BasePrefetchingDataLayer
的緩存為隊(duì)列BlockingQueue<Batch*>
妈倔,一般存放3個(gè)單位的數(shù)據(jù)博投,單位為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_
提供
4 第二層:Convolution Layer
4.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"
}
}
}
4.2 函數(shù)LayerRegistry::CreateLayer
說明:
- 不像DataLayer 直接執(zhí)行的是構(gòu)造函數(shù),此時(shí)執(zhí)行的是
GetConvolutuionLayer()
启涯,然后調(diào)用ConvolutionLayer()
贬堵,原因如下:
REGISTER_LAYER_CREATOR(Convolution, GetConvolutionLayer)
;
4.3 Layer::SetUp
在Layer::SetUp
中,調(diào)用了ConvolutionLayer
的基類BaseConvolutionLayer
的LayerSetUp及Reshape
函數(shù)结洼,該類的主要成員變量如下:
/**
* @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函數(shù)中黎做,主要是初始化了kernel_shape_、stride_松忍、pad_蒸殿、dilation_以及初始化網(wǎng)絡(luò)參數(shù),并存放與Layer::blobs_中。
- Reshape函數(shù)中宏所,conv_input_shape_酥艳、bottom_shape_等
5 第三層:Pooling Layer
5.1 protobuff定義
layer {
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
5.2 Layer::SetUp
通過調(diào)用虛函數(shù)LayerSetUp
及Reshape
對(duì)以下成員變量進(jìn)行初始化
/**
* @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_;
};
6 第四層、第五層
基本同第二層爬骤、第三層
7 第六層:InnerProduct Layer
7.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"
}
}
}
7.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_為輸入大小,對(duì)于該層
Bottom Blob
形狀為(N, C, H, W)霞玄,N為batch_size
骤铃,K_=CHW(Caffe)基本類Blob,Layer坷剧,Net(一)惰爬,M_=N。其中只有C惫企、H撕瞧、W跟內(nèi)積相關(guān)
8 第七層:ReLU Layer
8.1 protobuff定義
layer {
name: "relu1"
type: "ReLU"
bottom: "ip1"
top: "ip1"
}
8.2 說明
ReLULayer主要是用來做計(jì)算的,其繼承關(guān)系如下狞尔,詳細(xì)參加[4]丛版、[5]
9 第八層:InnerProduct Layer
參見第7節(jié)
10 第九層:SoftmaxWithLoss Layer
10.1 protobuff定義
layer {
name: "loss"
type: "SoftmaxWithLoss"
bottom: "ip2"
bottom: "label"
top: "loss"
}
10.2 LayerRegistry::CreateLayer
10.3 Layer::SetUp
值得注意的是:
類
SoftmaxWithLossLayer
包含類SoftmaxLayer
的實(shí)例
shared_ptr<Layer<Dtype> > softmax_layer_
softmax_layer_
在LayerSetUp
中賦值。此函數(shù)內(nèi)調(diào)用Layer::SetLossWeights初始化了該層的Top Blob(loss)
-
兩個(gè)類間的關(guān)系如下圖:
這里寫圖片描述 成員變量prob_作為Softmaxlayer的top blob
bottom blob[0]作為softmaxlayer的bottom blob
所以經(jīng)過softmaxlayer計(jì)算之后沪么,得出64*10(每個(gè)樣本的每個(gè)類別上的概率)存放在prob_中
11 剩余的工作
至此硼婿,訓(xùn)練網(wǎng)絡(luò)基本創(chuàng)建完畢,接下來剩下的工作主要有:
- 反向檢查一次網(wǎng)絡(luò)禽车,看哪些blobs會(huì)對(duì)loss產(chǎn)生影響寇漫,在LeNet5中,前面的9層均有影響
- 初始化權(quán)值共享
[1].http://caffe.berkeleyvision.org/doxygen/classcaffe_1_1BasePrefetchingDataLayer.html
[2].http://caffe.berkeleyvision.org/tutorial/net_layer_blob.html Implementation Details
[3].http://caffe.berkeleyvision.org/doxygen/classcaffe_1_1ConvolutionLayer.html
[4].http://caffe.berkeleyvision.org/doxygen/classcaffe_1_1ReLULayer.html
[5].http://caffe.berkeleyvision.org/tutorial/layers.html ReLU / Rectified-Linear and Leaky-ReLU