caffe-fasterrcnn程序理解

faster-rcnn 結構雜談

 參考博客:::https://www.cnblogs.com/longriyao/p/5832274.html
http://www.cnblogs.com/chaofn/p/9310912.html
https://blog.csdn.net/u014380165/article/details/80303040
https://blog.csdn.net/u014380165/article/details/80303812
https://blog.csdn.net/u011956147/article/details/53053381
很是好:https://blog.csdn.net/jiongnima/article/details/80478597

rcnn fast-rcnn faster-rcnn資料

 

---恢復內容開始---html

框架:https://github.com/rbgirshickpython

論文:連接: https://pan.baidu.com/s/1jIoChxG 密碼: ubgmc++

faster rcnn:git

    C++ 實現:      github

      https://github.com/YihangLou/FasterRCNN-Encapsulation-Cplusplus
      http://www.cnblogs.com/louyihang-loves-baiyan/p/5493344.html
      http://blog.csdn.net/xyy19920105/article/details/50440957
      http://www.cnblogs.com/louyihang-loves-baiyan/p/5485955.html算法

    使用講解編程

      http://www.cnblogs.com/CarryPotMan/p/5390336.html數組

    畫圖:網絡

      http://ethereon.github.io/netscope/#/editor數據結構

    工具:

      https://github.com/tzutalin/labelImg

      http://nbviewer.jupyter.org/github/ouxinyu/ouxinyu.github.io/blob/master/MyCodes/Default.ipynb

faster-rcnn結構圖: (只截取了最難理解的部分)

這個網絡看似很複雜,可是理解了其中關鍵的層,就基本能夠掌握這個結構了。要看源碼!!要看源碼!!要看源碼 !!重要的事情說三遍。

關鍵的層:

 數據輸入層:#表示模塊存放的路徑 faster-rcnn/lib/roi_data_layer/layer.py

layer {
name: 'input-data'
type: 'Python'
top: 'data'
top: 'im_info'
top: 'gt_boxes'
python_param {
module: 'roi_data_layer.layer' #表示模塊存放的路徑 faster-rcnn/lib/roi_data_layer/layer.py
layer: 'RoIDataLayer'
param_str: "'num_classes': 81"
}
}
這層好像什麼都作,將數據直接複製到下一層  

rpn-data層:文件存放位置 faster-rcnn/lib/rpn/anchor_target_layer.py
layer {
name: 'rpn-data'
type: 'Python'
bottom: 'rpn_cls_score'
bottom: 'gt_boxes'
bottom: 'im_info'
bottom: 'data'
top: 'rpn_labels'
top: 'rpn_bbox_targets'
top: 'rpn_bbox_inside_weights'
top: 'rpn_bbox_outside_weights'
python_param {
module: 'rpn.anchor_target_layer' #文件存放位置 faster-rcnn/lib/rpn/anchor_target_layer.py
layer: 'AnchorTargetLayer'
param_str: "'feat_stride': 16 \n'scales': !!python/tuple [4, 8, 16, 32]"
}
}
這一層很重要:  
  生成 anchor
  處理 gt_boxes等其餘工做 具體沒仔細看
proposal層:#文件存放路徑 faster-rcnn/rpn/proposal_layer.py
layer {
name: 'proposal'
type: 'Python'
bottom: 'rpn_cls_prob_reshape'
bottom: 'rpn_bbox_pred'
bottom: 'im_info'
top: 'rpn_rois'
python_param {
module: 'rpn.proposal_layer'
layer: 'ProposalLayer'
param_str: "'feat_stride': 16 \n'scales': !!python/tuple [4, 8, 16, 32]"
}
}
這層應該就是生成proposal了

rpn_loss_bbox層:文件存放位置比較特殊:
  這個是使用c++代碼實現的
    頭文件:/faster-rcnn/caffe-faster-rcnn/include/caffe/fast_rcnn_layers.hpp
    實現: faster-rcnn/caffe-faster-rcnn/src/caffe/smooth_L1_loss_layer.cpp smooth_L1_loss_layer.cu
layer {
name: "rpn_loss_bbox"
type: "SmoothL1Loss"
bottom: "rpn_bbox_pred"
bottom: "rpn_bbox_targets"
bottom: 'rpn_bbox_inside_weights'
bottom: 'rpn_bbox_outside_weights'
top: "rpn_loss_bbox"
loss_weight: 1
smooth_l1_loss_param { sigma: 3.0 }
}

最近開始學習深度學習,看了下Faster RCNN的代碼,在學習的過程當中也查閱了不少其餘人寫的博客,獲得了很大的幫助,因此也打算把本身一些粗淺的理解記錄下來,一是記錄下本身的菜鳥學習之路,方便本身事後查閱,二來能夠回饋網絡。目前編程能力有限,且是第一次寫博客,中間可能會有一些錯誤。
目錄

    目錄
            第一步準備
            第二步Stage 1 RPN init from ImageNet model
                在config參數的基礎上改動參數以適合當前任務主要有
                初始化化caffe
                準備roidb和imdb
                設置輸出路徑output_dir get_output_dirimdb函數在config中用來保存中間生成的caffemodule等
                正式開始訓練
                保存最後獲得的權重參數
            第三步Stage 1 RPN generate proposals
                關注rpn_generate函數
                保存獲得的proposal文件
            第四步Stage 1 Fast R-CNN using RPN proposals init from ImageNet model
            第五步Stage 2 RPN init from stage 1 Fast R-CNN model
            第六步Stage 2 RPN generate proposals
            第七步Stage 2 Fast R-CNN init from stage 2 RPN R-CNN model
            第八步輸出最後模型
            AnchorTargetLayer和ProposalLayer
            代碼文件夾說明
                tools
                RPN
                nms
            參考
            原文地址

第一步,準備

從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 mp
mp_queue = mp.Queue()

    1
    2

同時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()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

能夠看到第一個步驟是用ImageNet的模型M0來Finetuning RPN網絡獲得模型M1。以訓練爲例,這裏的args參數都在腳本 experiments/scrips/faster_rcnn_alt_opt.sh中找到。主要關注train_rpn函數。
對於train_rpn函數,主要分一下幾步:
1.在config參數的基礎上改動參數,以適合當前任務,主要有

cfg.TRAIN.HAS_RPN = True
cfg.TRAIN.BBOX_REG = False  # applies only to Fast R-CNN bbox regression
cfg.TRAIN.PROPOSAL_METHOD = 'gt'

    1
    2
    3

這裏,關注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:{…}
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

注意,在這裏,並無讀入任何數據,只是創建了圖片的索引。

imdb.set_proposal_method(cfg.TRAIN.PROPOSAL_METHOD)

    1

設置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 ymax
    gt _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

    1

4. 設置輸出路徑,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)

    1
    2
    3

調用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()

    1

至此,第一階段完成,在後面的任務開始時,若是有須要,會在這個輸出的地址找這一階段獲得的權重文件。
第三步,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()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

1.關注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

    1
    2
    3

至此,獲得imdb proposal
2.保存獲得的proposal文件

queue.put({'proposal_path': rpn_proposals_path})
rpn_stage1_out['proposal_path'] = mp_queue.get()['proposal_path']

    1
    2

至此,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()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

這一步,用上一步生成的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()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

這部分就是利用模型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()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

這一步,基於上一步獲得的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()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

這一步基於模型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)

    1
    2
    3
    4
    5
    6
    7

只是對上一步模型輸出的一個拷貝。
至此,整個faster-rcnn的訓練過程就結束了。
AnchorTargetLayer和ProposalLayer

前面說過還有這兩個層沒有說明,一個是anchortarget layer一個是proposal layer,下面逐一簡要分析。

class AnchorTargetLayer(caffe.Layer)

    1

首先是讀取參數,在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))

    1
    2

調用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)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

在這個函數中會引用NMS方法。
代碼文件夾說明

轉載出處:另一篇博客
tools

在tools文件夾中,是咱們直接調用的最外層的封裝文件。其中主要包含的文件爲:

    _init_paths.py :用來初始化路徑的,也就是以後的路徑會join(path,*)
    compress_net.py:用來壓縮參數的,使用了SVD來進行壓縮,這裏能夠發現,做者對於fc6層和fc7層進行了壓縮,也就是兩個全鏈接層。
    demo.py :一般,咱們會直接調用這個函數,若是要測試本身的模型和數據,這裏須要修改。這裏調用了fast_rcnn中的test、config、nums_wrapper函數。vis_detections用來作檢測,parse_args用來進行參數設置,以及damo和主函數。
    eval_recall.py:評估函數
    reval.py:re-evaluate,這裏調用了fast_rcnn以及dataset中的函數。其中,from_mats函數和from_dets函數分別loadmat文件和pkl文件。
    rpn_genetate.py:這個函數調用了rpn中的genetate函數,以後咱們會對rpn層作具體的介紹。這裏,主要是一個封裝調用的過程,咱們在這裏調用配置的參數、設置rpn的test參數,以及輸入輸出等操做。
    test_net.py:測試fast rcnn網絡。主要就是一些參數配置。
    train_faster_rcnn_alt_opt.py:訓練faster rcnn網絡使用交替的訓練,這裏就是根據faster rcnn文章中的具體實現。能夠在主函數中看到,其包括的步驟爲:
        RPN 1,使用imagenet model進行初始化參數,生成proposal,這裏存儲在mp_kwargs
        fast rcnn 1,使用 imagenet model 進行初始化參數,使用剛剛生成的proposal進行fast rcnn的訓練
        RPN 2使用 fast rcnn 中的參數進行初始化(這裏要注意哦),並生成proposal
        fast rcnn 2,使用RPN 2 中的 model進行初始化參數
        值得注意的是:在咱們訓練時,咱們能夠在get_solvers中的max_iters中設置迭代次數,在不肯定網絡是否能夠調通時,減小迭代次數能夠減小測試時間。
        咱們在訓練faster rcnn網絡時,就是調用這個文件訓練的
    train_net.py:使用fast rcnn,訓練本身數據集的網絡模型
    train_svms.py:使用最原始的RCNN網絡訓練post-hoc SVMs

RPN

這裏咱們主要看lib/rpn文件夾下的代碼。這裏主要介紹了rpn的模型,其中,包含的主要文件以下:

    generate_anchors.py: 生成多尺度和多比例的錨點。這裏由generate_anthors函數主要完成,能夠看到,使用了 3 個尺度( 128, 256, and 512)以及 3 個比例(1:1,1:2,2:1)。一個錨點由w, h, x_ctr, y_ctr固定,也就是寬、高、x center和y center固定。
    proposal_layer.py:這個函數是用來將RPN的輸出轉變爲object proposals的。做者新增了ProposalLayer類,這個類中,從新了set_up和forward函數,其中forward實現了:生成錨點box、對於每一個錨點提供box的參數細節、將預測框切成圖像、刪除寬、高小於閾值的框、將全部的(proposal, score) 對排序、獲取 pre_nms_topN proposals、獲取NMS 、獲取 after_nms_topN proposals。(注:NMS,nonmaximum suppression,非極大值抑制)
    anchor_target_layer.py:生成每一個錨點的訓練目標和標籤,將其分類爲1 (object), 0 (not object) , -1 (ignore).當label>0,也就是有object時,將會進行box的迴歸。其中,forward函數功能:在每個cell中,生成9個錨點,提供這9個錨點的細節信息,過濾掉超過圖像的錨點,測量同GT的overlap。
    proposal_target_layer.py:對於每個object proposal 生成訓練的目標和標籤,分類標籤從0-k,對於標籤>0的box進行迴歸。(注意,同anchor_target_layer.py不一樣,二者一個是生成anchor,一個是生成proposal)
    generate.py:使用一個rpn生成object proposals。

做者就是經過以上這些文件生成rpn的。
nms

lib/nms文件夾下是非極大值抑制,這部分你們應該已經很是熟悉了,其Python版本的核心函數爲py_cpu_nms.py,具體實現以及註釋以下:

def py_cpu_nms(dets, thresh):
    """Pure Python NMS baseline."""
    #x一、y一、x二、y二、以及score賦值
    x1 = dets[:, 0]
    y1 = dets[:, 1]
    x2 = dets[:, 2]
    y2 = dets[:, 3]
    scores = dets[:, 4]

    #每個op的面積
    areas = (x2 - x1 + 1) * (y2 - y1 + 1)
    #order是按照score排序的
    order = scores.argsort()[::-1]

    keep = []
    while order.size > 0:
        i = order[0]
        keep.append(i)
        xx1 = np.maximum(x1[i], x1[order[1:]])
        yy1 = np.maximum(y1[i], y1[order[1:]])
        xx2 = np.minimum(x2[i], x2[order[1:]])
        yy2 = np.minimum(y2[i], y2[order[1:]])

        #計算相交的面積
        w = np.maximum(0.0, xx2 - xx1 + 1)
        h = np.maximum(0.0, yy2 - yy1 + 1)
        inter = w * h
        #計算:重疊面積/(面積1+面積2-重疊面積)
        ovr = inter / (areas[i] + areas[order[1:]] - inter)

        inds = np.where(ovr <= thresh)[0]
        order = order[inds + 1]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

參考

在這裏,沒有貼出代碼的註釋,只是梳理了下Faster RCNN訓練的流程,由於代碼的註釋網絡上已經有不少,須要看代碼的註釋能夠參考下面幾個博客,我看代碼的時候也有參考:
[1] http://www.cnblogs.com/CarryPotMan/p/5390336.html
[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
---------------------  
做者:FelixFuu  
來源:CSDN  
原文:https://blog.csdn.net/u011956147/article/details/53053381  
版權聲明:本文爲博主原創文章,轉載請附上博文連接!

如上圖所示,(im_info = [M , N , scale_factor]),說一下它的前向傳播流程:

(1)通常對於輸入的圖像,P*Q的原圖先統一縮放爲M*N,而且在前面的特徵提取層(也可稱「共享卷積層」)中,因爲stride = 1 , pad = 1 , kernel size = 3 * 3,因此卷積先後圖像大小不變,只有四個Pool層起到了做用,所以原始圖像通過提取層後的feature map大小爲(M / 16)* (N /16),這樣作也是爲了使原始圖像到feature map的縮放係數爲整數從而使得feature map上的每一個特徵點和M*N的原始圖像的區域座標更方便地實現一一映射關係,這對後面生成proposal會有幫助;

(2)RPN結構中第一個層是一個3*3的卷積層,它做用在通過特徵提取層後的feature map上,我以爲這個3*3的卷積做用是爲了增長目標附近區域的語義理解;而後再通過兩條支路,每條都首先通過一個1*1的卷積。先看上面一條,這條的做用是對「foreground」和"background"進行二分類,因此通過1*1以後的深度爲18(9*2,對應每一個點生成9個anchors和每一個anchor兩個類別),再看下面一條,也是通過一個1*1的卷積,深度爲36(9*4)進行矩形框迴歸,而後這兩條支路匯合,首先去除掉background類別,而後對於剩下的forground類別,利用im_info先將anchors放大到M*N的原始圖像上,這些放大後在原始圖像上的區域就是一開始的proposals,數量不少因此咱們還要進行兩次篩選:第一次,判斷這些proposals是否大範圍超出圖像邊界,剔除嚴重超出邊界的;第二次,對剩下的proposals進行nms,而後對nms後的proposals根據cls scores進行排序,選取top-N個做爲最終的proposals(通常在600個左右),其實到這裏檢測任務差很少就完成了,但做者後面還加另外一步,至關於一個refine;

(3)以後就涉及到ROI Pooling層了,因爲RPN輸出的proposals對應的M*N的輸入圖像,因此咱們先經過im_info中的scale_factor將其映射到特徵提取層的feature map上,但因爲後面須要通過全鏈接層,而這裏每一個proposals映射到feature map上的大小均不一樣,因此咱們使用了ROI Pooling層將這些proposals映射後的大小固定(7*7或者6*6,好像論文和實現裏稍微有點不一樣,看代碼就知道了),而後這塊固定的區域再通過全鏈接層進行分類和座標框迴歸;

 關於如何訓練,參考這個:https://www.cnblogs.com/zf-blog/p/7142463.html

補充:

(1)bounding box regression:

相關文章
相關標籤/搜索