新板塊說明 今天開一個(gè)新板塊,,目標(biāo)是死磕現(xiàn)有的幾大機(jī)器學(xué)習(xí)框架的代碼,給想入門的小白們一些幫助,。 作為一個(gè)在圖像行業(yè)戰(zhàn)斗了幾年的程序員,深知入門一個(gè)框架,,和真的能用好一個(gè)框架是有很大的區(qū)別的,,而要想走的更遠(yuǎn),開源框架的底層代碼一定要去詳讀,。 同時(shí),,很多的坑也是在代碼中,不仔細(xì)讀是不知道的,。所以,,我會(huì)單開子菜單,來跟大家系統(tǒng)細(xì)致地學(xué)習(xí)一些主流的機(jī)器學(xué)習(xí)框架,。 今天這個(gè)是caffe,,大概會(huì)有7篇左右的文章,一般讀caffe代碼思路是按照caffe的層級(jí)結(jié)構(gòu)來,,blob到layer到net各自分層來讀,但我想提供一個(gè)另外的思路,,從數(shù)學(xué)公式到代碼實(shí)現(xiàn),。 從每一個(gè)文件背后具體的數(shù)學(xué)含義來讀,這對(duì)于我們非數(shù)學(xué)系或者數(shù)學(xué)基礎(chǔ)不是很好的工程人員來說,,是比較適合的,。 采取的形式就是,layer definition,,caffe layer,,caffe test layer的格式,舉個(gè)例子來說,,比如softmax,,那我就打算從softmax的數(shù)學(xué)定義,caffe softmax層的實(shí)現(xiàn),caffe softmax test layer的實(shí)現(xiàn),。重要代碼要加上test layer,,因?yàn)楫?dāng)我們自己實(shí)現(xiàn)某些類時(shí),往往需要梯度反向求導(dǎo),,這時(shí)候最好自己寫test來驗(yàn)證自己的代碼是否正確,。 好了,下面就開始吧,。當(dāng)然,,現(xiàn)在這是第一篇,所以我們還是不可避免先打下基礎(chǔ),,先要閱讀下面的內(nèi)容,,對(duì)caffe的代碼有基本的了解。這是include/caffe根目錄下面的代碼list,。 blob.hpp 一個(gè)一個(gè)來,。 01 1 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,。 以后要訪問這些數(shù)據(jù),,就會(huì)用到下面的函數(shù),其中cpu_data是只讀,,mutable_cpu_data是可寫,,gpu類似,。 上面還有一個(gè)疑問,那就是初次見到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è)篇幅,因此我們不過多停留在此,,知道blob是通過這樣的方式存取即可,。 02 caffe.hpp&cpp等 把這幾個(gè)放這里,一是這里很多是關(guān)于gpu和內(nè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來多說幾句,,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./caffedai-ma-jie-xi-4/ 深入了解需要自己去看,,從代碼的角度來看就是解決重復(fù)造輪子的問題,,減少重復(fù)代碼,在caffe的面試中經(jīng)常會(huì)問到噢,。 看下它的代碼,重要變量?jī)蓚€(gè) typedef Solver<Dtype>* (*Creator)(const typedef std::map<string, Creator> 重要函數(shù)兩個(gè),, static CreatorRegistry& Registry() { static CreatorRegistry* 其中需要注意的是,,SolverParameter是一個(gè)配置參數(shù)不說,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" caffe.pb.h中就包含了需要序列化的變量,。 datatransform.hpp中的變量如下: shared_ptr<Caffe::RNG> rng_; 可見存儲(chǔ)了常見的mean_value,。 datatransform.hpp中的的核心是重載的transform函數(shù),它可以按照不同的輸入來載入數(shù)據(jù),,我們平常在caffe內(nèi)部做的隨機(jī)crop,,flip等等操作都在這里完成,具體大家可以去研究源碼,,靜下心看非常簡(jiǎn)單,。 void Transform(const vector<Datum> & datum_vector,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的來源 vector<Dtype> losses_; 迭代優(yōu)化的函數(shù), virtual void Solve(const char* resume_file = NULL); 06 layer.hpp 這就是一個(gè)層的定義了,,想必大家很有興趣,,那具體都有什么呢? 我們首先看變量,, LayerParameter layer_param_; 然后看重要函數(shù) LayerSetUp,,用于layer初始化,一般是定義一些shape,,初始化一些變量,。 virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& Forward,,Backward的cpu和gpu版本,除了數(shù)據(jù)層等少量層外始終成對(duì)存在的前向和反向函數(shù),,forward是基于bottom計(jì)算top,,backward則是基于top計(jì)算bottom,很好理解,。 virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) = 0; 07 net.hpp 這是最大的一個(gè)hpp了,,也是最高層的,就是整個(gè)網(wǎng)絡(luò)的定義,。 看看重要變量: vector<shared_ptr<Layer<Dtype> > > layers_; layers_就是所有層,,layer_names_;存儲(chǔ)了名字,以后在我們inference的時(shí)候會(huì)需要經(jīng)常用到,。 vector<float> params_lr_; 上面是每一層學(xué)習(xí)率的參數(shù),,在我們想要固定某些層不讓其學(xué)習(xí),或者調(diào)整不同層的學(xué)習(xí)率時(shí),,會(huì)非常重要,。其實(shí)還有很多重要變量如, vector<Dtype> blob_loss_weights_; 都是經(jīng)常接觸的,,不一一描述了大家自己看代碼,。 下面是一個(gè)重載的重要函數(shù), void CopyTrainedLayersFrom(const 它是重要的初始化網(wǎng)絡(luò)的方法,,可以實(shí)現(xiàn)不同形式輸入的初始化,,在inference時(shí)會(huì)經(jīng)常使用的。 好了,,第一次基礎(chǔ)就這么多,,并沒有非常細(xì)致的講述而只是對(duì)重要內(nèi)容進(jìn)行介紹,在開始下面的文章之前,,一定要熟讀上面的這些hpp和對(duì)應(yīng)cpp文件,,對(duì)它有什么是什么,熟練于胸,。 |
|