Faster-RCNN四步交替法源碼閱讀筆記
Faster-rcnn四步交替訓練(alternative optimization)
Faster rcnn有兩種訓練方式,一種是四步交替訓練法,一種是end-to-end訓練法。主文件位於/tools/train_fast_rcnn_alt_opt.py。
第一步,訓練RPN,該網路用ImageNet預訓練的模型初始化,並端到端微調用於region proposal task;
第二步,利用第一步的RPN生成的region proposals,由Fast R-CNN訓練一個單獨的檢測網路,這個檢測網路同樣是由ImageNet預訓練的模型初始化的,這時候兩個網路還沒有共享卷積層;
第三步,用第二步的fast-rcnn網路模型初始化RPN再次進行訓練,但固定共享的卷積層,並且只微調RPN獨有的層,現在兩個網路共享卷積層了;
第四步,保持共享的卷積層固定,微調Fast R-CNN的fc層。這樣,兩個網路共享相同的卷積層,構成一個統一的網路。
源碼的實現分了很多步驟:
準備
- 初始化參數:採用Python的argparse,主要參數有--gpu , --net_name ,--weights,--cfg,--imdn,--set等(在cfg中只是修改了幾個參數,其他大部分參數在config.py中,涉及到訓練整個網路)
- cfg_from_file(args.cfg_file):這裡採用config.py裡面的cfg_from_file函數讀取cfg文件的參數,同時調用_merge_a_into_b函數把所有的參數整合
- faster rcnn是多進程, mp_queue是進程間用於通訊的數據結構
stage1 ,RPN, init from imagenet model
可以看到第一個步驟是用ImageNet的模型M0來Finetuning RPN網路得到模型M1。以訓練為例,這裡的args參數都在腳本 experiments/scrips/faster_rcnn_alt_opt.sh中找到。主要關注train_rpn函數。
對於train_rpn函數,主要分一下幾步:
1.在config參數的基礎上改動參數,以適合當前任務,主要有
這裡,關注proposal method 使用的是gt,後面會使用到gt_roidb函數.
2.初始化化caffe
3.準備roidb和imdb
主要涉及到的函數get_roidb
在get_roidb函數中調用factory中的get_imdb.根據__sets[name]中的key(一個lambda表達式)轉到pascol_voc類。
在這裡,並沒有讀入任何數據,只是建立了圖片的索引。
設置proposal方法,接上面,設置為gt,這裡只是設置了生成的方法,第一次調用發生在下一句,roidb = get_training_roidb(imdb) –> append_flipped_images()時的這行代碼:「boxes = self.roidb[i][『boxes』].copy()」,
其中get_training_roidb位於train.py,主要實現圖片的水平翻轉,並添加回去。實際是該函數調用了imdb. append_flipped_images也就是在這個函數,調用了pascal_voc.py中的gt_roidb,轉而調用了同一個文件中的_load_pascal_annotation,
該函數根據圖片的索引,到Annotations這個文件夾下去找相應的xml標註數據,然後載入所有的bounding box對象,xml的解析到此結束,接下來是roidb中的幾個類成員的賦值:
- boxes 一個二維數組,每一行存儲 xmin ymin xmax ymax
- gt _classes存儲了每個box所對應的類索引(類數組在初始化函數中聲明)
- gt _overlap是一個二維數組,共有num _classes(即類的個數)行,每一行對應的box的類索引處值為1,其餘皆為0,後來被轉成了稀疏矩陣
- seg _areas存儲著某個box的面積
- flipped 為false 代表該圖片還未被翻轉(後來在train.py里會將翻轉的圖片加進去,用該變數用於區分
在get_training_roidb函數中還調用了roidb中的prepare_roidb函數,這個函數就是用來準備imdb 的roidb,給roidb中的字典添加一些屬性,比如image(圖像的索引),width,height,通過前面的gt _overla屬性,得到max_classes和max_overlaps.
至此,Prepare_roidb之後,最後將這些成員變數組裝成roidb返回。
4.設置輸出路徑,output_dir = get_output_dir(imdb),函數在config中,用來保存中間生成的caffemodule等
5.正式開始訓練
調用train中的train_net函數(fast_rcnn/train_net.py),其中,首先filter_roidb,判斷roidb中的每個entry是否合理,合理定義為至少有一個前景box或背景box,roidb全是groud truth時,因為box與對應的類的重合度(overlaps)顯然為1,也就是說roidb起碼要有一個標記類。如果roidb包含了一些proposal,overlaps在[BG_THRESH_LO, BG_THRESH_HI]之間的都將被認為是背景,大於FG_THRESH才被認為是前景,roidb 至少要有一個前景或背景,否則將被過濾掉。將沒用的roidb過濾掉以後,返回的就是filtered_roidb。
在train文件中,需要關注的是SolverWrapper類。詳細見train.py,在這個類裡面,引入了caffe SGDSolver,最後一句
調用layer.py中的set_roidb方法,為layer(0)設置roidb,在這裡layer[0]就是ROILayer。
最後train_model。在這裡,就需要去實例化每個層,在這個階段,首先就會實現ROIlayer,詳細參考layer中的setup,在訓練時roi layer的forward函數,在第一個層,只需要進行數據拷貝,在不同的階段根據prototxt文件定義的網路結構拷貝數據,blobs = self._get_next_minibatch()這個函數讀取圖片數據(調用get_minibatch函數,這個函數在minibatch中,主要作用是為faster rcnn做實際的數據準備,在讀取數據的時候,分出了boxes,gt_boxes,im_info(寬高縮放)等)。
第一個層,對於stage1_rpn_train.pt文件中,該layer只有3個top blob:』data』、』im_info』、』gt_boxes』。
stage1_fast_rcnn_train.pt文件中,該layer有6個top blob: 『data』、』rois』、』labels』、』bbox_targets』、』bbox_inside_weights』、』bbox_outside_weights』,這些數據準備都在minibatch中。至此後數據便在caffe中流動了,直到訓練結束。畫出網路的結構 這裡只截取了一部分:
6.保存最後得到的權重參數
至此,第一階段訓練RPN完成, 後面的步驟訓練方法類似。
stage1_generate RPN proposals
- rpn_generate函數
前面和train_rpn基本相同,從rpn_proposals = imdb_proposals(rpn_net, imdb)開始,imdb_proposals函數在rpn.generate.py文件中,rpn_proposals是一個列表的列表,每個子列表。對於imdb_proposals,使用im = cv2.imread(imdb.image_path_at(i))讀入圖片數據,調用 im_proposals生成單張圖片的rpn proposals,以及得分。這裡,im_proposals函數會調用網路的forward,從而得到想要的boxes和scores,(這裡需要好好理解blobs_out = net.forward(data,im_info)中net forward和layer forward間的調用關係。)
在這裡,也會有proposal,同樣會使用Python實現的ProposalLayer,這個函數也在rpn文件夾內
- 保存得到的proposal文件
至此,Stage 1 RPN, generate proposals結束
Stage1 fast R-CNN using RPN proposals,init from Imagenet model
這一步,用上一步生成的proposal,以及imagenet模型M0來訓練fast-rcnn模型M2。
關注train_fast_rcnn
`同樣地,會設置參數,這裡注意cfg.TRAIN.PROPOSAL_METHOD = 『rpn』 不同於前面,後面調用的將是rpn_roidb。cfg.TRAIN.IMS_PER_BATCH = 2,每個mini-batch包含兩張圖片,以及它們proposal的roi區域。且在這一步是有rpn_file的(後面和rpn_roidb函數使用有關)。 其他的和前面差不多。
這裡在train_net的時候,會調用add_bbox_regression_targets位於roidb中,主要是添加bbox回歸目標,即添加roidb的『bbox_targets』屬性,同時根據cfg中的參數設定,求取bbox_targets的mean和std,因為需要訓練class-specific regressors在這裡就會涉及到bbox_overlaps函數,放在util.bbox中。
要注意的是在這一步get_roidb時,如前所說,使用的是rpn_roidb,會調用imdb. create_roidb_from_box_list該方法功能是從box_list中讀取每張圖的boxes,而這個box_list就是從上一步保存的proposal文件中讀取出來的,然後做一定的處理,詳細見代碼,重點是在最後會返回roidb,rpn_roidb中的gt_overlaps是rpn_file中的box與gt_roidb中box的gt_overlaps等計算IoU等處理後得到的,而不像gt_roidb()方法生成的gt_roidb中的gt_overlaps全部為1.0。同時使用了imdb.merge_roidb,類imdb的靜態方法【這裡不太懂,需要再學習下】,把rpn_roidb和gt_roidb歸併為一個roidb,在這裡,需要具體去了解合併的基本原理。
stage2 RPN,init from stage 1 Fast R-CNN model
這部分就是利用模型M2練rpn網路,這一次與stage1的rpn網路不同,這一次conv層的參數都是不動的,只做前向計算,(model下的stage1_rpn_train.pt和stage2_rpn_train.pt區別在於stage2的前面的公共卷積層學習率為0,不參與前向)訓練得到模型M3,這屬於微調了rpn網路。
stage 2 RPN,generate proposals
這一步,基於上一步得到的M3模型,產生proposal P2,網路結構和前面產生proposal P1的一樣。
7.stage 2 Fast R-CNN, init from stage 2 RPN R-CNN model
這一步基於模型M3和P2訓練fast rcnn得到最終模型M4,這一步,conv層和rpn都是參數固定,只是訓練了rcnn層(也就是全連接層),與stage1不同,stage1隻是固定了rpn層,其他層還是有訓練。模型結構與stage1相同:
8.輸出最後模型
只是對上一步模型輸出的一個拷貝。
至此,整個faster-rcnn的訓練過程就結束了。
推薦閱讀:
※TiDB 源碼閱讀系列文章(四)Insert 語句概覽
※LevelDB源碼解析4. 架構設計
※Retrofit原理解析最簡潔的思路
※Dive Into Code: VSCode 源碼閱讀(一)
※ROS導航包源碼學習6 --- recovery