最近開始學習深度學習,看了下Faster RCNN的代碼,在學習的過程中也查閱了很多其他人寫的博客,得到了很大的幫助,所以也打算把自己一些粗淺的理解記錄下來,一是記錄下自己的菜鳥學習之路,方便自己過后查閱,二來可以回饋網絡。目前編程能力有限,且是第一次寫博客,中間可能會有一些錯誤。
從train_faster_rcnn_alt_opt.py入:
初始化參數:args = parse_args() 采用的是Python的argparse 主要有–net_name,–gpu,–cfg等(在cfg中只是修改了幾個參數,其他大部分參數在congig.py中,涉及到訓練整個網絡)。cfg_from_file(args.cfg_file) 這里便是代用config中的函數cfg_from_file來讀取前面cfg文件中的參數,同時調用_merge_a_into_b函數把所有的參數整合,其中__C = edict() cfg = __C cfg是一個詞典(edict)數據結構。faster rcnn采用的是多進程,mp_queue是進程間用于通訊的數據結構import multiprocessing as mpmp_queue = mp.Queue()
1212同時solvers, max_iters, rpn_test_prototxt = get_solvers(args.net_name)得到solver參數接下來便進入了訓練的各個階段。
第二步,Stage 1 RPN, init from ImageNet model
cfg.TRAIN.SNAPSHOT_INFIX = 'stage1'mp_kwargs = dict( queue=mp_queue, imdb_name=args.imdb_name, init_model=args.pretrained_model, solver=solvers[0], max_iters=max_iters[0], cfg=cfg)p = mp.Process(target=train_rpn, kwargs=mp_kwargs)p.start()rpn_stage1_out = mp_queue.get()p.join()
123456789101112123456789101112可以看到第一個步驟是用ImageNet的模型M0來Finetuning RPN網絡得到模型M1。以訓練為例,這里的args參數都在腳本 experiments/scrips/faster_rcnn_alt_opt.sh中找到。主要關注train_rpn函數。對于train_rpn函數,主要分一下幾步:
1.在config參數的基礎上改動參數,以適合當前任務,主要有
cfg.TRAIN.HAS_RPN = Truecfg.TRAIN.BBOX_REG = False # applies only to Fast R-CNN bbox regressioncfg.TRAIN.PROPOSAL_METHOD = 'gt'
123123這里,關注proposal method 使用的是gt,后面會使用到gt_roidb函數,重要。
2. 初始化化caffe
3. 準備roidb和imdb
主要涉及到的函數get_roidb 在get_roidb函數中調用factory中的get_imdb根據__sets[name]中的key(一個lambda表達式)轉到pascol_voc類。class pascal_voc(imdb)在初始化自己的時候,先調用父類的初始化方法,例如:
{ year:’2007’ image _set:’trainval’ devkit _path:’data/VOCdevkit2007’ data _path:’data /VOCdevkit2007/VOC2007’ classes:(…)_如果想要訓練自己的數據,需要修改這里_ class _to _ind:{…} _一個將類名轉換成下標的字典 _ 建立索引0,1,2.... image _ext:’.jpg’ image _index: [‘000001’,’000003’,……]_根據trainval.txt獲取到的image索引_ roidb _handler: <Method gt_roidb > salt: <Object uuid > comp _id:’comp4’ config:{…}}
12345678910111213141234567891011121314注意,在這里,并沒有讀入任何數據,只是建立了圖片的索引。
imdb.set_proposal_method(cfg.TRAIN.PROPOSAL_METHOD)
11設置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中的gt_roidb,轉而調用了同一個文件中的_load_pascal_annotation,該函數根據圖片的索引,到Annotations這個文件夾下去找相應的xml標注數據,然后加載所有的bounding box對象,xml的解析到此結束,接下來是roidb中的幾個類成員的賦值:
boxes 一個二維數組,每一行存儲 xmin ymin xmax ymaxgt _classes存儲了每個box所對應的類索引(類數組在初始化函數中聲明)gt _overlap是一個二維數組,共有num _classes(即類的個數)行,每一行對應的box的類索引處值為1,其余皆為0,后來被轉成了稀疏矩陣seg _areas存儲著某個box的面積flipped 為false 代表該圖片還未被翻轉(后來在train.py里會將翻轉的圖片加進去,用該變量用于區分最后將這些成員變量組裝成roidb返回。 在get_training_roidb函數中還調用了roidb中的prepare_roidb函數,這個函數就是用來準備imdb 的roidb,給roidb中的字典添加一些屬性,比如image(圖像的索引),width,height,通過前面的gt _overla屬性,得到max_classes和max_overlaps.至此,
return roidb,imdb
114. 設置輸出路徑,output_dir = get_output_dir(imdb),函數在config中,用來保存中間生成的caffemodule等
5.正式開始訓練
model_paths = train_net(solver, roidb, output_dir, pretrained_model=init_model, max_iters=max_iters)
123123調用train中的train_net函數,其中,首先filter_roidb,判斷roidb中的每個entry是否合理,合理定義為至少有一個前景box或背景box,roidb全是groudtruth時,因為box與對應的類的重合度(overlaps)顯然為1,也就是說roidb起碼要有一個標記類。如果roidb包含了一些proposal,overlaps在[BG_THRESH_LO, BG_THRESH_HI]之間的都將被認為是背景,大于FG_THRESH才被認為是前景,roidb 至少要有一個前景或背景,否則將被過濾掉。將沒用的roidb過濾掉以后,返回的就是filtered_roidb。在train文件中,需要關注的是SolverWrapper類。詳細見train.py,在這個類里面,引入了caffe SGDSlover,最后一句self.solver.NET.layers[0].set_roidb(roidb)將roidb設置進layer(0)(在這里就是ROILayer)調用ayer.py中的set_roidb方法,為layer(0)設置roidb,同時打亂順序。最后train_model。在這里,就需要去實例化每個層,在這個階段,首先就會實現ROIlayer,詳細參考layer中的setup,在訓練時roilayer的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:top: ‘data’、’rois’、’labels’、’bbox_targets’、’bbox_inside_weights’、’bbox_outside_weights’,這些數據準備都在minibatch中。至此后數據便在caffe中流動了,直到訓練結束。畫出網絡的結構 這里只截取了一部分:
值得注意的是在rpn-data層使用的是AnchorTargetLayer,該層使用Python實現的,往后再介紹。
6.保存最后得到的權重參數
rpn_stage1_out = mp_queue.get()
11至此,第一階段完成,在后面的任務開始時,如果有需要,會在這個輸出的地址找這一階段得到的權重文件。
第三步,Stage 1 RPN, generate proposals
這一步就是調用上一步訓練得到的模型M1來生成proposal P1,在這一步只產生proposal,參數:
mp_kwargs = dict( queue=mp_queue, imdb_name=args.imdb_name, rpn_model_path=str(rpn_stage1_out['model_path']), cfg=cfg, rpn_test_prototxt=rpn_test_prototxt)p = mp.Process(target=rpn_generate, kwargs=mp_kwargs)p.start()rpn_stage1_out['proposal_path'] = mp_queue.get()['proposal_path']p.join()
12345678910123456789101.關注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文件夾內,后面再補充。
boxes = blobs_out['rois'][:, 1:].copy() / scale scores = blobs_out['scores'].copy()return boxes, scores
123123至此,得到imdb proposal
2.保存得到的proposal文件
queue.put({'proposal_path': rpn_proposals_path})rpn_stage1_out['proposal_path'] = mp_queue.get()['proposal_path']
1212至此,Stage 1 RPN, generate proposals結束
第四步,Stage 1 Fast R-CNN using RPN proposals, init from ImageNet model
參數:
cfg.TRAIN.SNAPSHOT_INFIX = 'stage1'mp_kwargs = dict( queue=mp_queue, imdb_name=args.imdb_name, init_model=args.pretrained_model, solver=solvers[1], max_iters=max_iters[1], cfg=cfg, rpn_file=rpn_stage1_out['proposal_path'])p = mp.Process(target=train_fast_rcnn, kwargs=mp_kwargs)p.start()fast_rcnn_stage1_out = mp_queue.get()p.join()
1234567891011121312345678910111213這一步,用上一步生成的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,在這里,需要具體去了解合并的基本原理。
第五步,Stage 2 RPN, init from stage 1 Fast R-CNN model
參數:
cfg.TRAIN.SNAPSHOT_INFIX = 'stage2'mp_kwargs = dict( queue=mp_queue, imdb_name=args.imdb_name, init_model=str(fast_rcnn_stage1_out['model_path']), solver=solvers[2], max_iters=max_iters[2], cfg=cfg)p = mp.Process(target=train_rpn, kwargs=mp_kwargs)rpn_stage2_out = mp_queue.get()
1234567891012345678910這部分就是利用模型M2練rpn網絡,這一次與stage1的rpn網絡不通,這一次conv層的參數都是不動的,只做前向計算,訓練得到模型M3,這屬于微調了rpn網絡。
第六步,Stage 2 RPN, generate proposals
參數:
mp_kwargs = dict( queue=mp_queue, imdb_name=args.imdb_name, rpn_model_path=str(rpn_stage2_out['model_path']), cfg=cfg, rpn_test_prototxt=rpn_test_prototxt)p = mp.Process(target=rpn_generate, kwargs=mp_kwargs)p.start()rpn_stage2_out['proposal_path'] = mp_queue.get()['proposal_path']p.join()
1234567891012345678910這一步,基于上一步得到的M3模型,產生proposal P2,網絡結構和前面產生proposal P1的一樣。
第七步,Stage 2 Fast R-CNN, init from stage 2 RPN R-CNN model
參數:
cfg.TRAIN.SNAPSHOT_INFIX = 'stage2'mp_kwargs = dict( queue=mp_queue, imdb_name=args.imdb_name, init_model=str(rpn_stage2_out['model_path']), solver=solvers[3], max_iters=max_iters[3], cfg=cfg, rpn_file=rpn_stage2_out['proposal_path'])p = mp.Process(target=train_fast_rcnn, kwargs=mp_kwargs)p.start()fast_rcnn_stage2_out = mp_queue.get()p.join()
1234567891011121312345678910111213這一步基于模型M3和P2訓練fast rcnn得到最終模型M4,這一步,conv層和rpn都是參數固定,只是訓練了rcnn層(也就是全連接層),與stage1不同,stage1只是固定了rpn層,其他層還是有訓練。模型結構與stage1相同:
第八步,輸出最后模型
final_path = os.path.join( os.path.dirname(fast_rcnn_stage2_out['model_path']), args.net_name + '_faster_rcnn_final.caffemodel')print 'cp {} -> {}'.format( fast_rcnn_stage2_out['model_path'], final_path)shutil.copy(fast_rcnn_stage2_out['model_path'], final_path)print 'Final model: {}'.format(final_path)
12345671234567只是對上一步模型輸出的一個拷貝。 至此,整個faster-rcnn的訓練過程就結束了。
AnchorTargetLayer和ProposalLayer
前面說過還有這兩個層沒有說明,一個是anchortarget layer一個是proposal layer,下面逐一簡要分析。
class AnchorTargetLayer(caffe.Layer)
11首先是讀取參數,在prototxt,實際上只讀取了param_str: “‘feat_stride’: 16”,這是個很重要的參數,目前我的理解是滑塊滑動的大小,對于識別物體的大小很有用,比如小物體的識別,需要把這個參數減小等。首先 setup部分,
anchor_scales = layer_params.get('scales', (8, 16, 32))self._anchors = generate_anchors(scales=np.array(anchor_scales))
1212調用generate_anchors方法生成最初始的9個anchor該函數位于generate_anchors.py 主要功能是生成多尺度,多寬高比的anchors,8,16,32其實就是scales:[2^3 2^4 2^5],base_size為16,具體是怎么實現的可以查閱源代碼。_ratio_enum()部分生成三種寬高比 1:2,1:1,2:1的anchor如下圖所示:(以下參考另外一篇博客)
_scale_enum()部分,生成三種尺寸的anchor,以_ratio_enum()部分生成的anchor[0 0 15 15]為例,擴展了三種尺度 128*128,256*256,512*512,如下圖所示:
另外一個函數就是forward()。 在faster rcnn中會根據不同圖的輸入,得到不同的feature map,height, width = bottom[0].data.shape[-2:]首先得到conv5的高寬,以及gt box gt_boxes = bottom[1].data,圖片信息im_info = bottom[2].data[0, :],然后計算偏移量,shift_x = np.arange(0, width) * self._feat_stride,在這里,你會發現,例如你得到的fm是H=61,W=36,然后你乘以16,得到的圖形大概就是1000*600,其實這個16大概就是網絡的縮放比例。接下來就是生成anchor,以及對anchor做一定的篩選,詳見代碼。
另外一個需要理解的就是proposal layer,這個只是在測試的時候用,許多東西和AnchorTargetLayer類似,不詳細介紹,可以查看代碼。主要看看forward函數,函數算法介紹在注釋部分寫的很詳細:
# Algorithm:# for each (H, W) location i# generate A anchor boxes centered on cell i# apply predicted bbox deltas at cell i to each of the A anchors# clip predicted boxes to image# remove predicted boxes with either height or width < threshold# sort all (proposal, score) pairs by score from highest to lowest# take top pre_nms_topN proposals before NMS# apply NMS with threshold 0.7 to remaining proposals# take after_nms_topN proposals after NMS# return the top proposals (-> RoIs top, scores top)
12345678910111234567891011在這個函數中會引用NMS方法。
代碼文件夾說明
[2] http://blog.csdn.net/u010668907/article/category/6237110 [3] http://blog.csdn.net/sunyiyou9/article/category/6269359 [4] http://blog.csdn.net/bailufeiyan/article/details/50749694
原文地址:
http://blog.csdn.net/u011956147/article/details/53053381
新聞熱點
疑難解答