caffe源码阅读(1)_整体框架和简介(摘录)
原文鏈接:https://www.zhihu.com/question/27982282
1.Caffe代碼層次。
回答里面有人說熟悉Blob,Layer,Net,Solver這樣的幾大類,我比較贊同。我基本是從這個順序開始學習的,這四個類復雜性從低到高,貫穿了整個Caffe。把他們分為三個層次介紹。
- Blob:作為數據傳輸的媒介,無論是網絡權重參數,還是輸入數據,都是轉化為Blob數據結構來存儲
- Layer:作為網絡的基礎單元,神經網絡中層與層間的數據節點、前后傳遞都在該數據結構中被實現,層類種類豐富,比如常用的卷積層、全連接層、pooling層等等,大大地增加了網絡的多樣性
- Net:作為網絡的整體骨架,決定了網絡中的層次數目以及各個層的類別等信息
- Solver:作為網絡的求解策略,涉及到求解優化問題的策略選擇以及參數確定方面,修改這個模塊的話一般都會是研究DL的優化求解的方向。
1.1. Blob的類型描述
Caffe內部采用的數據類型主要是對protocol buffer所定義的數據結構的繼承,因此可以在盡可能小的內存占用下獲得很高的效率,雖然追求性能的同時Caffe也會犧牲了一些代碼可讀性。
直觀來說,可以把Blob看成一個有4維的結構體(包含數據和梯度),而實際上,它們只是一維的指針而已,其4維結構通過shape屬性得以計算出來。
1.2. Blob的重要成員函數和變量
shared_ptr<SyncedMemory> data_ //數據 shared_ptr<SyncedMemory> diff_ //梯度 void Blob<Dtype>::Reshape(const int num, const int channels, const int height, const int width) 重新修改Blob的形狀(4維),并根據形狀來申請動態內存存儲數據和梯度。
inline int count(int start_axis, int end_axis) const 計算Blob所需要的基本數據單元的數量。 在更高一級的Layer中Blob用下面的形式表示學習到的參數:
vector<shared_ptr<Blob<Dtype> > > blobs_; 這里使用的是一個Blob的容器是因為某些Layer包含多組學習參數,比如多個卷積核的卷積層。
以及Layer所傳遞的數據形式,后面還會涉及到這里:
vector<Blob<Dtype>*> ⊥ vector<Blob<Dtype>*> *top
2.2. Layer:
2.2.1. 5大Layer派生類型
Caffe十分強調網絡的層次性,也就是說卷積操作,非線性變換(ReLU等),Pooling,權值連接等全部都由某一種Layer來表示。具體來說分為5大類Layer
- NeuronLayer類 定義于neuron_layers.hpp中,其派生類主要是元素級別的運算(比如Dropout運算,激活函數ReLu,Sigmoid等),運算均為同址計算(in-place computation,返回值覆蓋原值而占用新的內存)。
- LossLayer類 定義于loss_layers.hpp中,其派生類會產生loss,只有這些層能夠產生loss。
- 數據層 定義于data_layer.hpp中,作為網絡的最底層,主要實現數據格式的轉換。
- 特征表達層(我自己分的類)定義于vision_layers.hpp(為什么叫vision這個名字,我目前還不清楚),實現特征表達功能,更具體地說包含卷積操作,Pooling操作,他們基本都會產生新的內存占用(Pooling相對較小)。
- 網絡連接層和激活函數(我自己分的類)定義于common_layers.hpp,Caffe提供了單個層與多個層的連接,并在這個頭文件中聲明。這里還包括了常用的全連接層InnerProductLayer類。
在Layer內部,數據主要有兩種傳遞方式,正向傳導(Forward)和反向傳導(Backward)。Forward和Backward有CPU和GPU(部分有)兩種實現。Caffe中所有的Layer都要用這兩種方法傳遞數據。
virtual void Forward(const vector<Blob<Dtype>*> &bottom, vector<Blob<Dtype>*> *top) = 0; virtual void Backward(const vector<Blob<Dtype>*> &top, const vector<bool> &propagate_down, vector<Blob<Dtype>*> *bottom) = 0; Layer類派生出來的層類通過這實現這兩個虛函數,產生了各式各樣功能的層類。Forward是從根據bottom計算top的過程,Backward則相反(根據top計算bottom)。注意這里為什么用了一個包含Blob的容器(vector),對于大多數Layer來說輸入和輸出都各連接只有一個Layer,然而對于某些Layer存在一對多的情況,比如LossLayer和某些連接層。在網路結構定義文件(*.proto)中每一層的參數bottom和top數目就決定了vector中元素數目。
layers {bottom: "decode1neuron" // 該層底下連接的第一個Layer bottom: "flatdata" // 該層底下連接的第二個Layer top: "l2_error" // 該層頂上連接的一個Layer name: "loss" // 該層的名字 type: EUCLIDEAN_LOSS // 該層的類型 loss_weight: 0 } 2.2.3. Layer的重要成員變量
loss
vector<Dtype> loss_; 每一層又有一個loss_值,只不多大多數Layer都是0,只有LossLayer才可能產生非0的loss_。計算loss是會把所有層的loss_相加。
learnable parameters
vector<shared_ptr<Blob<Dtype> > > blobs_; 前面提到過的,Layer學習到的參數。
2.3. Net:
Net用容器的形式將多個Layer有序地放在一起,其自身實現的功能主要是對逐層Layer進行初始化,以及提供Update( )的接口(更新網絡參數),本身不能對參數進行有效地學習過程。
vector<shared_ptr<Layer<Dtype> > > layers_; 同樣Net也有它自己的
vector<Blob<Dtype>*>& Forward(const vector<Blob<Dtype>* > & bottom, Dtype* loss = NULL); void Net<Dtype>::Backward(); 他們是對整個網絡的前向和方向傳導,各調用一次就可以計算出網絡的loss了。
2.4. Solver
這個類中包含一個Net的指針,主要是實現了訓練模型參數所采用的優化算法,它所派生的類就可以對整個網絡進行訓練了。
shared_ptr<Net<Dtype> > net_; 不同的模型訓練方法通過重載函數ComputeUpdateValue( )實現計算update參數的核心功能
virtual void ComputeUpdateValue() = 0; 最后當進行整個網絡訓練過程(也就是你運行Caffe訓練某個模型)的時候,實際上是在運行caffe.cpp中的train( )函數,而這個函數實際上是實例化一個Solver對象,初始化后調用了Solver中的Solve( )方法。而這個Solve( )函數主要就是在迭代運行下面這兩個函數,就是剛才介紹的哪幾個函數。
ComputeUpdateValue(); net_->Update();
==============================================================
至此,從底層到頂層對Caffe的主要結構都應該有了大致的概念。為了集中重點介紹Caffe的代碼結構,文中略去了大量Caffe相關的實現細節和技巧,比如Layer和Net的參數如何初始化,proto文件的定義,基于cblas的卷積等操作的實現(cblas實現卷積這一點我的個人主頁GanYuFei中的《Caffe學習筆記5-BLAS與boost::thread加速》有介紹)等等就不一一列舉了。
整體來看Layer部分代碼最多,也反映出Caffe比較重視豐富網絡單元的類型,然而由于Caffe的代碼結構高度層次化,使得某些研究以及應用(比如研究類似非逐層連接的神經網絡這種復雜的網絡連接方式)難以在該平臺實現。這也就是一開始說的一個不足。
另外,Caffe基本數據單元都用Blob,使得數據在內存中的存儲變得十分高效,緊湊,從而提升了整體訓練能力,而同時帶來的問題是我們看見的一些可讀性上的不便,比如forward的參數也是直接用Blob而不是設計一個新類以增強可讀性。所以說性能的提升是以可讀性為代價的。
最后一點也是最重要的一點,我從Caffe學到了很多。第一次看的C++項目就看到這么好的代碼,實在是受益匪淺,在這里也感謝作者賈揚清等人的貢獻。
這里再補充幾點: 1.使用Linux 的grep指令來快速追蹤某個關鍵字:在caffe根目錄下輸入: grep -n -H -R "REGISTER_LAYER_CREATOR"(舉例),其中-n 顯示行號 -H顯示文件名 -R遞歸查找每個子目錄
? ??
轉載于:https://www.cnblogs.com/zf-blog/p/6427624.html
總結
以上是生活随笔為你收集整理的caffe源码阅读(1)_整体框架和简介(摘录)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入浅出MySql索引
- 下一篇: java定时器_拾遗Timer定时器