YOLOv5新手避坑指南:从‘口罩检测’案例看自定义数据集的那些‘雷’
YOLOv5实战避坑手册:口罩检测数据集构建全流程详解
第一次接触目标检测的新手们,往往会在数据集准备阶段踩遍所有能踩的坑。上周有位读者发来他的训练日志——连续三天卡在RuntimeError: No labels found的错误提示上,而问题根源只是data.yaml里少了个空格。这让我意识到,市面上大多数教程都忽略了那些看似简单却致命的细节。
1. 数据采集:90%的模型效果取决于这一步
口罩检测项目的成败,从选择第一张样本图片时就已经决定了。许多初学者习惯性地上网随意抓取图片,结果训练时发现模型连最简单的正面人脸都识别不准。问题出在哪?
- 场景覆盖不足:只收集了明星写真般的正面清晰照片,缺少侧脸、遮挡、模糊等真实场景
- 分辨率陷阱:混合使用1920x1080和640x480的图片会导致Anchor匹配异常
- 光源干扰:强光照射下的反光口罩与普通口罩在模型眼中可能是两种物体
建议采用这样的采集策略:
# 使用爬虫时的筛选条件示例 min_width = 640 # 最小分辨率宽度 max_variation = 0.3 # 允许的宽高比差异 required_scenes = ['indoor', 'outdoor', 'backlight'] # 必须包含的场景类型提示:Kaggle的MAFA数据集包含7,876张带遮挡的口罩人脸,是极佳的补充数据源
2. 标注工程:Labelme的隐藏陷阱与解决方案
当看到新手用Labelme标注出这样的结果时,我就知道后续训练必然失败:
典型错误包括:
- 矩形框边缘紧贴口罩(应保留5-10像素缓冲)
- 标注了被完全遮挡的口罩(无法提供有效特征)
- 同一张图片出现不同尺寸的标注框(需保持比例一致)
正确的标注流程应该是:
- 启动Labelme时指定Python环境(避免版本冲突)
conda activate yolov5 labelme --autosave - 使用快捷键加速标注(W创建矩形,Ctrl+S保存)
- 定期执行标注验证脚本:
import labelme.utils labelme.utils.verify_annotations("path/to/json_folder")
3. 格式转换:JSON到TXT的魔鬼细节
原始教程提供的转换脚本存在三个隐患:
- 未处理中文路径问题
- 缺少异常坐标校验
- 忽略多物体重叠情况
这里给出工业级转换方案:
def safe_convert(img_size, box): # 校验坐标有效性 x1, y1, x2, y2 = box assert 0 <= x1 < x2 <= img_size[0], f"Invalid X coordinates: {box}" assert 0 <= y1 < y2 <= img_size[1], f"Invalid Y coordinates: {box}" # 标准化处理 dw = 1.0 / img_size[0] dh = 1.0 / img_size[1] x = (x1 + x2) / 2.0 - 1 y = (y1 + y2) / 2.0 - 1 w = x2 - x1 h = y2 - y1 return [round(x*dw,6), round(y*dh,6), round(w*dw,6), round(h*dh,6)]常见转换错误对照表:
| 错误类型 | 症状 | 解决方案 |
|---|---|---|
| 坐标溢出 | 训练时出现NaN损失 | 添加assert校验 |
| 精度丢失 | 验证集AP突然下降 | 保留6位小数 |
| 编码错误 | 'gb2312'解码失败 | 使用utf-8-sig |
4. 数据集架构:被大多数教程忽略的目录规范
YOLOv5对目录结构有隐藏要求,违反这些规则不会报错但会影响性能:
MaskDetection/ ├── datasets/ │ ├── train/ │ │ ├── images/ # 必须用复数形式 │ │ └── labels/ # 必须与images同级 │ ├── val/ │ │ ├── images/ │ │ └── labels/ │ └── test/ │ ├── images/ │ └── labels/ └── data.yaml # 必须使用Unix路径分隔符关键细节:
- 用
/代替\(Windows路径会导致augmentation失效) - images和labels必须严格配对(使用校验脚本):
python -c "from yolov5.utils.datasets import check_dataset; check_dataset('data.yaml')" - 样本分布应遵循60/20/20原则(训练/验证/测试)
5. data.yaml的死亡陷阱:一个空格引发的血案
下面这个看似正常的yaml文件,会让训练过程直接崩溃:
train: ../train/images val: ../valid/images # 这里多了个空格 nc: 1 names: ['mask']必须注意:
- 路径后禁止空格
- 类别名必须小写(YOLOv5内部会强制lower())
- 建议添加自动校验段:
# 高级配置(可选但推荐) roboflow: workspace: mask-detection project: version-3 license: CC BY 4.0 url: https://universe.roboflow.com/...当遇到Dataset not found错误时,按这个顺序排查:
- 执行
python -c "import yaml; print(yaml.safe_load(open('data.yaml')))"验证yaml语法 - 检查路径是否存在
ls $(cat data.yaml | grep 'train:' | awk '{print $2}') - 确认图片和标签数量匹配
wc -l train.txtvsls train/labels/ | wc -l
6. 实战中的典型报错与秒解方案
案例1:训练时突然崩溃显示CUDA out of memory
- 真凶:某张图片尺寸为30000x20000像素
- 快速定位:
from PIL import Image import os for img in os.listdir('train/images'): with Image.open(f'train/images/{img}') as im: if max(im.size) > 10000: print(f"异常图片: {img} {im.size}")
案例2:验证时mAP始终为0
- 根源:标签文件使用了中文标点
- 修复命令:
find . -name "*.txt" -exec sed -i 's/[,;]/ /g' {} \;
案例3:训练损失震荡剧烈
- 诱因:混合了COCO和VOC两种标注格式
- 诊断方法:
grep -E '[0-9]\.[0-9]{4,}' labels/*.txt | wc -l # 输出非0表示存在浮点坐标(COCO格式)
在完成所有准备工作后,建议运行这个终极检查脚本:
from yolov5.utils.general import check_dataset def dataset_diagnosis(yaml_path): results = check_dataset(yaml_path) assert results['train']['exists'], "训练集路径错误" assert results['val']['exists'], "验证集路径错误" assert len(results['labels']) == results['nc'], "类别数量不匹配" print("✅ 数据集通过基础验证") dataset_diagnosis('data.yaml')记住,当YOLOv5报错时,90%的问题都出在数据准备阶段。某次我花了三天时间调试模型参数,最后发现只是某个标签文件多了个空行。养成好的数据卫生习惯,比掌握任何高级技巧都重要。
