基于yolov8根据CVAT标注结果训练模型
Published in:2025-04-09 |
Words: 10.3k | Reading time: 44min | reading:

模型简介

YOLO 是一种实时目标检测算法家族的名称。与传统的需要先进行区域提议(Region Proposal)再进行分类的两阶段检测器(如 Faster R-CNN)不同,YOLO 将目标检测视为一个回归问题,直接从整个图像中预测边界框的位置和类别概率,实现了端到端(end-to-end)的检测。

  • 核心思想:
    1. 网格划分: 将输入图像划分为 S x S 的网格 (Grid Cells)。
    2. 单元格负责制: 如果一个物体的中心落入某个网格单元,则该单元负责检测该物体。
    3. 边界框预测: 每个网格单元预测 B 个边界框 (Bounding Boxes) 以及这些框的置信度 (Confidence Score,表示框内含有物体的概率及框的准确度)。每个边界框包含 5 个预测值:x, y (框中心相对单元格的坐标), w, h (框宽高相对整张图像的比例), 和 confidence
    4. 类别概率预测: 每个网格单元还预测 C 个条件类别概率 (Conditional Class Probabilities),即在包含物体的前提下,该物体属于每个类别的概率。
    5. 最终检测: 将每个边界框的置信度与对应单元格的类别概率相乘,得到每个框针对每个类别的得分。通过设置阈值过滤低分框,并使用非极大值抑制 (Non-Maximum Suppression, NMS) 消除冗余的重叠框,得到最终的检测结果。

CVAT简介

基于 Web 的、功能强大的计算机视觉数据标注工具。它支持对图像和视频进行多种类型的标注,是计算机视觉项目(尤其是监督学习)中数据准备阶段的重要工具

  • 主要功能:
    • 多种标注类型:
      • 边界框 (Bounding Boxes): 用于目标检测任务,框出物体的位置。
      • 多边形 (Polygons): 用于实例分割任务,精确勾勒物体的轮廓。
      • 关键点 (Points / Skeleton): 用于姿态估计、人脸关键点检测等任务。
      • 分割掩码 (Segmentation Masks): 用于语义分割任务,为图像中的每个像素分配类别。
      • 标签 (Tags): 用于图像分类任务,为整个图像分配类别标签。
      • 轨迹/立方体 (Tracks/Cuboids): 支持视频标注,可以跟踪物体或标注3D边界框。
    • 图像和视频支持: 可以直接对单张图片或整个视频序列进行标注。视频标注支持插值(interpolation),可以减少手动标注的工作量。
    • 半自动和自动标注: 集成了多种 AI 模型(如目标检测、分割模型,包括 YOLO、SAM 等),可以预标注数据,然后由人工进行修正,提高效率。支持连接外部 AI 模型(如通过 Nuclio)。
    • 协作: 支持多用户协作,可以分配任务、进行审核等。
    • 数据管理: 提供项目 (Project) 和任务 (Task) 管理功能,方便组织数据。
    • 多种导出格式: 支持导出为多种常见的数据集格式,如 COCO JSON, Pascal VOC XML, YOLO, MOT, Segmentation Mask 等,方便与各种深度学习框架对接。
    • 可扩展性: 开源,可以通过插件或集成自定义工具进行扩展。
    • 部署: 可以通过 Docker 在本地、服务器或云上部署。

img

CVAT标注结果导出

使用从 CVAT 导出的 YOLO 格式数据,并在 CPU 上训练一个新的 YOLOv8 模型(使用 Ultralytics 框架),以下详细步骤。

重要提示: 在 CPU 上训练目标检测模型(尤其是像 YOLOv8 这样较现代的模型)将会非常非常慢。根据你的数据集大小、模型大小和 CPU 性能,一个 epoch 可能需要数小时甚至更长时间。这通常只适用于非常小的数据集、快速原型验证或没有 GPU 可用的情况。请有心理准备。

步骤 1:从 CVAT 导出 YOLO 格式数据

  1. 登录 CVAT 并导航到你已完成标注的任务 (Task)。
  2. 点击任务卡片上的 “Actions” (或任务页面内的菜单按钮)。
  3. 选择 “Export task dataset”
  4. 在弹出的窗口中:
    • 选择格式: 务必选择 YOLOYOLO ZIP
    • 子集划分 (可选但推荐): 如果你在 CVAT 中已将数据分配到 Train, Validation, Test 子集,请确保勾选了相应的选项来导出这些子集。这会省去你手动划分的步骤。如果没划分,则导出整个数据集。
    • 保存图像: 确保勾选了 “Save images” 选项。
  5. 点击 “OK”“Export”
  6. 等待导出任务完成,然后下载生成的 ZIP 文件

步骤 2:准备数据集文件

  1. 解压 ZIP 文件: 将下载的 ZIP 文件解压到你电脑上一个方便访问的位置,例如 ~/datasets/my_yolo_dataset
  2. 检查文件结构: 解压后,检查里面的内容。一个典型的 YOLO 导出(特别是包含子集划分的)可能包含:
    • data.yaml (或 obj.dataobj.names 文件):这是配置文件,非常重要。
    • train/ (或类似的名称)
      • images/: 包含训练集的图像文件 (.jpg, .png 等)。
      • labels/: 包含训练集的标注文件 (.txt),每个图像对应一个。
    • valid/ (或 val/)
      • images/: 包含验证集的图像文件。
      • labels/: 包含验证集的标注文件。
    • test/ (可选)
      • images/: 包含测试集的图像文件。
      • labels/: 包含测试集的标注文件。
    • 如果导出时没有划分: 你可能只有一个 images/ 和一个 labels/ 文件夹,以及配置文件。
  3. 验证/修改 data.yaml (或创建它): 这是告诉 YOLOv8 框架如何找到你的数据的关键文件。
    • 如果已有 data.yaml: 用文本编辑器打开它。它看起来类似这样:
      1
      2
      3
      4
      5
      6
      7
      8
      path: ../datasets/my_yolo_dataset # !!可能需要修改:数据集根目录的相对或绝对路径
      train: train/images # !!训练集图片路径 (相对于 path)
      val: valid/images # !!验证集图片路径 (相对于 path)
      # test: test/images # 可选:测试集图片路径

      # Classes
      nc: 9 # !!类别数量,必须准确
      names: ['bus', 'car', 'clock', ...] # !!类别名称列表,顺序必须与 labels/*.txt 中的类别ID (0, 1, 2...) 完全对应!
      你需要仔细检查并可能修改:
      • path: 确保它正确指向你解压数据集的根目录相对于你将要运行训练命令的位置。使用绝对路径通常更安全。例如:/home/your_user/datasets/my_yolo_dataset
      • train, val, test: 确保它们正确指向包含图像文件的文件夹(相对于 path 定义的路径)。
      • nc: 确保类别数量正确无误。
      • names: 极其重要! 确保这个列表中的类别名称顺序与 CVAT 导出时使用的类别 ID(从 0 开始)以及 labels/ 文件夹下 .txt 文件中使用的数字 ID 完全一致。CVAT 导出 YOLO 格式时通常会保证这一点,但务必检查。
    • 如果只有 obj.dataobj.names: 你需要手动创建一个 data.yaml 文件
      • 打开 obj.names,将里面的类别名称按顺序列入 data.yamlnames 字段。计算类别数量填入 nc
      • 打开 obj.data,它通常包含类别数量、训练集/验证集图像列表文件的路径等信息。你需要根据这些信息和你的实际文件结构来填写 data.yaml 中的 path, train, val 字段。你可能需要将 obj.data 中指向的图像列表文件(例如 train.txt, valid.txt)也放到你的数据集目录中,或者直接修改 data.yaml 指向包含图像的文件夹。创建 data.yaml 通常更符合现代 YOLOv8 的习惯。
  4. 手动划分数据集 (如果导出时未划分):
    • 如果你只有一个 images/labels/ 文件夹,你需要手动将其划分为训练集和验证集。
    • 创建子目录:在数据集根目录下创建 train/images, train/labels, valid/images, valid/labels
    • 编写脚本或手动移动文件:
      • 确定划分比例(例如,80% 训练,20% 验证)。
      • 遍历 images/ 文件夹中的所有图像文件。
      • 对于每个图像文件(例如 image1.jpg),找到其对应的标注文件(labels/image1.txt)。
      • 根据随机抽样或固定规则,将这对文件(图像和标注)移动到 train/valid/ 下相应的 imageslabels 文件夹中。
      • 确保图像和它的 .txt 标注文件始终在同一集合(train 或 valid)中。
    • 更新 data.yaml 中的 trainval 路径指向你新创建的 train/imagesvalid/images 文件夹。

步骤 3:设置 Python 和 Ultralytics 环境

  1. 安装 Python: 确保你安装了较新版本的 Python(推荐 3.8 - 3.11)。
  2. 创建虚拟环境 (强烈推荐): 这可以避免库版本冲突。
    1
    2
    3
    4
    5
    6
    7
    8
    # 使用 venv (Python 内置)
    python -m venv yolo_cpu_env
    source yolo_cpu_env/bin/activate # Linux/macOS
    # yolo_cpu_env\Scripts\activate # Windows

    # 或者使用 Conda
    # conda create -n yolo_cpu_env python=3.9
    # conda activate yolo_cpu_env
  3. 安装 Ultralytics YOLOv8: 在激活的虚拟环境中运行:
    1
    pip install ultralytics
    这会自动安装 PyTorch 的 CPU 版本(因为它检测不到兼容的 GPU/CUDA)以及其他必要的依赖库。

步骤 4:配置训练参数

  1. 选择预训练模型: 为了利用迁移学习并加快(相对而言)收敛速度,你需要从一个预训练模型开始。对于 CPU 训练,强烈建议从小模型开始
    • yolov8n.pt (Nano): 最快,占用资源最少,但精度相对较低。CPU 训练的首选起点。
    • yolov8s.pt (Small): 稍大,稍慢,精度稍高。如果 yolov8n 效果不佳且你的 CPU 尚可承受,可以尝试。
    • 更大的模型 (m, l, x) 在 CPU 上训练几乎不可行。
    • Ultralytics 会在你第一次使用模型时自动下载 .pt 文件。
  2. 确定训练参数:
    • model: 你选择的预训练模型,例如 yolov8n.pt
    • data: 指向你准备好的 data.yaml 文件的路径。
    • epochs: 训练的总轮数。对于 CPU,可以先设置一个较小的值(例如 10-50)来测试流程和观察初步结果,后续再增加。完整训练可能需要 100-300 轮甚至更多,但这在 CPU 上会耗费极长时间。
    • batch: 批处理大小。CPU 训练的关键参数。由于 CPU 计算能力和内存带宽限制,你需要设置一个非常小的值,例如 2, 4, 8。可以从 4 或 8 开始尝试,如果内存不足 (OOM) 或 CPU 占用过高导致系统卡顿,则需要减小。
    • imgsz: 输入图像的尺寸。通常设置为 640。更大的尺寸会显著增加 CPU 计算负担和内存占用。CPU 训练建议保持 640 或更小。
    • device: 必须设置为 cpudevice=cpu
    • workers: 数据加载时使用的 CPU 核心数。可以设置为你 CPU 核心数的一部分,例如 2, 4。不一定越多越好,过高可能导致 CPU 争用。

步骤 5:执行训练

  1. 打开终端或命令行。

  2. 激活你的虚拟环境 (如 source yolo_cpu_env/bin/activate)。

  3. 导航到你的项目目录 (通常是你存放 data.yaml 或打算运行脚本的地方,确保 data.yaml 中的路径相对于此位置是正确的)。

  4. 运行训练命令:

    1
    yolo train model=yolov8n.pt data=/path/to/your/data.yaml epochs=50 batch=4 imgsz=640 device=cpu workers=4
    • 请将 /path/to/your/data.yaml 替换为你的 data.yaml 文件的实际路径。
    • 根据需要调整 epochs, batch, workers 的值。
    • 再次强调:请有耐心! 你会看到训练开始,并显示每个 epoch 的进度、损失值等,但这会非常慢。
  5. 或者使用 Python 脚本进行训练: 创建一个 Python 文件(例如 train_cpu.py):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    from ultralytics import YOLO
    import torch # 导入 torch 以便检查

    if __name__ == '__main__':
    # 检查 PyTorch 是否确实在使用 CPU
    print(f"PyTorch device: {torch.cuda.is_available() and torch.device('cuda') or torch.device('cpu')}")

    # 加载一个预训练模型 (例如 yolov8n.pt)
    # 模型文件如果本地没有,首次运行时会自动下载
    model = YOLO('yolov8n.pt')

    # 开始训练
    try:
    results = model.train(
    data='/path/to/your/data.yaml', # !!替换为你的 data.yaml 路径
    epochs=50, # 训练轮数
    batch=4, # 批大小 (根据CPU和内存调整)
    imgsz=640, # 图像尺寸
    device='cpu', # !!指定使用 CPU
    workers=4, # 数据加载进程数 (根据CPU核心数调整)
    # 其他可选参数:
    # project='runs/train_cpu', # 自定义保存结果的项目名
    # name='exp_cpu_run1', # 自定义本次运行的名称
    # patience=20, # 早停轮数 (如果验证集指标在20轮内没有提升则停止)
    # lr0=0.01, # 初始学习率 (通常用默认值)
    )
    print("训练完成!")
    print(f"结果保存在: {results.save_dir}") # results 对象在 v8 中不直接包含 save_dir,需要从 model 或 trainer 获取,或者直接查找 runs/train 目录

    except Exception as e:
    print(f"训练过程中发生错误: {e}")

    然后运行脚本:python train_cpu.py

  • GPU版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# -*- coding: utf-8 -*-
"""

pip install ultralytics

pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118


"""
# -*- coding: utf-8 -*-
import torch # Import torch to check CUDA availability first
from ultralytics import YOLO
import os # Import os for path joining if needed

if __name__ == '__main__':
# --- Verify CUDA Availability ---
if torch.cuda.is_available():
device_to_use = 0 # Use the first available GPU (index 0)
# device_to_use = 'cuda' # Alternative way to specify default GPU
print(f"CUDA is available! Training on GPU device: {torch.cuda.get_device_name(device_to_use)}")
else:
print("错误:未检测到兼容的 NVIDIA GPU 或 CUDA 配置不正确。")
print("请检查驱动、CUDA Toolkit、cuDNN 和 GPU 版本的 PyTorch 是否已正确安装。")
print("将尝试在 CPU 上运行,但这会非常慢...")
device_to_use = 'cpu'

# --- Configuration ---
# !!确保 data.yaml 路径正确!! (使用 os.path.join 或 pathlib 更佳)
data_yaml_path = r'C:\Users\DELL\Desktop\test_data\test_data\data.yaml'
model_to_load = 'yolov8x.pt' # Using the extra-large model
num_epochs = 50 # Number of training epochs
batch_size = 16 # !! Batch size for GPU (Adjust based on VRAM: 8, 16, 32, etc.)
image_size = 640 # Input image size
num_workers = 4 # Data loading workers (Adjust based on CPU cores)
project_name = 'runs/train_gpu' # Directory to save results
run_name = 'exp_gpu_yolov8x' # Specific name for this run

# --- Load Model ---
# Model file will be downloaded automatically if not present locally
print(f"Loading model: {model_to_load}")
model = YOLO(model_to_load)

# --- Start Training ---
print("-" * 30)
print("开始训练...")
print(f" Data YAML: {data_yaml_path}")
print(f" Epochs: {num_epochs}")
print(f" Batch Size: {batch_size}")
print(f" Image Size: {image_size}")
print(f" Device: {device_to_use}")
print(f" Workers: {num_workers}")
print("-" * 30)

try:
results = model.train(
data=data_yaml_path,
epochs=num_epochs,
batch=batch_size,
imgsz=image_size,
device=device_to_use, # !! Use the determined device (e.g., 0 or 'cpu')
workers=num_workers,
project=project_name, # Specify project directory
name=run_name, # Specify run name
# Other potentially useful optional parameters for GPU:
# cache=True, # Cache images in RAM/disk for faster loading (if RAM/disk allows)
# patience=20, # Early stopping patience
# lr0=0.01, # Initial learning rate
# optimizer='AdamW', # Optimizer (e.g., 'AdamW', 'SGD')
# amp=True, # Use Automatic Mixed Precision (can speed up training, reduce memory, requires compatible GPU)
)
print("-" * 30)
print("训练完成!")
# Note: The 'results' object itself might not directly contain the save path in all versions/contexts.
# The results are reliably saved in the project/name directory.
print(f"训练结果和模型权重保存在: {os.path.join(project_name, run_name)}")
print("-" * 30)

except torch.cuda.OutOfMemoryError:
print("-" * 30)
print("错误:GPU 显存不足 (Out of Memory)!")
print(f"尝试减小批处理大小 (batch={batch_size})。例如,尝试 batch=8 或 batch=4。")
print("如果显存仍然不足,可以考虑减小图像尺寸 (imgsz) 或使用更小的模型 (如 yolov8l.pt, yolov8m.pt)。")
print("-" * 30)
except FileNotFoundError as e:
print("-" * 30)
print(f"错误:找不到文件或目录: {e}")
print("请仔细检查 data.yaml 文件中的路径配置是否正确,特别是 'path', 'train', 'val' 字段。")
print(f"确保 data.yaml 文件本身位于: {data_yaml_path}")
print("-" * 30)
except Exception as e:
print("-" * 30)
print(f"训练过程中发生意外错误: {e}")
import traceback
traceback.print_exc() # Print detailed traceback for debugging
print("-" * 30)

img

步骤 6:监控训练和评估结果

  1. 观察终端输出: 查看每个 epoch 的进度、损失函数值(box_loss, cls_loss, dfl_loss)是否在下降,以及在验证集上的指标(如 P, R, mAP50, mAP50-95)是否在提升。
  2. 查找结果: 训练过程和结果默认会保存在 runs/train/exp<N> 目录下(例如 runs/train/exp, runs/train/exp2 等)。里面包含:
    • weights/: 保存的模型权重文件。best.pt 是基于验证集指标最佳的模型,last.pt 是最后一个 epoch 的模型。best.pt 通常是你需要的模型。
    • 日志文件 (TensorBoard logs)。
    • 配置文件副本。
    • 验证结果图表和图片(例如 confusion_matrix.png, results.png, val_batch0_pred.jpg 等)。
  3. 验证模型: 训练完成后(或中途),你可以使用 best.pt 在验证集上进行评估:
    1
    yolo val model=runs/train/exp<N>/weights/best.pt data=/path/to/your/data.yaml device=cpu

步骤 7:使用训练好的模型

训练完成后,你可以使用 best.pt 文件进行推理(目标检测):

1
2
3
yolo predict model=runs/train/exp<N>/weights/best.pt source=/path/to/image.jpg device=cpu
# 或者对视频进行推理 (CPU上会很慢)
# yolo predict model=runs/train/exp<N>/weights/best.pt source=/path/to/video.mp4 device=cpu

或者在 Python 脚本中使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from ultralytics import YOLO

# 加载你训练好的模型
model = YOLO('runs/train/exp<N>/weights/best.pt')

# 进行预测
results = model.predict(source='/path/to/image.jpg', device='cpu')

# 处理结果 (例如,绘制边界框)
for r in results:
boxes = r.boxes # Boxes object for bounding box outputs
masks = r.masks # Masks object for segmentation masks outputs
keypoints = r.keypoints # Keypoints object for pose outputs
probs = r.probs # Probs object for classification outputs
# r.show() # display to screen
r.save(filename='result.jpg') # save to disk

CPU训练的局限性:

  • 速度极慢:
  • 内存限制: 小心 RAM 耗尽,特别是使用较大的 batchimgsz 时。
  • 可行性: 对于非常大的数据集或需要高精度(需要训练更长时间或更大模型)的任务,CPU 训练可能实际上不可行。考虑使用 Google Colab(提供免费 GPU)、Kaggle Kernels 或其他云 GPU 服务可能是更好的选择。

验证数据集获取

获取验证数据集(Validation Set)是训练过程中非常重要的一步,用于评估模型在未见过数据上的表现并调整超参数。当你的数据来自 CVAT 并导出了 YOLO 格式时,有以下几种主要方法来获取或指定验证数据集:

方法一:在 CVAT 中划分并导出 (推荐)

这是最方便、最不容易出错的方法,推荐优先考虑。

  1. 在 CVAT 中分配子集 (Subset):
    • 在你 CVAT 的任务 (Task) 中,你可以将标注好的数据帧或图片分配到不同的子集。默认情况下,所有数据都在 DefaultTrain 子集中。
    • 进入你的任务,选择一些帧或图片(例如,随机选择总数据的 10-20%)。
    • 点击 “Actions” -> “Change subset”。
    • 在弹出的对话框中,选择 Validation 作为目标子集,然后确认。
    • 这样你就告诉 CVAT,这些选中的数据属于验证集。你也可以同样地分配 Test 子集(如果需要)。
  2. 导出时包含子集:
    • 在你导出任务数据集时,选择 YOLOYOLO ZIP 格式。
    • 关键: 在导出选项中,确保你选择了导出所有子集,或者该格式默认就会将不同的子集分开导出。例如,YOLO ZIP 格式通常会自动创建 train/, valid/, test/ 这样的子目录结构。
  3. 检查导出的文件:
    • 解压下载的 ZIP 文件。
    • 检查里面是否已经包含了类似 train/valid/ (或 val/) 的文件夹,每个文件夹下又分别有 images/labels/ 子目录。
    • 同时检查 data.yaml 文件,它应该已经自动配置好了 train:val: 指向这些对应的文件夹(例如 train: train/images, val: valid/images)。
    • 如果导出文件已经是这种结构,那么你不需要再做任何事情,直接使用这个 data.yaml 文件进行训练即可。

方法二:手动划分已导出的数据集

如果你从 CVAT 导出时没有划分小子集,或者导出的格式没有自动区分,那么你只有一个包含所有图像和标签的集合。你需要手动将其划分为训练集和验证集。

  1. 确定划分比例: 根据你的总数据量,决定一个合适的划分比例。常见的比例是 80% 训练 / 20% 验证,或者 70% / 30%。如果数据量很大,验证集比例可以适当降低。
  2. 创建目录结构: 在你的数据集根目录下(例如 /home/user/my_dataset),创建如下结构:
    1
    2
    3
    4
    5
    6
    7
    8
    my_dataset/
    ├── train/
    │ ├── images/
    │ └── labels/
    ├── valid/
    │ ├── images/
    │ └── labels/
    └── data.yaml # 你需要创建或修改的文件
    (可能还需要保留原始的 images/labels/ 文件夹,或者直接从那里移动文件)。
  3. 编写脚本或手动移动文件 (推荐脚本):
    • 核心原则: 必须保证同一张图片 (.jpg, .png 等) 和其对应的标注文件 (.txt) 始终被划分到同一个集合 (要么都在 train,要么都在 valid)。文件名通常是一一对应的(除了扩展名)。
    • 脚本方法 (Python 示例):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
import os
import random
import shutil
from pathlib import Path
import yaml # 需要安装 PyYAML: pip install pyyaml

# --- 配置 ---
# !!修改这里为你解压后的 CVAT 导出数据集的根目录!!
# 使用 Path 对象处理路径
dataset_root = Path(r'/Users/zg/Downloads/task_13').resolve() # <<--- 修改这里,并确保是绝对路径

# !!指定你想要创建的新数据集根目录(如果想分开存放)!!
# 如果设为 None,则在原 dataset_root 下创建 train/valid 目录
# 使用 Path 对象处理路径
output_root = dataset_root # <<--- 可以修改为新的路径,例如 Path('./yolov8_dataset').resolve()

# 使用 Path 对象定义源路径
source_data_dir = dataset_root / 'obj_train_data' # 包含图像和标签的源文件夹
obj_names_file = dataset_root / 'obj.names' # 类别名称文件
train_txt_file = dataset_root / 'train.txt' # 原始图像列表文件

train_ratio = 0.8 # 训练集比例 (例如 0.8 表示 80%)
random_seed = 42 # 设置随机种子以保证每次划分结果一致 (可选)
# ------------

# 设置随机种子
if random_seed:
random.seed(random_seed)

# --- 检查输入文件/目录是否存在 ---
if not dataset_root.is_dir():
print(f"错误:数据集根目录不存在: {dataset_root}")
exit()
if not source_data_dir.is_dir():
print(f"错误:源数据目录不存在: {source_data_dir}")
exit()
if not obj_names_file.is_file():
print(f"错误:类别文件不存在: {obj_names_file}")
exit()

image_paths_from_txt = None
if not train_txt_file.is_file():
print(f"警告:原始 train.txt 文件不存在: {train_txt_file}")
print("将尝试直接扫描源数据目录中的图像文件...")
else:
# --- 从 train.txt 读取图像路径 ---
try:
print(f"正在从 {train_txt_file} 读取图像路径...")
with open(train_txt_file, 'r') as f:
# 读取原始路径行
raw_paths = [line.strip() for line in f if line.strip()]

image_paths_from_txt = []
print("正在验证 train.txt 中的图像路径...")
processed_raw_paths_count = 0 # 计数器
for raw_path in raw_paths:
processed_raw_paths_count += 1
# --- 修复逻辑开始 ---
# 1. 提取文件名 (例如 'frame_000000.png')
filename = Path(raw_path).name
# 2. 构建预期应该存在的路径 (在已知的 source_data_dir 下)
# 使用 resolve() 确保是绝对路径
expected_path = (source_data_dir / filename).resolve()
# --- 修复逻辑结束 ---

# 3. 验证这个构建出的预期路径是否存在
if expected_path.is_file():
image_paths_from_txt.append(expected_path) # 添加 *正确且存在的* 路径
else:
# 打印更具体的警告,显示原始行和尝试查找的位置
print(f"警告:在 {source_data_dir} 中找不到 train.txt 行 '{raw_path}' 对应的文件 '{filename}' (预期路径: {expected_path}),将跳过。")

print(f"从 {train_txt_file} 原始读取 {len(raw_paths)} 行。")
if not image_paths_from_txt:
# 如果修正后仍然没有有效路径,则触发后备扫描
raise ValueError("train.txt 文件中没有找到任何有效且存在的图像文件。")
print(f"验证后,有效图像路径数量: {len(image_paths_from_txt)}")

except Exception as e:
print(f"处理 train.txt 时出错: {e}")
print("将尝试直接扫描源数据目录中的图像文件...")
image_paths_from_txt = None

# --- 如果无法从 train.txt 读取或验证失败,则扫描目录 ---
if image_paths_from_txt is None:
print(f"正在扫描目录 {source_data_dir} 中的图像文件...")
# 直接使用 source_data_dir (已经是 Path 对象)
image_paths_from_dir = list(source_data_dir.glob('*.jpg')) + \
list(source_data_dir.glob('*.png')) + \
list(source_data_dir.glob('*.jpeg'))
if not image_paths_from_dir:
print(f"错误:在 {source_data_dir} 中未找到任何图像文件。")
exit()
# 确保路径是绝对路径
image_paths = [p.resolve() for p in image_paths_from_dir]
print(f"在目录中找到 {len(image_paths)} 个图像文件。")
else:
# 如果从 train.txt 成功读取并验证了路径,则使用这个列表
image_paths = image_paths_from_txt

# 在继续之前,最终检查 image_paths 是否为空
if not image_paths:
print("错误:未能收集到任何有效的图像路径来继续处理。")
exit()

# --- 读取类别名称 ---
try:
with open(obj_names_file, 'r') as f:
class_names = [line.strip() for line in f if line.strip()]
num_classes = len(class_names)
if num_classes == 0:
print(f"错误:类别文件 {obj_names_file} 为空。")
exit()
print(f"读取到 {num_classes} 个类别: {class_names}")
except Exception as e:
print(f"读取类别文件 {obj_names_file} 时出错: {e}")
exit()

# --- 创建输出目录 ---
# 确保 output_root 是 Path 对象
if not isinstance(output_root, Path):
output_root = Path(output_root).resolve()

# 使用 Path 对象定义目标目录
train_img_dir = output_root / 'train' / 'images'
train_lbl_dir = output_root / 'train' / 'labels'
valid_img_dir = output_root / 'valid' / 'images'
valid_lbl_dir = output_root / 'valid' / 'labels'

# exist_ok=True 避免目录已存在时报错
train_img_dir.mkdir(parents=True, exist_ok=True)
train_lbl_dir.mkdir(parents=True, exist_ok=True)
valid_img_dir.mkdir(parents=True, exist_ok=True)
valid_lbl_dir.mkdir(parents=True, exist_ok=True)

# --- 划分数据集 ---
# 打乱图像路径列表 (Path 对象列表)
random.shuffle(image_paths)

# 计算训练集和验证集的分割点
split_index = int(len(image_paths) * train_ratio)
train_image_paths = image_paths[:split_index]
valid_image_paths = image_paths[split_index:]

print("-" * 30)
print(f"总有效图像数: {len(image_paths)}")
print(f"划分后训练集图像数: {len(train_image_paths)}")
print(f"划分后验证集图像数: {len(valid_image_paths)}")
print("-" * 30)

# --- 复制或移动文件函数 ---
# copy_mode=True 复制文件, copy_mode=False 移动文件
copy_mode = True # 建议使用复制以保留原始数据

def process_files(source_img_paths, target_img_dir, target_lbl_dir, copy=True):
"""将源图像和对应的标签文件复制或移动到目标目录"""
processed_count = 0
for img_path in source_img_paths: # img_path 是 Path 对象
# 构建对应的标签文件路径 (.txt),标签文件在源目录 source_data_dir 下
# 使用 img_path.stem 获取不带扩展名的文件名
lbl_path = source_data_dir / (img_path.stem + '.txt')

# 检查图像和标签文件是否存在
# 图像路径 img_path 已经验证过存在
if lbl_path.is_file():
# 目标路径
target_img_path = target_img_dir / img_path.name
target_lbl_path = target_lbl_dir / lbl_path.name
try:
if copy:
shutil.copy2(str(img_path), str(target_img_path)) # copy2 接收字符串路径
shutil.copy2(str(lbl_path), str(target_lbl_path))
else:
shutil.move(str(img_path), str(target_img_path)) # move 接收字符串路径
shutil.move(str(lbl_path), str(target_lbl_path))
processed_count += 1
except Exception as e:
print(f"处理文件 {img_path.name} 时出错: {e}")
else:
# 图像文件应该存在,因为我们是从验证过的列表处理的
# if not img_path.is_file():
# print(f"警告:找不到图像文件 {img_path},跳过。")
print(f"警告:找不到对应的标签文件 {lbl_path},跳过图像 {img_path.name}。")
return processed_count

# --- 执行文件处理 ---
print(f"正在{'复制' if copy_mode else '移动'}训练集文件...")
train_processed = process_files(train_image_paths, train_img_dir, train_lbl_dir, copy=copy_mode)
print(f"成功处理 {train_processed} 个训练集文件对。")

print(f"正在{'复制' if copy_mode else '移动'}验证集文件...")
valid_processed = process_files(valid_image_paths, valid_img_dir, valid_lbl_dir, copy=copy_mode)
print(f"成功处理 {valid_processed} 个验证集文件对。")

# --- 生成 data.yaml 文件 ---
yaml_path = output_root / 'data.yaml'

# 使用相对于 output_root 的路径
yaml_data = {
# 使用 resolve() 获取绝对路径字符串
'path': str(output_root.resolve()),
# 相对路径指向新创建的目录
'train': 'train/images',
'val': 'valid/images',
# 'test': 'test/images', # 如果有测试集
'nc': num_classes,
'names': class_names
}

try:
with open(yaml_path, 'w') as f:
yaml.dump(yaml_data, f, sort_keys=False, default_flow_style=False) # default_flow_style=False 更好看
print("-" * 30)
print(f"data.yaml 文件已生成: {yaml_path}")
print("内容:")
print(yaml.dump(yaml_data, sort_keys=False, default_flow_style=False))
print("-" * 30)
print("数据集划分完成!")
except Exception as e:
print(f"生成 data.yaml 文件时出错: {e}")

  • 你需要根据你的实际情况修改 dataset_root, source_images_dir, source_labels_dirtrain_ratio

  • 这个脚本会将文件移动到新目录。如果你想保留原始文件,可以将 shutil.move 改为 shutil.copy

  • 手动方法 (仅适用于少量数据): 创建 trainvalid 子目录,然后手动将图像和对应的 .txt 文件拖拽到相应的 imageslabels 文件夹中,确保比例大致正确且配对无误。非常容易出错。

  1. 修改/创建 data.yaml 文件:
    • 在你手动创建了 train/valid/ 目录结构后,你需要像下面这样配置 data.yaml
      1
      2
      3
      4
      5
      6
      7
      path: /home/user/my_dataset   # 数据集根目录
      train: train/images # 指向训练图像文件夹
      val: valid/images # 指向验证图像文件夹
      # test: test/images # 如果有测试集

      nc: 3 # 你的类别数量
      names: ['car', 'person', 'truck'] # 你的类别名称列表

方法三:使用 .txt 文件列表 (如果你选择了方案A来创建data.yaml)

如果你是根据 obj.data 文件中的 train = .../train.txtvalid = .../valid.txt 来配置 data.yaml 的,那么你需要确保:

  1. train.txt 文件确实存在,并且里面列出了所有训练集图像的正确路径(绝对路径或相对于 data.yamlpath 的路径)。
  2. 你有一个类似的 valid.txt 文件,里面列出了所有验证集图像的正确路径
  3. data.yaml 文件中的 train:val: 字段正确指向了这两个 .txt 文件。

如果你只有一个包含所有图像路径的 .txt 文件,你需要像方法二中划分文件一样,将这个 .txt 文件也划分成两个文件train.txtvalid.txt,确保划分比例合适。

总结:

  • 最佳方式: 在 CVAT 中就使用 “Subset” 功能区分好训练集和验证集,然后导出 YOLO ZIP 格式,通常 data.yaml 就配置好了。
  • 次佳方式: 如果导出的数据未划分,使用脚本(如上例)将其自动划分为 train/valid/ 目录结构,然后相应地配置 data.yaml 指向这些目录。
  • 如果依赖 .txt 列表: 确保你有分别列出训练图像和验证图像路径的 train.txtvalid.txt 文件,并在 data.yaml 中正确引用它们。

无论哪种方法,最终目标都是要有一个明确区分的训练数据集和一个验证数据集,并且 data.yaml 文件能够准确地告诉 YOLOv8 框架去哪里找到它们。

训练结果

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
python train_model.py 
PyTorch device: cpu
Ultralytics 8.3.105 🚀 Python-3.12.3 torch-2.6.0+cu124 CPU (Intel Xeon E5-2690 v3 2.60GHz)
engine/trainer: task=detect, mode=train, model=yolov8n.pt, data=/home/czq2/test_data/data.yaml, epochs=50, time=None, patience=100, batch=4, imgsz=640, save=True, save_period=-1, cache=False, device=cpu, workers=4, project=None, name=train2, exist_ok=False, pretrained=True, optimizer=auto, verbose=True, seed=0, deterministic=True, single_cls=False, rect=False, cos_lr=False, close_mosaic=10, resume=False, amp=True, fraction=1.0, profile=False, freeze=None, multi_scale=False, overlap_mask=True, mask_ratio=4, dropout=0.0, val=True, split=val, save_json=False, conf=None, iou=0.7, max_det=300, half=False, dnn=False, plots=True, source=None, vid_stride=1, stream_buffer=False, visualize=False, augment=False, agnostic_nms=False, classes=None, retina_masks=False, embed=None, show=False, save_frames=False, save_txt=False, save_conf=False, save_crop=False, show_labels=True, show_conf=True, show_boxes=True, line_width=None, format=torchscript, keras=False, optimize=False, int8=False, dynamic=False, simplify=True, opset=None, workspace=None, nms=False, lr0=0.01, lrf=0.01, momentum=0.937, weight_decay=0.0005, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1, box=7.5, cls=0.5, dfl=1.5, pose=12.0, kobj=1.0, nbs=64, hsv_h=0.015, hsv_s=0.7, hsv_v=0.4, degrees=0.0, translate=0.1, scale=0.5, shear=0.0, perspective=0.0, flipud=0.0, fliplr=0.5, bgr=0.0, mosaic=1.0, mixup=0.0, copy_paste=0.0, copy_paste_mode=flip, auto_augment=randaugment, erasing=0.4, crop_fraction=1.0, cfg=None, tracker=botsort.yaml, save_dir=runs/detect/train2
Downloading https://ultralytics.com/assets/Arial.Unicode.ttf to '/home/czq2/.config/Ultralytics/Arial.Unicode.ttf'...
100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 22.2M/22.2M [00:01<00:00, 21.2MB/s]
Overriding model.yaml nc=80 with nc=4

from n params module arguments
0 -1 1 464 ultralytics.nn.modules.conv.Conv [3, 16, 3, 2]
1 -1 1 4672 ultralytics.nn.modules.conv.Conv [16, 32, 3, 2]
2 -1 1 7360 ultralytics.nn.modules.block.C2f [32, 32, 1, True]
3 -1 1 18560 ultralytics.nn.modules.conv.Conv [32, 64, 3, 2]
4 -1 2 49664 ultralytics.nn.modules.block.C2f [64, 64, 2, True]
5 -1 1 73984 ultralytics.nn.modules.conv.Conv [64, 128, 3, 2]
6 -1 2 197632 ultralytics.nn.modules.block.C2f [128, 128, 2, True]
7 -1 1 295424 ultralytics.nn.modules.conv.Conv [128, 256, 3, 2]
8 -1 1 460288 ultralytics.nn.modules.block.C2f [256, 256, 1, True]
9 -1 1 164608 ultralytics.nn.modules.block.SPPF [256, 256, 5]
10 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest']
11 [-1, 6] 1 0 ultralytics.nn.modules.conv.Concat [1]
12 -1 1 148224 ultralytics.nn.modules.block.C2f [384, 128, 1]
13 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest']
14 [-1, 4] 1 0 ultralytics.nn.modules.conv.Concat [1]
15 -1 1 37248 ultralytics.nn.modules.block.C2f [192, 64, 1]
16 -1 1 36992 ultralytics.nn.modules.conv.Conv [64, 64, 3, 2]
17 [-1, 12] 1 0 ultralytics.nn.modules.conv.Concat [1]
18 -1 1 123648 ultralytics.nn.modules.block.C2f [192, 128, 1]
19 -1 1 147712 ultralytics.nn.modules.conv.Conv [128, 128, 3, 2]
20 [-1, 9] 1 0 ultralytics.nn.modules.conv.Concat [1]
21 -1 1 493056 ultralytics.nn.modules.block.C2f [384, 256, 1]
22 [15, 18, 21] 1 752092 ultralytics.nn.modules.head.Detect [4, [64, 128, 256]]
Model summary: 129 layers, 3,011,628 parameters, 3,011,612 gradients, 8.2 GFLOPs

Transferred 319/355 items from pretrained weights
Freezing layer 'model.22.dfl.conv.weight'
train: Scanning /home/czq2/test_data/train/labels... 495 images, 201 backgrounds, 0 corrupt: 100%|██████████| 495/495 [00:14<00:00, 34.44it/s]
train: New cache created: /home/czq2/test_data/train/labels.cache
val: Scanning /home/czq2/test_data/valid/labels... 124 images, 47 backgrounds, 0 corrupt: 100%|██████████| 124/124 [00:03<00:00, 37.91it/s]
val: New cache created: /home/czq2/test_data/valid/labels.cache
Plotting labels to runs/detect/train2/labels.jpg...
/home/czq2/yolo_cpu_env/lib/python3.12/site-packages/ultralytics/utils/plotting.py:587: UserWarning: Glyph 35270 (\N{CJK UNIFIED IDEOGRAPH-89C6}) missing from font(s) DejaVu Sans.
plt.savefig(fname, dpi=200)
/home/czq2/yolo_cpu_env/lib/python3.12/site-packages/ultralytics/utils/plotting.py:587: UserWarning: Glyph 39057 (\N{CJK UNIFIED IDEOGRAPH-9891}) missing from font(s) DejaVu Sans.
optimizer: 'optimizer=auto' found, ignoring 'lr0=0.01' and 'momentum=0.937' and determining best 'optimizer', 'lr0' and 'momentum' automatically...
optimizer: AdamW(lr=0.00125, momentum=0.9) with parameter groups 57 weight(decay=0.0), 64 weight(decay=0.0005), 63 bias(decay=0.0)
Image sizes 640 train, 640 val
Using 0 dataloader workers
Logging results to runs/detect/train2
Starting training for 50 epochs...

Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size
1/50 0G 0.6873 4.307 1.265 5 640: 2%|▏ | 2/124 [00:02<02:20, 1.15s/it]Downloading https://ultralytics.com/assets/Arial.ttf to '/home/czq2/.config/Ultralytics/Arial.ttf'...
100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 755k/755k [00:00<00:00, 2.17MB/s]
1/50 0G 1.137 2.561 1.378 7 640: 100%|██████████| 124/124 [02:08<00:00, 1.03s/it] | 512k/755k [00:00<00:00, 1.86MB/s]
Class Images Instances Box(P R mAP50 mAP50-95): 100%|██████████| 16/16 [00:19<00:00, 1.24s/it]
all 124 77 0.7 0.948 0.802 0.538

  • 参数定义

  • 我们来详细解释一下 YOLOv8 训练过程中这一行输出的含义:

1
2
Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
12/50 0G 0.8517 0.8027 1.157 4 640: 100%|██████████| 124/124 [02:08<00:00, 1.04s/it]

1
2
Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 16/16 [00:19<00:00,  1.23s/it]
all 124 77 1 0.999 0.995 0.853

第一部分:训练进度和损失 (Training Progress and Loss)

1
2
Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
12/50 0G 0.8517 0.8027 1.157 4 640: 100%|██████████| 124/124 [02:08<00:00, 1.04s/it]
  • Epoch: 12/50
    • 表示当前训练正在进行第 12 轮 (epoch),总共计划训练 50 轮。一个 epoch 指的是模型完整地看过一遍所有的训练数据。
  • GPU_mem: 0G
    • 显示当前 GPU 显存 (VRAM) 的使用量。这里显示 0G 非常奇怪,尤其是如果你指定了使用 GPU 进行训练。
      • 可能的原因 1 (最可能): 训练实际上是在 CPU 上运行的,即使你可能指定了 device=0device='cuda'。这通常发生在 PyTorch 无法正确检测或使用你的 GPU 时(驱动、CUDA、cuDNN 或 PyTorch 版本问题)。你需要回过头去验证你的 GPU 环境是否配置正确,以及 PyTorch 是否能真正使用 GPU (torch.cuda.is_available() 必须为 True)。
      • 可能的原因 2 (不太可能): 你的模型和批次大小非常小,以至于显存占用可以忽略不计(对于 YOLOv8 尤其是 x 模型和 batch=4 来说几乎不可能)。
      • 可能的原因 3 (极少): 监控显存的工具或接口暂时失效。
    • 正常的 GPU 训练 应该会显示一个非零的显存使用值,例如 5.8G, 10.2G 等,具体取决于模型大小、批大小和图像尺寸。
  • box_loss: 0.8517
    • 边界框损失 (Bounding Box Loss)。这个值衡量的是模型预测的边界框位置/大小与真实边界框之间的差异。值越低越好。
  • cls_loss: 0.8027
    • 分类损失 (Classification Loss)。这个值衡量的是模型预测的物体类别与真实类别之间的差异。值越低越好。
  • dfl_loss: 1.157
    • 分布焦点损失 (Distribution Focal Loss)。这是 YOLOv8(尤其是 v8 及更新版本)引入的一种用于边界框回归的损失,它将边界框的连续坐标回归问题转化为离散的概率分布预测问题,有助于提高定位精度。值越低越好。
  • Instances: 4
    • 当前正在处理的这个批次 (batch) 中包含的目标实例 (objects) 的数量。这里是 4 个。注意这不是批大小(batch size,即图像数量)。
  • Size: 640
    • 训练时输入模型的图像尺寸 (通常是 height 和 width 都设为这个值,即 640x640)。
  • 100%|██████████| 124/124 [02:08<00:00, 1.04s/it]:
    • 这是一个进度条,显示当前 epoch 内训练数据的处理进度。
    • 100%: 表示当前 epoch 的训练数据已全部处理完毕。
    • 124/124: 表示当前 epoch 共处理了 124 个批次 (batches),现在已经完成了第 124 个批次。 (总批次数 = 总训练图像数 / batch_size)
    • [02:08<00:00]: 当前 epoch 已用时 2 分 8 秒,预计剩余时间 0 秒。
    • 1.04s/it]: 处理一个批次 (iteration) 平均需要 1.04 秒。这个速度对于 GPU 来说相当慢,进一步印证了可能是在 CPU 上运行。GPU 训练通常是 ms/it (毫秒每迭代)。

第二部分:验证结果评估 (Validation Results Evaluation)

这部分通常在每个 epoch 的训练结束后(或者根据设置的频率)运行,使用验证集 (Validation Set) 来评估当前模型的性能。

1
2
Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 16/16 [00:19<00:00,  1.23s/it]
all 124 77 1 0.999 0.995 0.853
  • Class: all
    • 表示这一行显示的是所有类别的平均性能指标。如果你的数据集有多个类别,通常还会在这里或下面几行列出每个单独类别的指标。
  • Images: 124
    • 你的验证集中包含 124 张图像。
  • Instances: 77
    • 你的验证集中所有图像总共包含 77 个已标注的目标实例。
  • Box(P R mAP50 mAP50-95): 这是目标检测任务最核心的评估指标:
    • P (Precision / 精确率): 1
      • 指模型预测为正例(检测到物体)的样本中,真正是正例的比例。计算公式:TP / (TP + FP) (真阳性 / (真阳性 + 假阳性))。
      • 值为 1 意味着所有模型检测到的物体都是正确的(没有误报)。这个值通常在 IoU (Intersection over Union) 阈值为 0.5 时计算,并且可能是针对某个最优置信度阈值得出的。
    • R (Recall / 召回率): 0.999
      • 指所有真实的正例(所有实际存在的物体)中,被模型成功预测出来的比例。计算公式:TP / (TP + FN) (真阳性 / (真阳性 + 假阴性))。
      • 0.999 意味着模型找回了验证集中几乎所有(99.9%)的真实物体(漏报非常少)。这个值通常也是在 IoU 阈值为 0.5 时计算,并且可能是针对某个最优置信度阈值得出的。
    • mAP50 (mean Average Precision @ IoU=0.5): 0.995
      • IoU (交并比) 阈值设置为 0.5 时,计算所有类别的平均精度 (Average Precision, AP),然后对所有类别求平均值 (mean AP)。AP 是综合了精确率和召回率的指标,它衡量的是 PR 曲线下的面积。
      • 0.995 是一个非常高的值,表示在 IoU=0.5 的标准下,模型性能非常好。
    • mAP50-95 (mean Average Precision @ IoU=0.5:0.95): 0.853
      • 这是更严格、也是 COCO 数据集竞赛等场景中更常用的标准。它计算了 IoU 阈值从 0.5 到 0.95、步长为 0.05 的一系列 IoU 值下的 mAP,然后对这些 mAP 求平均。
      • 这个指标对检测框的定位精度要求更高。0.853 (即 85.3%) 是一个相当不错的性能,表明模型不仅能检测到物体,而且定位也比较准确。
  • 100%|██████████| 16/16 [00:19<00:00, 1.23s/it]:
    • 这是验证过程的进度条。
    • 16/16: 表示验证集被分成了 16 个批次进行处理,现在已经处理完了。 (验证批次数 = 总验证图像数 / 验证批大小,验证批大小可能与训练批大小不同)。
    • [00:19<00:00]: 整个验证过程用时 19 秒。
    • 1.23s/it]: 处理一个验证批次平均需要 1.23 秒。这个速度同样说明很可能是在 CPU 上运行。
Next:
Nuclio与CVAT使用yolov8目标检测模型实现半自动化标注