赞
踩
目录
yolov5 github的地址:https://github.com/ultralytics/yolov5
使用的labelImg标注工具,对数据进行标注。标注工具具体使用方法,可以自己去查一下。
在yolov5-master/data文件下新建下列文件夹
images文件夹放置图像文件,Annotations文件夹放置标注好的xml文件,ImageSets文件夹放置生成的训练txt文件,labels放置生成的标签文件。
在yolov5-master文件夹下新建dataprocess_split.py文件并运行,代码如下:
- import os
- import random
- trainval_percent = 0.1
- train_percent = 0.9
- xmlfilepath = 'data/Annotations'
- txtsavepath = 'data/ImageSets'
- 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('data/ImageSets/trainval.txt', 'w')
- ftest = open('data/ImageSets/test.txt', 'w')
- ftrain = open('data/ImageSets/train.txt', 'w')
- fval = open('data/ImageSets/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()
-
在yolov5-master文件夹下新建dataprocess_voc_label.py文件并运行,代码如下:
-
- import xml.etree.ElementTree as ET
- import pickle
- import os
- from os import listdir, getcwd
- from os.path import join
- sets = ['train', 'test','val']
- classes = ['face']
- def convert(size, box):
- dw = 1. / size[0]
- dh = 1. / size[1]
- x = (box[0] + box[1]) / 2.0
- y = (box[2] + box[3]) / 2.0
- 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(image_id):
- in_file = open('data/Annotations/%s.xml' % (image_id))
- out_file = open('data/labels/%s.txt' % (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 image_set in sets:
- if not os.path.exists('data/labels/'):
- os.makedirs('data/labels/')
- image_ids = open('data/ImageSets/%s.txt' % (image_set)).read().strip().split()
- list_file = open('data/%s.txt' % (image_set), 'w')
- for image_id in image_ids:
- list_file.write('data/images/%s.jpg\n' % (image_id))
- convert_annotation(image_id)
- list_file.close()
-
1 数据集的配置
数据准备后,训练之前,修改配置文件。在data文件夹下,找到coco.yaml文件。复制一份,重命名为me.yaml(自己随意命名)。修改文件内容如下:
2 模型的配置文件
聚类得出先验框(可选)(聚类重新生成anchors运行时间较长)
最新版的yolov5,它会自动kmeans算出anchors。
dataprocess_kmeans.py代码如下:
- import numpy as np
-
- def iou(box, clusters):
- """
- Calculates the Intersection over Union (IoU) between a box and k clusters.
- :param box: tuple or array, shifted to the origin (i. e. width and height)
- :param clusters: numpy array of shape (k, 2) where k is the number of clusters
- :return: numpy array of shape (k, 0) where k is the number of clusters
- """
- x = np.minimum(clusters[:, 0], box[0])
- y = np.minimum(clusters[:, 1], box[1])
- if np.count_nonzero(x == 0) > 0 or np.count_nonzero(y == 0) > 0:
- raise ValueError("Box has no area") # 如果报这个错,可以把这行改成pass即可
-
- intersection = x * y
- box_area = box[0] * box[1]
- cluster_area = clusters[:, 0] * clusters[:, 1]
-
- iou_ = intersection / (box_area + cluster_area - intersection)
-
- return iou_
-
- def avg_iou(boxes, clusters):
- """
- Calculates the average Intersection over Union (IoU) between a numpy array of boxes and k clusters.
- :param boxes: numpy array of shape (r, 2), where r is the number of rows
- :param clusters: numpy array of shape (k, 2) where k is the number of clusters
- :return: average IoU as a single float
- """
- return np.mean([np.max(iou(boxes[i], clusters)) for i in range(boxes.shape[0])])
-
- def translate_boxes(boxes):
- """
- Translates all the boxes to the origin.
- :param boxes: numpy array of shape (r, 4)
- :return: numpy array of shape (r, 2)
- """
- new_boxes = boxes.copy()
- for row in range(new_boxes.shape[0]):
- new_boxes[row][2] = np.abs(new_boxes[row][2] - new_boxes[row][0])
- new_boxes[row][3] = np.abs(new_boxes[row][3] - new_boxes[row][1])
- return np.delete(new_boxes, [0, 1], axis=1)
-
-
- def kmeans(boxes, k, dist=np.median):
- """
- Calculates k-means clustering with the Intersection over Union (IoU) metric.
- :param boxes: numpy array of shape (r, 2), where r is the number of rows
- :param k: number of clusters
- :param dist: distance function
- :return: numpy array of shape (k, 2)
- """
- rows = boxes.shape[0]
-
- distances = np.empty((rows, k))
- last_clusters = np.zeros((rows,))
-
- np.random.seed()
-
- # the Forgy method will fail if the whole array contains the same rows
- clusters = boxes[np.random.choice(rows, k, replace=False)]
-
- while True:
- for row in range(rows):
- distances[row] = 1 - iou(boxes[row], clusters)
-
- nearest_clusters = np.argmin(distances, axis=1)
-
- if (last_clusters == nearest_clusters).all():
- break
-
- for cluster in range(k):
- clusters[cluster] = dist(boxes[nearest_clusters == cluster], axis=0)
-
- last_clusters = nearest_clusters
-
- return clusters
聚类生成新anchors的文件dataprocess_clauculate_anchors.py,代码中的路径请根据自己的路径进行修改,代码内容如下:
- import os
- import numpy as np
- import xml.etree.cElementTree as et
- from kmeans import kmeans, avg_iou
-
- FILE_ROOT = "yolov5/data/" # 根路径
- ANNOTATION_ROOT = "Annotations" # 数据集标签文件夹路径
- ANNOTATION_PATH = FILE_ROOT + ANNOTATION_ROOT
-
- ANCHORS_TXT_PATH = "yolov5/data/anchors.txt"
-
- CLUSTERS = 9
- CLASS_NAMES = ['face']
-
- def load_data(anno_dir, class_names):
- xml_names = os.listdir(anno_dir)
- boxes = []
- for xml_name in xml_names:
- xml_pth = os.path.join(anno_dir, xml_name)
- tree = et.parse(xml_pth)
-
- width = float(tree.findtext("./size/width"))
- height = float(tree.findtext("./size/height"))
-
- for obj in tree.findall("./object"):
- cls_name = obj.findtext("name")
- if cls_name in class_names:
- xmin = float(obj.findtext("bndbox/xmin")) / width
- ymin = float(obj.findtext("bndbox/ymin")) / height
- xmax = float(obj.findtext("bndbox/xmax")) / width
- ymax = float(obj.findtext("bndbox/ymax")) / height
-
- box = [xmax - xmin, ymax - ymin]
- boxes.append(box)
- else:
- continue
- return np.array(boxes)
-
- if __name__ == '__main__':
-
- anchors_txt = open(ANCHORS_TXT_PATH, "w")
-
- train_boxes = load_data(ANNOTATION_PATH, CLASS_NAMES)
- count = 1
- best_accuracy = 0
- best_anchors = []
- best_ratios = []
-
- for i in range(10): ##### 可以修改,不要太大,否则时间很长
- anchors_tmp = []
- clusters = kmeans(train_boxes, k=CLUSTERS)
- idx = clusters[:, 0].argsort()
- clusters = clusters[idx]
- # print(clusters)
-
- for j in range(CLUSTERS):
- anchor = [round(clusters[j][0] * 640, 2), round(clusters[j][1] * 640, 2)]
- anchors_tmp.append(anchor)
- print(f"Anchors:{anchor}")
-
- temp_accuracy = avg_iou(train_boxes, clusters) * 100
- print("Train_Accuracy:{:.2f}%".format(temp_accuracy))
-
- ratios = np.around(clusters[:, 0] / clusters[:, 1], decimals=2).tolist()
- ratios.sort()
- print("Ratios:{}".format(ratios))
- print(20 * "*" + " {} ".format(count) + 20 * "*")
-
- count += 1
-
- if temp_accuracy > best_accuracy:
- best_accuracy = temp_accuracy
- best_anchors = anchors_tmp
- best_ratios = ratios
-
- anchors_txt.write("Best Accuracy = " + str(round(best_accuracy, 2)) + '%' + "\r\n")
- anchors_txt.write("Best Anchors = " + str(best_anchors) + "\r\n")
- anchors_txt.write("Best Ratios = " + str(best_ratios))
- anchors_txt.close()
运行dataprocess_clauculate_anchors.py生成一个anchors.txt文件,内容如下:
在yolov5目录下的model文件夹下是模型的配置文件,这边提供s、m、l、x版本,逐渐增大(随着架构的增大,训练时间也是逐渐增大)。
假设采用yolov5s.yaml,只用修改一个参数,把nc改成自己的类别数。根据anchors.txt 中的 Best Anchors 修改,需要取整(可选)。
此时需要下载yolov5的预权重文件,请自行下载。
下载后,将yolov5s.pt权重文件放到weights文件夹下即可。
3 训练文件
对train.py文件进行修改后,就可以开始训练。修改如下几处:
运行train.py
训练结束后,生成的模型路径为:yolov5\runs\train\exp5\weights
将模型best.pt复制到yolov5-master/下,并改名yolov5s.pt。
detect.py文件修改如下:
运行detect.py,结果图所存的路径为:yolov5\runs\detect
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。