在Yolov1原理详细解读及实战(一)理论篇 中,我们对Yolov1网络结构、算法流程、训练及推理原理进行了详细剖析,本章进入实战环节,话不多说,马上开始!
vscode+WSL:Ubuntu 18.04+python 3.9.7
YOLOv1是Darknet深度学习框架进行训练和推理,首先安装Darknet。
步骤1:安装Darknet
git clone https://github.com/pjreddie/darknet
cd darknet
make -j8
wget http://pjreddie.com/media/files/yolov1.weights
步骤3:验证是否安装成功
./darknet yolo test cfg/yolov1.cfg yolov1.weights data/kite.jpg
运行结束后,在darknet目录下打开predictions.jpg即可查看结果。
步骤1:下载VOC数据集。
wget https://pjreddie.com/media/files/VOCtrainval_06-Nov-2007.tar
解压后的文件结构如下:
└── VOCdevkit #根目录
└── VOC2007 #不同年份的数据集,这里只下载了2007
├── Annotations #存放xml文件,与JPEGImages中的图片一一对应,解释图片的内容等等
├── ImageSets #该目录下存放的都是txt文件,txt文件中每一行包含一个图片的名称,末尾会加上±1表示正负样本
│ ├── Action
│ ├── Layout
│ ├── Main #存放的是分类和检测的数据集分割文件
│ │ ├── train.txt #用于训练的图片名称
│ │ ├── val.txt #用于验证的图片名称
│ │ ├── test.txt #用于测试的图片名称
│ │ ├── trainval.txt #train与val的合集
│ └── Segmentation
├── JPEGImages #存放源图片
├── SegmentationClass #存放的是图片,语义(class)分割相关
└── SegmentationObject #存放的是图片,实例(object)分割相关
步骤2:将VOC2007格式数据转换为Yolo训练的格式。
Darknet需要的label不是xml格式,而是一张图片对应一个txt的形式,即每个标注框占一行。因此需要将xml格式转为txt格式。代码如下:
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
sets=[('2007', 'train'), ('2007', 'val')]
classes = ["bicycle", "boat", "dog", "cat","person"]
def convert(size, box):
dw = 1./(size[0])
dh = 1./(size[1])
x = (box[0] + box[1])/2.0 - 1
y = (box[2] + box[3])/2.0 - 1
w = box[1] - box[0]
h = box[3] - box[2]
x = x*dw
w = w*dw
y = y*dh
h = h*dh
return (x,y,w,h)
def convert_annotation(year, image_id):
in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml'%(year, image_id))
out_file = open('VOCdevkit/VOC%s/labels/%s.txt'%(year, image_id), 'w')
tree=ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
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 = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))
bb = convert((w,h), b)
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
wd = getcwd()
print(wd)
for year, image_set in sets:
if not os.path.exists('VOCdevkit/VOC%s/labels/'%(year)):
os.makedirs('VOCdevkit/VOC%s/labels/'%(year))
image_ids = open('VOCdevkit/VOC%s/ImageSets/Main/%s.txt'%(year, image_set)).read().strip().split()
list_file = open('%s_%s.txt'%(year, image_set), 'w')
for image_id in image_ids:
list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg\n'%(wd, year, image_id))
convert_annotation(year, image_id)
list_file.close()
os.system("cat 2007_train.txt 2007_val.txt> train.txt")
运行结束之后,可以看到,生成了labels文件夹和2007_train.txt、2007_val.txt、train.txt。
labels文件夹中存放了每张图片的标注文件。例如:8 0.585 0.7306666666666667 0.122 0.3413333333333333
。含义如下:
其中,x,y,width和height是指标注框的左上角点坐标及宽高信息。
2007_train.txt、2007_val.txt表示训练集和验证集中图片的路径。代码最后执行 type 2007_train.txt 2007_val.txt > train.txt
将训练集和验证集放在了同一个txt中。
步骤1:修改darknet/src/yolo.c
//改为自己的数据集类别
char *voc_names[] = {"bicycle", "boat", "dog", "cat","person"};
// train_images保存的是上述生成的train.txt
char *train_images = "VOCdevkit/VOC2007/train.txt";
//backup_directory是训练过程中生成的weights权重文件保存的路径
char *backup_directory = "darknet/backup/";
//修改数据集类别数为5(我的是5类)
draw_detections(im, l.side*l.side*l.n, thresh, boxes, probs, voc_names, alphabet, 5);
else if(0==strcmp(argv[2], "demo")) demo(cfg, weights, thresh, cam_index, filename, voc_names, 5, frame_skip, prefix);
步骤2:修改darknet/src/yolo_kernels.cu
# 修改数据集类别数为5(我的是5类)
draw_detections(det, l.side*l.side*l.n, demo_thresh, boxes, probs, voc_names, voc_labels, 5);
步骤3:修改darknet/cfg/yolov1/yolo.cfg
output= 735 //最后一层的全连接层,output=(5×2+classnum(=5))×7×7)
activation=linear
[detection]
classes= 5 //数据集类别
步骤4:重新编译Darknet。
cd darknet
make -j8
步骤1:下载darknet.conv.weights。darknet.conv.weights是卷积网络在分类网络上预训练的权重,在此基础上训练。
wget http://pjreddie.com/media/files/darknet.conv.weights
步骤2:训练。
./darknet yolo train cfg/yolov1/yolo.cfg darknet.conv.weights
一切正常的话,就开始训练了。如图所示。
./darknet yolo test cfg/yolov1/yolo.cfg backup/yolo_final.weights
然后输入一张图片,测试结果如下: