windows系统下使用yolo3训练自己的数据集并进行物体检测测试

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文件夹,这个文件夹用来储存我们的对数据集进行 训练和验证拆分后的文本。

扫描二维码关注公众号,回复: 8485953 查看本文章

第四步:对我们的数据集进行分类,分成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.以上是两个让我比较深刻的问题,还有一些问题暂且想不起来,如果有人提问我将问题写在这里。或者去参考文章下看看提问兴许对你们有帮助。

发布了1 篇原创文章 · 获赞 1 · 访问量 171

猜你喜欢

转载自blog.csdn.net/weixin_45058635/article/details/103892227
今日推荐