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

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

最近,用faster rcnn跑一些本身的數據,數據集爲某遙感圖像數據集——RSOD,標註格式跟pascal_voc差很少,但因爲是學生團隊標註,中間有一些標註錯誤,也爲後面訓練埋了不少坑。下面是用本身的數據集跑時遇到的一些問題,必定必定要注意:在肯定程序徹底調通前,務必把迭代次數設一個較小的值(好比100),節省調試時間。git

錯誤目錄github

1 ./tools/train_faster_rcnn_alt_opt.py is not found緩存

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

3 'module' object has no attribute 'text_format'app

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.000this

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里路徑相關的文件。

以上。

相關文章
相關標籤/搜索