1. 程式人生 > >Fast RCNN的訓練與測試

Fast RCNN的訓練與測試

1.準備工作

1.1軟體準備

   首先,需要安裝Caffepycaffe

caffe原作者網頁://caffe.berkeleyvision.org/installation.html

   注意:必須在Makefile.config配置檔案中開啟Python層支援。

   # In your Makefile.config, make sure to have this lineuncommented

   WITH_PYTHON_LAYER := 1

   其次,可能需要Python安裝包:cythonpython-opencveasydict

   先裝一個python包管理器pip

   sudo apt-get install python-pip

   再裝那三個包:

   sudo pip install cython

   #sudopip installpython-opencv

   sudo pip installeasydict

   再次,可能需要MATLAB,主要用於對PASCALVOC資料集的評估。

1.2硬體準備

   對於訓練較小的網路(CaffeNet,VGG_CNN_M_1024),至少需要3G記憶體GPU(如:TitanK20K40...

   對於訓練VGG16,至少需要一個K40(約11G記憶體),這裡我們就不考慮了。

2.安裝(用於demo

2.1githubcloneFastRCNN。最好就直接這麼clone,不要自己去下載,不然還滿麻煩的。

   # Make sure to clone with --recursive

   git clone --recursivehttps://github.com/rbgirshick/fast-rcnn.git

2.2生成Cython模組(下面的fast-rcnn都是指fast-rcnn的解壓位置)

   cd fast-rcnn/lib

   make

2.3生成Caffepycaffe

   cd fast-rcnn/caffe-fast-rcnn

Fast RCNNcaffe-fast-rcnn檔案編譯時,由於Fast RCNN中的caffe版本已經比較老,裡面一些關於cudnn的配置檔案需要從最新的caffe

中對應的檔案拷貝過來替代掉:

(1).用最新caffe原始碼的以下檔案替換掉fast_rcnn的對應檔案
include/caffe/layers/cudnn_relu_layer.hpp, 

src/caffe/layers/cudnn_relu_layer.cpp, 

src/caffe/layers/cudnn_relu_layer.cu 

include/caffe/layers/cudnn_sigmoid_layer.hpp, 

src/caffe/layers/cudnn_sigmoid_layer.cpp, 

src/caffe/layers/cudnn_sigmoid_layer.cu 

include/caffe/layers/cudnn_tanh_layer.hpp, 

src/caffe/layers/cudnn_tanh_layer.cpp,

src/caffe/layers/cudnn_tanh_layer.cu 

src/caffe/layers/cudnn_conv_layer.cpp,

src/caffe/layers/cudnn_conv_layer.cu

(2).用caffe原始碼中的這個檔案替換掉fast_rcnn對應檔案
include/caffe/util/cudnn.hpp
(3).將fast_rcnn 中的src/caffe/layers/cudnn_conv_layer.cu檔案中的所有
cudnnConvolutionBackwardData_v3 函式名替換為為 cudnnConvolutionBackwardData
cudnnConvolutionBackwardFilter_v3
函式名替換為 cudnnConvolutionBackwardFilter

在執行fast_rcnn檔案中tools檔案中的demo.py檔案時,要把caffe-fast-rcnn檔案中的Makefile.config檔案中的USE_CUDNN=1註釋掉,若是不註釋掉這一行,那麼在執行demo檔案是總會報浮點錯誤。這個錯誤在網上找不到確切的解決方法,還好電腦本身的gpu效能非常好,即使不使用cudnn加速來訓練和測試後面的資料集,其速度也很快。

做完這些再編譯caffe-fast-rcnn

 make -j8 && make pycaffe

2.4下載相關的壓縮包

data/scripts資料夾下有fetch_fast_rcnn_models.shfetch_imagenet_models.shfetch_selective_search_models.sh三個檔案來下載三個訓練測試時需要的壓縮包。在fast_rcnn路徑下執行命令:

./data/scripts/fetch_fast_rcnn_models.sh

./data/scripts/fetch_imagenet_models.sh

./data/scripts/fetch_selective_search_models.sh

——fetch_fast_rcnn_models.sh: 是用來下載Fast RCNN檢測器的;

——fetch_imagenet_models.sh:是用來下載作者預先訓練好的ImageNet模型

——fetch_selective_search_models.sh: 是用來下載預先用selective search計算好的objectproposal

注:這三個壓縮包的下載用命令端下載的話可能非常慢或者下不了,建議開啟.sh檔案直接複製裡面的地址用瀏覽器翻牆自己下載,下載完後在data資料夾下解壓好。

3.執行demo

3.1 Python

   cd fast-rcnn

   ./tools/demo.py

 如果用CPU模式,就是

   cd  fast-rcnn

   ./tools/demo.py --cpu

demo中是用VGG16網路,在PASCALVOC2007上訓練的模型來執行檢測的,這個模型比較大,如果把caffe弄崩潰了,可以換一個小一點的網路,其實還更快一點,如

   ./tools/demo.py --net caffenet

  或者

   ./tools/demo.py --net vgg_cnn_m_1024

  或者就用CPU模式好了。

3.2 MATLAB版(暫時沒找到編譯好的caffe應該需要配置caffeMATLAB介面

  在matlab資料夾下開啟matlab,下面是我的matlab的安裝地址。

   cd fast-rcnn/matlab

   /usr/local/MATLAB/R2016b/bin/matlab # wait for matlab to start...

  把fast-rcnn/caffe-fast-rcnn/matlab下的caffe資料夾拷貝到fast-rcnn/matlab中,為防止記憶體不夠,我們還是以CaffeNet為例,把fast-rcnn-demo.m中的所有VGG16改為CaffeNet。在matlab命令列下輸入命令:

   >> fast_rcnn_demo

3.3 一些獲取object proposal的演算法程式碼

Selective Search: originalmatlab code, python wrapper

    EdgeBoxes: matlabcode

    GOP and LPO: pythoncode

    MCG: matlabcode

    RIGOR: matlabcode

4.準備資料集

4.1 首先要下載訓練集、驗證集、測試集,例子是VOC2007。資源在牆外,將給出百度雲盤中的地址。

   wget//pascallin.ecs.soton.ac.uk/challenges/VOC/voc2007/VOCtrainval_06-Nov-2007.tar

   wget//pascallin.ecs.soton.ac.uk/challenges/VOC/voc2007/VOCtest_06-Nov-2007.tar

   wget//pascallin.ecs.soton.ac.uk/challenges/VOC/voc2007/VOCdevkit_08-Jun-2007.tar

4.2 提取所有壓縮包到同一個下面稱為VOCdevkit 的資料夾下。

   tar xvf VOCtrainval_06-Nov-2007.tar

   tar xvf VOCtest_06-Nov-2007.tar

   tar xvf VOCdevkit_08-Jun-2007.tar

要有這些基本的目錄:

   VOCdevkit/                    # development kit

  VOCdevkit/VOCcode/               # VOC utility code

:VOCcode資料夾並且該資料夾內是有一些檔案的,這個資料夾可能會由於VOC訓練集、驗證集、測試集這三個壓縮包沒下好的原因丟失掉,那需要重新下載不然測試時會報錯。

  VOCdevkit/VOC2007         # image sets, annotations, etc.

4.3 建立對VOC2007資料集的symlink,也就是連結fast-rcnnVOC2007的目錄。

   cd fast-rcnn/data

   ln -s VOCdevkitVOCdevkit2007

   這個方法非常好,因為別的工程裡面也可能用到這個資料集,這樣就不用多次拷貝了,節省了很多儲存空間,windows下面就沒有。

4.4 可以再用同樣的辦法得到VOC20102012的資料集,如果有需要的話。

   export PATH=$PATH:"/usr/local/MATLAB/R2014a/bin"

又提示說ImportError: No module namedyaml,那就下載安裝一個:

sudo apt-get installpython-yaml

再次執行程式碼就可以了。如果顯示記憶體不夠,可以用nvidia-smi隨時檢視記憶體使用情況。每10000次迭代會生成一個model,結果存放在output資料夾中。

 訓練VGG_CNN_M_1024網路時,會提示說記憶體不夠,就把fast-rcnn/lib/fast_rcnn下的config.py中每個minibatch所用的圖片由2改為1,如果還不行,說明GPU記憶體太小,只能換GPU了。

./tools/train_net.py --gpu 0 --solvermodels/VGG_CNN_M_1024/solver.prototxt --weightsdata/imagenet_models/VGG_CNN_M_1024.v2.caffemodel

訓練VGG16網路,據作者說,即使把每個minibatch所用的圖片由2改為1,也需要將近5GGPU記憶體,3G以上記憶體的可以嘗試一下,cudnn可能在一定程度上起到了優化作用。

注:在資料集上訓練模型時,會呼叫到一個matlab的路徑,所以需要實現安裝好matlab不需要配置caffematlab介面),然後新增好matlab的路徑。

在訓練模型開始後有可能會報關於程式的錯誤,可能和使用models下使用模型中的solver.prototxtimagenet_models中預訓練檔案的路徑有關,建議使用完整路徑

5.2測試模型

在自己的模型還沒有訓練好,或者訓練得不夠好的時候,可以試試作者提供的模型:

 ./tools/test_net.py --gpu 0 --defmodels/CaffeNet/test.prototxt  --netdata/fast_rcnn_models/caffenet_fast_rcnn_iter_40000.caffemodel

下面再測試自己的模型:

./tools/test_net.py --gpu 0 --def models/CaffeNet/test.prototxt --netoutput/default/voc_2007_trainval/caffenet_fast_rcnn_iter_40000.caffemodel

測試的結果也在output資料夾中。

注:測試模型時,使用的命令中涉及的模型下的檔案指令碼也需要使用完整的路徑,不然還是會報錯;

需要在fast-rcnn\data檔案下自己新建一個VOCdevkit\results\VOC2007\Main檔案。

5.3用全連線層壓縮的SVD來壓縮FRCNN模型

./tools/compress_net.py --def models/CaffeNet/test.prototxt--def-svd models/CaffeNet/compressed/test.prototxt --netoutput/default/voc_2007_trainval/caffenet_fast_rcnn_iter_40000.caffemodel

壓縮後的模型和壓縮前的模型是放在一起的,只是名字不一樣,在output下的相應資料夾下。再測試這個壓縮後的模型:

./tools/test_net.py --gpu 0 --defmodels/CaffeNet/compressed/test.prototxt --netoutput/default/voc_2007_trainval/vcaffenet_fast_rcnn_iter_40000_svd_fc6_1024_fc7_256.caffemodel

注:所有的都建議使用完整(絕對)路徑。

注:experiments存放配置檔案以及執行的log檔案,另外這個目錄下有scripts 用來獲取imagenet的模型,以及作者訓練好的fast rcnn模型,以及相應的pascal-voc資料集。執行scripts檔案下的.sh檔案後會在log檔案夾生成整個執行的過程記錄,看這個又便於學習整個過程(如):

·./experiments/scripts/all_vgg_cnn_m_1024.sh o VGG_CNN_M_1024 pascal_voc

(建議使用絕對路徑)

KITTI資料集製作成VOC格式的方法:

# 在data/資料夾下新建KITTIdevkit/KITTI兩層子目錄,所需檔案放在KITTI/中

Annotations/

└── 000000.xml

ImageSets/

└── main/

└── trainval.txt

└── test.txt# 等等

JPEGImages/

└── 000000.png

Labels/

└── 000000.txt# 自建資料夾,存放原始標註資訊,待轉化為xml,不屬於VOC格式

create_train_test_txt.py# 3個python工具,後面有詳細介紹

modify_annotations_txt.py

txt_to_xml.py


KITTI資料集使用的.png格式的圖片轉換成VOC使用的.jpg圖片並放在JPEGImages資料夾中:

·ls -1 *.png | xargs -n 1 bash -c ‘convert “$0” “${0% .png}.jpg”’

·rm -rf *.png(在轉換圖片的路徑下刪除.png圖片,上面指令轉換後不會自動刪除.png圖片)

1、轉換KITTI類別

PASCAL VOC資料集總共20個類別,如果用於特定場景,20個類別確實多了。此次博主為資料集設定3個類別, ‘Car’’Cyclist’’Pedestrian’只不過標註資訊中還有其他型別的車和人,直接略過有點浪費,博主希望將 ‘Van’, ‘Truck’, ‘Tram’ 合併到 ‘Car’ 類別中去,將 ‘Person_sitting’ 合併到 ‘Pedestrian’ 類別中去(‘Misc’  ‘Dontcare’ 這兩類直接忽略)。這裡使用的是modify_annotations_txt.py工具,原始碼如下:

#!/usr/bin/env python  

# -*- coding: UTF-8 -*-  

# modify_annotations_txt.py  

import glob  

import string  

txt_list = glob.glob('./Labels/*.txt')  # 儲存Labels資料夾所有txt檔案路徑

def show_category(txt_list):  

    category_list= []  

    for item in txt_list:  

        try:  

            with open(item) as tdf:  

                for each_line in tdf:  

                    labeldata = each_line.strip().split(' ') # 去掉前後多餘的字元並把其分開

                    category_list.append(labeldata[0]) # 只要第一個欄位,即類別

        except IOError as ioerr:  

            print('File error:'+str(ioerr))  

    print(set(category_list)) # 輸出集合

def merge(line):  

    each_line=''  

    for i in range(len(line)):  

        if i!= (len(line)-1):  

            each_line=each_line+line[i]+' '  

        else:  

            each_line=each_line+line[i] # 最後一條欄位後面不加空格

    each_line=each_line+'\n'  

    return (each_line)  

print('before modify categories are:\n')  

show_category(txt_list)  

for item in txt_list:  

    new_txt=[]  

    try:  

        with open(item, 'r') as r_tdf:  

            for each_line in r_tdf:  

                labeldata = each_line.strip().split(' ')  

                if labeldata[0] in ['Truck','Van','Tram','Car']:  # 合併汽車類

                    labeldata[0] = labeldata[0].replace(labeldata[0],'car')  

                if labeldata[0] in ['Person_sitting','Cyclist','Pedestrian']:  # 合併行人類

                   labeldata[0]=labeldata[0].replace(labeldata[0],

'pedestrian')  

                if labeldata[0] == 'DontCare':  # 忽略Dontcare類

                    continue  

                if labeldata[0] == 'Misc'# 忽略Misc類

                    continue  

                new_txt.append(merge(labeldata)) # 重新寫入新的txt檔案

        with open(item,'w+') as w_tdf: # w+是開啟原檔案將內容刪除,另寫新內容進去

            for temp in new_txt:  

                w_tdf.write(temp)  

    except IOError as ioerr:  

        print('File error:'+str(ioerr))  

print('\nafter modify categories are:\n')  

show_category(txt_list)

執行命令:python modify_annotations_txt.py 來執行py程式

2、轉換txt標註資訊為xml格式

對原始txt檔案進行上述處理後,接下來需要將標註檔案從txt轉化為xml並去掉標註資訊中用不上的部分,只留下3類,還有把座標值從float型轉化為int型,最後所有生成的xml檔案要存放在Annotations資料夾中。這裡使用的是txt_to_xml.py工具

#!/usr/bin/env python  

# -*- coding: UTF-8 -*-  

# txt_to_xml.py  

# 根據一個給定的XML Schema,使用DOM樹的形式從空白檔案生成一個XML

from xml.dom.minidom import Document  

import cv2  

import os  

def generate_xml(name,split_lines,img_size,class_ind):  

    doc = Document()   # 建立DOM文件物件

    annotation = doc.createElement('annotation')  

    doc.appendChild(annotation)  

    title = doc.createElement('folder')  

    title_text = doc.createTextNode('VOC2007')  

    title.appendChild(title_text)  

    annotation.appendChild(title)  

    img_name=name+'.jpg'  

    title = doc.createElement('filename')  

    title_text = doc.createTextNode(img_name)  

    title.appendChild(title_text)  

    annotation.appendChild(title)  

    source = doc.createElement('source')  

    annotation.appendChild(source)  

    title = doc.createElement('database')  

    title_text = doc.createTextNode('The VOC2007 Database')

    title.appendChild(title_text)  

    source.appendChild(title)  

    title = doc.createElement('annotation')  

    title_text = doc.createTextNode('PASCAL VOC2007')  

    title.appendChild(title_text)  

    source.appendChild(title)  

    size = doc.createElement('size')  

    annotation.appendChild(size)  

    title = doc.createElement('width')  

    title_text = doc.createTextNode(str(img_size[1]))  

    title.appendChild(title_text)  

    size.appendChild(title)  

    title = doc.createElement('height')  

    title_text = doc.createTextNode(str(img_size[0]))  

    title.appendChild(title_text)  

    size.appendChild(title)  

    title = doc.createElement('depth')  

    title_text = doc.createTextNode(str(img_size[2]))  

    title.appendChild(title_text)  

    size.appendChild(title)  

    for split_line in split_lines:  

        line=split_line.strip().split()  

        if line[0] in class_ind:  

            object = doc.createElement('object')  

            annotation.appendChild(object)  

            title = doc.createElement('name')  

            title_text = doc.createTextNode(line[0])  

            title.appendChild(title_text)  

            object.appendChild(title)  

            title = doc.createElement('difficult')  

            title_text = doc.createTextNode('0')  

            title.appendChild(title_text)  

            object.appendChild(title)  

            bndbox = doc.createElement('bndbox')  

            object.appendChild(bndbox)  

            title = doc.createElement('xmin')  

            title_text = doc.createTextNode(str(int(float(line[4]))))  

            title.appendChild(title_text)  

            bndbox.appendChild(title)  

            title = doc.createElement('ymin')  

            title_text = doc.createTextNode(str(int(float(line[5]))))  

            title.appendChild(title_text)  

            bndbox.appendChild(title)  

            title = doc.createElement('xmax')  

            title_text = doc.createTextNode(str(int(float(line[6]))))  

            title.appendChild(title_text)  

            bndbox.appendChild(title)  

            title = doc.createElement('ymax')  

            title_text = doc.createTextNode(str(int(float(line[7]))))  

            title.appendChild(title_text)  

            bndbox.appendChild(title)  

# 將DOM物件doc寫入檔案

    f = open('Annotations/'+name+'.xml','w')  

    f.write(doc.toprettyxml(indent = ''))  

    f.close()  

if __name__ == '__main__':  

    class_ind=('pedestrian', 'car')

    cur_dir=os.getcwd()  

    labels_dir=os.path.join(cur_dir,'Labels')  

    for parent, dirnames, filenames in os.walk(labels_dir): # 分別得到根目錄,子目錄和根目錄下檔案

        for file_name in filenames:  

            full_path=os.path.join(parent, file_name)   # 獲取檔案全路徑

            #print full_path  

            f=open(full_path)  

            split_lines = f.readlines()  

            name= file_name[:-4# 後四位是副檔名.txt,只取前面的檔名

            #print name  

            img_name=name+'.jpg'   

            img_path=os.path.join('./JPEGImages',img_name)  # 路徑需要自行修改

            #print img_path  

            img_size=cv2.imread(img_path).shape  

            generate_xml(name,split_lines,img_size,class_ind)  

print('all txts has converted into xmls')

執行命令:python txt_to_xml.py來執行py程式

3、生成訓練驗證集和測試集列表

最後,在相同路徑下建立資料夾ImageSets及其子資料夾