🍀2023.11.20 更新了划分数据集的脚本
在自定义数据上训练 yolov8
目标检测模型的步骤可以总结如下 6
步:
- 🌟收集数据集
- 🌟标注数据集
- 🌟划分数据集
- 🌟配置训练环境
- 🌟训练模型
- 🌟评估模型
1. 收集数据集
随着深度学习技术在计算机视觉领域的广泛应用,行人检测和车辆检测等任务已成为热门研究领域。然而,实际应用中,可用的预训练模型可能并不适用于所有应用场景。
例如,虽然预先训练的模型可以检测出行人,但它无法区分“好人”和“坏人”,因为它没有接受相关的训练。因此,我们需要为自定义检测模型提供足够数量的带有标注信息的图像数据,来训练模型以区分“好人”和“坏人”。
本文将介绍几种收集数据集的常见方法,帮助大家更好地解决实际问题。
1.1 使用开源已标记数据集
使用开源数据集是收集数据的最简便方式之一。例如,imagenet
是一个大型图像数据库,包含超过1400
万张图像,可用于深度学习模型的训练。此外,像coco
、pascal voc
这样的数据集也经常用于目标检测模型的训练和评估。但是这些数据库中的图像通常来自不同的领域和应用场景,因此可能无法完全满足特定研究的需求。
1.2 爬取网络图像
另一种选择是通过网络搜索图像,并手动选择要下载的图像。然而,由于需要收集大量数据,因此此方法的效率较低。需要注意的是,网络上的图像可能受到版权保护。在使用这些图像之前,务必检查图像的版权信息。
或者,您可以编写一个程序来爬取网络并下载所需的图像,但是这需要对数据进行清洗,以确保数据质量。同样需要注意检查每个图像的版权信息。
1.3 自己拍摄数据集
对于一些特定的应用场景,如自动驾驶和安防监控等,需要收集特定场景下的数据,这时候就需要进行自主拍摄。可以在实际场景中拍摄图像或视频,并对其进行标注,以获得适用于特定场景的高质量数据集。
1.4 使用数据增强生成数据集
我们知道深度学习模型需要大量的数据。当我们只有一个小数据集时,可能不足以训练一个好的模型。在这种情况下,我们可以使用数据增强来生成更多训练数据。
常见的增强方式就是几何变换,类似翻转、裁剪、旋转和平移这些。
1.5 使用算法合成图像
最后一种获取目标检测数据集的方法是使用合成图像。合成图像是通过使用图像处理软件(例如photoshop
)在图像中添加对象、更改背景或合成多个图像以创建新的图像。这种方法可以提供一些特殊情况或无法通过其他方式获得的图像,但是合成图像通常无法完全代替真实场景的数据,可能会对模型的准确性产生一定的影响。
或者我们可以使用生成对抗网络 (gan
) 来生成数据集。
值得注意的是,收集训练数据集只是我们训练自定义检测模型的第一步。。。接下来我们要介绍如何标注数据集,当然这一步是假设你的图片已经准备完成。
本次案例依然使用我个人的月饼数据集
下载地址:
2. 标注数据集
2.1 确认标注格式
yolov8
所用数据集格式与 yolov5 yolov7
相同,采用格式如下:
<object-class-id> <x> <y> <width> <height>
常用的标注工具有很多,比如labelimg
、labelme
、via
等,但是这些工具都需要安装使用,我这里给大家介绍一款在线标注数据集的工具make sense
,打开即用,非常的便捷,在标注之前,我们来看一下一般情况下遵循的标注规则:
- 目标框必须框住整个目标物体,不能有遗漏和重叠。
- 目标框应该与目标物体尽可能接近,但不能与目标物体重合。
- 目标框的宽度和高度应该为正数,不能为零或负数。
- 如果一张图片中有多个目标物体,每个目标物体应该用一个独立的目标框进行标注,不允许多个目标共用一个框。
- 如果目标物体的形状不规则,可以使用多个框进行标注,但必须框住整个目标物体。
- 目标框的坐标必须在数据集中统一。
2.2 开始标注
确认好标注格式后我们就可以开始标注了,进入网页后点击 get started
开始使用。
首先点击 drop images
然后 ctrl+a
选中整个数据集里面的图片。
随后添加标签信息,有几类就添加几个,因为我这里只检测月饼一类,所以只添加一个标签 moon cake
。
随后就进入了漫长的标注环节,这里大家一定要认真标注,不然对最终模型的影响还是很大的。
大约 3 3 3 个小时以后就标注完毕了。。。。
我们点击action
->
export annotation
导出 yolo
格式的标签文件。
导出之后的标签文件就是这个样子的,我们可以随机抽查几个看看有没有问题。
3. 划分数据集
也就是说,我们现在导出后的图片和标签是这个样子的:
moon_cake
├─images
└─all
└─labels
└─all
但是 yolov8
所需要的数据集路径的格式是下面这样子的(yolov8
支持不止这一种格式),我们接下来要通过脚本来来划分一下数据集:
├── yolov8_dataset
└── train
└── images (folder including all training images)
└── labels (folder including all training labels)
└── test
└── images (folder including all testing images)
└── labels (folder including all testing labels)
└── valid
└── images (folder including all testing images)
└── labels (folder including all testing labels)
具体其实只要修改路径就行了,代码我都做了注释。
# by csdn 迪菲赫尔曼
import os
import random
import shutil
def copy_files(src_dir, dst_dir, filenames, extension):
os.makedirs(dst_dir, exist_ok=true)
missing_files = 0
for filename in filenames:
src_path = os.path.join(src_dir, filename + extension)
dst_path = os.path.join(dst_dir, filename + extension)
# check if the file exists before copying
if os.path.exists(src_path):
shutil.copy(src_path, dst_path)
else:
print(f"warning: file not found for {filename}")
missing_files += 1
return missing_files
def split_and_copy_dataset(image_dir, label_dir, output_dir, train_ratio=0.7, valid_ratio=0.15, test_ratio=0.15):
# 获取所有图像文件的文件名(不包括文件扩展名)
image_filenames = [os.path.splitext(f)[0] for f in os.listdir(image_dir)]
# 随机打乱文件名列表
random.shuffle(image_filenames)
# 计算训练集、验证集和测试集的数量
total_count = len(image_filenames)
train_count = int(total_count * train_ratio)
valid_count = int(total_count * valid_ratio)
test_count = total_count - train_count - valid_count
# 定义输出文件夹路径
train_image_dir = os.path.join(output_dir, 'train', 'images')
train_label_dir = os.path.join(output_dir, 'train', 'labels')
valid_image_dir = os.path.join(output_dir, 'valid', 'images')
valid_label_dir = os.path.join(output_dir, 'valid', 'labels')
test_image_dir = os.path.join(output_dir, 'test', 'images')
test_label_dir = os.path.join(output_dir, 'test', 'labels')
# 复制图像和标签文件到对应的文件夹
train_missing_files = copy_files(image_dir, train_image_dir, image_filenames[:train_count], '.jpg')
train_missing_files += copy_files(label_dir, train_label_dir, image_filenames[:train_count], '.txt')
valid_missing_files = copy_files(image_dir, valid_image_dir, image_filenames[train_count:train_count + valid_count], '.jpg')
valid_missing_files += copy_files(label_dir, valid_label_dir, image_filenames[train_count:train_count + valid_count], '.txt')
test_missing_files = copy_files(image_dir, test_image_dir, image_filenames[train_count + valid_count:], '.jpg')
test_missing_files += copy_files(label_dir, test_label_dir, image_filenames[train_count + valid_count:], '.txt')
# print the count of each dataset
print(f"train dataset count: {train_count}, missing files: {train_missing_files}")
print(f"validation dataset count: {valid_count}, missing files: {valid_missing_files}")
print(f"test dataset count: {test_count}, missing files: {test_missing_files}")
# 使用例子
image_dir = 'datasets/coco128/images/train2017'
label_dir = 'datasets/coco128/labels/train2017'
output_dir = './my_dataset'
split_and_copy_dataset(image_dir, label_dir, output_dir)
运行完脚本后我们的数据集就会划分成这个格式了,现在数据准备工作就彻底完成了,接下来我们开始着手训练模型。
4. 配置训练环境
4.1 获取代码
git clone https://github.com/ultralytics/ultralytics
4.2 安装环境
cd ultralytics
pip install -e .
pip install ultralytics
5. 训练模型
5.1 新建一个数据集yaml文件
这个是我新建的,里面写绝对路径(主要是怕出错):
# moncake
train: d:\pycharm_projects\ultralytics\ultralytics\datasets\mooncake\train # train images (relative to 'path') 128 images
val: d:\pycharm_projects\ultralytics\ultralytics\datasets\mooncake\valid # val images (relative to 'path') 128 images
test: d:\pycharm_projects\ultralytics\ultralytics\datasets\mooncake\test # test images (optional)
# classes
names:
0: mooncake
这个是自带的,里面写相对路径,和我们的写法不同,但是都可以使用,据我所只还有很多种数据集读取方式:
# coco128
path: ../datasets/coco128 # dataset root dir
train: images/train2017 # train images (relative to 'path') 128 images
val: images/train2017 # val images (relative to 'path') 128 images
test: # test images (optional)
# classes
names:
0: person
1: bicycle
2: car
'''
'''
79: toothbrush
相应的数据集位置就在这里,我们可以和coco128
对比一下,这两种划分格式都可以的,这里一定要注意路径问题!
5.2 预测模型
这是官方提供的一些命令行方式,下面我们分开来介绍一下:
cli
指令推理方式:
yolo task=detect mode=train model=yolov8n.pt args...
classify predict yolov8n-cls.yaml args...
segment val yolov8n-seg.yaml args...
export yolov8n.pt format=onnx args...
python
指令推理方式: 订阅专栏的同学,里面有更全面的功能。
from ultralytics import yolo
# load a model
model = yolo('yolov8n.pt') # load an official model
model = yolo('path/to/best.pt') # load a custom model
# predict with the model
results = model('https://ultralytics.com/images/bus.jpg') # predict on an image
终端中直接键入以下指令就可以实现对图进行推理了,推理后如果不指定文件夹,就会默认保存到runs/detect/predict
下。
yolo task=detect mode=predict model=yolov8n.pt source=data/images device=0 save=true
就这张图来说,v8
确实比v5
牛🍺,左上角的标志都检测出来了,但是阳台上的自行车还是没检测出来。
yolov8
关于模型的各种参数其实都写到了一起,在ultralytics/yolo/cfg/default.yaml
,这是与先前版本最大的不同,通过使用这些指令我们就可以实现各种我们所需的操作。
参数名 | 默认值 | 说明 |
---|---|---|
source | 无 | 图像或视频所在的目录 |
show | false | 如果可能,显示结果 |
save_txt | false | 将结果保存为.txt文件 |
save_conf | false | 保存带置信度得分的结果 |
save_crop | false | 保存裁剪后带结果的图像 |
show_labels | true | 在绘图中显示对象标签 |
show_conf | true | 在绘图中显示对象置信度得分 |
vid_stride | 1 | 视频帧率跨度 |
line_thickness | 3 | 边界框的厚度(像素) |
visualize | false | 可视化模型特征 |
augment | false | 对预测源应用图像增强 |
agnostic_nms | false | 类无关nms |
classes | 空列表 | 按类别筛选结果,例如:class = 0或class = [0,2,3] |
retina_masks | false | 使用高分辨率分割掩模 |
boxes | true | 在分割预测中显示边界框 |
5.3 训练模型
模型训练阶段的原理和预测步骤一致,都可以直接通过命令行搞定,关于这部分参数依然在ultralytics/yolo/cfg/default.yaml
中,但我们要训练自己的数据集时记得在 data
参数后指定我们自己的数据集yaml
文件路径哦。
以下提供三种指令,分别对应了不同的需求。(这里有一点值得注意, 我直接写data=mooncake.yaml
是报错的!这个涉及到一个坑的问题,没遇到的同学暂且忽略)
cli
指令训练方式:
# 从yaml构建一个新模型,从头开始训练
yolo detect train data=coco128.yaml model=yolov8n.yaml epochs=100 imgsz=640
yolo detect train data=ultralytics/datasets/mooncake.yaml model=yolov8n.yaml epochs=100 imgsz=640
# 从预训练的*.pt模型开始训练
yolo detect train data=coco128.yaml model=yolov8n.pt epochs=100 imgsz=640
yolo detect train data=ultralytics/datasets/mooncake.yaml model=yolov8n.pt epochs=100 imgsz=640
# 从yaml中构建一个新模型,将预训练的权重转移到它并开始训练
yolo detect train data=coco128.yaml model=yolov8n.yaml pretrained=yolov8n.pt epochs=100 imgsz=640
yolo detect train data=ultralytics/datasets/mooncake.yaml model=yolov8n.yaml pretrained=yolov8n.pt epochs=100 imgsz=640
python
指令训练方式: 订阅专栏的同学,里面有更全面的功能。
from ultralytics import yolo
# load a model # 三选一
model = yolo('yolov8n.yaml') # build a new model from yaml
model = yolo('yolov8n.pt') # load a pretrained model (recommended for training)
model = yolo('yolov8n.yaml').load('yolov8n.pt') # build from yaml and transfer weights
# train the model
model.train(data='coco128.yaml', epochs=100, imgsz=640)
参数 | 默认值 | 描述 |
---|---|---|
model | 模型文件路径,如yolov8n.pt,yolov8n.yaml | |
data | 数据文件路径,如coco128.yaml | |
epochs | 100 | 训练的轮数 |
patience | 50 | 等待没有观察到改进的轮数,以便提前停止训练 |
batch | 16 | 每个批次的图像数量(-1表示自动批处理) |
imgsz | 640 | 输入图像的大小,可以是整数或w,h |
save | true | 保存训练检查点和预测结果 |
save_period | -1 | 每x个轮数保存检查点(如果<1,则禁用) |
cache | false | true / ram,磁盘或false。使用缓存加载数据 |
device | 运行的设备,如cuda device=0或device=0,1,2,3或device=cpu | |
workers | 8 | 加载数据的工作线程数(每个rank如果ddp) |
project | 项目名称 | |
name | 实验名称,结果保存到“project / name”目录中 | |
exist_ok | false | 是否覆盖现有实验 |
pretrained | false | 是否使用预训练模型 |
optimizer | sgd | 要使用的优化器,choices=[‘sgd’, ‘adam’, ‘adamw’, ‘rmsprop’] |
verbose | true | 是否打印详细输出 |
seed | 0 | 用于可重复性的随机种子 |
deterministic | true | 是否启用确定性模式 |
single_cls | false | 将多类数据训练为单类 |
image_weights | false | 使用加权图像选择进行训练 |
rect | false | 如果mode ='train’则进行矩形训练,如果mode ='val’则进行矩形验证 |
cos_lr | false | 使用余弦学习率调度程序 |
close_mosaic | 10 | 对于最后10个轮数禁用马赛克增强 |
resume | false | 从上一个检查点恢复训练 |
amp | true | 自动混合精度(amp)训练,choices=[true, false],true运行amp检查 |
在训练过程中(训练结束后也可以看)我们可以通过tensorboard
实时查看模型的训练进度, 只需要在终端中键入如下的指令,这个在我们每次训练时候都会有提示:
tensorboard --logdir runs\detect\train2
训练结束后我们可以查看得到的一些指标数据:
我这里展示一张pr
曲线图。
5.4 验证模型
验证模型同样是简单命令行即可实现,如果没有修改中的 ultralytics/yolo/cfg/default.yaml
默认值,同样别忘了指定自己数据集的yaml
,即data=ultralytics/datasets/mooncake.yaml
cli
指令验证方式:
yolo task=detect mode=val model=yolov8n.pt
# mode=val 就是看验证集
yolo task=detect mode=val split=val model=runs/detect/train2/weights/best.pt data=ultralytics/datasets/mooncake.yaml
# mode=test 就是看测试集
yolo task=detect mode=val split=test model=runs/detect/train2/weights/best.pt data=ultralytics/datasets/mooncake.yaml
python
指令验证方式: 订阅专栏的同学,里面有更全面的功能。
from ultralytics import yolo
# load a model
model = yolo('yolov8n.pt') # load an official model
model = yolo('path/to/best.pt') # load a custom model
# validate the model
metrics = model.val() # no arguments needed, dataset and settings remembered
metrics.box.map # map50-95
metrics.box.map50 # map50
metrics.box.map75 # map75
metrics.box.maps # a list contains map50-95 of each category
同样的,我们验证完后依然可以得到一个文件夹:
我们可以看一下检测效果:
参数名 | 默认值 | 描述 |
---|---|---|
val | true | 在训练过程中进行验证/测试 |
split | val | 用于验证的数据集划分,可选项有’val’、‘test’或’train’ |
save_json | false | 是否将结果保存为json文件 |
save_hybrid | false | 是否保存标签的混合版本(标签+额外的预测结果) |
conf | 0.25(predict),0.001(val) | 检测的物体置信度阈值(默认值),在训练和验证过程中使用不同的阈值 |
iou | 0.7 | 非极大值抑制(nms)的交并比(iou)阈值 |
max_det | 300 | 每个图像最多检测出的目标数量 |
half | false | 是否使用半精度(fp16) |
dnn | false | 是否使用opencv dnn进行onnx推理 |
plots | true | 在训练/验证过程中是否保存图表 |
5.5 导出模型
只需使用如下命令,就可完成模型的导出。
cli
指令导出方式:
yolo task=detect mode=export model=runs/detect/train/weights/best.pt
python
指令导出方式:
from ultralytics import yolo
# load a model
model = yolo('yolov8n.pt') # load an official model
model = yolo('path/to/best.pt') # load a custom trained
# export the model
model.export(format='onnx')
导出有关的具体参数如下:
参数名 | 默认值 | 描述 |
---|---|---|
format | torchscript | 导出的模型格式 |
keras | false | 是否使用keras |
optimize | false | torchscript: 是否针对移动设备进行优化 |
int8 | false | coreml/tf的int8量化 |
dynamic | false | onnx/tf/tensorrt: 是否动态轴 |
simplify | false | onnx: 是否简化模型 |
opset | onnx: 操作版本(可选) | |
workspace | 4 | tensorrt: 工作空间大小(gb) |
nms | false | coreml: 是否添加nms |
至此使用yolov8
训练自己的目标检测数据集-【收集数据集】-【标注数据集】-【划分数据集】-【配置训练环境】-【训练模型】-【评估模型】-【导出模型】就完成了,欢迎大家一起交流学习~
🍀 2023.6.23 新增 python
推理、训练、验证、导出、方式代码。
发表评论