運(yùn)行環(huán)境:
Ubuntu 16.04+Python3.6+TensorFlow-gpu1.2.1+CUDA8.0+cudnn5.1
前提:
已搭建好深度學(xué)習(xí)環(huán)境,沒有的話可以看我的其它文章(待更)。
注意:
1.本文講解的是基于GPU訓(xùn)練,Python是基于Anaconda安裝;
2.Faster-R-CNN只支持Tensorflow1.2的版本,故版本不宜過高,否則報(bào)錯(cuò);
降低TensorFlow版本:
conda install tensorflow-gpu==1.2.1
選擇一個(gè)路徑下載模型:
git clone https://github.com/endernewton/tf-faster-rcnn.git
下載后會(huì)有一個(gè)tf-faster-rcnn的文件夾,進(jìn)入lib目錄下:
cd tf-faster-rcnn/lib
修改tf-faster-rcnn/lib/setup.py文件翻至最后面的-arch參數(shù),將其改為sm_61(對(duì)應(yīng)1050Ti和1080Ti)具體顯卡的算力參數(shù)配置可在這個(gè)網(wǎng)站查找https://developer.nvidia.com/cuda-gpus,算力中對(duì)應(yīng)的sm_6.1即為這里的sm_61.
vim setup.py
安裝easydict, cython, opencv-python等包:
pip install easydict
pip install cython
pip install opencv-python
pip install matplotlib
python -m pip install Pillow
在lib目錄下編譯cython:
make clean
make
cd ..
安裝COCO API:
cd data
git clone https://github.com/pdollar/coco.git
cd coco/PythonAPI
make
cd ../../..
在tf-faster-rcnn目錄下下載VOC2007數(shù)據(jù)集:
wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtrainval_06-Nov-2007.tar
wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar
wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCdevkit_08-Jun-2007.tar
解壓下載的壓縮包:
tar xvf VOCtrainval_06-Nov-2007.tar
tar xvf VOCtest_06-Nov-2007.tar
tar xvf VOCdevkit_08-Jun-2007.tar
解壓后會(huì)發(fā)現(xiàn)該目錄下出現(xiàn)了一個(gè)VOCdevkit文件夾,這就是VOC2007數(shù)據(jù)集,將VOCdevkit文件夾重命名為VOCdevkit2007,并將其移動(dòng)到data路徑下:
mv VOCdevkit/ data/VOCdevkit2007
下載預(yù)訓(xùn)練模型,github給的鏈接已失效,可在百度網(wǎng)盤下載 密碼:lzns。
下載后將其放在data目錄下,并進(jìn)行解壓:
tar xvf voc_0712_80k-110k.tgz
然后在tf-faster-rcnn目錄下建立預(yù)訓(xùn)練模型軟鏈接:
NET=res101
TRAIN_IMDB=voc_2007_trainval+voc_2012_trainval
mkdir -p output/${NET}/${TRAIN_IMDB}
cd output/${NET}/${TRAIN_IMDB}
ln -s ../../../data/voc_2007_trainval+voc_2012_trainval ./default
cd ../../.
運(yùn)行demo:
CUDA_VISIBLE_DEVICES=0 ./tools/demo.py
使用訓(xùn)練過的模型對(duì)數(shù)據(jù)進(jìn)行測(cè)試:
這里需要修改tf-faster-rcnn/lib/datasets/voc_eval.py的幾個(gè)數(shù)據(jù):
gedit lib/datasets/voc_eval.py
# with open(cachefile,'w') as f #修改前內(nèi)容
with open(cachefile,'wb') as f #修改后內(nèi)容
......
# cachefile = os.path.join(cachedir, '%s_annots.pkl' % imagesetfile) #修改前內(nèi)容
cachefile = os.path.join(cachedir, '%s_annots.pkl' % imagesetfile.split("/")[-1].split(".")[0]) #修改后內(nèi)容
接下來運(yùn)行:
GPU_ID=0
./experiments/scripts/test_faster_rcnn.sh $GPU_ID pascal_voc_0712 res101
訓(xùn)練模型:
此操作是在tf-faster-rcnn目錄下進(jìn)行
下載VGG和resnet模型,下載后對(duì)其解壓后的命名為vgg_16.ckpt和resnet_v1_101.ckpt
將其改名為vgg16.ckpt和res101.ckpt,
并在data目錄下創(chuàng)建一個(gè)imagenet_weights文件夾,
并將解壓后的文集移至該目錄下:
下載vgg16模型:
mkdir -p data/imagenet_weights
cd data/imagenet_weights
wget -v http://download.tensorflow.org/models/vgg_16_2016_08_28.tar.gz
tar -xzvf vgg_16_2016_08_28.tar.gz
mv vgg_16.ckpt vgg16.ckpt
cd ../..
下載res101模型:
mkdir -p data/imagenet_weights
cd data/imagenet_weights
wget -v http://download.tensorflow.org/models/resnet_v1_101_2016_08_28.tar.gz
tar -xzvf resnet_v1_101_2016_08_28.tar.gz
mv resnet_v1_101.ckpt res101.ckpt
cd ../..
為了節(jié)省時(shí)間并排除錯(cuò)誤,我把迭代次數(shù)只設(shè)置了20次,把./experiments/scripts/train_faster_rcnn.sh里的第22行把ITERS=70000改成ITERS=20,同時(shí)記得把./experiments/scripts/test_faster_rcnn.sh的ITERS也改成20。
執(zhí)行訓(xùn)練:
./experiments/scripts/train_faster_rcnn.sh 0 pascal_voc vgg16
注意:因?yàn)槲沂褂玫氖莗ascal_voc數(shù)據(jù)集,所以只需要更改對(duì)應(yīng)數(shù)據(jù)集的ITERS的就行了,訓(xùn)練和測(cè)試的都要改,因?yàn)樵趖rain_faster_rcnn.sh的末尾會(huì)執(zhí)行test_faster_rcnn.sh。
如果訓(xùn)練通過,不報(bào)錯(cuò),則說明程序運(yùn)行成功。
以上是各種配置及檢驗(yàn)程序能否正常運(yùn)行,下面將講解訓(xùn)練自己的數(shù)據(jù)集
替換自己的數(shù)據(jù)集:
將前面下載的VOC2007數(shù)據(jù)集中的Annatations中的文件刪去,換成自己的xml文件,將原數(shù)據(jù)集中的JPEGImages中的圖片刪去,換成自己的.jpg圖片,但需要注意的是圖片和xml文件都要為000001.jpg,000001.xml的六位數(shù)命名格式,一一對(duì)應(yīng),所有類別放在一起。
使用代碼生成訓(xùn)練集測(cè)試集:
我用的時(shí)MATLAB代碼,Python沒有嘗試,在此貼出作為備忘。
Python代碼:
#注意修改路徑,代碼中的Annotations和Imagesets文件均為VOCdevkit/VOC2007/路徑下的文件,自己操作時(shí)要寫對(duì)自己的文件路徑,否則生成的.txt文件錯(cuò)誤會(huì)導(dǎo)致程序無法運(yùn)行
import os
import random
def _main():
trainval_percent = 0.5
train_percent = 0.5
xmlfilepath = 'Annotations' #存放xml文件的路徑
total_xml = os.listdir(xmlfilepath)
num = len(total_xml)
list = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list, tv)
train = random.sample(trainval, tr)
ftrainval = open('ImageSets/Main/trainval.txt', 'w')
ftest = open('ImageSets/Main/test.txt', 'w')
ftrain = open('ImageSets/Main/train.txt', 'w')
fval = open('ImageSets/Main/val.txt', 'w')
for i in list:
name = total_xml[i][:-4] + '\n'
if i in trainval:
ftrainval.write(name)
if i in train:
ftest.write(name)
else:
fval.write(name)
else:
ftrain.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest.close()
if __name__ == '__main__':
_main()
MATLAB代碼:
%%
%該代碼根據(jù)已生成的xml,制作VOC2007數(shù)據(jù)集中的trainval.txt;train.txt;test.txt和val.txt
%trainval占總數(shù)據(jù)集的50%,test占總數(shù)據(jù)集的50%;train占trainval的50%,val占trainval的50%;
%上面所占百分比可根據(jù)自己的數(shù)據(jù)集修改,如果數(shù)據(jù)集比較少,test和val可少一些
%%
%注意修改下面兩個(gè)路徑
xmlfilepath='Annotations';
txtsavepath='ImageSets\Main\';
xmlfile=dir(xmlfilepath);
numOfxml=length(xmlfile)-2;%減去.和.. 總的數(shù)據(jù)集大小
trainval=sort(randperm(numOfxml,floor(numOfxml/2)));%trainval為數(shù)據(jù)集的50%
test=sort(setdiff(1:numOfxml,trainval));%test為剩余50%
trainvalsize=length(trainval);%trainval的大小
train=sort(trainval(randperm(trainvalsize,floor(trainvalsize/2))));
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);
將Annotations和JPEGImages文件路徑設(shè)置好后運(yùn)行,會(huì)生成四個(gè).txt文件,分別是:
test.txt,train.txt,trainval.txt,val.txt
將這四個(gè)文件放到下面兩個(gè)目錄下:
tf-faster-rcnn/data/VOCdevkit2007/VOC2007/ImageSets/Layout
tf-faster-rcnn/data/VOCdevkit2007/VOC2007/ImageSets/Main
在tf-faster-rcnn/lib/datasets目錄下的pascal_voc.py里第36行更改自己的類別,'background'切記不可刪掉,把后面的原來的20個(gè)label換成自己的
self._classes = ('__background__', 'man', 'woman')
'#自己的類名'
在train_faster_rcnn.sh和test_faster_rcnn.sh中修改迭代次數(shù):
ITEMS=#自己設(shè)置,本人設(shè)置為50000
在開始訓(xùn)練之前,還需要把之前訓(xùn)練產(chǎn)生的模型以及cache刪除掉,分別在下面三個(gè)路徑下:
tf-faster-rcnn/output/vgg16/voc_2007_trainval/default
tf-faster-rcnn/data/cache
tf-faster-rcnn/data/VOCdevkit2007/annotations_cache
然后就可以開始訓(xùn)練了:
./experiments/scripts/train_faster_rcnn.sh 0 pascal_voc vgg16
把后面的vgg16換成res101即可更改模型進(jìn)行訓(xùn)練,訓(xùn)練中會(huì)將模型保存在以下目錄中:
output/vgg16/voc_2007_trainval/default
output/res101/voc_2007_trainval/default
到此為止,已經(jīng)成功訓(xùn)練了自己的數(shù)據(jù)集,但如何讓它顯示檢測(cè)結(jié)果的圖片呢?下面將進(jìn)行講解。
運(yùn)行demo顯示自己的數(shù)據(jù)的測(cè)試結(jié)果:
在tools文件目錄下,打開demo.py文件修改參數(shù):
修改類別:
CLASSES = ('__background__', 'man', 'woman', '#自己的類')
修改模型:
主要是修改迭代次數(shù),最后的70000,10000就是對(duì)應(yīng)模型在訓(xùn)練至該迭代次數(shù)下保存的模型參數(shù)
NETS = {'vgg16': ('vgg16_faster_rcnn_iter_70000.ckpt',),'res101':('res101_faster_rcnn_iter_10000.ckpt',)}
修改類別:
net.create_architecture("TEST",3, # 自己的類別數(shù)+1
tag='default',anchor_scales=[8, 16, 32])
將圖片換成自己要測(cè)試的圖片:
im_names = ['000033.jpg', '000062.jpg', '000279.jpg',
'000603.jpg', '000798.jpg', '001080.jpg',
'001084.jpg', '001210.jpg', '001587.jpg',
'001851.jpg', '001852.jpg', '000000.jpg']
這里需要注意的是自己要測(cè)試的圖片必須放在data/demo路徑下,否則需要修改demo.py中存放demo測(cè)試圖片的路徑,相對(duì)麻煩容易出錯(cuò)。
運(yùn)行demo:
./tools/demo.py
注意,這里默認(rèn)為res101模型做demo測(cè)試,如果想換做vgg16模型測(cè)試demo,則要進(jìn)行如下操作:
在tf-faster-rcnn下建立路徑:
output/vgg16/voc_2007_trainval+voc_2012_trainval/default
將訓(xùn)練保存在output/vgg16/voc_2007_trainval/default路徑中的vgg16模型中的同一迭代次數(shù)下的4個(gè)文件復(fù)制到上面建立的路徑下,然后將其中的.pkl文件重命名為.ckpt文件,即可。
然后運(yùn)行代碼,指定網(wǎng)絡(luò)為vgg16:
python ./tools/demo.py --net vgg16
批量測(cè)試test.txt中的圖片并將結(jié)果保存在文件夾中
前面的demo只能測(cè)試自己指定的幾張圖片,如果想測(cè)試大量圖片會(huì)比較麻煩,這里舉例批量測(cè)試test.txt中的圖片,并將結(jié)果保存在文件中。
這里需要修改demo.py文件:
#!/usr/bin/env python
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import _init_paths
from model.config import cfg
from model.test import im_detect
from model.nms_wrapper import nms
from utils.timer import Timer
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
import os, cv2
import argparse
from nets.vgg16 import vgg16
from nets.resnet_v1 import resnetv1
CLASSES = ('__background__',
'man', 'woman', 'car') # 修改自己的類別
NETS = {'vgg16': ('vgg16_faster_rcnn_iter_70000.ckpt',),'res101': ('res101_faster_rcnn_iter_50000.ckpt',)} # 修改自己的模型名字
DATASETS= {'pascal_voc': ('voc_2007_trainval',),'pascal_voc_0712': ('voc_2007_trainval+voc_2012_trainval',)}
def vis_detections(image_name, im, class_name, dets, thresh=0.5): # 此處的函數(shù)添加一個(gè)形參
"""Draw detected bounding boxes."""
inds = np.where(dets[:, -1] >= thresh)[0]
if len(inds) == 0:
return
im = im[:, :, (2, 1, 0)]
fig, ax = plt.subplots(figsize=(12, 12))
ax.imshow(im, aspect='equal')
for i in inds:
bbox = dets[i, :4]
score = dets[i, -1]
ax.add_patch(
plt.Rectangle((bbox[0], bbox[1]),
bbox[2] - bbox[0],
bbox[3] - bbox[1], fill=False,
edgecolor='red', linewidth=3.5)
)
ax.text(bbox[0], bbox[1] - 2,
'{:s} {:.3f}'.format(class_name, score),
bbox=dict(facecolor='blue', alpha=0.5),
fontsize=14, color='white')
ax.set_title(('{} detections with '
'p({} | box) >= {:.1f}').format(class_name, class_name,
thresh),
fontsize=14)
plt.axis('off')
plt.tight_layout()
plt.draw()
# 添加下面兩行,注意修改路徑
plt.savefig('/home/pxt/tf-faster-rcnn/result/'+image_name) # 保存結(jié)果的路徑
print("save image to /home/pxt/tf-faster-rcnn/result/{}".format(image_name))
def demo(image_name, sess, net): #第一個(gè)形參
"""Detect object classes in an image using pre-computed object proposals."""
# Load the demo image
im_file = os.path.join(cfg.DATA_DIR, 'demo', image_name)
im = cv2.imread(im_file)
# Detect all object classes and regress object bounds
timer = Timer()
timer.tic()
scores, boxes = im_detect(sess, net, im)
timer.toc()
print('Detection took {:.3f}s for {:d} object proposals'.format(timer.total_time, boxes.shape[0]))
# Visualize detections for each class
CONF_THRESH = 0.8
NMS_THRESH = 0.3
for cls_ind, cls in enumerate(CLASSES[1:]):
cls_ind += 1 # because we skipped background
cls_boxes = boxes[:, 4*cls_ind:4*(cls_ind + 1)]
cls_scores = scores[:, cls_ind]
dets = np.hstack((cls_boxes,
cls_scores[:, np.newaxis])).astype(np.float32)
keep = nms(dets, NMS_THRESH)
dets = dets[keep, :]
vis_detections(image_name, im, cls, dets, thresh=CONF_THRESH) # 添加此處調(diào)用的參數(shù)
def parse_args():
"""Parse input arguments."""
parser = argparse.ArgumentParser(description='Tensorflow Faster R-CNN demo')
parser.add_argument('--net', dest='demo_net', help='Network to use [vgg16 res101]',
choices=NETS.keys(), default='res101')
parser.add_argument('--dataset', dest='dataset', help='Trained dataset [pascal_voc pascal_voc_0712]',
choices=DATASETS.keys(), default='pascal_voc_0712')
args = parser.parse_args()
return args
if __name__ == '__main__':
cfg.TEST.HAS_RPN = True # Use RPN for proposals
args = parse_args()
# model path
demonet = args.demo_net
dataset = args.dataset
tfmodel = os.path.join('output', demonet, DATASETS[dataset][0], 'default',
NETS[demonet][0])
if not os.path.isfile(tfmodel + '.meta'):
raise IOError(('{:s} not found.\nDid you download the proper networks from '
'our server and place them properly?').format(tfmodel + '.meta'))
# set config
tfconfig = tf.ConfigProto(allow_soft_placement=True)
tfconfig.gpu_options.allow_growth=True
# init session
sess = tf.Session(config=tfconfig)
# load network
if demonet == 'vgg16':
net = vgg16()
elif demonet == 'res101':
net = resnetv1(num_layers=101)
else:
raise NotImplementedError
net.create_architecture("TEST", 4, # 類別+1
tag='default', anchor_scales=[8, 16, 32])
saver = tf.train.Saver()
saver.restore(sess, tfmodel)
print('Loaded network {:s}'.format(tfmodel))
# 添加下面幾行
fi=open('/home/pxt/tf-faster-rcnn/data/VOCdevkit2007/VOC2007/ImageSets/Main/test.txt')#輸入要批量檢測(cè)的圖片名字合集,直接用訓(xùn)練時(shí)的test.txt就行。
txt=fi.readlines()
im_names = []
for line in txt:
line=line.strip('\n')
line=(line+'.jpg')
im_names.append(line)
print(im_names)
fi.close()
# 把之前的這幾行注釋或刪去
#im_names = ['000033.jpg', '000062.jpg', '000279.jpg',
# '000603.jpg', '000798.jpg', '001080.jpg',
# '001084.jpg', '001210.jpg', '001587.jpg',
# '001851.jpg', '001852.jpg', '000000.jpg']
for im_name in im_names:
print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
print('Demo for data/demo/{}'.format(im_name))
demo(im_name, sess, net)
#plt.show() #最好注釋這一行,不然會(huì)將大量圖片全部顯示出來