上个月需要使用目标检测进行项目开发,简单了解了下目标检测的技术之后,果断选择使用yolo来进行尝试。
在查看了无数篇博客之后,头疼脑热,各种花样的问题之后,果断记录了自行爬坑的过程,希望对后续使用yolo3目标检测的小伙伴有所帮助。

1.搭建yolov3训练的基础环境

1.1软件版本

需要安装的版本为:

  • python 3.6
  • tensorflow 2.0.0
  • keras 2.3.1

切记keras与tensorflow的版本必须匹配,可以参照此链接

1.2下载yolov3对应的源码压缩文件

我把之前我使用成功的一套放在此链接,大家可以直接使用此压缩包(里面包含了yolov3.weights文件)

自行提取

链接:https://pan.baidu.com/s/1-lWJYvslr3XZWHvXdlnjhA  密码:qu6t

1.3测试yolov3环境

解压开上述解压包之后,进入解压之后的目录,在终端输入命令:

python3 yolo_video.py --image

根据提示输入图片路径名称进行测试。

具体操作截图如下:

yolov 目标检测fps越大越好吗 yolov3目标检测完整步骤_神经网络


yolov 目标检测fps越大越好吗 yolov3目标检测完整步骤_python_02


yolov 目标检测fps越大越好吗 yolov3目标检测完整步骤_python_03


至此Tensorflow2+keras下的yolov3环境下的图片目标检测搭建完毕!!!!!

2.使用yolov3训练自己的数据集

2.1.根据yolov3的数据格式要求整理数据集

格式要求如下:图片所在路径名称 x,y,w,h,所属类别(从0开始,与class文件顺序对应)
例如:

/Users/angelia/Documents/detection/crown/images/015.jpg 81,13,674,558,0
/Users/angelia/Documents/detection/crown/images/016.jpg 18,27,571,447,1
/Users/angelia/Documents/detection/crown/images/017.jpg 16,31,584,447,2

根据自己的数据图片的形式,转化为以上格式即可。

该步弯路最多,其他博客基本上都是使用标注工具是labelimg+voc制作自己的数据集。

我使用的我们项目组中自己实现的一个小工具(有需要的从链接中自行获取)用来做标注,没有专业的标注工具那么复杂,同时也就没有办法使用voc方法制作自己的数据集,在此处一度不清楚如何继续,因为我找不到yolov3到底需要什么格式的数据,最终在某一篇神秘的博客的角落中,我读懂到“哦,原来经过什么labelimg的XML格式的数据文件+VOC固定格式的转换,原来需要的数据长这个样子啊。这么简单的数据格式…顿时感觉被暴击了一万次”。

经过了一小会的脚本编写,完成了yolov3要求的数据格式。

----我把我的已知数据格式以及如何转化的脚本贴出来,供有需求的伙伴参考。

1.已知的cutlog.txt
数据文件(以下为其中三行数据,每行分别为:图片名称 x:数值 y:数值 width:数值 height:数值 old image width:数值 old image height:数值 ):

IV000338 (2).JPG x:307 y:255 width:128 height:115 old image width:768 old image height:576
IV000338.JPG x:292 y:420 width:232 height:157 old image width:768 old image height:576
IV000339 (2).JPG x:277 y:212 width:214 height:183 old image width:768 old image height:576

yolov 目标检测fps越大越好吗 yolov3目标检测完整步骤_tensorflow_04

2.数据图片存放文件夹

其中存放着不同目标的文件夹,对应存储我们裁剪的对应目标的图片

yolov 目标检测fps越大越好吗 yolov3目标检测完整步骤_大数据_05


3.执行format_label_yolo_general.py脚本产生数据

根据自己的数据目录,修改cutLog_dir 以及img_dir 以及 yolo_train_path这三个变量即可。

具体format_label_yolo_general.py内容如下:

import os

#裁剪之后的数据父目录
cutLog_dir = 'handle_nozzle_data_24/'
#获取图片所在目录的绝对路径
img_dir='/Users/angelia/Documents/detection/test/Nozzle_ALL/'
#生成的yolo格式的训练数据文件目录
yolo_train_path='nozzle_model_data/train_yolo.txt'
def f2s(num):
    return str(round(num, 6))

def main():
    # 2. 处理标签数据
    all_txt = open(yolo_train_path, 'w')
    getYoloData(cutLog_dir,all_txt)
    all_txt.close()
    

def getYoloData(cutLog_dir,all_txt):
    # 得到目录下列表名(相当于ls命令)
    #dir_list = open(cutLog_dir,'r')
    #判断是否为目录
    if os.path.isdir(cutLog_dir):
         for dirs in os.listdir(cutLog_dir):
            if dirs.startswith('.'):
                continue
            else:
                name=os.path.join(cutLog_dir,dirs)
                getYoloData(name,all_txt)
    else:
        #不是目录是文件
        #print(cutLog_dir)
        if cutLog_dir.split(".")[1].lower()=='txt':
            file=open(cutLog_dir,'r')
            for line in file:
                #获取 图片的绝对路径 x ,y w , h ,类别
                #获取图片的名字
                imageName=line.split('.')[0]+'.'+line.split('.')[1].split(' ')[0]
                if imageName.find(' ')!=-1:
                    index=imageName.find(' ')
                    imageName=imageName[:index]+imageName[index+1:] 
                    print(imageName)
                arr=line.split(".")[1].split(' ')
                #获取xywh
                org_x=arr[1].split(':')[1]
                org_y=arr[2].split(':')[1]
                org_w=arr[3].split(':')[1]
                org_h=arr[4].split(':')[1]

                #获取类别
                dirArr=cutLog_dir.split('/')[len(cutLog_dir.split('/'))-2].split('_')
                classs=dirArr[len(dirArr)-1].split('/')[0]
                #图片的绝对路径
                tgt_path=os.path.join(img_dir,classs+'/'+imageName)
                tgt_line = tgt_path+' '+ org_x + ',' + org_y + ',' + org_w + ',' + org_h + ','+f2s(int(classs)-1)+'\n'
                all_txt.writelines(tgt_line)
if __name__ == '__main__':
    main()

执行此脚本:

python3 format_label_yolo_general.py

查看是否在yolo_train_path变量对应路径下是否生成复合要求的数据格式文件。

至此数据准备结束!!!!!!

2.2.使用脚本train.py进行训练

1.提前准备二个文件:

1.目标检测的类别标签文件 
	1.该文件中一行为一个单独的类别标签名
	2.文件名称无所谓,一般是XXX_classes.txt
2.先验框大小个数文件
	1.此文件可以使用默认yolo自带的,可以根据kmeans对自己的数据集进行聚类,获取适合自己数据的先验框
	2.此次我使用默认值,默认值可以model_data/yolo_anchors.txt中获取。
	3.一般文件名是XXX_anchors.txt

yolov 目标检测fps越大越好吗 yolov3目标检测完整步骤_神经网络_06


2.修改train.py脚本中的内容

1.指定整理成yolov3支持的数据格式的训练文件全路径名(train_yolo.txt)
2.指定存放模型的目录 该目录必须存在 eg:logs/000
3.指定目标检测的类别文件nozzle_classes.txt
4.指定先验框 默认是yolo自带的 可以根据kmeans对自己的数据集进行聚类,获取适合自己数据的先验框
5.图片大小
6.每批次的图片个数
7.迭代训练的次数
以下为涉及到修改的代码,其他代码保持不变。
def _main():
	#1.指定整理成yolov3支持的数据格式的训练文件
    annotation_path = '../nozzle_model_data/train_yolo.txt'
    #2.指定存放模型的目录 该目录必须存在
    log_dir = '../logs/000/'
    #3.指定目标检测的类别文件
    classes_path = '../nozzle_model_data/nozzle_classes.txt'
    #4.指定先验框 默认是yolo自带的 可以根据kmeans对自己的数据集进行聚类,获取适合自己数据的先验框
    anchors_path = '../nozzle_model_data/nozzle_anchors.txt'
    #获取类别标签名字
    class_names = get_classes(classes_path)
    #获取分类个数
    num_classes = len(class_names)
    #获取先验框
    anchors = get_anchors(anchors_path)
	
	#5.图片进入yolov3训练时的大小 yolov3会自动压缩处理
    #需要注意,此大小必须是32的整数倍,宽高可以不一致
    #416是yolo处理效果比较好的大小
    input_shape = (416,416) # multiple of 32, hw
    #获取模型对象
    model = create_model(input_shape, anchors, len(class_names) )
    is_tiny_version = len(anchors) == 6  # default setting
    if is_tiny_version:
        model = create_tiny_model(input_shape, anchors, num_classes,
                                  freeze_body=2, weights_path='model_data/tiny_yolo_weights.h5')
    else:
        model = create_model(input_shape, anchors, num_classes,
                             freeze_body=2,
                             weights_path='yolov3.h5')  # make sure you know what you freeze

    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=3)
    reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=3, verbose=1)
    early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1)

    val_split = 0.1
    with open(annotation_path) as f:
        lines = f.readlines()
    np.random.seed(10101)
    np.random.shuffle(lines)
    np.random.seed(None)
    num_val = int(len(lines) * val_split)
    num_train = len(lines) - num_val

    # Train with frozen layers first, to get a stable loss.
    # Adjust num epochs to your dataset. This step is enough to obtain a not bad model.
    if True:
        model.compile(optimizer=Adam(lr=1e-3), loss={'yolo_loss': lambda y_true, y_pred: y_pred})
        #model.compile(loss=keras.losses.SparseCategoricalCrossentropy(),optimizer=keras.optimizers.RMSprop(lr=2e-5),metrics=['accuracy'])
        #6.每批处理的图片个数
        batch_size = 32
        print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))
        model.fit_generator(data_generator_wrapper(lines[:num_train], batch_size, input_shape, anchors, num_classes),
                            steps_per_epoch=max(1, num_train // batch_size),
                            validation_data=data_generator_wrapper(lines[num_train:], batch_size, input_shape, anchors,
                                                                   num_classes),
                            validation_steps=max(1, num_val // batch_size),
                            #7.迭代训练的次数
                            epochs=2,
                            initial_epoch=0,
                            callbacks=[logging, checkpoint])
        model.save_weights(log_dir + 'trained_weights_stage_1.h5')

3.修改yolov3.cfg文件

[net]
# Testing 把该行之下,Training中间的注释掉,因为我们要进行训练而不是测试
#batch=1
#subdivisions=1
# Training 
batch=32  #批次,显存不够可减小,但会出现Nan问题(解决办法:增大batch。。。)
subdivisions=16 #训练迭代包含16组,每组2张图片
width=416 #指定宽度
height=416 #指定高度
channels=3
momentum=0.9
decay=0.0005 #权重衰减,防止过拟合
angle=0
saturation = 1.5
exposure = 1.5
hue=.1

learning_rate=0.001 #学习率 学习率决定了参数移动到最优值的速度快慢
burn_in=1000
max_batches = 50200
policy=steps
steps=40000,45000
scales=.1,.1
......
[convolutional]
size=1
stride=1
pad=1
filters=30  #此处的30需要根据目标检测的分类 以及 3*(分类个数+5) 
activation=linear


[yolo]
mask = 6,7,8
anchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326
classes=5 #此处的5需要根据目标检测的分类决定
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=1  #如果显存很小,将random设置为0,关闭多尺度训练
......

如何查找呢:检索yolo关键字 其 上一层的filters需要修改,一共三处。
4.修改了yolov3.cfg,需要重新生成h5文件

#进入到解压之后的目录
#删除该目录下自带的模型
rm  model_data/yolo.h5
#根据修改之后的yolov3配置文件以及yolov3的权重模型执行转换操作,将权重转化为keras支持的h5模型
python3 convert.py -w yolov3.cfg yolov3.weights model_data/yolo.h5

5.使用python3 train.py进行训练

python3 train.py

刚开始训练损失值会很大,但也会很快的降下去;
训练完成之后,可以去存放模型的目录下查看是否生成模型文件

2.3.测试训练结果

修改yolo.py文件
根据训练的模型文件进行测试(需要注意yolo.py和yolo_video.py不是原本里面的)

备份一份yolo.py

修改yolo.py里面的model_path,anchors_path,classes_path的值

1.其中model_path为自行训练之后的模型文件
2.其他两个与nozzle_train.py里的保持一致

其他配置根据自己的需求进行修改。

修改完成之后使用python3 yolo.py运行一下。

输入python3 yolo_video.py --image 根据终端提示输入图片名称。

相关资源下载

  1. 截取目标坐标的小工具链接:https://pan.baidu.com/s/1gHQSnrmP9ejSwTJeDqImvw 密码:0psd

写在最后的一些话

当前使用的是默认的权重转化成模型进行训练,因此使用的是CPU,运行速度较慢(对于小数据集效果可能感受不到)。

一般直接yolo3默认权重进行训练的会比很少,除非数据集与训练yolo3权重的图片数据非常相似。

对于我的数据集来说,相似度几乎为0,因此直接使用yolo3权重训练对于我来说,效果甚微。

下节我将直接使用darknet53训练自己数据集的权重,在对接yolo3进行目标检测。

最好,祝大家搭建过程尽可能好运!