1. 程式人生 > >將FPN中的一些層改成C++

將FPN中的一些層改成C++

因為需要用C++的程式碼跑測試,因而需要將prototxt中一些原來用python寫的層改成c++格式。

之前跑faster rcnn的時候,將rpn層參考部落格修改過,但是因為對原始碼不瞭解,這次改三個層無從下手,下面記錄一下過程。需要修改的三個層分別為rpn-data,roi-data和proposal層。

rpn-data:

layer {
  name: 'rpn-data'
  type: 'Module'
  bottom: 'rpn_cls_score/p2'
  bottom: 'rpn_cls_score/p3'
  bottom: 'rpn_cls_score/p4'
  bottom: 'rpn_cls_score/p5'
  bottom: 'rpn_cls_score/p6'
  bottom: 'gt_boxes'
  bottom: 'im_info'
  top: 'rpn_labels'
  top: 'rpn_bbox_targets'
  top: 'rpn_bbox_inside_weights'
  top: 'rpn_bbox_outside_weights'
  module_param {
    module: "modules"
    type: "FPNAnchorTarget"
    param_str: "{ 'feat_strides': [4,8,16,32,64] }"
  }
  propagate_down: 0
  propagate_down: 0
  propagate_down: 0
  propagate_down: 0
  propagate_down: 0
  propagate_down: 0
  propagate_down: 0
}

roi-data:

layer {
  name: 'roi-data'
  type: 'Module'
  bottom: 'rpn_rois'
  bottom: 'gt_boxes'
  top: 'rois/h2'
  top: 'rois/h3'
  top: 'rois/h4'
  top: 'rois/h5'
#  top: 'rois/h6'
  top: 'labels'
  top: 'bbox_targets'
  top: 'bbox_inside_weights'
  top: 'bbox_outside_weights'
  propagate_down: 0
  propagate_down: 0
  module_param {
    module: 'modules'
    type: 'FPNProposalTarget'
  }
}

proposal:

layer {
  name: 'proposal'
  type: 'Module'
  bottom: 'fpn_out_reshape/p2'
  bottom: 'rpn_bbox_pred/p2'
  bottom: 'fpn_out_reshape/p3'
  bottom: 'rpn_bbox_pred/p3'
  bottom: 'fpn_out_reshape/p4'
  bottom: 'rpn_bbox_pred/p4'
  bottom: 'fpn_out_reshape/p5'
  bottom: 'rpn_bbox_pred/p5'
  bottom: 'fpn_out_reshape/p6'
  bottom: 'rpn_bbox_pred/p6'
  bottom: 'im_info'
  top: 'rpn_rois'
  module_param {
    module: 'modules'
    type: 'FPNProposal'
    param_str: "{'feat_strides': [4,8,16,32,64]}"
  }
  propagate_down: 0
  propagate_down: 0
  propagate_down: 0
  propagate_down: 0
  propagate_down: 0
  propagate_down: 0
  propagate_down: 0
  propagate_down: 0
  propagate_down: 0
  propagate_down: 0
  propagate_down: 0
  include { phase: TRAIN }
}

注意到其中的module_param和model的'modules'都是需要根據實際情況改掉的。在呼叫module_param的地方,呼叫方法為this->layer_param_.module_param(),而直接編譯caffe的時候是無法通過編譯的,會報找不到module_param()的error。分析了原始碼,發現程式碼中使用YAML解析結構(在faster rcnn中為用LayerParameter直接定義解析引數,之後也可以嘗試用這種方法),因此需要在caffe.proto裡新增Moduleparameter。

(雖然yaml-cpp安裝成功了,但是還是報錯undefined yaml,所以打算嘗試上面的方法:

  1.   (備份後)將train和test的pt裡的module_param更改成fpn_param
  2. 在caffe.proto裡新增parameter,FPNParameter fpn_param;然後發現,根據caffe.proto中,引數的型別不同,會在caafe.pb.h和.cc中自動生成不同的引數。對於optional變數,會生成has_xx,對於repeated變數,會生成xx.size(),可以對比dummy_data_layer.cpp和pooling_layer.cpp兩個檔案,而生成引數的主要作用是用來在.cpp中取值的時候做判斷)

事實上嘗試失敗了,因為沒有搞清楚層與層之間的關係

 

該prototxt在roi_pooling層只寫了一個feat_strides,而實際上在Faster Rcnn中,還需要anchor_scales和anchor_ratios。因此查看了fpn_proposal_layer.cpp,發現其中的結構是,如果pt中為空,則自動設定。(可以在後期加在prototxt中修改試一試)

 

另外可以看到,在roi-data層中,c++版本是沒有另外傳入引數的,而python版本是添加了num_class引數。然後發現該n_class引數是在frcnn_param裡傳入的,而裡面的引數是從指令碼中parse出來的,因此不需要在這裡設定了。

 

另外,在fpn_proposal_layer.cu裡,有cub/cub.cuh,找不到檔案,所以先註釋掉了。

 

================分界線======================

11.07 我又回來了,並搞明白了幾個問題,為:

  1. cub.cub.cuh為cuda的一個頭檔案。

  2. c++版本中的另外新增引數,是在輸入層用window_data這個層傳入進來的。而我之前的思路是要把train的pt檔案中python層也改成c++,於是我一開始的思路是把所需要的引數全部傳進來,而現在思路更改為只需要改test的pt檔案中的python層為c++格式,這種情況下只需要把cfg中在此呼叫的引數重新傳進來就行了。

 

那麼,在此重新分析一下邏輯:

1.首先要找到需要更改的python層,並簡要分析

test的prototxt裡,Python層為「input-data」,「rpn-data」,「proposal」,「roi-data」

test的prototxt裡,Python層為「proposal」,「as-rois」

在這裡的proposal層在python中呼叫的是(rpn.proposal_layer),roi-data是(rpn.proposal_target_layer),as-rois是(rpn.as_rois)。

自此可以看出,需要更改的只有propsal層和as-rois層。那麼我們去看python檔案。

2.分析as-rois層

開啟python的這個檔案,可以看到該層呼叫的外部函式只有config.py函式,並只用了其中的「cfg.TEST.RPN_POST_NMS_TOP_N」這一個引數變數,而該層的作用是將proposal和gt對比,並分類,那麼該層應該還是挺好改的,因為只涉及了自己一個指令碼檔案。

3.分析propsal

這個檔案呼叫的函式比較多,而python中把呼叫的函式寫在了多個檔案中,畫個表表示:

呼叫的指令碼及其函式
fast_rcnn.config cfg
generate_anchors generate_anchors
fast_rcnn.bbox_transform bbox_transform_inv , clip_boxes
fast_rcnn.nms_wrapper nms

參照之前修改的rpn的c++檔案,其將這些函式寫在了一個c++檔案中。

4.分析Faster Rcnn和FPN的python指令碼檔案

主要去看了proposal_layer這個檔案,其中區別最大的是FPN是有多個feat_stride,而Faster Rcnn只有一個,其他的函式基本一樣。

 

分析完需要修改的檔案後,整理一下需要了解的背景知識:

1.Python層的邏輯

Python中每個class需要有四個層,分別為『setup』、『forward』、『backward』、『reshape』。如果有其他需要的函式,可以在class外面定義。

其中backward的引數為(self,top,propagate_down,bottom),其他三個為(self,top,bottom)。

bottom和top的引數順序根據prototxt中順序決定。

2.C++層的邏輯

c++中邏輯比較麻煩,因為要標頭檔案定義很多引數。

3.Python層的一些引數解釋

im_info:bottom[0].data[0,:],存放的是(height , width , scale)

4.caffe層的一些引數解釋

blob中的一些函式

常用的math函式

caffe_set:用引數alpha對引數Y進行初始化

template <typename Dtype>
void caffe_set(const int N, const Dtype alpha, Dtype* Y) {
  if (alpha == 0) {
    memset(Y, 0, sizeof(Dtype) * N);  // NOLINT(caffe/alt_fn)
    return;
  }
  for (int i = 0; i < N; ++i) {
    Y[i] = alpha; 
  }
}

caffe_axpy:Y=alpha*X+Y ,N為X和Y中element個數

template <>
void caffe_axpy<float>(const int N, const float alpha, const float* X,
    float* Y) { cblas_saxpy(N, alpha, X, 1, Y, 1); }

CopyFrom()

template <typename Dtype>
void Blob<Dtype>::CopyFrom(const Blob& source, bool copy_diff, bool reshape) {
}
//從source 拷貝資料 , copy_diff控制是拷貝diff還是data

 _axpy:

 

================分界線======================

11.08 我又回來了,出現了一些問題:

我測試了之前該rpn的faster rcnn,發現直接跑測試是無法跑的,但是直接detect是可以的,問題出在blob的尺寸問題上,目前還沒有解決。

猜測原因是config指令碼設定的問題

解決思路

1.檢視rpn的top格式

2.檢視roipooling的bottom格式

 

================分界線======================

11.09 我又回來了,解決問題了!!!哭了哭了

昨天確定出錯的位置在於,c++版本用blob傳遞資訊(雖然python也是),但是很坑很關鍵的一點是,python中很多地方是用numpy計算的,而as we all know,blob是四維的,於是在我使用的環境下,就是在test.py指令碼中,根據rois得到的boxes,呼叫了bbox_transform_inv函式,由於用到了其本身的資料型別和blob的型別,四維和二維不匹配於是出錯了。

在test指令碼中,net.blob['rois'].data.shape可以檢視rois的維度,查到是四維。而實際上沒改C++版本之前,傳遞的該引數維度是二維,很巧和的是,前者shape為[1,5,1,1],後者為[1,5],於是我就分別去看c++和python的rpn層檔案,發現c++層在最後reshape成了4維。於是經我不縝密的猜測,最後兩位資料其實只是為了對其的,其實沒有用,也就是說,只需要在python檔案中需要rois的地方,把原資料的前兩維拷貝就好了

解決方法也很簡單,在test.py中用到rois的地方(boxes)的shape改掉:

rois = net.blobs['rois'].data.copy()
boxes = rois[:,1:5] / im_scales[0]
box_min_resize = min(cfg.TEST.RPN_POST_NMS_TOP_N,boxes.shape[0])
boxes.resize([box_min_resize,4])

 

接下來就開始搞as_rois.py了,github上沒有對應的層真的哭了!!本來計劃週日稍微改改就能跑的c++模型,沒想到搞到了今天,低估了工作量但是對caffe 的這幾個層的瞭解更深刻了。。。

 

================分界線======================

11.13 我又回來了,全部改好並且能跑了,問題就是結果不對- -

記錄一些問題

1.一開始把vector的變數定義在.hpp中,然後在cpp中操作,這造成一個問題,就是執行速度會越來越慢,可能造成了記憶體洩漏沒有清理空間。於是我用了一個簡單的方法就是把hpp中定義的變數放cpp中定義了,這樣就相當於他在程式執行結束後就自動銷燬了。

2.如果新增cpp層需要make clean,如果只是修改cpp層,不需要make clean