上手快:模型與相應優化都是以文本形式而非代碼形式給出。 Caffe給出了模型的定義、最優化設定以及預訓練的權重,友善立即上手。
速度快:能夠運作最棒的模型與海量的資料。 Caffe與cuDNN結合使用,測試AlexNet模型,在K40上處理每張圖檔隻需要1.17ms.
子產品化:友善擴充到新的任務和設定上。 可以使用Caffe提供的各層類型來定義自己的模型。
開放性:公開的代碼和參考模型用于再現。
社群好:可以通過BSD-2參與開發與讨論。
Caffe中的網絡都是有向無環圖的集合,可以直接定義:資料以blobs的形式在層間流動。
Caffe層的定義由2部分組成:層屬性與層參數,例如
Caffe架構主要有五個元件,Blob,Solver,Net,Layer,Proto,其結構圖如下圖1所示。Solver負責深度網絡的訓練,每個Solver中包含一個訓練網絡對象和一個測試網絡對象。每個網絡則由若幹個Layer構成。每個Layer的輸入和輸出Feature map表示為Input Blob和Output Blob。Blob是Caffe實際存儲資料的結構,是一個不定維的矩陣,在Caffe中一般用來表示一個拉直的四維矩陣,四個次元分别對應Batch Size(N),Feature Map的通道數(C),Feature Map高度(H)和寬度(W)。Proto則基于Google的Protobuf開源項目,是一種類似XML的資料交換格式,使用者隻需要按格式定義對象的資料成員,可以在多種語言中實作對象的序列化與反序列化,在Caffe中用于網絡模型的結構定義、存儲和讀取。
下面介紹Caffe中的基本資料存儲類Blob。Blob常用的存儲格式:
Blob使用SyncedMemory類進行資料存儲并同步CPU和GPU上的資料,資料成員 data_指向實際存儲資料的記憶體或顯存塊,shape_存儲了目前blob的次元資訊,diff_這個儲存了反向傳遞時候的梯度資訊。在Blob中其實不是隻有num,channel,height,width這種四維形式,它是一個不定次元的資料結構,将資料展開存儲,而次元單獨存在一個vector<int> 類型的shape_變量中,這樣每個次元都可以任意變化。來一起看看Blob的關鍵函數,data_at這個函數可以讀取的存儲在此類中的資料,diff_at可以用來讀取反向傳回來的誤差。順便給個提示,盡量使用data_at(const vector<int>& index)來查找資料。Reshape函數可以修改blob的存儲大小,count用來傳回存儲資料的數量。BlobProto類負責了将Blob資料進行打包序列化到Caffe的模型中。
Blob可根據CPU主機到GPU裝置的同步需要,屏蔽CPU/GPU混合運算在計算上的開銷。主機和裝置上的記憶體按需求配置設定,已提高記憶體的使用率。使用GPU時,Caffe中CPU代碼先從磁盤中加載資料到blob,同時請求先配置設定一個GPU裝置核以使用GPU進行計算,再将計算好的blob資料送入到下一層,隻要所有的layers均有GPU實作,這種情況下所有的中間資料都會保留在GPU上。
這裡一個執行個體,用以确定blob何時回複制資料:
Solver通過協調Net的前向計算和反向梯度計算來進行參數更新,進而達到loss的目的。目前Caffe的模型學習分為兩個部分:由Solver進行優化、更新參數;由Net計算出loss和gradient。solver具體的工作:1、用于優化過程的記錄,建立訓練網絡和測試網絡。2、用過forward和backward過程來疊代優化更新參數。3、周期性的用測試網絡評估模型性能。4、優化過程中記錄模型和solver狀态的快照。一些參數的配置都在solver.prototxt格式的檔案中:
接下來我們看看Solver這個優化對象在Caffe中是如何實作的。SolverRegistry這個類就是我們看到的上面的factory類,負責給我們一個優化算法的産品,外部隻需要把資料和網絡結構定義好,它就可以自己優化了。Solver* CreateSolver(const SolverParameter& param)這個函數就是工廠模式下的CreateProduct的操作
接下裡我們看看Solver中的關鍵函數。Solver中Solve函數的流程圖如下:

Solver類中Step函數流程圖:
Solver中關鍵的就是調用Sovle函數和Step函數的流程,你隻需要對照Solver類中兩個函數的具體實作,看懂上面兩個流程圖就可以了解Caffe訓練執行的過程了。
Net是由一些列層組成的有向無環圖DAG,一個典型的Net開始于data layer ----> 從磁盤中加載資料----> 終止于loss layer。(計算和重構目标函數。)這個是我們使用Proto建立出來的深度網絡對象,這個類負責了深度網絡的前向和反向傳遞。以下是Net類的初始化方法NetInit函數調用流程:
類中的關鍵函數簡單剖析
1).ForwardBackward:按順序調用了Forward和Backward。
2).ForwardFromTo(int start, int end):執行從start層到end層的前向傳遞,采用簡單的for循環調用。,forward隻要計算損失loss
3).BackwardFromTo(int start, int end):和前面的ForwardFromTo函數類似,調用從start層到end層的反向傳遞。backward主要根據loss來計算梯度,caffe通過自動求導并反向組合每一層的梯度來計算整個網絡的梯度。
4).ToProto函數完成網絡的序列化到檔案,循環調用了每個層的ToProto函數。
Net::Init()進行模型的初始化。初始化主要實作兩個操作:建立blobs和layers以搭建整個DAG網絡,并且調用layers的SetUp()函數。
Layer是Net的基本組成單元,例如一個卷積層或一個Pooling層。本小節将介紹Layer類的實作。
NeuronLayer類:定義于neuron_layers.hpp中,其派生類主要是元素級别的運算(比如Dropout運算,激活函數ReLu,Sigmoid等),運算均為同址計算(in-place computation,傳回值覆寫原值而占用新的記憶體)。
LossLayer類:定義于loss_layers.hpp中,其派生類會産生loss,隻有這些層能夠産生loss。
資料層:定義于data_layer.hpp中,作為網絡的最底層,主要實作資料格式的轉換。
特征表達層(我自己分的類):定義于vision_layers.hpp,實作特征表達功能,更具體地說包含卷積操作,Pooling操作,他們基本都會産生新的記憶體占用(Pooling相對較小)。
網絡連接配接層和激活函數(我自己分的類):定義于common_layers.hpp,Caffe提供了單個層與多個層的連接配接,并在這個頭檔案中聲明。這裡還包括了常用的全連接配接層InnerProductLayer類。
layer主要定義了三種運算,setup,forward,backward
在Layer内部,資料主要有兩種傳遞方式,正向傳導(Forward)和反向傳導(Backward)。Forward和Backward有CPU和GPU(部分有)兩種實作。Caffe中所有的Layer都要用這兩種方法傳遞資料。Layer類派生出來的層類通過實作這兩個虛函數,産生了各式各樣功能的層類。Forward是從根據bottom計算top的過程,Backward則相反(根據top計算bottom)。注意這裡為什麼用了一個包含Blob的容器(vector),對于大多數Layer來說輸入和輸出都各連接配接隻有一個Layer,然而對于某些Layer存在一對多的情況,比如LossLayer和某些連接配接層。在網路結構定義檔案(*.proto)中每一層的參數bottom和top數目就決定了vector中元素數目。
當神已無能為力,那便是魔渡衆生