上个月需要使用目标检测进行项目开发,简单了解了下目标检测的技术之后,果断选择使用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
根据提示输入图片路径名称进行测试。
具体操作截图如下:
至此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
2.数据图片存放文件夹
其中存放着不同目标的文件夹,对应存储我们裁剪的对应目标的图片
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
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
根据终端提示输入图片名称。
相关资源下载
- 截取目标坐标的小工具链接:https://pan.baidu.com/s/1gHQSnrmP9ejSwTJeDqImvw 密码:0psd
写在最后的一些话
当前使用的是默认的权重转化成模型进行训练,因此使用的是CPU,运行速度较慢(对于小数据集效果可能感受不到)。
一般直接yolo3默认权重进行训练的会比很少,除非数据集与训练yolo3权重的图片数据非常相似。
对于我的数据集来说,相似度几乎为0,因此直接使用yolo3权重训练对于我来说,效果甚微。
下节我将直接使用darknet53训练自己数据集的权重,在对接yolo3进行目标检测。
最好,祝大家搭建过程尽可能好运!