YOLO V8 实战:私有数据集训练全指南
1、训练环境配置
为了运行 YOLOv8,首先需要配置适当的环境。这里建议利用 Anaconda 来创建一个虚拟环境,这样有助于隔离和管理项目依赖,避免潜在的版本冲突。
在虚拟环境中安装 PyTorch 框架,并安装 Ultralytics 库,YOLOv8 的核心算法和功能都包含在这个库中。为了方便国内用户快速安装,我们将使用清华大学提供的 Python 软件包索引镜像站。运行以下命令来安装 Ultralytics 库:
pip install ultralytics -i https://pypi.tuna.tsinghua.edu.cn/simple
2、准备训练数据
在使用 YOLOv8 进行目标检测训练的过程中,采用 YOLO 数据格式通常是常见的选择。为了确保您的自定义数据集能够无缝对接 YOLOv8 的训练流程,接下来,我们将详细介绍如何将您的数据集转换为兼容的 YOLO 格式,确保数据集的结构和注释信息符合 YOLOv8 对输入数据的规范要求。
数据集组织结构
我们数据集被组织在一个名为 mydata
的文件夹(名称自定义)中,其目录结构包含了利用标注的 XML 文件和对应的图片,以及为 YOLOv8 训练准备的 TXT 格式标签数据文件:
- mydata:
- data : 存储训练集、测试集、验证集的文件列表
- images : 存储数据集中的图片,是目标检测模型训练的视觉输入部分
- labels : 存储与图片对应的 YOLO 数据格式的 TXT 标签数据文件
- xml : 存储与图片对应的 XML 标注文件(可选,用来生成 labels 数据)
- mask : 存储与图片对应的 MASK 标注文件(可选,用来生成 labels 数据)
将标注数据转化为 YOLO 数据格式
YOLO 数据格式是 YOLO 目标检测算法的图像标注格式,其注释文件以 .txt 结尾,包含类别索引和边界框(bounding box)的中心坐标、宽度和高度,这些值都被归一化到 [0,1]。
其文件内容的每一行代表图像中的一个目标,用空格隔开上述五个值(类别索引、x_center、y_center、width、height),如下所示:
0 0.50625 0.37 0.245 0.54
1 0.71625 0.47 0.16 0.24
对于使用 VOC 数据格式的数据集,标注信息是通过 XML 文件提供的,每个 XML 文件对应一张图片,并且包含了图像中所有目标的具体位置和类别等信息。您可以参考以下代码将 VOC 格式的 XML 文件转换为 YOLO 数据格式的 TXT 标签文件:
import os
import xml.etree.ElementTree as ET
class_mapping = {
'cat': 0,
'dog': 1,
}
def convert_bbox(size, box):
dw = 1. / size[0]
dh = 1. / size[1]
x = ((box[0] + box[1]) / 2.0) * dw
y = ((box[2] + box[3]) / 2.0) * dw
w = (box[1] - box[0]) * dh
h = (box[3] - box[2]) * dh
return (x, y, w, h)
def voc2yolo(xml_label_path, yolo_txt_path):
os.makedirs(yolo_txt_path, exist_ok=True)
for filename in os.listdir(xml_label_path):
if filename.endswith('.xml'):
filepath = os.path.join(xml_label_path, filename)
txt_name = os.path.splitext(filename)[0] + '.txt'
txt_path = os.path.join(yolo_txt_path, txt_name)
tree = ET.parse(filepath)
root = tree.getroot()
size = root.find('size')
width = int(size.find('width').text)
height = int(size.find('height').text)
with open(txt_path, 'w') as f:
for obj in root.iter('object'):
class_name = obj.find('name').text
if class_name in class_mapping:
class_id = class_mapping[class_name]
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))
bbox = convert_bbox((width, height), b)
print(f"Writing bounding box to {txt_path}")
f.write(str(class_id) + " " + " ".join([str(a) for a in bbox]) + '\n')
src_directory = "/home/detect/datasets/mydata/"
images_path = os.path.join(src_directory, 'images')
labels_path = os.path.join(src_directory, 'labels')
xml_path = os.path.join(src_directory, 'xml')
voc2yolo(xml_path, labels_path)
对于图像分割任务的数据集,标注信息将以 png 文件的形式提供,每个 png 文件对应一张图片,包含了图像上的分割掩码(mask)信息,例如目标区域、类别等信息。您可以参考以下代码将 mask 转换为 YOLO 数据格式的 TXT 标签文件:
import os
import cv2
def mask2yolo(mask_label_path, yolo_txt_path):
os.makedirs(yolo_txt_path, exist_ok=True)
for filename in os.listdir(mask_label_path):
if filename.endswith('.png'):
filepath = os.path.join(mask_label_path, filename)
txt_name = os.path.splitext(filename)[0] + '.txt'
txt_path = os.path.join(yolo_txt_path, txt_name)
image = cv2.imread(filepath, cv2.IMREAD_GRAYSCALE)
_, thresh = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
with open(txt_path, 'w') as f:
for contour in contours:
x, y, width, height = cv2.boundingRect(contour)
x_center = (x + width / 2) / image.shape[1]
y_center = (y + height / 2) / image.shape[0]
width_norm = width / image.shape[1]
height_norm = height / image.shape[0]
print(f"Writing bounding box to {txt_path}")
f.write(f"0 {x_center:.6f} {y_center:.6f} {width_norm:.6f} {height_norm:.6f}\n")
src_directory = "/home/detect/datasets/mydata/"
images_path = os.path.join(src_directory, 'images')
labels_path = os.path.join(src_directory, 'labels')
mask_path = os.path.join(src_directory, 'mask')
mask2yolo(mask_path, labels_path)
创建训练集和验证集
在准备用于YOLO模型训练的数据集时,我们通常需要分别创建包含训练集 (train) 和验证集 (val) 图像路径的文本文件。下面的代码段是自动执行此任务的脚本,它将查找指定图像目录下所有图像路径,然后将这些图像按照比例随机分配到训练集和验证集中,并生成训练集和验证集的划分文件。
import os
import random
train_ratio = 0.9
image_dir = '/home/detect/datasets/mydata/images'
train_txt_path = '/home/detect/datasets/mydata/data/train.txt'
val_txt_path = '/home/detect/datasets/mydata/data/val.txt'
os.makedirs(os.path.dirname(train_txt_path), exist_ok=True)
os.makedirs(os.path.dirname(val_txt_path), exist_ok=True)
supported_formats = ('.jpg', '.jpeg', '.png', '.bmp')
image_paths = [os.path.abspath(os.path.join(dp, f))
for dp, dn, filenames in os.walk(image_dir)
for f in filenames if f.endswith(supported_formats)]
random.shuffle(image_paths)
split_index = int(train_ratio * len(image_paths))
train_image_paths = image_paths[:split_index]
val_image_paths = image_paths[split_index:]
with open(train_txt_path, 'w') as file:
for image_path in train_image_paths:
file.write(f"{image_path}\n")
with open(val_txt_path, 'w') as file:
for image_path in val_image_paths:
file.write(f"{image_path}\n")
print(f"Training images have been written to {train_txt_path}, total: {len(train_image_paths)}")
print(f"Validation images have been written to {val_txt_path}, total: {len(val_image_paths)}")
3、相关配置文件
数据集配置文件
创建包含数据集配置信息的 .yaml
文件,它提供了结构化的方法来存储训练集和验证集的划分文件路径,以及分类信息等其他与数据集相关的配置。以下是 mydata.yaml
文件的一个示例内容,其中包含 train.txt 和 val.txt 文件路径的条目,以及数据集的分类信息:
train: /home/detect/datasets/mydata/data/train.txt
val: /home/detect/datasets/mydata/data/val.txt
nc: 2
names: ['cat', 'dog']
YOLO 模型配置文件
在 ultralytics/cfg/models/v8/
目录中,您可以找到各种 YOLOv8 模型的配置文件。您可以选择合适的模型配置文件,并根据自己的需求进行调整。
在本教程中,我们将遵循默认配置,因此不需要对其做任何更改。我们将直接使用这些预设参数,以便您能快速地上手并运行模型。
4、YOLO 模型训练
下载预训练权重
如果您的训练服务器能够顺畅访问 GitHub,在训练过程中,系统将会自动下载并载入相应的预训练模型权重进行初始化。
如若不然,则需要手动前往 Ultralytics Assets 下载您所需模型的预训练权重文件,并传输到训练服务器指定目录下。
开始模型训练
使用我们自己的数据集进行 YOLOv8n 模型训练,持续 1000 个 epoch。训练设备可以通过设备参数进行指定,默认将自动选择,若GPU不可用,则使用 CPU 作为训练设备,否则将使用第一个可用的 GPU。
=== “Python”
from ultralytics import YOLO
model = YOLO("yolov8n.yaml") # 从 YAML 构建新模型
model = YOLO("yolov8n.pt") # 加载预训练模型权重
model = YOLO("yolov8n.yaml").load("yolov8n.pt") # YAML构建并加载权重
# 模型训练
results = model.train(data="mydata.yaml", epochs=1000, imgsz=640, batch=256)
# 使用多 GPU 进行模型训练
results = model.train(data="mydata.yaml", epochs=1000, imgsz=640, batch=256, device=[0, 1])
=== “CLI”
# 从 YAML 构建新模型,并开始模型训练
yolo detect train data=mydata.yaml model=yolov8n.yaml epochs=1000 imgsz=640 batch=256
# 加载预训练模型权重,并开始模型训练
yolo detect train data=mydata.yaml model=yolov8n.pt epochs=1000 imgsz=640 batch=256
# YAML构建并加载权重,同时开始模型训练
yolo detect train data=mydata.yaml model=yolov8n.yaml pretrained=yolov8n.pt epochs=1000 imgsz=640 batch=256
# YAML构建并加载权重,同时使用多 GPU 开始模型训练
yolo detect train data=mydata.yaml model=yolov8n.yaml pretrained=yolov8n.pt epochs=1000 imgsz=640 batch=256 device=0,1
YOLOv8 迎来了对 Apple M1 和 M2 芯片的优化支持,允许用户通过 Metal Performance Shaders (MPS) 在这些平台上进行高效的训练。此外,它还支持从中断点恢复训练,并提供了改进的日志记录功能。
有关更多的功能选项和训练参数的详细配置,您可以参阅【官方训练文档】以获取全面的指南和说明
5、YOLO 模型推理
Ultralytics YOLOv8 提供了具有强大功能的预测模式,该模式专为在各种数据源上实现高性能实时推理而设计。
在推理过程中,Ultralytics YOLO 模型会返回 Python Results 对象的列表,或者在传递 stream=True
参数时,返回内存高效的 Python Results 对象生成器。官方推理示例代码如下:
from ultralytics import YOLO
# Load a pretrained YOLOv8n model
model = YOLO("yolov8n.pt")
''' Run batched inference on a list of images'''
# return a list of Results objects
results = model(["im1.jpg", "im2.jpg"])
# # return a generator of Results objects
# results = model(["im1.jpg", "im2.jpg"], stream=True)
for result in results:
boxes = result.boxes # Boxes object for bounding box outputs
masks = result.masks # Masks object for segmentation masks outputs
keypoints = result.keypoints # Keypoints object for pose outputs
probs = result.probs # Probs object for classification outputs
obb = result.obb # Oriented boxes object for OBB outputs
result.show()
result.save(filename="result.jpg")
此外,您还可以详细处理得到的 bounding box 输出信息。以下是简单的示例代码:
detect_output = {"boxes": [], "scores": [], "classes": [], "names": [], "masks": []}
for result in results:
detect_output["boxes"].append(result.boxes.xyxy.cpu())
detect_output["scores"].append(result.boxes.conf.cpu())
names = self.model.names
classes = result.boxes.cls.cpu().numpy().astype(int)
class_names = [names[c] for c in classes]
detect_output["classes"].extend(classes)
detect_output["names"].extend(class_names)
if result.masks is not None:
detect_output["masks"].extend(result.masks.numpy())
有关更多关于模型推理输入支持和结果对象的详细参数信息,请参阅【官方预测文档】以获取全面的指南和说明。