【caffe解读】 caffe从数学公式到代码实现1-导论
文章首發(fā)于微信公眾號(hào)《與有三學(xué)AI》
[caffe解讀] caffe從數(shù)學(xué)公式到代碼實(shí)現(xiàn)1-導(dǎo)論
真的很多年沒有認(rèn)真寫csdn博客了,我回來(lái)了
今天開一個(gè)新板塊,目標(biāo)是死磕現(xiàn)有的幾大機(jī)器學(xué)習(xí)框架的代碼,給想入門的小白們一些幫助。
作為一個(gè)在圖像行業(yè)戰(zhàn)斗了幾年的程序員,深知入門一個(gè)框架,和真的能用好一個(gè)框架是有很大的區(qū)別的,而要想走的更遠(yuǎn),開源框架的底層代碼一定要去詳讀。
同時(shí),很多的坑也是在代碼中,不仔細(xì)讀是不知道的。所以,我會(huì)來(lái)跟大家系統(tǒng)細(xì)致地學(xué)習(xí)一些主流的機(jī)器學(xué)習(xí)框架。
今天這個(gè)是caffe,大概會(huì)有7篇左右的文章,一般讀caffe代碼思路是按照caffe的層級(jí)結(jié)構(gòu)來(lái),blob到layer到net各自分層來(lái)讀,但我想提供一個(gè)另外的思路,從數(shù)學(xué)公式到代碼實(shí)現(xiàn)。
從每一個(gè)文件背后具體的數(shù)學(xué)含義來(lái)讀,這對(duì)于我們非數(shù)學(xué)系或者數(shù)學(xué)基礎(chǔ)不是很好的工程人員來(lái)說(shuō),是比較適合的。
采取的形式就是,layer?definition,caffe?layer,caffe?testlayer的格式,舉個(gè)例子來(lái)說(shuō),比如softmax,那我就打算從softmax的數(shù)學(xué)定義,caffe?softmax層的實(shí)現(xiàn),caffesoftmax?test?layer的實(shí)現(xiàn)。重要代碼要加上test?layer,因?yàn)楫?dāng)我們自己實(shí)現(xiàn)某些類時(shí),往往需要梯度反向求導(dǎo),這時(shí)候最好自己寫test來(lái)驗(yàn)證自己的代碼是否正確。
好了,下面就開始吧。當(dāng)然,現(xiàn)在這是第一篇,所以我們還是不可避免先打下基礎(chǔ),先要閱讀下面的內(nèi)容,對(duì)caffe的代碼有基本的了解。這是include/caffe根目錄下面的代碼list。
blob.hpp
caffe.hpp
common.hpp
data_transformer.hpp
filler.hpp
internal_thread.hpp
layer.hpp
layer_factory.hpp
net.hpp
parallel.hpp
sgd_solvers.hpp
solver.hpp
solver_factory.hpp
syncedmem.hpp
一個(gè)一個(gè)來(lái)。
?
01 blob.hpp&cpp
blob是caffe中的基礎(chǔ)數(shù)據(jù)單元,一個(gè)blob是一個(gè)四維張量,(N,C,H,W),N是batch?size大小,C是channel,H,W分別是圖像寬高,由于caffe擅長(zhǎng)于做圖像,所以這個(gè)定義天然適合圖像。故一個(gè)256*256的rgb圖像,blob?size是(1,3,256,256)。
blob.hpp,需要注意的就是下面的變量和函數(shù)
其中data_存儲(chǔ)數(shù)據(jù),diff_存儲(chǔ)梯度,shape_分別是blob_的尺度,count_是所有數(shù)據(jù)數(shù)目,即N*C*H*W。
以后要訪問(wèn)這些數(shù)據(jù),就會(huì)用到下面的函數(shù),其中cpu_data是只讀,mutable_cpu_data是可寫,gpu類似。
上面還有一個(gè)疑問(wèn),那就是初次見到SyncedMemory類會(huì)不知道它是做什么的,它主要負(fù)責(zé)在GPU或者CPU上分配內(nèi)存以及保持?jǐn)?shù)據(jù)的同步作用。
可參考下面資料:
http://blog.csdn.net/xizero00/article/details/51001206
http://www.cnblogs.com/korbin/p/5606770.html
由于展開是另一個(gè)篇幅,因此我們不過(guò)多停留在此,知道blob是通過(guò)這樣的方式存取即可。
?
02 caffe.hpp&cpp等
把這幾個(gè)放這里,一是這里很多是關(guān)于gpu和內(nèi)存等較為底層的編程的,看起來(lái)比較費(fèi)勁,一般也不需要去修改,大家會(huì)用即可。重點(diǎn)會(huì)講一下factory。
caffe.hpp頭文件包含其他基礎(chǔ)hpp。
internal_thread.hpp,與線程有關(guān)的變量函數(shù)。
parallel.hpp,與并行有關(guān)的變量函數(shù)。
syncedmem.hpp,內(nèi)存分配和Caffe的底層數(shù)據(jù)的切換
solver_factory.hpp,layer_factory.hpp,顧名思義,分別是caffe?solver的工廠類模板定義和普通layer的模板定義。
舉例拿sovler來(lái)多說(shuō)幾句,solver_factory.hpp,其中solver指的是優(yōu)化方法,由于caffe優(yōu)化采用的就是梯度下降的方法,包括SGD,NesterovSolver,RMSPropSolver,AdamSolver等通通都定義在sgd_solvers.hpp中。
工廠設(shè)計(jì)模型,簡(jiǎn)單了解如下
http://developer.51cto.com/art/201107/277728.htm
http://alanse7en.github.io/caffedai-ma-jie-xi-4/
深入了解需要自己去看,從代碼的角度來(lái)看就是解決重復(fù)造輪子的問(wèn)題,減少重復(fù)代碼,在caffe的面試中經(jīng)常會(huì)問(wèn)到噢。
看下它的代碼,重要變量?jī)蓚€(gè)
typedef?Solver<Dtype>*?(*Creator)(const
SolverParameter&);
typedef?std::map<string,?Creator>
CreatorRegistry;
重要函數(shù)兩個(gè),
static?CreatorRegistry&?Registry()?{?staticCreatorRegistry*
g_registry_?=?new?CreatorRegistry();
??return?*g_registry_;
}
static?Solver<Dtype>*?CreateSolver(const
SolverParameter&?param)?{
??const?string&?type?=?param.type();
??CreatorRegistry&?registry?=?Registry();
??CHECK_EQ(registry.count(type),?1)?<<?"Unknownsolver?type:?"?<<?type
??????<<?"?(known?types:?"?<<?SolverTypeListString()<<?")";
??return?registry[type](param);
}
其中需要注意的是,SolverParameter是一個(gè)配置參數(shù)不說(shuō),CreatorRegistry就是我們以后自定義層需要知道的,需要知道registry是一個(gè)map,存儲(chǔ)的就是字符串以及對(duì)應(yīng)的以函數(shù)指針形式存儲(chǔ)的Creator類型的函數(shù),而注冊(cè)都會(huì)在cpp中進(jìn)行,以后詳解。
common.hpp,是一些與io有關(guān)的函數(shù)與變量,cpu與gpu模式設(shè)定變量Brew?mode_;函數(shù)set_mode,setDevice,以及與隨機(jī)數(shù)有關(guān)的函數(shù)變量shared_ptr<RNG>random_generator_;
?
03 datatransform.hpp&cpp
這是很重要的一個(gè)文件,當(dāng)我們自定義數(shù)據(jù)層的時(shí)候會(huì)用到,它的作用就是從磁盤中讀取數(shù)據(jù)塞進(jìn)caffe定義的變量?jī)?nèi)存中。從它的頭文件就可以看出,它依賴于blob,common,以及caffe.pb.h
#include?"caffe/blob.hpp"
#include?"caffe/common.hpp"
#include?"caffe/proto/caffe.pb.h"
caffe.pb.h中就包含了需要序列化的變量。
datatransform.hpp中的變量如下:
shared_ptr<Caffe::RNG>?rng_;
Phase?phase_;
Blob<Dtype>?data_mean_;
vector<Dtype>?mean_values_;
可見存儲(chǔ)了常見的mean_value。
datatransform.hpp中的的核心是重載的transform函數(shù),它可以按照不同的輸入來(lái)載入數(shù)據(jù),我們平常在caffe內(nèi)部做的隨機(jī)crop,flip等等操作都在這里完成,具體大家可以去研究源碼,靜下心看非常簡(jiǎn)單。
void?Transform(const?vector<Datum>?&datum_vector,Blob<Dtype>*?transformed_blob);
void?Transform(const?vector<cv::Mat>?&?mat_vector,Blob<Dtype>*
transformed_blob);
void?Transform(const?cv::Mat&?cv_img,Blob<Dtype>*?transformed_blob);
?
04 filler.hpp
它沒有對(duì)應(yīng)的cpp,所有實(shí)現(xiàn)都在hpp中,因?yàn)楹芎?jiǎn)單,它就是對(duì)權(quán)重初始化的,其中包含,constantfiller,Gaussianfiller,XavierFiller,MSRAFiller等等,相信大家都比較熟了。
?
05 solver.hpp
這就是caffe?迭代求解優(yōu)化的函數(shù)定義,其中重要變量loss就在這里,這就是訓(xùn)練caffe時(shí)顯示出的loss的來(lái)源
vector<Dtype>?losses_;
Dtype?smoothed_loss_;
SolverParameter?param_;
迭代優(yōu)化的函數(shù),
virtual?void?Solve(const?char*?resume_file?=?NULL);
inline?void?Solve(const?string?resume_file)?{Solve(resume_file.c_str());?}
void?Step(int?iters);
?
06 layer.hpp
這就是一個(gè)層的定義了,想必大家很有興趣,那具體都有什么呢?
我們首先看變量,
LayerParameter?layer_param_;
vector<Dtype>?loss_;
然后看重要函數(shù)
LayerSetUp,用于layer初始化,一般是定義一些shape,初始化一些變量。
virtual?void?LayerSetUp(constvector<Blob<Dtype>*>&?bottom,?constvector<Blob<Dtype>*>&
Forward,Backward的cpu和gpu版本,除了數(shù)據(jù)層等少量層外始終成對(duì)存在的前向和反向函數(shù),forward是基于bottom計(jì)算top,backward則是基于top計(jì)算bottom,很好理解。
virtual?void?Forward_cpu(constvector<Blob<Dtype>*>&?bottom,?constvector<Blob<Dtype>*>&?top)?=?0;
virtual?void?Forward_gpu(constvector<Blob<Dtype>*>&?bottom,?constvector<Blob<Dtype>*>&?top)?{
??Fackward_cpu(bottom,top);
}
virtual?void?Backward_cpu(constvector<Blob<Dtype>*>&?top,
const?vector<bool>&?propagate_down,?constvector<Blob<Dtype>*>&?bottom)?=?0;
virtual?void?Backward_gpu(constvector<Blob<Dtype>*>&?top,?const?vector<bool>&propagate_down,?const?vector<Blob<Dtype>*>&bottom)?{
??Backward_cpu(top,?propagate_down,?bottom);
}
?
07 net.hpp
這是最大的一個(gè)hpp了,也是最高層的,就是整個(gè)網(wǎng)絡(luò)的定義。
看看重要變量:
vector<shared_ptr<Layer<Dtype>?>?>?layers_;
vector<string>?layer_names_;
layers_就是所有層,layer_names_;存儲(chǔ)了名字,以后在我們inference的時(shí)候會(huì)需要經(jīng)常用到。
vector<float>?params_lr_;
vector<bool>?has_params_lr_;
上面是每一層學(xué)習(xí)率的參數(shù),在我們想要固定某些層不讓其學(xué)習(xí),或者調(diào)整不同層的學(xué)習(xí)率時(shí),會(huì)非常重要。其實(shí)還有很多重要變量如,
vector<Dtype>?blob_loss_weights_;
Phase?phase_;
都是經(jīng)常接觸的,不一一描述了大家自己看代碼。
下面是一個(gè)重載的重要函數(shù),
void?CopyTrainedLayersFrom(const
NetParameter&?param);
void?CopyTrainedLayersFrom(const?string
trained_filename);
void?CopyTrainedLayersFromBinaryProto(conststring
trained_filename);
void?CopyTrainedLayersFromHDF5(const?string
trained_filename);
它是重要的初始化網(wǎng)絡(luò)的方法,可以實(shí)現(xiàn)不同形式輸入的初始化,在inference時(shí)會(huì)經(jīng)常使用的。
?
好了,第一次基礎(chǔ)就這么多,并沒有非常細(xì)致的講述而只是對(duì)重要內(nèi)容進(jìn)行介紹,在開始下面的文章之前,一定要熟讀上面的這些hpp和對(duì)應(yīng)cpp文件,對(duì)它有什么是什么,熟練于胸。
?
同時(shí),在我的知乎專欄也會(huì)開始同步更新這個(gè)模塊,歡迎來(lái)交流
https://zhuanlan.zhihu.com/c_151876233
注:部分圖片來(lái)自網(wǎng)絡(luò)
—END—
打一個(gè)小廣告,我在gitchat開設(shè)了一些課程和chat,歡迎交流。
?
感謝各位看官的耐心閱讀,不足之處希望多多指教。后續(xù)內(nèi)容將會(huì)不定期奉上,歡迎大家關(guān)注有三公眾號(hào) 有三AI!
?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的【caffe解读】 caffe从数学公式到代码实现1-导论的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【行业进展】谷歌4大AI黑科技部门,你可
- 下一篇: 【caffe解读】 caffe从数学公式