数据集准备:从零到一构建你的YOLO训练数据

数据,说白了就是AI模型的「食物」。你喂什么,模型就学什么。我见过太多人花大把时间调参,结果模型效果差,最后发现是数据本身就有问题。嗯,这一章咱们就把数据准备这件事彻底讲透。

核心要点:一个高质量的数据集,比一个花哨的模型结构更重要。数据质量决定模型上限,算法只是逼近这个上限。

数据采集策略:别让模型「瞎猜」

数据采集不是随便拍几张照片就完事。我早期做过一个项目,检测工厂流水线上的零件,结果模型在实验室跑得好好的,一到现场就崩。为什么?因为训练数据全是白天拍的,现场是昏暗的车间。

好的数据采集,你得考虑这几个维度:

维度 说明 常见坑
光照条件 晴天、阴天、黄昏、夜晚、逆光、顺光 只采集一种光照,模型泛化差
拍摄角度 俯视、平视、侧视、仰视 角度单一,模型无法识别其他视角
目标尺度 近景、中景、远景、局部遮挡 只拍大目标,小目标直接漏检
背景多样性 不同场景、不同背景纹理 背景单一,模型学的是背景而非目标
目标状态 静止、运动、模糊、变形 只拍静止物体,运动场景全废

我的经验:每类目标至少采集 1000-2000 张图片。如果条件有限,最少也要 500 张。少于这个数,模型基本学不到什么。

还有一个很多人忽略的点——负样本。就是那些不包含目标的图片。我曾经在一个项目中加了 20% 的负样本,误检率直接降了一半。你想想看,模型得知道「没有目标」长什么样,不然它会把什么都当成目标。

LabelImg 标注工具:安装与使用

标注工具我试过不少,LabelImg 是我用得最顺手的。开源、免费、跨平台,支持 YOLO 格式直接导出。说白了,它就是为 YOLO 量身定做的。

安装

Windows 用户直接下载 exe 安装包就行。Linux 和 macOS 用户,我建议用 pip 安装:

# 安装依赖
pip install labelimg

# 启动
labelimg

嗯,这里要注意。如果你用的是 Python 3.10 以上版本,可能会遇到 PyQt5 兼容问题。我遇到过,解决方案是手动指定版本:

pip install labelimg==1.8.6

使用流程

  1. 打开图片目录:点击「Open Dir」选择你的图片文件夹
  2. 设置保存目录:点击「Change Save Dir」选择标注文件存放位置
  3. 切换 YOLO 格式:点击「View」→「Auto Save mode」勾选,再点击「PascalVOC」切换到 YOLO 格式
  4. 开始标注:按 W 键进入绘制模式,框选目标,输入类别名称
  5. 快捷键:D 下一张,A 上一张,Ctrl+S 保存

避坑指南:我曾经一次性标注了 2000 张图片,结果发现保存格式是 PascalVOC 而不是 YOLO。全部重新导出,浪费了整整一天。所以,开始标注前一定确认格式设置正确。

YOLO TXT 标注格式:其实很简单

YOLO 的标注格式,说白了就是一个 txt 文件,每张图片对应一个同名 txt。文件内容长这样:

0 0.5 0.5 0.3 0.4
1 0.8 0.2 0.1 0.15

每一行代表一个目标,格式是:

<class_id> <x_center> <y_center> <width> <height>

注意,这里的坐标都是归一化的。什么意思?就是除以图片的宽和高,让所有值都在 0 到 1 之间。

字段 说明 示例
class_id 类别索引,从 0 开始 0 表示 person
x_center 目标中心点 x 坐标 / 图片宽度 0.5 表示正中间
y_center 目标中心点 y 坐标 / 图片高度 0.5 表示正中间
width 目标宽度 / 图片宽度 0.3 表示占图片宽度的 30%
height 目标高度 / 图片高度 0.4 表示占图片高度的 40%

检查标注:标注完成后,可以用 LabelImg 的「Open」功能随机打开几张图片,看看框的位置对不对。我习惯每标注 100 张就抽查一次,避免批量出错。

数据集划分:train/val/test 的黄金比例

数据准备好了,接下来就是划分。很多人直接随机分,其实有讲究。

我常用的比例是:

  • 训练集(train):70% - 模型学习用
  • 验证集(val):20% - 调参和早停用
  • 测试集(test):10% - 最终评估用

为什么 test 只留 10%?因为 test 集是用来做最终评估的,不能参与任何训练过程。你想想看,如果 test 集太大,训练数据就少了,模型学不好。10% 足够评估模型效果了。

划分的时候,我建议用脚本自动完成,别手动拖:

import os
import random
import shutil

def split_dataset(img_dir, label_dir, output_dir, train_ratio=0.7, val_ratio=0.2):
    images = [f for f in os.listdir(img_dir) if f.endswith(('.jpg', '.png', '.jpeg'))]
    random.shuffle(images)
    
    train_count = int(len(images) * train_ratio)
    val_count = int(len(images) * val_ratio)
    
    splits = {
        'train': images[:train_count],
        'val': images[train_count:train_count+val_count],
        'test': images[train_count+val_count:]
    }
    
    for split_name, split_images in splits.items():
        os.makedirs(f'{output_dir}/{split_name}/images', exist_ok=True)
        os.makedirs(f'{output_dir}/{split_name}/labels', exist_ok=True)
        
        for img in split_images:
            shutil.copy(f'{img_dir}/{img}', f'{output_dir}/{split_name}/images/{img}')
            label_file = img.rsplit('.', 1)[0] + '.txt'
            if os.path.exists(f'{label_dir}/{label_file}'):
                shutil.copy(f'{label_dir}/{label_file}', f'{output_dir}/{split_name}/labels/{label_file}')

# 使用
split_dataset('images/', 'labels/', 'yolo_dataset/')

注意:划分前一定要确保每张图片都有对应的标注文件。我遇到过图片有 1000 张,标注只有 980 个 txt,结果训练时报错。用脚本先检查一遍:

for img in images:
    label = img.rsplit('.', 1)[0] + '.txt'
    if not os.path.exists(f'labels/{label}'):
        print(f'Missing label for {img}')

本章知识结构

下面这张图,把整个数据准备的流程串起来了。你可以把它当作一个检查清单:

数据集准备流程 数据采集 光照·角度·尺度·背景 LabelImg 标注 YOLO TXT 格式 格式校验 图片-标注一一对应 数据集划分 train 70% · val 20% · test 10% train 模型训练 val 调参·早停 test 最终评估 关键原则 test 集不能参与训练 · 每张图片必须有标注 · 类别分布要均衡

最后几点提醒

  • 类别命名要统一:别今天叫「person」,明天叫「people」。LabelImg 里可以提前创建 classes.txt 文件,里面一行一个类别名。
  • 标注质量比数量重要:1000 张标注精准的图片,比 5000 张标注粗糙的图片效果好得多。我宁愿花时间复查,也不愿后期花几周调参。
  • 数据增强别在标注前做:先标注原始图片,再在训练时做数据增强。否则你标注了翻转后的图片,原始图片反而没标注,数据就乱了。

数据准备这一步,确实枯燥,但它是整个训练流程的基石。我见过太多人急着训练,结果数据一塌糊涂,最后回头重做。嗯,慢慢来,比较快。