win10 Faster-RCNN訓練自己數據集遇到的問題集錦 (轉)


題注: 在win10下訓練實在是有太多坑了,在此感謝網上的前輩和大神,雖然有的還會把你引向另一個坑~~.

最近,用faster rcnn跑一些自己的數據,數據集為某遙感圖像數據集——RSOD,標注格式跟pascal_voc差不多,但由於是學生團隊標注,中間有一些標注錯誤,也為后面訓練埋了很多坑。下面是用自己的數據集跑時遇到的一些問題,一定一定要注意:在確定程序完全調通前,務必把迭代次數設一個較小的值(比如100),節省調試時間。

錯誤目錄

1 ./tools/train_faster_rcnn_alt_opt.py is not found

2 assert (boxes[:, 2] >= boxes[:, 0]).all()

3 'module' object has no attribute 'text_format'

4 Typeerror:Slice indices must be integers or None or have __index__ method

5 TypeError: 'numpy.float64' object cannot be interpreted as an index

error=cudaSuccess(2 vs. 0) out of memory?

7 loss_bbox = nan,result: Mean AP=0.000

AttributeError: 'NoneType' object has no attribute 'astype'

錯誤1: 執行sudo ./train_faster_rcnn_alt_opt.sh 0 ZF pascal_voc,報錯:./tools/train_faster_rcnn_alt_opt.py is not found

解決方法:執行sh文件位置錯誤,應退回到py-faster-rcnn目錄下,執行sudo ./experiments/scripts/train_faster_rcnn_alt_opt.sh 0 ZF pascal_voc

 

錯誤2:在調用append_flipped_images函數時出現: assert (boxes[:, 2] >= boxes[:, 0]).all()

網上查資料說:出現這個問題主要是自己的數據集標注出錯。由於我們使用自己的數據集,可能出現x坐標為0的情況,而pascal_voc數據標注都是從1開始計數的,所以faster rcnn代碼里會轉化成0-based形式,對Xmin,Xmax,Ymin,Ymax進行-1操作,從而會出現溢出,如果x=0,減1后溢出為65535。更有甚者,標記坐標為負數或者超出圖像范圍。主要解決方法有:

(1)修改lib/datasets/imdb.py,在boxes[:, 2] = widths[i] - oldx1 - 1后插入:

 

[python] view plain copy
 
 
print?
  1. for b in range(len(boxes)):  
  2.     if boxes[b][2]< boxes[b][0]:  
  3.         boxes[b][0] = 0  
for b in range(len(boxes)):
    if boxes[b][2]< boxes[b][0]:
        boxes[b][0] = 0

這種方法其實頭痛醫頭,且認為溢出只有可能是 boxes[b][0] ,但后面事實告訴我, boxes[b][2] 也有可能溢出。不推薦。

(2)修改lib/datasets/pascal_voc.py中_load_pascal_annotation函數,該函數是讀取pascal_voc格式標注文件的,下面幾句中的-1全部去掉(pascal_voc標注是1-based,所以需要-1轉化成0-based,如果我們的數據標注是0-based,再-1就可能溢出,所以要去掉)。如果只是0-based的問題(而沒有標注為負數或超出圖像邊界的坐標),這里就應該解決問題了。

[python] view plain copy
 
 
print?
  1. x1 = float(bbox.find('xmin').text)#-1  
  2. y1 = float(bbox.find('ymin').text)#-1  
  3. x2 = float(bbox.find('xmax').text)#-1  
  4. y2 = float(bbox.find('ymax').text)#-1  
x1 = float(bbox.find('xmin').text)#-1
y1 = float(bbox.find('ymin').text)#-1
x2 = float(bbox.find('xmax').text)#-1
y2 = float(bbox.find('ymax').text)#-1

(3)標注文件矩形越界

我執行了上面兩步,運行stage 1 RPN, init from ImageNet Model時還是報錯。說明可能不僅僅是遇到x=0的情況了,有可能標注本身有錯誤,比如groundtruth的x1<0或x2>imageWidth。決定先看看到底是那張圖像的問題。在lib/datasets/imdb.py的

[python] view plain copy
 
 
print?
  1. assert (boxes[:, 2] >= boxes[:, 0]).all()  
assert (boxes[:, 2] >= boxes[:, 0]).all()

這句前面加上:

[python] view plain copy
 
 
print?
  1. print self.image_index[i]  
print self.image_index[i]

打印當前處理的圖像名,運行之后報錯前最后一個打印的圖像名就是出問題的圖像啦,檢測Annotation中該圖像的標注是不是有矩形越界的情況。經查,還真有一個目標的x1被標注成了-2。

更正這個標注錯誤后,正當我覺得終於大功告成之時,依然報錯……咬着牙對自己說“我有耐心”。這次報錯出現在“Stage 1 Fast R-CNN using RPN proposals, init from ImageNet model”這個階段,也就是說此時調用append_flipped_images函數處理的是rpn產生的proposals而非標注文件中的groundtruth。不科學啊,groundtruth既然沒問題,proposals怎么會溢出呢?結論:沒刪緩存!把py-faster-rcnn/data/cache中的文件和 py-faster-rcnn/data/VOCdevkit2007/annotations_cache中的文件統統刪除。是這篇博客給我的啟發。在此之前,我花了些功夫執迷於找標注錯誤,如果只是想解決問題就沒有必要往下看了,但作為分析問題的思路,可以記錄一下:

首先我決定看看到底哪個proposal的問題。還是看看是哪張圖像的問題,在lib/datasets/imdb.py的

[python] view plain copy
 
 
print?
  1. assert (boxes[:, 2] >= boxes[:, 0]).all()  
assert (boxes[:, 2] >= boxes[:, 0]).all()

這句前面加上:

[python] view plain copy
 
 
print?
  1. print ("num_image:%d"%(i))  
print ("num_image:%d"%(i))

然后運行,打印圖像在訓練集中的索引(這次不需要知道圖像名),找到告警前最后打印的那個索引,比如我找到的告警前索引為320,下一步就是看看這個圖片上所有的proposal是不是正常,同樣地,在告警語句前插入:

[python] view plain copy
 
 
print?
  1. if i==320:  
  2.     print self.image_index[i]  
  3.     for z in xrange(len(boxes)):  
  4.         print ('x2:%d  x1:%d'%(boxes[z][2],boxes[z][0]))  
  5.         if boxes[z][2]<boxes[z][0]:  
  6.      print"here is the bad point!!!"  
            if i==320:
                print self.image_index[i]
                for z in xrange(len(boxes)):
                    print ('x2:%d  x1:%d'%(boxes[z][2],boxes[z][0]))
                    if boxes[z][2]<boxes[z][0]:
	                print"here is the bad point!!!"

再次運行后看日志,發現here is the bad point!!!出現在一組“x2=-64491  x1=1011”后,因為我的圖像寬度是1044,而1044-65535=-64491,所以其實是x2越界了,因boxes[:, 2] = widths[i] - oldx1 - 1,其實也就是圖像反轉前對應的oldx1=65534溢出,為什么rpn產生的proposal也會溢出呢?正常情況下,rpn產生的proposal是絕不會超過圖像范圍的,除非——標准的groundtruth就超出了!而groundtruth如果有問題,stage 1 RPN, init from ImageNet Model這個階段就應該報錯了,所以是一定是緩存的問題。

錯誤3:pb2.text_format(...)這里報錯'module' object has no attribute 'text_format'

解決方法:在./lib/fast_rcnn/train.py文件里import google.protobuf.text_format。網上有人說把protobuf版本回退到2.5.0,但這樣會是caffe編譯出現新問題——“cannot import name symbol database”,還需要去github上下對應的缺失文件,所以不建議。

 

錯誤4:執行到lib/proposal_target_layer.py時報錯Typeerror:Slice indices must be integers or None or have __index__ method

解決方法:這個錯誤的原因是,numpy1.12.0之后不在支持float型的index。網上很多人說numpy版本要降到1.11.0,但我這樣做了之后又有新的報錯:ImportError: numpy.core.multiarray failed to import。正確的解決辦法是:numpy不要降版本(如果已經降了版本,直接更新到最新版本就好),只用修改lib/proposal_target_layer.py兩處:(PS:我就在這里耽誤了好久)

在126行后加上:

[python] view plain copy
 
 
print?
  1. start=int(start)  
  2. end=int(end)  
start=int(start)
end=int(end)

在166行后加上:

[python] view plain copy
 
 
print?
  1. fg_rois_per_this_image=int(fg_rois_per_this_image)  
fg_rois_per_this_image=int(fg_rois_per_this_image)

 

錯誤5:py-faster-rcnn/tools/../lib/roi_data_layer/minibatch.py的_sample_rois函數中報錯TypeError: 'numpy.float64' object cannot be interpreted as an index

解決方法:這與錯誤(4)其實是一個問題,都是numpy版本導致的。一樣地,不支持網上很多答案說的降低版本的方法,更穩妥的辦法是修改工程代碼。這里給出的解決方案。修改minibatch.py文件:

第26行:

[python] view plain copy
 
 
print?
  1. fg_rois_per_image = np.round(cfg.TRAIN.FG_FRACTION * rois_per_image)  
fg_rois_per_image = np.round(cfg.TRAIN.FG_FRACTION * rois_per_image)

改為:

[python] view plain copy
 
 
print?
  1. fg_rois_per_image = np.round(cfg.TRAIN.FG_FRACTION * rois_per_image).astype(np.int)  
fg_rois_per_image = np.round(cfg.TRAIN.FG_FRACTION * rois_per_image).astype(np.int)

第173行:

[ruby] view plain copy
 
 
print?
  1. cls = clss[ind]  
cls = clss[ind]

改為:

[python] view plain copy
 
 
print?
  1. cls = int(clss[ind])  
cls = int(clss[ind])

另外還有3處需要加上.astype(np.int),分別是:

[python] view plain copy
 
 
print?
  1. #lib/datasets/ds_utils.py line 12 :  
  2. hashes = np.round(boxes * scale).dot(v)  
  3. #lib/fast_rcnn/test.py line 129:  
  4. hashes = np.round(blobs['rois'] * cfg.DEDUP_BOXES).dot(v)  
  5. #lib/rpn/proposal_target_layer.py line 60 :   
  6. fg_rois_per_image = np.round(cfg.TRAIN.FG_FRACTION * rois_per_image)  
#lib/datasets/ds_utils.py line 12 :
hashes = np.round(boxes * scale).dot(v)
#lib/fast_rcnn/test.py line 129:
hashes = np.round(blobs['rois'] * cfg.DEDUP_BOXES).dot(v)
#lib/rpn/proposal_target_layer.py line 60 : 
fg_rois_per_image = np.round(cfg.TRAIN.FG_FRACTION * rois_per_image)

 

錯誤6:error=cudaSuccess(2 vs. 0) out of memory

GPU內存不足,有兩種可能:(1)batchsize太大;(2)GPU被其他進程占用過多。

解決方法:先看GPU占用情況:watch -n 1 nvidia-smi,實時顯示GPU占用情況,運行訓練程序看占用變化。如果確定GPU被其他程序大量占用,可以關掉其他進程 kill -9 PID。如果是我們的訓練程序占用太多,則考慮將batchsize減少。

 

錯誤7:在lib/fast_rcnn/bbox_transform.py文件時RuntimeWarning: invalid value encountered in log targets_dw = np.log(gt_widths / ex_widths),然后loss_bbox = nan,最終的Mean AP=0.000

網上很多人說要降低學習率,其實這是指標不治本,不過是把報錯的時間推遲罷了,而且學習率過低,本身就有很大的風險陷入局部最優。

經過分析調試,發現這個問題還是自己的數據集標注越界的問題!!!越界有6種形式:x1<0;  x2>width;  x2<x1;  y1<0;  y2>height;  y2<y1。不巧的是,源代碼作者是針對pascal_voc數據寫的,壓根就沒有考慮標注出錯的可能性。發布的代碼中只在append_flipped_images函數里 assert (boxes[:, 2] >= boxes[:, 0]).all(),也就是只斷言了水平翻轉后的坐標x2>=x1,這個地方報錯可能是x的標注錯誤,參考前面的錯誤2。但是,對於y的標注錯誤,根本沒有檢查。

分析過程:先找的報warning的 lib/fast_rcnn/bbox_transform.py,函數bbox_transform,函數注釋參考這里。在

 

[python] view plain copy
 
 
print?
  1. targets_dw = np.log(gt_widths / ex_widths)  
    targets_dw = np.log(gt_widths / ex_widths)

前面加上:

[python] view plain copy
 
 
print?
  1. print(gt_widths)  
  2. print(ex_widths)  
  3. print(gt_heights)  
  4. print(ex_heights)  
  5. assert(gt_widths>0).all()  
  6. assert(gt_heights>0).all()  
  7. assert(ex_widths>0).all()  
  8. assert(ex_heights>0).all()  
    print(gt_widths)
    print(ex_widths)
    print(gt_heights)
    print(ex_heights)
    assert(gt_widths>0).all()
    assert(gt_heights>0).all()
    assert(ex_widths>0).all()
    assert(ex_heights>0).all()

然后運行,我發現AssertError出現在assert(ex_heights>0).all(),也就是說存在anchor高度為負數的,而height跟標注數據y方向對應,所以考慮是標注數據y的錯誤。類似於錯誤2,我回到lib/datasets/imdb.py,append_flipped_images函數中加入對y標注的檢查。直接粘貼代碼吧:

 

 

[python] view plain copy
 
 
print?
  1. #源代碼中沒有獲取圖像高度信息的函數,補充上  
  2. def _get_heights(self):  
  3.   return [PIL.Image.open(self.image_path_at(i)).size[1]  
  4.           for i in xrange(self.num_images)]  
  5. def append_flipped_images(self):  
  6.     num_images = self.num_images  
  7.     widths = self._get_widths()  
  8.     heights = self._get_heights()#add to get image height  
  9.     for i in xrange(num_images):  
  10.         boxes = self.roidb[i]['boxes'].copy()  
  11.         oldx1 = boxes[:, 0].copy()  
  12.         oldx2 = boxes[:, 2].copy()  
  13.         print self.image_index[i]#print image name  
  14.         assert (boxes[:,1]<=boxes[:,3]).all()#assert that ymin<=ymax  
  15.         assert (boxes[:,1]>=0).all()#assert ymin>=0,for 0-based  
  16.         assert (boxes[:,3]<heights[i]).all()#assert ymax<height[i],for 0-based  
  17.         assert (oldx2<widths[i]).all()#assert xmax<withd[i],for 0-based  
  18.         assert (oldx1>=0).all()#assert xmin>=0, for 0-based  
  19.         assert (oldx2 >= oldx1).all()#assert xmax>=xmin, for 0-based  
  20.         boxes[:, 0] = widths[i] - oldx2 - 1  
  21.         boxes[:, 2] = widths[i] - oldx1 - 1  
  22.         #print ("num_image:%d"%(i))  
  23.         assert (boxes[:, 2] >= boxes[:, 0]).all()  
  24.         entry = {'boxes' : boxes,  
  25.                  'gt_overlaps' : self.roidb[i]['gt_overlaps'],  
  26.                  'gt_classes' : self.roidb[i]['gt_classes'],  
  27.                  'flipped' : True}  
  28.         self.roidb.append(entry)  
  29.     self._image_index = self._image_index * 2  
    #源代碼中沒有獲取圖像高度信息的函數,補充上
    def _get_heights(self):
      return [PIL.Image.open(self.image_path_at(i)).size[1]
              for i in xrange(self.num_images)]
    def append_flipped_images(self):
        num_images = self.num_images
        widths = self._get_widths()
        heights = self._get_heights()#add to get image height
        for i in xrange(num_images):
            boxes = self.roidb[i]['boxes'].copy()
            oldx1 = boxes[:, 0].copy()
            oldx2 = boxes[:, 2].copy()
            print self.image_index[i]#print image name
            assert (boxes[:,1]<=boxes[:,3]).all()#assert that ymin<=ymax
            assert (boxes[:,1]>=0).all()#assert ymin>=0,for 0-based
            assert (boxes[:,3]<heights[i]).all()#assert ymax<height[i],for 0-based
            assert (oldx2<widths[i]).all()#assert xmax<withd[i],for 0-based
            assert (oldx1>=0).all()#assert xmin>=0, for 0-based
            assert (oldx2 >= oldx1).all()#assert xmax>=xmin, for 0-based
            boxes[:, 0] = widths[i] - oldx2 - 1
            boxes[:, 2] = widths[i] - oldx1 - 1
            #print ("num_image:%d"%(i))
            assert (boxes[:, 2] >= boxes[:, 0]).all()
            entry = {'boxes' : boxes,
                     'gt_overlaps' : self.roidb[i]['gt_overlaps'],
                     'gt_classes' : self.roidb[i]['gt_classes'],
                     'flipped' : True}
            self.roidb.append(entry)
        self._image_index = self._image_index * 2

 

然后運行,遇到y有標注錯誤的地方就會報AssertError,然后看日志上最后一個打印的圖像名,到對應的Annotation上查看錯誤標記,改過來后不要忘記刪除py-faster-rcnn/data/cache緩存。然后再運行,遇到AssertError再改對應圖像的標准,再刪緩存……重復直到所有的標注錯誤都找出來。然后就大功告成了,MAP不再等於0.000了!

 

錯誤8:訓練大功告成,mAP=0.66,可以測試一下了。具體的這個博客寫的很清楚。在執行demo.py文件時報錯:im_orig = im.astype(np.float32, copy=True),AttributeError: 'NoneType' object has no attribute 'astype'。

解決方法:仔細檢查路徑和文件名,查看demo.py里路徑相關的文件。着重看一下測試的圖片文件名稱

以上。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM