1. 程式人生 > >Faster RCNN程式碼理解(Python) ---訓練過程

Faster RCNN程式碼理解(Python) ---訓練過程

最近開始學習深度學習,看了下Faster RCNN的程式碼,在學習的過程中也查閱了很多其他人寫的部落格,得到了很大的幫助,所以也打算把自己一些粗淺的理解記錄下來,一是記錄下自己的菜鳥學習之路,方便自己過後查閱,二來可以回饋網路。目前程式設計能力有限,且是第一次寫部落格,中間可能會有一些錯誤。

目錄

第一步,準備

從train_faster_rcnn_alt_opt.py入:

  1. 初始化引數:args = parse_args() 採用的是Python的argparse
    主要有–net_name,–gpu,–cfg等(在cfg中只是修改了幾個引數,其他大部分引數在congig.py中,涉及到訓練整個網路)。
  2. cfg_from_file(args.cfg_file) 這裡便是代用config中的函式cfg_from_file來讀取前面cfg檔案中的引數,同時呼叫_merge_a_into_b函式把所有的引數整合,其中__C = edict() cfg = __C cfg是一個詞典(edict)資料結構。
  3. faster rcnn採用的是多程序,mp_queue是程序間用於通訊的資料結構
import multiprocessing as mp
mp_queue = mp.Queue()
  • 1
  • 2
  • 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
  • 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
  • 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

注意,在這裡,並沒有讀入任何資料,只是建立了圖片的索引。

imdb.set_proposal_method(cfg.TRAIN.PROPOSAL_METHOD)
  • 1
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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

AnchorTargetLayer和ProposalLayer

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

class AnchorTargetLayer(caffe.Layer)
  • 1
  • 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
  • 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
  • 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."""
    #x1、y1、x2、y2、以及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
  • 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

參考

原文地址:

繼fasterrcnn後,又一個pva-fasterrcnn的配置教程,希望可以幫助大家。若不能配置成功,請與我聯絡,郵箱:

注意:有些複製的終端命令如果不能在終端執行,請注意英文全形半形問題,可以將命令輸入終端,無須複製貼上命令

第一部分:下載並編譯pvanet
1、終端輸入:
git clone --recursive https://github.com/sanghoon/pva-faster-rcnn.git
2、建立Cython模組:
這個地方很容易錯,缺什麼下載什麼就能解決啦,出現問題的小夥伴可以來群裡討論哈,很多問題我當時沒有記錄,所以來群裡討論討論就行啦,群號你懂得大笑
cd $pva-faster-rcnn/lib
make –j16(為啥用16相信大家也很清楚啦)
3、編譯caffe及pycaffe
cd $pva-faster-rcnn/caffe-fast-rcnn
cp Makefile.config.example Makefile.config
修改Makefile.config檔案:
去掉cudnn前面#(我用cudnn4.0,僅供參考
Opencv_version不用註釋(我用的2.4.10,3.0沒有測試過,所以不用註釋
BLAS=atlas(我測試的庫,mkl沒有試過)(sudo apt-get install libatlas-base-dev即可安裝)
去掉with_python_layer前面的#,注意一定要去掉,不然無法訓練資料(測試的時候不需要)

修改後儲存退出

make –j16
make pycaffe –j16

第二部分:下載預訓練模型(外網真心慢啊大哭
為小夥伴們提供百度網盤下載連結大笑
連結:http://pan.baidu.com/s/1kVRRPDd 密碼:1cdt
1、開啟檔案將test.model放入$pva-faster-rcnn/models/pvanet/full/這個目錄下
2、將test(1).model重新命名為test.model放入$pva-faster-rcnn/models/pvanet/comp/目錄下
其他的模型我就沒有去試驗啦,後續有時間就會更新這些模型的訓練方法的


第三部分:下載voc07資料

開啟終端(任何目錄)輸入:
wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtrainval_06-Nov-2007.tar
wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar
wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCdevkit_08-Jun-2007.tar

解壓(嚴格按照此順序):
tar xvf VOCtrainval_06-Nov-2007.tar
tar xvf VOCtest_06-Nov-2007.tar
tar xvf VOCdevkit_08-Jun-2007.tar

將生成的VOCdevkit資料夾更名為VOCdevkit2007移動至$pva-faster-rcnn/data/目錄下面


第四部分:voc07測試預訓練模型

終端輸入:
cd $pva-faster-rcnn
1、full/test.model測試:
./tools/test_net.py --gpu 0 --def models/pvanet/full/test.pt --net models/pvanet/full/test.model --cfg models/pvanet/cfgs/submit_160715.yml
2、Comp/test.model測試:
./tools/test_net.py --gpu 0 --def models/pvanet/comp/test.pt --net models/pvanet/comp/test.model --cfg models/pvanet/cfgs/submit_160715.yml
此測試會得到系列類別的AP值

第五部分:預訓練model訓練並測試voc07資料
終端輸入:
1、訓練:
cd $pva-faster-rcnn
./tools/train_net.py --gpu 0 --solver models/pvanet/example_finetune/solver.prototxt --weights models/pvanet/full/test.model –iters 100000 --cfg models/pvanet/cfgs/train.yml –imdb voc_2007_trainval(其他引數預設)
訓練結果會在pva根目錄生成output資料夾,模型就在裡面吐舌頭
2、測試:
./tools/test_net.py --gpu 0 --def models/pvanet/example_finetune/test.prototxt --net output/faster_rcnn_pavnet/voc_2007_trainval/pvanet_frcnn_iter_100000.caffemodel --cfg models/pvanet/cfgs/submit_160715.yml (其他引數預設)


第六部分:預訓練model
訓練並測試自己資料
1、資料製作:
還記得fasterrcnn資料如何製作的嗎?參考部落格http://blog.csdn.net/samylee/article/details/51201744
Tips:參考voc07的資料格式,我們可發現,生成的資料最後只需保留Annotations、ImageSets和JPEGImages三個資料夾即可,ImageSets/Main檔案中用到的只有trainval.txt和test.txt這兩個檔案(資料分佈:trainval=train+val,all_data=trainval+test),多類別如果增加負樣本或模糊樣本可以考慮1、0及-1操作(其中1表示有目標,0表示模糊目標,-1表示沒有目標
資料集製作完成後將這三個資料夾複製至一個資料夾(命名為VOC2007)當中,再移動至$pva-faster-rcnn/data/VOCdevkit2007中,替換原來的VOC2007

2、類別設定:(有幾類設定幾類,類似fasterrcnn,我的資料集為1類行人資料)

1)開啟檔案/models/pvanet/example_finetune/train.prototxt

num_classes:2(開頭處)(我實驗時一類)

2)修改lib/datasets/pascal_voc.py

self._classes = ('__background__', # always index 0
                              'people')(只有這一類)

3)修改lib/datasets/imdb.py

資料整理,在一行程式碼為 boxes[:, 2] = widths[i] - oldx1 - 1下加入程式碼:
 for b in range(len(boxes)):

      if boxes[b][2]< boxes[b][0]:

         boxes[b][0] = 0

4)修改完pascal_voc.py和imdb.py後進入lib/datasets目錄下刪除原來的pascal_voc.pyc和imdb.pyc檔案,重新生成這兩個檔案,因為這兩個檔案是python編譯後的檔案,系統會直接呼叫。

終端進入lib/datasets檔案目錄輸入:

python(此處應出現python的版本)

>>>import py_compile

>>>py_compile.compile(r'imdb.py')

>>>py_compile.compile(r'pascal_voc.py')

3、訓練資料:
cd $pva-faster-rcnn
./tools/train_net.py --gpu 0 --solver models/pvanet/example_finetune/solver.prototxt --weights models/pvanet/full/test.model –iters 100000 --cfg models/pvanet/cfgs/train.yml –imdb voc_2007_trainval
(其他引數預設)
4、測試模型:
./tools/test_net.py --gpu 0 --def models/pvanet/example_finetune/test.prototxt --net output/faster_rcnn_pavnet/voc_2007_trainval/pvanet_frcnn_iter_100000.caffemodel --cfg models/pvanet/cfgs/submit_160715.yml (其他引數預設)


第七部分:實驗結果

目前我訓練了10000左右,loss為0.1左右,其他四個引數分別在0.2、0.1、0.01、0.01左右
後續工作可做demo演示程式,小夥伴們盡請期待!