ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

红花数据集+YOLOv5目标检测:标签格式、训练配置与避坑实战指南

红花数据集+YOLOv5目标检测:标签格式、训练配置与避坑实战指南 简介YOLOv5目标检测配套的红花数据集包含1437张jpg红花图像、1437个txt标签文件与1437个xml标注文件共2000个文件压缩包约125.19MB。数据集已完成标注可直接用于YOLOv5模型的训练与验证省去自行标注的繁琐流程。资源代码采用参数化编程参数可方便更改编程思路清晰且注释明细适合计算机、电子信息工程、数学等专业学生用于课程设计、期末大作业或毕业设计中的目标检测项目。目前已获得523人学习下载。通过该资源可快速上手红花类别检测任务理解YOLOv5数据组织方式与标注文件格式同时借助清晰的代码注释和可调参数便于针对不同场景灵活修改实验配置有效提升项目开发效率。1. 红花目标检测数据集零标注成本的 YOLOv5 训练起点做 YOLOv5 目标检测最耗时间的工作往往不是调参而是准备一份像红花数据集这样已经标注好的训练数据。花小、背景杂、目标密集如果从零开始采集和标注一个课程设计周期基本就废掉了。这个资源包把数据集和对应已标注文件打包在一起txt 标签和图片命名一一对应解压后改一下路径就能进入训练流程。它解决的是“数据从哪来、标签对不对、能不能直接跑”三个核心问题适合目标检测入门、课程设计和毕业设计里需要快速出实验结果的场景。2. 解压后先看这个txt 标签格式、图片配对检查与数据集完整性自查一个标注好的目标检测数据集最怕的不是标签少而是标签和图片对不上。这份资源里给出的是一批以数字命名的 txt 文件比如 0754.txt、1054.txt、0454.txt它们对应的图片通常就是同名的 0754.jpg、1054.jpg、0454.jpg。在解压后第一时间先做一次完整的命名对齐检查能省掉后面训练时报 “no labels” 或者“图片找不到”的排查时间。2.1 红花数据集里 txt 标签的内容是什么YOLOv5 使用的标签格式是归一化的文本框信息每一行代表一个标注目标格式为class x_center y_center width height其中 x_center、y_center 是目标中心点相对于图片宽高的比例width 和 height 是目标框宽高相对于图片尺寸的比例取值范围都在 0 到 1 之间。比如某个 txt 里一行写着0 0.621094 0.358073 0.187500 0.240234这表示类别编号为 0中心点在图片横向 62.1%、纵向 35.8% 的位置目标框约占图片宽度 18.75%、高度 24.02%。这个格式可以直接被 YOLOv5 的 datasets.py 读取不需要再额外转换。我一般会用head -n 5 0454.txt先翻看一下标签内容。如果每行只有四个数字而没有开头的类别编号说明这份标注是更早期的格式需要补类别如果每行是整数坐标则需要把它换算成归一化坐标。拿到资源后先做这样一个检查可以避免带着错误格式一路跑到训练报错。为什么 YOLOv5 要求归一化坐标因为训练时图片会先被缩放到统一尺寸归一化坐标可以保证缩放前后目标框的相对位置不变。如果保留像素坐标图片一缩放所有框就全偏了。这也是很多从 LabelImg 直接导出的 XML 不能直接喂给 YOLOv5 的原因——XML 里存的是像素绝对坐标必须经过换算。2.2 文件名对齐检查批量核对图片和标签这份资源的标签文件名看起来是一组四位数字像是采集时的时间戳或者流水号。真正重要的是标签文件和图片文件前缀必须完全一致。在 Linux 或者 Windows 上解压后我会先跑一段小脚本做集合比对。import os from pathlib import Path img_dir Path(images) label_dir Path(labels) img_stems {p.stem for p in img_dir.iterdir() if p.suffix.lower() in (.jpg, .jpeg, .png)} txt_stems {p.stem for p in label_dir.iterdir() if p.suffix.lower() .txt} missing_label img_stems - txt_stems missing_img txt_stems - img_stems print(有图片但没有标签, sorted(missing_label)) print(有标签但没有图片, sorted(missing_img))这段脚本做的事很简单分别收集图片文件名的前缀和标签文件名的前缀然后用两个集合的差集找出对不上的文件。输出为空就说明配对完整可以直接进入训练输出不为空时优先处理缺失的标签因为训练时缺失标签的那张图会被 YOLOv5 自动忽略但缺失图片的标签文件会让数据加载阶段计算出的目标数量出现偏差。这里有一个容易被忽略的点图片可能同时存在.jpg和.jpeg两种后缀脚本里已经把这两种都算进去了。如果数据集里还有.png同样在这个后缀集合里补上即可不要只默认成 jpg。另外要注意的是不要在脚本里用os.listdir手写字符串拼接来判断配对遇到文件名里有连续空格或者中划线时容易出问题用Path.stem是最稳的。2.3 目录结构自查images 与 labels 的相对关系YOLOv5 默认datasets目录下需要同时存在images和labels两个目录且 images 下的图片路径和 labels 下的标签路径是镜像的。也就是图片在images/train/0454.jpg标签就要在labels/train/0454.txt。这个资源包如果原来目录不是这个约定结构解压后就需要自己重新组织一下。常见做法是把图片统一放到images/train和images/val标签放到labels/train和labels/val保证目录层级一一对应。如果资源里 txt 文件只有一份并没有按 train/val 分目录那么需要在训练前自己切分。我会先把所有文件复制到一个临时目录用 8:2 的比例做随机划分然后按上面说的目录结构摆放。这样做的好处是后面改 data.yaml 时只需要写两类路径不用在代码里做任何 hack。另外一个常被忽略的问题是标签文件的行尾符。Windows 上编辑过的 txt 可能会带\r\nYOLOv5 在 Linux 下读取时偶尔会报行解析错误。遇到这种情况用sed -i s/\r$// labels/*.txt统一清理一次就行属于一分钟能解决的事。类似的问题还包括 txt 文件名大小写YOLOv5 在 Linux 下匹配文件名时区分大小写0454.TXT和0454.txt会被当成两个文件解压后建议统一转成小写后缀。2.4 标签坐标合法性检查越界和宽高为零要提前拦下来坐标格式对不代表坐标值一定合法。红花数据集如果来自自动标注流程偶尔会出现某个框的中心点坐标在 0 到 1 之外或者宽高为 0。这种情况一旦进入训练轻则 loss 变成 nan重则模型训练崩掉。我会在训练前跑一段标签合法性检查脚本。from pathlib import Path label_dir Path(labels) for txt_path in sorted(label_dir.glob(*.txt)): with open(txt_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f格式异常: {txt_path.name}: {line.strip()}) continue cls_id, cx, cy, w, h map(float, parts) if not (0 cx 1 and 0 cy 1): print(f中心点越界: {txt_path.name}: {line.strip()}) if w 0 or h 0: print(f宽高异常: {txt_path.name}: {line.strip()})这里对每一行做三个层面的检查字段数是否为 5中心点坐标是否在 0 到 1 区间宽高是否大于 0。任何一项异常都打印出来方便定位到具体文件。注意我没有把 w 和 h 的上限卡到 1 以内因为理论上宽高等于 1 表示目标框占满整张图这在红花数据里不太可能出现但不算必须拦截的异常。3. 用这份红花数据训练 YOLOv5环境、配置与跑通验证的完整记录数据准备好之后剩下的就是把 YOLOv5 训练流程跑通。这个环节我会分成环境、data.yaml、训练命令、推理验证四步来做。每一步都值得认真过一遍因为红花目标比较小训练超参设置不合适时模型很容易只学会检测花丛而不是单朵花。3.1 环境版本选型YOLOv5 分支与 PyTorch 的匹配红花数据集配套的标签是 YOLOv5 标准格式直接使用 ultralytics 的 YOLOv5 官方仓库即可。建议使用 6.0 或 7.0 分支这两个分支的文档多、踩坑记录全显卡驱动兼容性也更好。PyTorch 版本方面我一般推荐 1.8 到 2.0 之间CUDA 11.3 或 11.8 都是比较稳妥的组合。环境安装用 requirements.txt 就能解决但要注意不要盲目装最新版。比如 PyTorch 2.x 与旧版 torchvision 可能因为版本不匹配直接报RuntimeError: operator torchvision::nms does not exist这个问题我在老机器上碰到过两次所以现在都会先核对 torch 与 torchvision 的版本号是否配套。装完后用一段很短的命令验证 GPU 是否可用。import torch print(torch.__version__, torch.cuda.is_available(), torch.cuda.get_device_name(0))输出 True 说明环境没问题。如果这里显示 False后面的训练会慢得让人怀疑人生建议先把驱动和 CUDA 装对再继续。判断环境问题的顺序也有讲究先看nvidia-smi输出的驱动版本和 CUDA 版本再确认 PyTorch 编译时的 CUDA 版本最后才是torch.cuda.is_available()的结果。很多同学一上来就重装 PyTorch结果发现是驱动太老白白折腾半天。3.2 data.yaml 的写法把红花类别和路径写清楚训练前需要给 YOLOv5 准备一份数据描述文件。红花这个场景只有一个类别配置文件非常简短train: /home/user/datasets/redflower/images/train val: /home/user/datasets/redflower/images/val nc: 1 names: 0: redflowerYOLOv5 读取标签时会把 txt 文件里的第一个数字当成类别索引然后与 names 列表对应。这里用绝对路径更省事避免因为相对路径启动位置不同导致数据集加载失败。nc表示类别数量红花数据集这里固定写 1。names里写什么名字不影响训练但会影响后续模型推理时显示的文字标签所以最好还是用有意义的名称比如 redflower。如果你的标签里出现了1这个类别号那说明标注时可能把红花的不同状态分了类或者标注软件默认从 1 开始。这种情况下先在数据集里统计一下类别编号分布再决定 nc 是 1 还是 2。最怕的是标签里既有 0 又有 1但配置文件只写了nc: 1训练过程会直接报class index out of range。统计类别编号用一行命令就够awk {print $1} labels/*.txt | sort -u3.3 训练命令与超参数红花目标小的注意事项训练命令我一般这么写python train.py \ --img 640 \ --batch 16 \ --epochs 120 \ --data data/redflower.yaml \ --weights yolov5s.pt \ --device 0 \ --cache--img 640是输入图片缩放尺寸红花如果整体都比较小建议不要降得太低否则小目标会直接变成几个像素点。--batch 16是批次大小由显存决定显存小于 8G 时改成 8 更稳妥。--epochs 120是训练轮数红花数据量如果不大100 到 150 轮就足够观察收敛趋势了。--weights yolov5s.pt用的是预训练权重比从头训练收敛快得多。--cache会把图像先缓存进内存减少磁盘 IO数据集不太大时能明显提速。参数作用红花场景建议--img输入图片尺寸640不要低于 416--batch批次大小16显存小改 8--epochs训练轮数120数据少可减到 100--weights预训练权重yolov5s.pt 或 yolov5m.pt--device训练设备0 表示第一张显卡--cache图像缓存到内存数据集小于 500M 时建议开启也可以选择 yolov5m.pt 作为预训练权重模型稍大对红花这种小目标特征提取能力会强一些但训练时间和显存占用都会增加。如果训练中遇到显存溢出优先把 batch 减半而不是降低图片尺寸否则检测框精度会明显下降。整个训练过程中需要盯住的指标是val/box_loss、val/obj_loss和mAP0.5。红花场景下模型容易出现“玄学过拟合”——训练集 loss 降到很低但在验证集上 mAP 始终上不去。这时候我会检查是不是数据增强开得太少或者训练集和验证集分布差异过大。一个简单验证方式是把训练集里随机几张图和验证集图放一起对比肉眼看光照和拍摄角度是否差别巨大。3.4 训练结束后跑一次推理验证训练完成后用测试图片跑一次 detect 就能直观看到效果。命令python detect.py \ --weights runs/train/exp/weights/best.pt \ --source dataset/images/val/0454.jpg \ --conf 0.5 \ --save-txt这里--conf 0.5是置信度阈值如果图片上花比较多且重叠严重可以把阈值调到 0.25 看看召回效果。--save-txt会把检测结果另存为 txt 文件方便和原始标签做对比。推理输出目录默认在runs/detect/exp打开里面的图片能直接看到红花的检测框位置和置信度。用--save-txt保存的检测结果同样是 YOLO 格式可以直接和已有的标签文件做逐行比较。比较时注意原始标签里的框是标注者画的模型预测的框会有一定偏移只要偏移量不要超过一个身位就属于正常范围。如果发现检测框普遍比标签框大一圈可能是训练时--img和推理时--img不一致造成的统一后重新跑一次推理即可。4. 避坑指南红花数据集训练中的五个高频翻车现场数据集本身标注得再干净训练链路里仍有几个固定的坑。这里记录的五个问题是我带学生跑目标检测项目时反复遇到的典型案例按“现象、原因、解决”的方式整理如下。4.1 标签文件成了空文件现象训练时日志显示WARNING: No labels found in labels/train/...但用文本编辑器打开 txt 文件又能看到内容。原因最常见的是标签文件编码不是 UTF-8或者文件开头有不可见字符YOLOv5 在按行解析时把内容直接过滤掉了。另一个原因是部分标注工具导出的 txt 使用了 UTF-8 BOM 头造成第一个标签解析失败。解决把所有 txt 重新保存为 UTF-8 无 BOM 格式。在 Linux 下可以执行sed -i 1s/^\xEF\xBB\xBF// labels/*.txt一次性去掉 BOM。处理后再跑一次训练警告就会消失。如果是在 Windows 下操作用 Notepad 打开全部文件后批量转码也行。4.2 标签类别编号从 1 开始导致越界现象训练刚开始就报IndexError: index 1 is out of bounds for axis 0 with size 1或者训练能跑但 loss 一直不降。原因这份资源的原始标注过程可能使用了 LabelImg 或 Roboflow导出时选择了不同的类别编号规则导致 txt 中的类别为 1而 data.yaml 里nc: 1只允许编号 0。解决用脚本统计所有标签的类别编号。常见做法是执行awk {print $1} labels/*.txt | sort -u看出现哪些值。如果只有 1把 txt 里每行的第一列全部替换成 0 即可如果同时有 0 和 1则需要确认数据本身是否真的包含两个类别再决定是合并标签还是调整 nc。4.3 train 路径写错导致 No labels现象训练日志在Scanning labels后显示0 labels损失全部为 0模型完全学不到东西。原因data.yaml 里的 train 和 val 路径指向了不含标签的目录或者 images 和 labels 目录结构不对称。YOLOv5 会根据图片路径自动在 labels 目录寻找同名 txt如果图片在images/train下而标签在labels/val下就会全部找不到。解决先打印一下目录树确认层级。我在动手前会执行find dataset -name *.txt | head和find dataset -name *.jpg | head把两边路径各看一遍。确认结构后再按第 2.3 节的组织方式把目录迁移一遍。4.4 模型前期不收敛loss 一路横盘现象前 20 轮 loss 几乎不变且一直在 2.0 左右mAP 始终为 0。原因大多不是配置问题而是学习率设置和 warmup 策略对当前数据集不适用。YOLOv5 默认的lr00.01在大部分数据集上没问题但红花数据如果图片数量特别少模型会很快过拟合表现为 loss 进入平台期。解决把学习率调低到--lr0 0.001同时把 warmup 轮数调大让模型先用更小的学习率把特征提取部分稳定下来。我在小数据集上都是这么处理基本都能见到 loss 明显下降。另外检查一下是不是 num_workers 设得太大经常有同学把--workers调到 16 以上导致数据加载瓶颈训练看起来像卡住了一样。4.5 推理结果与标注框偏移明显现象detect 输出的检测框和红花实际位置有明显偏移并且框偏大或者偏小。原因训练时--img 640和推理时--img 1280不一致或者训练用矩形训练但推理时没有。YOLOv5 在训练时会对图片做缩放如果推理尺寸和训练尺寸差异过大框的位置换算就会出现系统误差。解决训练和推理使用相同输入尺寸。训练时用了--img 640推理时也显示写上--img 640不要省略参数。如果显存足够可以统一用--img 1280重新训练红花这种小目标往往能有更稳的框。5. 数据增强、标签转换与训练集划分让红花检测模型更稳的二次加工有些情况下直接拿原始数据集去训练还不够。比如要做课程设计时老师可能要求同时提供 VOC 或 XML 格式的标签或者红花图片数量不足百张直接训练很容易过拟合。这一章的做法都是我平时会实际用到的二次加工手段。5.1 txt 转 XML给课程设计和跨框架使用留一份底稿YOLOv5 用的是 txt 归一化坐标很多同学在答辩时需要展示“标注文件”或者进行数据格式对比。一个通用的做法是把 txt 转成 VOC XML这样可以用 LabelImg 重新打开检查也可以转给 Faster R-CNN 等其他模型使用。import os import xml.etree.ElementTree as ET def convert_yolo_to_xml(txt_path, xml_path, img_width, img_height, class_names): root ET.Element(annotation) size ET.SubElement(root, size) ET.SubElement(size, width).text str(img_width) ET.SubElement(size, height).text str(img_height) ET.SubElement(size, depth).text 3 with open(txt_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls_id, cx, cy, w, h map(float, parts) xmin int((cx - w / 2) * img_width) ymin int((cy - h / 2) * img_height) xmax int((cx w / 2) * img_width) ymax int((cy h / 2) * img_height) obj ET.SubElement(root, object) ET.SubElement(obj, name).text class_names[int(cls_id)] bndbox ET.SubElement(obj, bndbox) ET.SubElement(bndbox, xmin).text str(xmin) ET.SubElement(bndbox, ymin).text str(ymin) ET.SubElement(bndbox, xmax).text str(xmax) ET.SubElement(bndbox, ymax).text str(ymax) tree ET.ElementTree(root) tree.write(xml_path, encodingutf-8, xml_declarationTrue)这段代码读取每一行 YOLO 格式标签把归一化的中心坐标和宽高换算成真实像素坐标再写进 XML 的 bndbox 结构。img_width 和 img_height 需要传入图片的真实尺寸可以从 PIL 读取。class_names 列表要和 YOLOv5 的 names 保持一致否则导出的 XML 类名会错。转换后在 LabelImg 里打开核对几张图确认框位置没有出现左右偏移。转换时有几个容易踩的点一是 xmin、ymin 计算后可能是负数说明标注框有一部分在图片外面这种框要么保留原值在 XML 里要么在转换前先裁掉出界部分二是int()强转会丢失精度但 XML 格式本身不支持浮点坐标丢掉的这部分对训练影响不大。如果之后要再转回 YOLO 格式直接用整型坐标重新归一化即可误差在一个像素以内。5.2 自动划分训练集和验证集不手动拖文件红花数据集如果是一个整体目录手动按照 8:2 拖文件既不精确又容易漏。我一般用一个脚本来完成划分顺便把结果打印出来。import random from pathlib import Path src_img Path(dataset/images) src_label Path(dataset/labels) train_img Path(dataset/images/train) val_img Path(dataset/images/val) train_label Path(dataset/labels/train) val_label Path(dataset/labels/val) for d in [train_img, val_img, train_label, val_label]: d.mkdir(parentsTrue, exist_okTrue) files list(src_img.glob(*.jpg)) random.seed(42) random.shuffle(files) split_idx int(len(files) * 0.8) for f in files[:split_idx]: label src_label / (f.stem .txt) if label.exists(): f.rename(train_img / f.name) label.rename(train_label / label.name) for f in files[split_idx:]: label src_label / (f.stem .txt) if label.exists(): f.rename(val_img / f.name) label.rename(val_label / label.name) print(ftrain: {len(files[:split_idx])}, val: {len(files[split_idx:])})脚本在划分前打乱了文件顺序随机种子固定为 42保证每次运行结果一致。这里有一个容易被忽略的点如果某张图片没有对应标签脚本会跳过但划分时它已经占掉了图片位置导致 train 和 val 的图片数加起来不等于总数。我一般会在划分后再次跑一次第 2.2 节的配对脚本双重确认。这个脚本用的是rename文件会直接从原目录移走。如果原目录还要保留一份完整数据做对比实验把rename换成copy2更合适代价是磁盘占用会翻倍。数据量不大时无所谓数据量上千张就要掂量一下了。5.3 数据增强参数怎么调红花小目标场景的一些经验YOLOv5 内置的数据增强比较丰富常用开关包括 mosaic、fliplr、hsv_h、hsv_s、hsv_v 等。这些参数可以写在 hyp 配置文件里也可以直接用命令行覆盖。我在红花这种小目标场景下的经验是不要开太大的 fliplr红花左右对称性不算强大量水平翻转反而会让模型多学一些不必要的变化mosaic 可以保持默认它能把多张图拼在一起对提升小目标感受野很有帮助。具体的 augmentation 参数分布在data/hyps/hyp.scratch-low.yaml里文件不长open 出来就能直接看懂。hsv_h、hsv_s、hsv_v控制色相、饱和度和明度的扰动范围红花颜色是红色系如果扰动范围太大红色的花可能被增强成橙色甚至紫色样本分布反而被扭曲。我一般会把hsv_h从默认的 0.015 降到 0.01hsv_s保持默认hsv_v从 0.4 调整到 0.3让模型更关注形状和纹理特征而不是颜色变化。如果同时在训练和验证时都使用增强会导致模型评估结果虚高。YOLOv5 默认验证集不开启增强但你要确认没有在 val 阶段额外传--augment参数。这一点在对比不同模型效果时尤其重要否则很难判断 mAP 提升到底是来自模型改进还是增强策略。6. 验证那一步用测试图观察红花检测效果顺便把模型参数留档训练完毕后不要只盯着训练结束时的 log 就收工。我习惯再跑三个验证动作看 confusion_matrix.png、重新在验证集上计算 mAP、用多张不同角度图片观察漏检情况。这三个动作能在几分钟内暴露模型的边界问题。runs/train/exp目录下会生成 results.png 和 confusion_matrix.png。results.png 里能看到 box_loss、obj_loss、precision、recall 和 mAP 的折线如果 mAP0.5 最后超过 0.9且 val/box_loss 还在稳定下降说明模型还有继续训练的空间。confusion_matrix.png 在单类别场景下主要看背景误检的比例红花数据集最常见的误检是把绿色的叶片边缘识别成花如果背景误检比例大于 0.1说明模型的定位精度还有提升空间。进一步可以做模型热力图可视化。常见做法是使用 pytorch-grad-cam 中的 GradCAM 方法把模型最后一层卷积的输出特征图叠加到原始图片上。虽然 YOLOv5 不是为热力图设计的但借助 GradCAM 仍能看出模型到底是依据花朵中心区域还是叶片纹理在做判断。这一步对论文里的“可视化分析”章节很有价值。我还习惯把训练用的参数和环境版本记录下来保存成一个 config.txt 放在 runs/train/exp 下。内容包括--img、--batch、--epochs、--hyp的最终值以及 torch 和 CUDA 版本。这样之后再跑对比实验时不用回忆上一个模型是怎么训出来的。顺手把最优那一轮的权重单独复制出来防止后续实验把 best.pt 覆盖掉。红花这个场景比较特殊花期不同、拍摄光线不同都会导致模型失效。从那以后我每次拿到标注好的数据集第一件事就是跑一遍第 2.2 节的配对检查训练结束后再强制跑一次验证集统计而不是只看训练日志。这一套流程走完模型到底能用不能用心里基本有数。希望这篇笔记能帮你在用这份红花数据集时少走几步弯路。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进