windows系統下使用yolo3訓練自己的數據集並進行物體檢測測試
用yolo3對自己的數據集進行訓練,查閱一些相關文章,發現都是使用老版本的文章,一些細節方面有很多改動,我在文章中會列出我遇到的一些新問題和一些老問題,在寫訓練步驟的同時多爲大家列出一些常見問題。新手入門大神們多多指點。
如果是linux環境下可以訪問參考文章
模型代碼下載地址:
https://github.com/qqwweee/keras-yolo3
參考文章:
1.https://blog.csdn.net/m0_37857151/article/details/81330699
2.https://blog.csdn.net/patrick_Lxc/article/details/80615433
一、下載項目源碼,進行快速測試
進入模型代碼網址,下載源碼目錄到本地,根據github作者的指導完成快速測試,由於現在新代碼跟老本版不一樣了,相關命令有些許的改動。
1.步驟一讓去yolo官網下載一個權重文件:https://pjreddie.com/media/files/yolov3.weights,文件200多M,如果沒有VPN下載速度慢的話可以從這裏下載:https://pan.baidu.com/s/15NsB5hbwa_N-eJ6-sNwgLw 提取碼:kj44 。
2.下載好weight權重文件後放入在github上下載的keras-yolo3-master文件夾下,執行convert.py文件,用於將權重文件轉爲 .h5格式的文件,生成的h5將被保存在model_data目錄下。在當前目錄下打開終端並輸入命令:
python convert.py yolov3.cfg yolov3.weights model_data/yolo.h5
3.用生成好的.h5文件進行目標檢測。
a:如果對圖片進行測試,輸入命令,運行後會讓你在終端上輸入圖片路徑:
python yolo_video.py --image
b:如果要對視頻進行測試,直接輸入命令:
python3 yolo_video.py --input=原視頻地址 --output=新生成地址
+++++++++++++++++++++++++++++++++++++++++++++++++分割線
貼個效果圖:
二、用自己的數據集訓練自已的模型
文章開頭列出的參考文章是使用了VOC2007數據集的格式,新手不用在意是否使用什麼VOC格式,之所以用這個本質是爲減少對源代碼的修改量,就算自己創建幾個文件夾也無所謂,無非就是在代碼中修改目錄罷了。
我們創建的文件夾,第一Annotations是儲存標註後的xml文件,第二ImageSets/Main是存儲生成所需文本文件,第三JPEGImages是存儲圖片,第四個model_data儲存我們的classes和最終生成的.h5文件,第五個yolo3是在githup下載模型中的文件夾,沒有改動,直接複製過來就好,後面其它的文件也是模型中複製過來或者後文中貼出的代碼。
注:爲了少改代碼大家可以參考我的VOC2007目錄,跟原版本格式有刪減,剔除了沒用的部分:
第一步:創建JPEGImages文件夾,將你要訓練的圖片放到JPEGImages中。
第二步:闖將Annotations文件夾,將你標註所生成的xml文件放到Annotations文件夾中。如果不知道怎麼標註的童鞋們點擊藍色字體:講述如何標註的文章
第三步:創建ImageSets/Main文件夾,這個文件夾用來儲存我們的對數據集進行 訓練和驗證拆分後的文本。
第四步:對我們的數據集進行分類,分成train.txt和val.txt。
複製以下代碼至根目錄VOC2007下,命名convert_to_txt.py
import os
import random
trainval_percent = 0.1
train_percent = 0.9
xmlfilepath = 'Annotations'
txtsavepath = 'ImageSets\Main'
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()
此代碼在上述參考文章中都有,該目錄的朋友注意下目錄地址,按本文創建的目錄不需要刪減。
運行後在ImageSets/Main目錄下生成4個txt文本:
第五步:將生成的txt文本重新整理生成我們yolo3模型需要的txt格式,就是把圖片的文件名和xml文件中box的座標融合在一起。運行voc_annotation.py文件,注意這裏面是要進行修改的:
import xml.etree.ElementTree as ET
from os import getcwd
sets=[('2007', 'train'), ('2007', 'val'), ('2007', 'test')]
classes = [‘car’,'person','bus']
def convert_annotation(year, image_id, list_file):
in_file = open('Annotations/%s.xml'%( image_id))
tree=ET.parse(in_file)
root = tree.getroot()
for obj in root.iter('object'):
difficult = obj.find('difficult').text
cls = obj.find('name').text
if cls not in classes or int(difficult)==1:
continue
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
b = (int(xmlbox.find('xmin').text), int(xmlbox.find('ymin').text), int(xmlbox.find('xmax').text), int(xmlbox.find('ymax').text))
list_file.write(" " + ",".join([str(a) for a in b]) + ',' + str(cls_id))
wd = getcwd()
for year, image_set in sets:
image_ids = open('ImageSets/Main/%s.txt'%(image_set)).read().strip().split()
list_file = open('%s.txt'%(image_set), 'w')
for image_id in image_ids:
list_file.write('JPEGImages/%s.jpg'%(image_id))
convert_annotation(year, image_id, list_file)
list_file.write('\n')
list_file.close()
注意:
1.第6行的 classes 改成你們自己的類
2.第10,27,30行的目錄是否是自己的目錄
3.我與原文的目錄稍加不同,如果完全按照本文目錄生成的直接複製上面代碼
4.新生成的txt內容是這個樣子的!!!!!!!
第六步:創建model_data文件夾,並生成voc_classes.txt,裏面填寫的是你的classes。
第七步:修改train.py文件。爲什麼修改呢?在參考文章中說到:因爲原作者的代碼中會加載預先對coco數據集已經訓練完成的yolo3權重文件,我們不需要預加載他以前訓練過的權重,我們要訓練自己的模型,所以我們剔除掉原文中沒用的東西。下面貼出修改後的train.py,我也是在參考文章中直接複製的,謝謝這些大哥們的代碼。
"""
Retrain the YOLO model for your own dataset.
"""
import numpy as np
import keras.backend as K
from keras.layers import Input, Lambda
from keras.models import Model
from keras.callbacks import TensorBoard, ModelCheckpoint, EarlyStopping
from yolo3.model import preprocess_true_boxes, yolo_body, tiny_yolo_body, yolo_loss
from yolo3.utils import get_random_data
def _main():
annotation_path = 'train.txt'
log_dir = 'logs/000/'
classes_path = 'model_data/voc_classes.txt'
anchors_path = 'model_data/yolo_anchors.txt'
class_names = get_classes(classes_path)
anchors = get_anchors(anchors_path)
input_shape = (416, 416) # multiple of 32, hw
model = create_model(input_shape, anchors, len(class_names))
train(model, annotation_path, input_shape, anchors, len(class_names), log_dir=log_dir)
def train(model, annotation_path, input_shape, anchors, num_classes, log_dir='logs/'):
model.compile(optimizer='adam', loss={
'yolo_loss': lambda y_true, y_pred: y_pred})
logging = TensorBoard(log_dir=log_dir)
checkpoint = ModelCheckpoint(log_dir + "ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5",
monitor='val_loss', save_weights_only=True, save_best_only=True, period=1)
batch_size = 10
val_split = 0.1
with open(annotation_path) as f:
lines = f.readlines()
np.random.shuffle(lines)
num_val = int(len(lines) * val_split)
num_train = len(lines) - num_val
print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))
model.fit_generator(data_generator_wrap(lines[:num_train], batch_size, input_shape, anchors, num_classes),
steps_per_epoch=max(1, num_train // batch_size),
validation_data=data_generator_wrap(lines[num_train:], batch_size, input_shape, anchors,
num_classes),
validation_steps=max(1, num_val // batch_size),
epochs=100,
initial_epoch=0)
model.save_weights(log_dir + 'trained_weights.h5')
def get_classes(classes_path):
with open(classes_path) as f:
class_names = f.readlines()
class_names = [c.strip() for c in class_names]
return class_names
def get_anchors(anchors_path):
with open(anchors_path) as f:
anchors = f.readline()
anchors = [float(x) for x in anchors.split(',')]
return np.array(anchors).reshape(-1, 2)
def create_model(input_shape, anchors, num_classes, load_pretrained=False, freeze_body=False,
weights_path='model_data/yolo_weights.h5'):
K.clear_session() # get a new session
image_input = Input(shape=(None, None, 3))
h, w = input_shape
num_anchors = len(anchors)
y_true = [Input(shape=(h // {0: 32, 1: 16, 2: 8}[l], w // {0: 32, 1: 16, 2: 8}[l], \
num_anchors // 3, num_classes + 5)) for l in range(3)]
model_body = yolo_body(image_input, num_anchors // 3, num_classes)
print('Create YOLOv3 model with {} anchors and {} classes.'.format(num_anchors, num_classes))
if load_pretrained:
model_body.load_weights(weights_path, by_name=True, skip_mismatch=True)
print('Load weights {}.'.format(weights_path))
if freeze_body:
# Do not freeze 3 output layers.
num = len(model_body.layers) - 3
for i in range(num): model_body.layers[i].trainable = False
print('Freeze the first {} layers of total {} layers.'.format(num, len(model_body.layers)))
model_loss = Lambda(yolo_loss, output_shape=(1,), name='yolo_loss',
arguments={'anchors': anchors, 'num_classes': num_classes, 'ignore_thresh': 0.5})(
[*model_body.output, *y_true])
model = Model([model_body.input, *y_true], model_loss)
return model
def data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes):
n = len(annotation_lines)
np.random.shuffle(annotation_lines)
i = 0
while True:
image_data = []
box_data = []
for b in range(batch_size):
i %= n
image, box = get_random_data(annotation_lines[i], input_shape, random=True)
image_data.append(image)
box_data.append(box)
i += 1
image_data = np.array(image_data)
box_data = np.array(box_data)
y_true = preprocess_true_boxes(box_data, input_shape, anchors, num_classes)
yield [image_data, *y_true], np.zeros(batch_size)
def data_generator_wrap(annotation_lines, batch_size, input_shape, anchors, num_classes):
n = len(annotation_lines)
if n == 0 or batch_size <= 0: return None
return data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes)
if __name__ == '__main__':
_main()
直接運行train.py就可以了,至此訓練步驟結束。其中有一些運行問題我將在文章末尾寫出來,大家可以先看看在進行訓練,避免做無用功!!!
三、使用生成好的模型文件來預測
我是用1080ti 11G顯存跑的,其中bitch_size=10 epochs=80,訓練了6個小時,loss訓練到30。參考文章中說loss訓練到10就可以了,我實在懶得等的就先湊活用了。我先說下步驟。
第一步:可以根據第一章的內容,快速測試!!!!!!
第二步:用原文中的方式得一張一張圖片輸入路徑太慢了,我對代碼進行了修改,批量對圖片進行測試。
->先創建一個pic文件,裏面儲存待測試圖片。在生成一個res文件,儲存測試結果。
->將新建yolo_image.py文件,直接複製下面代碼:
import time
import argparse
from yolo import YOLO, detect_video
from PIL import Image
import os
def detect_img(yolo):
filename = os.listdir('pic')
for i in filename:
img = f"pic/{i}"
try:
image = Image.open(img)
except:
print('Open Error! Try again!')
continue
else:
r_image = yolo.detect_image(image)
r_image.save(f'res/{i}')
yolo.close_session()
if __name__ == '__main__':
print("=========================開始預測===============================")
start = time.time()
parser = argparse.ArgumentParser(argument_default=argparse.SUPPRESS)
'''
Command line options
'''
parser.add_argument(
'--model', type=str,
help='path to model weight file, default ' + YOLO.get_defaults("model_path")
)
parser.add_argument(
'--anchors', type=str,
help='path to anchor definitions, default ' + YOLO.get_defaults("anchors_path")
)
parser.add_argument(
'--classes', type=str,
help='path to class definitions, default ' + YOLO.get_defaults("classes_path")
)
parser.add_argument(
'--gpu_num', type=int,
help='Number of GPU to use, default ' + str(YOLO.get_defaults("gpu_num"))
)
parser.add_argument(
'--image', default=False, action="store_true",
help='Image detection mode, will ignore all positional arguments'
)
'''
Command line positional arguments -- for video detection mode
'''
parser.add_argument(
"--input", nargs='?', type=str,required=False,default='./path2your_video',
help = "Video input path"
)
parser.add_argument(
"--output", nargs='?', type=str, default="",
help = "[Optional] Video output path"
)
FLAGS = parser.parse_args()
detect_img(YOLO(**vars(FLAGS)))
end = time.time()
t = end-start
print('用時:',int(t),'s')
直接運行 yolo_image.py,結果直接在res文件中查看即可。
++++++++++++++++++++++++++++++++++++++++++++分割線
貼測試效果圖:
2600張數據集,訓練時間6小時,loss=30,訓練時間太短效果一般,識別效果時有時無。
四、訓練過程中的問題及注意事項
1.在訓練自己的數據集之前,執行train.py之前,先在目錄下創建logs/000文件夾,否則訓練幾個小時後,在完成的那一刻報錯!!!!太坑了這個問題!
OSError: Unable to open file
2.我分別用1080ti 11g和2080 8g顯卡跑,1080ti可以直接跑,但是2080跑回報顯存不足的錯誤,這個錯誤我們先從模型本身入手,先將train.py中bitch_size值改小,最好先改成1,然後我在運行,依然會報顯存不足。
tensorflow.python.framework.errors_impl.InternalError: Blas SGEMM launch failed : m=43264, n=32, k=64
[[{{node conv2d_3/convolution}}]]
[[{{node loss/add_74}}]]
然後我覺得8G顯存足夠了,用faster-rcnn和ssd都沒問題,我在想會不會是因爲顯存一下被佔用滿導致顯卡運行有問題,然後我在yolo3文件夾中的model.py中添加了限制gpu使用的代碼:
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
config.gpu_options.per_process_gpu_memory_fraction = 0.8
set_session(tf.Session(config=config))
此時bitch_size=1,完美執行。然後又一點點試,bitch_size設置爲5都沒有問題。
3.以上是兩個讓我比較深刻的問題,還有一些問題暫且想不起來,如果有人提問我將問題寫在這裏。或者去參考文章下看看提問興許對你們有幫助。