1. 程式人生 > >py-faster-rcnn在Windows下的end2end訓練

py-faster-rcnn在Windows下的end2end訓練

一、製作資料集

1. 關於訓練的圖片

不論你是網上找的圖片或者你用別人的資料集,記住一點你的圖片不能太小,width和height最好不要小於150。需要是jpeg的圖片。

2.製作xml檔案

1)LabelImg

如果你的資料集比較小的話,你可以考慮用LabelImg手工打框https://github.com/tzutalin/labelImg。關於labelimg的具體使用方法我在這就不詳細說明了,大家可以去網上找一下。labelimg生成的xml直接就能給frcnn訓練使用。

2)自己製作xml

如果你的資料集比較小的話,你還可以考慮用上面的方法手工打框。如果你的資料集有1w+你就可以考慮自動生成xml檔案。網上有些資料基本用的是matlab座標生成xml。我給出一段python的生成xml的程式碼


def write_xml(bbox,w,h,iter):
    '''
    bbox為你儲存的當前圖片的類別的資訊和對應座標的dict
    w,h為你當前儲存圖片的width和height
    iter為你圖片的序號
    '''
    root=Element("annotation")
    folder=SubElement(root,"folder")#1
    folder.text="JPEGImages"
    filename=SubElement(root,"filename")#1
    filename.text=iter
    path=SubElement(root,"path")#1
    path.text='D:\\py-faster-rcnn\\data\\VOCdevkit2007\\VOC2007\\JPEGImages'+'\\'+iter+'.jpg'#把這個路徑改為你的路徑就行
    source=SubElement(root,"source")#1
    database=SubElement(source,"database")#2
    database.text="Unknown"
    size=SubElement(root,"size")#1
    width=SubElement(size,"width")#2
    height=SubElement(size,"height")#2
    depth=SubElement(size,"depth")#2
    width.text=str(w)
    height.text=str(h)
    depth.text='3'
    segmented=SubElement(root,"segmented")#1
    segmented.text='0'
    for i in bbox:
        object=SubElement(root,"object")#1
        name=SubElement(object,"name")#2
        name.text=i['cls']
        pose=SubElement(object,"pose")#2
        pose.text="Unspecified"
        truncated=SubElement(object,"truncated")#2
        truncated.text='0'
        difficult=SubElement(object,"difficult")#2
        difficult.text='0'
        bndbox=SubElement(object,"bndbox")#2
        xmin=SubElement(bndbox,"xmin")#3
        ymin=SubElement(bndbox,"ymin")#3
        xmax=SubElement(bndbox,"xmax")#3
        ymax=SubElement(bndbox,"ymax")#3
        xmin.text=str(i['xmin'])
        ymin.text=str(i['ymin'])
        xmax.text=str(i['xmax'])
        ymax.text=str(i['ymax'])
    xml=tostring(root,pretty_print=True)
    file=open('D:/py-faster-rcnn/data/VOCdevkit2007/VOC2007/Annotations/'+iter+'.xml','w+')#這裡的路徑也改為你自己的路徑
    file.write(xml)


3.製作訓練、測試、驗證集

這個網上可以參考的資料比較多,我直接copy一個小鹹魚的用matlab的程式碼

我建議train和trainval的部分佔得比例可以更大一點

%%  
%該程式碼根據已生成的xml,製作VOC2007資料集中的trainval.txt;train.txt;test.txt和val.txt  
%trainval佔總資料集的50%,test佔總資料集的50%;train佔trainval的50%,val佔trainval的50%;  
%上面所佔百分比可根據自己的資料集修改,如果資料集比較少,test和val可少一些  
%%  
%注意修改下面四個值  
xmlfilepath='E:\Annotations';  
txtsavepath='E:\ImageSets\Main\';  
trainval_percent=0.5;%trainval佔整個資料集的百分比,剩下部分就是test所佔百分比  
train_percent=0.5;%train佔trainval的百分比,剩下部分就是val所佔百分比  
  
  
%%  
xmlfile=dir(xmlfilepath);  
numOfxml=length(xmlfile)-2;%減去.和..  總的資料集大小  
  
  
trainval=sort(randperm(numOfxml,floor(numOfxml*trainval_percent)));  
test=sort(setdiff(1:numOfxml,trainval));  
  
  
trainvalsize=length(trainval);%trainval的大小  
train=sort(trainval(randperm(trainvalsize,floor(trainvalsize*train_percent))));  
val=sort(setdiff(trainval,train));  
  
  
ftrainval=fopen([txtsavepath 'trainval.txt'],'w');  
ftest=fopen([txtsavepath 'test.txt'],'w');  
ftrain=fopen([txtsavepath 'train.txt'],'w');  
fval=fopen([txtsavepath 'val.txt'],'w');  
  
  
for i=1:numOfxml  
    if ismember(i,trainval)  
        fprintf(ftrainval,'%s\n',xmlfile(i+2).name(1:end-4));  
        if ismember(i,train)  
            fprintf(ftrain,'%s\n',xmlfile(i+2).name(1:end-4));  
        else  
            fprintf(fval,'%s\n',xmlfile(i+2).name(1:end-4));  
        end  
    else  
        fprintf(ftest,'%s\n',xmlfile(i+2).name(1:end-4));  
    end  
end  
fclose(ftrainval);  
fclose(ftrain);  
fclose(fval);  
fclose(ftest);


4.檔案儲存路徑

jpg,txt,xml分別儲存到data\VOCdevkit2007\VOC2007\下的JPEGImages、ImageSets\Main、Annotations資料夾

二、根據自己的資料集修改檔案

1.模型配置檔案

我用end2end的方式訓練,這裡我用vgg_cnn_m_1024為例說明。所以我們先開啟models\pascal_voc\VGG_CNN_M_1024\faster_rcnn_end2end\train.prototxt,有4處需要修改

layer {
  name: 'input-data'
  type: 'Python'
  top: 'data'
  top: 'im_info'
  top: 'gt_boxes'
  python_param {
    module: 'roi_data_layer.layer'
    layer: 'RoIDataLayer'
    param_str: "'num_classes': 3" #這裡改為你訓練類別數+1
  }
}

layer {
  name: 'roi-data'
  type: 'Python'
  bottom: 'rpn_rois'
  bottom: 'gt_boxes'
  top: 'rois'
  top: 'labels'
  top: 'bbox_targets'
  top: 'bbox_inside_weights'
  top: 'bbox_outside_weights'
  python_param {
    module: 'rpn.proposal_target_layer'
    layer: 'ProposalTargetLayer'
    param_str: "'num_classes': 3" #這裡改為你訓練類別數+1
  }
}
layer {
  name: "cls_score"
  type: "InnerProduct"
  bottom: "fc7"
  top: "cls_score"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 3  #這裡改為你訓練類別數+1
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layer {
  name: "bbox_pred"
  type: "InnerProduct"
  bottom: "fc7"
  top: "bbox_pred"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 12  #這裡改為你的(類別數+1)*4
    weight_filler {
      type: "gaussian"
      std: 0.001
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
然後我們修改models\pascal_voc\VGG_CNN_M_1024\faster_rcnn_end2end\test.prototxt

layer {
  name: "relu7"
  type: "ReLU"
  bottom: "fc7"
  top: "fc7"
}
layer {
  name: "cls_score"
  type: "InnerProduct"
  bottom: "fc7"
  top: "cls_score"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  inner_product_param {
    num_output: 3  #這裡改為你訓練類別數+1
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
layer {
  name: "bbox_pred"
  type: "InnerProduct"
  bottom: "fc7"
  top: "bbox_pred"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  inner_product_param {
    num_output: 12  #這裡改為你的(類別數+1)*4
    weight_filler {
      type: "gaussian"
      std: 0.001
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}

另外在 solver裡可以調訓練的學習率等引數,在這篇文章裡不做說明

==================以下修改lib中的檔案==================

2.修改imdb.py

    def append_flipped_images(self):  
        num_images = self.num_images  
        widths = [PIL.Image.open(self.image_path_at(i)).size[0]  
                  for i in xrange(num_images)]  
        for i in xrange(num_images):  
            boxes = self.roidb[i]['boxes'].copy()  
            oldx1 = boxes[:, 0].copy()  
            oldx2 = boxes[:, 2].copy()  
            boxes[:, 0] = widths[i] - oldx2 - 1  
            boxes[:, 2] = widths[i] - oldx1 - 1
            for b in range(len(boxes)):
                if boxes[b][2]< boxes[b][0]:
                    boxes[b][0] = 0			
            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 
找到這個函式,並修改為如上

3、修改rpn層的5個檔案

在如下目錄下,將檔案中param_str_全部改為param_str

4、修改config.py

將訓練和測試的proposals改為gt

# Train using these proposals
__C.TRAIN.PROPOSAL_METHOD = 'gt'
# Test using these proposals
__C.TEST.PROPOSAL_METHOD = 'gt

5、修改pascal_voc.py

因為我們使用VOC來訓練,所以這個是我們主要修改的訓練的檔案。

 def __init__(self, image_set, year, devkit_path=None):
        imdb.__init__(self, 'voc_' + year + '_' + image_set)
        self._year = year
        self._image_set = image_set
        self._devkit_path = self._get_default_path() if devkit_path is None \
                            else devkit_path
        self._data_path = os.path.join(self._devkit_path, 'VOC' + self._year)
        self._classes = ('__background__', # always index 0
                            'cn-character','seal')
        self._class_to_ind = dict(zip(self.classes, xrange(self.num_classes)))
        self._image_ext = '.jpg'
        self._image_index = self._load_image_set_index()
        # Default to roidb handler
        self._roidb_handler = self.selective_search_roidb
        self._salt = str(uuid.uuid4())
        self._comp_id = 'comp4'

在self.classes這裡,'__background__'使我們的背景類,不要動他。下面的改為你自己標籤的內容。

修改以下2段內容。否則你的test部分一定會出問題。

 def _get_voc_results_file_template(self):
        # VOCdevkit/results/VOC2007/Main/<comp_id>_det_test_aeroplane.txt
        filename = self._get_comp_id() + '_det_' + self._image_set + '_{:s}.txt'
        path = os.path.join(
            self._devkit_path,
            'VOC' + self._year,
            'Main',
            '{}' + '_test.txt')
        return path
 def _write_voc_results_file(self, all_boxes):
        for cls_ind, cls in enumerate(self.classes):
            if cls == '__background__':
                continue
            print 'Writing {} VOC results file'.format(cls)
            filename = self._get_voc_results_file_template().format(cls)
            with open(filename, 'w+') as f:
                for im_ind, index in enumerate(self.image_index):
                    dets = all_boxes[cls_ind][im_ind]
                    if dets == []:
                        continue
                    # the VOCdevkit expects 1-based indices
                    for k in xrange(dets.shape[0]):
                        f.write('{:s} {:.3f} {:.1f} {:.1f} {:.1f} {:.1f}\n'.
                                format(index, dets[k, -1],
                                       dets[k, 0] + 1, dets[k, 1] + 1,
                                       dets[k, 2] + 1, dets[k, 3] + 1))

三、end2end訓練

1、刪除快取檔案

每次訓練前將data\cache 和 data\VOCdevkit2007\annotations_cache中的檔案刪除。

2、開始訓練

在py-faster-rcnn的根目錄下開啟git bash輸入

./experiments/scripts/faster_rcnn_end2end.sh 0 VGG_CNN_M_1024 pascal_voc

當然你可以去experiments\scripts\faster_rcnn_end2end.sh中調自己的訓練的一些引數,也可以中VGG16、ZF模型去訓練。我這裡就用預設給的引數說明。

出現了這種東西的話,那就是訓練成功了。用vgg1024的話還是很快的,還是要看你的配置,我用1080ti的話也就85min左右。我就沒有讓他訓練結束了。

四、測試

1、建立自己的demo.py

如果想方便的話,直接把已經有的demo.py複製一份,並把它的標籤改為自己的標籤,把模型改為自己的模型。

這是我的demo,類別和模型部分,供參考

CLASSES = ('__background__',
           'cn-character','seal')

NETS = {'vgg16': ('VGG16',
                  'vgg16_faster_rcnn_iter_70000.caffemodel'),
	'vgg1024':('VGG_CNN_M_1024',
		 'vgg_cnn_m_1024_faster_rcnn_iter_70000.caffemodel'),
        'zf': ('ZF',
                  'ZF_faster_rcnn_final.caffemodel')}
if __name__ == '__main__':
    cfg.TEST.HAS_RPN = True  # Use RPN for proposals

    args = parse_args()

    prototxt = os.path.join(cfg.MODELS_DIR, NETS[args.demo_net][0],
                            'faster_rcnn_end2end', 'test.prototxt')
    caffemodel = os.path.join(cfg.DATA_DIR, 'faster_rcnn_models',
                              NETS[args.demo_net][1])

    if not os.path.isfile(caffemodel):
        raise IOError(('{:s} not found.\nDid you run ./data/script/'
                       'fetch_faster_rcnn_models.sh?').format(caffemodel))

    if args.cpu_mode:
        caffe.set_mode_cpu()
    else:
        caffe.set_mode_gpu()
        caffe.set_device(args.gpu_id)
        cfg.GPU_ID = args.gpu_id
    net = caffe.Net(prototxt, caffemodel, caffe.TEST)

    print '\n\nLoaded network {:s}'.format(caffemodel)

    # Warmup on a dummy image
    im = 128 * np.ones((300, 500, 3), dtype=np.uint8)
    for i in xrange(2):
        _, _= im_detect(net, im)

    im_names = ['f1.jpg','f8.jpg','f7.jpg','f6.jpg','f5.jpg','f4.jpg','f3.jpg','f2.jpg',]
    for im_name in im_names:
        print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
        print 'Demo for data/demo/{}'.format(im_name)
        demo(net, im_name)

    plt.show()
在這個部分,將你要測試的圖片寫在im_names裡,並把圖片放在data\demo這個資料夾下。

2、輸出的模型

將output\裡你剛剛訓練好的caffemodel複製到data\faster_rcnn_models

3、結果

執行你自己的demo.py即可得到結果

我這個中文文字的識別初步還是可以的,但還需要再加強一下