本文主要描写了使用官网给出的YOLOv3目标检测模型(也就是使用darknet),在Linux下对WIDERFACE数据集进行训练,以此来实现人脸检测的任务。
https://www.cnblogs.com/Assist/p/11091501.html
https://blog.csdn.net/lilai619/article/details/79695109
https://blog.csdn.net/sunqiande88/article/details/102414883
YOLOv3源码既可以在Linux下使用,也可以在Windows下使用,Windows的使用可以参考链接中的使用方法,本文是基于Linux下使用的。


首先修改第一处,如上图,可以修改GPU=1,表示使用GPU,CUDNN=1,表示使用CUDN加速,下面的同理,我在运行时,只使用了GPU=1,速度已经可以达到我的要求。
修改第二处,NVCC的位置,将后面的nvcc修改成自己nvcc在服务器上的位置,如下图


下载的YOLOv3代码中包含了voc_label.py文件,这个文件就是用于生成训练图片路径的代码,我修改后的代码如下,修改的地方主要就是数据集的路径,总共修改了8处,最后一行代码也可以根据自己的需求进行修改。
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')] #修改处1
classes = ["face"] #修改处2
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('/home/amax/cxw/tensorflow-yolov3-master/voc/train/VOCdevkit/VOC%s/Annotations/%s.xml'%(year, image_id)) #修改处3
out_file = open('/home/amax/cxw/tensorflow-yolov3-master/voc/train/VOCdevkit/VOC%s/labels/%s.txt'%(year, image_id), 'w') #修改处4
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 = "/home/amax/cxw/tensorflow-yolov3-master/voc/train" #修改处5
for year, image_set in sets:
if not os.path.exists('/home/amax/cxw/tensorflow-yolov3-master/voc/train/VOCdevkit/VOC%s/labels/'%(year)): #修改处6
os.makedirs('/home/amax/cxw/tensorflow-yolov3-master/voc/train/VOCdevkit/VOC%s/labels/'%(year)) #修改处7
image_ids = open('/home/amax/cxw/tensorflow-yolov3-master/voc/train/VOCdevkit/VOC%s/ImageSets/Main/%s.txt'%(year, image_set)).read().strip().split() #修改处8
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")

这一部分你可能觉得很突兀,但这是为了下一章节修改anchors需要的工作,这个部分你可以先跳过,当需要的时候再回到这。
import numpy as np
class YOLO_Kmeans:
def __init__(self, cluster_number, filename):
self.cluster_number = cluster_number
self.filename = "voc_train.txt"
def iou(self, boxes, clusters): # 1 box -> k clusters
n = boxes.shape[0]
k = self.cluster_number
box_area = boxes[:, 0] * boxes[:, 1]
box_area = box_area.repeat(k)
box_area = np.reshape(box_area, (n, k))
cluster_area = clusters[:, 0] * clusters[:, 1]
cluster_area = np.tile(cluster_area, [1, n])
cluster_area = np.reshape(cluster_area, (n, k))
box_w_matrix = np.reshape(boxes[:, 0].repeat(k), (n, k))
cluster_w_matrix = np.reshape(np.tile(clusters[:, 0], (1, n)), (n, k))
min_w_matrix = np.minimum(cluster_w_matrix, box_w_matrix)
box_h_matrix = np.reshape(boxes[:, 1].repeat(k), (n, k))
cluster_h_matrix = np.reshape(np.tile(clusters[:, 1], (1, n)), (n, k))
min_h_matrix = np.minimum(cluster_h_matrix, box_h_matrix)
inter_area = np.multiply(min_w_matrix, min_h_matrix)
result = inter_area / (box_area + cluster_area - inter_area)
return result
def avg_iou(self, boxes, clusters):
accuracy = np.mean([np.max(self.iou(boxes, clusters), axis=1)])
return accuracy
def kmeans(self, boxes, k, dist=np.median):
box_number = boxes.shape[0]
distances = np.empty((box_number, k))
last_nearest = np.zeros((box_number,))
np.random.seed()
clusters = boxes[np.random.choice(
box_number, k, replace=False)] # init k clusters
while True:
distances = 1 - self.iou(boxes, clusters)
current_nearest = np.argmin(distances, axis=1)
if (last_nearest == current_nearest).all():
break # clusters won't change
for cluster in range(k):
clusters[cluster] = dist( # update clusters
boxes[current_nearest == cluster], axis=0)
last_nearest = current_nearest
return clusters
def result2txt(self, data):
f = open("yolo_anchors.txt", 'w')
row = np.shape(data)[0]
for i in range(row):
if i == 0:
x_y = "%d,%d" % (data[i][0], data[i][1])
else:
x_y = ", %d,%d" % (data[i][0], data[i][1])
f.write(x_y)
f.close()
def txt2boxes(self):
f = open(self.filename, 'r')
dataSet = []
for line in f:
infos = line.split(" ")
length = len(infos)
for i in range(1, length):
width = int(infos[i].split(",")[2]) - \
int(infos[i].split(",")[0])
height = int(infos[i].split(",")[3]) - \
int(infos[i].split(",")[1])
dataSet.append([width, height])
result = np.array(dataSet)
f.close()
return result
def txt2clusters(self):
all_boxes = self.txt2boxes()
result = self.kmeans(all_boxes, k=self.cluster_number)
result = result[np.lexsort(result.T[0, None])]
self.result2txt(result)
print("K anchors:\n {}".format(result))
print("Accuracy: {:.2f}%".format(
self.avg_iou(all_boxes, result) * 100))
if __name__ == "__main__":
cluster_number = 9
filename = "voc_train.txt"
kmeans = YOLO_Kmeans(cluster_number, filename)
kmeans.txt2clusters()
此部分需要修改.data、.names、.cfg等文件,修改成适合单目标检测(face),文件中的random属性可以设置为0(显存比较小的时候,此部分位于三个[yolo]处,下文)


- 修改classes=1,只进行人脸检测
- 修改train和valid路径,本文上一步生成的三个文件中的前两个的路径
- 修改names路径,本章节第一小步修改的names文件路径
- backup属性可以不用修改,它的用途忘记了,健忘

> 其中batch需要根据自己电脑的配置进行修改,服务器一般可以使用64,32,自己的电脑可能要低一些,16,32都可,根据自己的需求,subdivisions也得根据自己的电脑修改,如果太高,会严重消耗CPU资源。

> 此部分使用了step策略,初始学习率设置为0.001,40000步后学习率降低为0.0001,45000步后学习率降低为0.00001,可以根据自己的需求进行调整,

> 前面K均值聚类算法得到的九组anchors就是用于此部分的,修改成先前得到的九组anchors;修改classes为1,filters为18((1+5)*3)。
预训练权重可以使用darknet53的权重,此权重已经包含了最主要的网络结构,便于模型的扩展,并且文件还小。
对数据集进行训练,可以在后台执行,使用nohup等命令。
./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74 -gpus 0,1

./darknet detector train cfg/voc-my.data cfg/yolov3-voc-my.cfg backup/yolov3-voc-100000.weights -gpus 0,1
其中,训练出的模型权重保存在backup文件夹下。
nohup ./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74 -gpus 0,1 &
如果出现没有权限,把错误百度,有解决方法。
其他错误,一般都能在网上百度到,博客的连接中也写明了大部分的错误,如果make阶段出现错误,建议只开GPU=1,我使用过程中,由于是老师的服务器,没有大部分的权限,报了很多错误,因此只使用了GPU=1.
export LD_LIBRARY_PATH=/usr/local/cuda/lib64 && sudo ldconfig
通过输入密码后,成功解决了上述错误。