很多初次閱讀caffe原始碼的同學可能不知道其中的layer具體是如何被定義的。假如我自己寫了個新的***layer,caffe是怎麼知道它的存在的呢?怎麼呼叫它的建構函式的呢?caffe為了管理各種各樣的layer,實現了叫做「工廠模式」的設計方法。它的長處是,我們在新增自己的layer時不需要修改caffe的核心**;但缺點也很明顯,就是使用了許多高階的c++語言特性,**不太讓人看得懂,尤其是我這種c++基礎不太好的。本著學習的目的,也為了便於其他新手更快地入門,我簡短地總結一下caffe中layer的定義過程。
0. 關於「工廠模式」的概述
什麼是工廠模式呢?
很簡單,假如有乙個基類product,兩個子類car,plane,都繼承product。現在需要乙個函式,要求返回product的例項,同時要求:傳入"car"引數時返回car類物件,傳入"plane"時返回plane物件。很簡單,用if就能搞定:
class product{};
class car:product{};
class plane:product{};
product* make(string what)
這就是乙個最簡單的工廠模式。這個函式能在執行時根據變數返回不同子類的物件。
這就是為什麼caffe要用到工廠模式。我們知道caffe裡面有很多種layer,有卷積的,有全連線的,還有管資料和算loss的。這些***layer都是layer類的子類。我們知道caffe要在執行時根據prototxt去決定建立什麼型別的layer,自然要用到這種模式。那麼問題來了,為什麼不直接寫一串if,而非要寫一堆讓人看不懂的巨集呢?原因在於,假如我們要在上面的例子裡加乙個新的子類boat,那就必然要修改if的部分。這等於是為了加乙個新類而修改了核心**,caffe不希望發生這種事。那麼,caffe的工廠模式是怎麼實現的呢?
1. 從register_layer_class(***)入手
在每個***layer的cpp中,應該都能找到乙個register_layer_class的巨集。正是這個巨集定義使caffe能找到並例項化我們自己的類。它定義在layer_factory.hpp中,是這樣的:
#define register_layer_class(type) \
template \
shared_ptr> creator_##type##layer(const layerparameter& param) \
\register_layer_creator(type, creator_##type##layer)
暈了,這些##是什麼鬼,c++還可以這麼寫麼。其實這是巨集的一些語法。'##'代表的是把巨集引數和##前後的內容連起來。具體到這裡,假如我自己定義了myawesomelayer,
最後就會加上register_layer_class(myawesome)。這就等價於寫了以下**:
template shared_ptr> creator_myawesomelayer(const layerparameter& param)
register_layer_creator(myawesome, creator_myawesomelayer)
看起來是定義了乙個函式,這個函式接受param(關於層的一些引數),new乙個我們的myawesomelayer的物件,然後以layer的指標返回回來。是不是有點意思了呢?
這個函式之後又用了乙個巨集,我們看看它幹了什麼:
#define register_layer_creator(type, creator) \
static layerregistererg_creator_f_##type(#type, creator); \
static layerregistererg_creator_d_##type(#type, creator); \
暈了,這乙個#又是什麼意思啊。這其實也是巨集的語法,它代表把巨集的引數變成字串。具體到這裡,就等於寫了以下**:
static layerregistererg_creator_f_myawesome("myawesome", creator_myawesomelayer);
static layerregistererg_creator_d_myawesome("myawesome", creator_myawesomelayer);
這裡建立了兩個layerregisterer類的靜態物件,給建構函式傳了兩個引數。乙個是字串,是我們的layer的名字,另乙個是剛剛建立的creator_myawesomelayer方法的函式指標。建立這兩個物件是在做什麼呢?看它們的定義:
template class layerregisterer
};
其實建立它們的過程不過是呼叫了addcreator這個函式,把傳入的字串和函式指標又傳給了它。
// adds a creator.
static void addcreator(const string& type, creator creator)
這裡面是通過registry方法拿到了乙個registry指標,關鍵步驟是:registry["myawesome"] = creator。這看著像是乙個類似python裡的字典的東西,也就是個雜湊表,鍵是layer的型別,值是乙個能返回這個layer物件的函式。有點意思!繼續找registry的定義:
typedef shared_ptr> (*creator)(const layerparameter&);
typedef std::mapcreatorregistry;
static creatorregistry& registry()
這一段裡面有個大坑。假如要實現工廠模式,剛剛提到的那個「字典」不應該是全域性的麼,我們不應該一直往裡加東西麼?怎麼這裡每次都返回乙個新的呢?這裡一定要注意那個static。static creatorregistry* g_registry_ = new creatorregistry() 其實只在最初執行了一次,g_registry_放在靜態記憶體裡,函式結束了是不會消失的!後面每次呼叫並不會執行new,返回的都是同乙個東西的指標!
另外,typedef shared_ptr> (*creator)(const layerparameter&)這句話也有點莫名其妙,這個typedef是啥意思。其實,這和typedef int myint是類似的。後者是給int類起了個別名叫myint,然後就能這樣寫:myint a=10; 前者其實是給一類函式指標起了個別名,這類函式指標都是以layerparameter&做引數,返回shared_ptr<>。 假如這樣寫: creator pcreator;, 那pcreator就是乙個這樣的函式指標了。
2. caffe如何呼叫我們的層
看了1之後,不難發現,原來caffe的工廠模式,不過是往乙個雜湊表裡面放了一些(型別名-->建立該類物件的函式)這樣的對映罷了!上面所有這些操作都幾乎是在編譯時以及程式執行的最最最開始進行的,所以caffe在真正執行的時候就能找到我們自己定義的層了。來看看是怎麼找到的吧。看net.cpp的96行左右的位置:
layers_.push_back(layerregistry::createlayer(layer_param));
這句話位於乙個for迴圈裡面,就是根據讀到的prototxt,乙個乙個地建立layer物件了。來看看createlayer方法:
static shared_ptr> createlayer(const layerparameter& param)
這不正是呼叫了我們一開始放進字典裡面的函式,以此根據layer的型別名稱字串建立指定的layer物件的麼?
3. 其他
layer的cpp中還有乙個巨集:
instantiate_class(myawesomelayer);
這是在幹什麼呢?通過檢視定義,發現這等價於寫了這些**:
char ginstantiationguardmyawesomelayer;
//explicit instantiation of template class
template class myawesomelayer;
template class myawesomelayer;
定義了乙個字元,然後又是兩句莫名其妙的**。其實,這兩個是顯式地初始化兩個myawesomelayer類,乙個是float的,乙個是double的。template其實可以看做"類的類",類需要例項化才能得到物件,同樣模板也要這樣例項化一下才能得到對於不同資料型別的類定義。
大神寫**喜歡用一些高階的玩意,然後菜雞就看不太懂;不過其實就那麼點東西,學一學還是能學會的。希望寫下來能幫到和我一樣菜的菜雞。菜雞不一定能變大神,但努努力還是能變成不那麼菜的菜雞的。共勉!
caffe中layer層介紹
1.初識caffe 1.1.caffe相對與其他dl框架的優點和缺點 優點 缺點 對於某些研究方向來說的人並不適合。這個需要對caffe的結構有一定了解,後面提到 1.2.caffe 層次。回答裡面有人說熟悉blob,layer,net,solver這樣的幾大類,我比較贊同。我基本是從這個順序開始學...
caffe中增加自己的layer
假設新增加的層命名為 new 1.在src proto的layerparameter 的 layertype下 加 new 數字 2.在src layer factory.cpp中,加 case layerparameter layertype new return new newlayer par...
在caffe中新增新的Layer
參考github上面的答案 here s roughly the process i follow.大致過程翻譯 然後在對應的hpp標頭檔案中新增該層的類的定義。包括內聯實現type和 blob 方法指定blob的數字,如果你只是實現cpu部分的 則忽略 gpu的宣告 2.實現你要新增的層 應該指的...