ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

小麦田杂草检测YOLO数据集实战:1400张标注图与避坑要点

小麦田杂草检测YOLO数据集实战:1400张标注图与避坑要点 简介一套面向目标检测学习与研究的小麦田地杂草图像数据集包含约1400份已标注数据及对应YOLO格式标签覆盖8类常见杂草类别适合用来训练YOLOv5等检测模型也可作为农业视觉课题的基准数据。数据包共2000个文件其中JPG图像585个、txt标注文件1414个另含1个Python可视化脚本压缩包整体约276.19MB。txt标注采用YOLO标准格式记录类别与归一化坐标可直接接入主流检测框架具体类别名称见资源内classes文件脚本支持一键预览标注效果便于核对图像与标签的匹配情况。数据集已完成训练集与验证集划分省去自行切分的步骤拿到后即可开展模型训练与效果评估。目前已有100人学习下载适合具备一定深度学习基础、希望获取农业场景标注数据开展实验的开发者若结合作者博客中的YOLOv5改进实战内容还能进一步探索模型优化思路。1. 小麦田杂草检测数据集YOLO 标注格式1400 张真实田间图直接开训目标检测落地农业十有八九卡在数据上——不是没图是没有带标注的图。这份小麦田地杂草图像目标检测数据约 1400 张全部带 YOLO 标注格式标签覆盖 8 类田间杂草训练集、验证集已划分好解开就能直接开始训练。图像拍自真实小麦田时间在 2021 年 4 月到 5 月文件名里的 wheat_radish 表明主体是小麦田中的萝卜类杂草光照、遮挡、土壤背景都是真实农艺场景比网上的摆拍合成图可信得多。适合两类人做智慧农业和杂草识别落地的工程师拿这份基准集验证检测模型、测试模型改进效果刚接触 YOLO 目标检测的学生图量小、跑得快能完整走完检查、训练、评估、部署流程。2. 数据集结构与标注格式八个类别的编号映射与划分隐患2.1 目录底细与标签行格式拿到压缩包先别急着解压训练先花两分钟把目录结构摸清。按照 YOLO 系列的习惯约定这份数据解开后应当是 images 和 labels 两大部分各自包含 train、val 两个子目录图像是 jpg标签是同名 txt。常见的标准结构如下wheat_weed/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ ├── classes.txt └── show.py如果你解压后看到的是 train 和 val 目录里直接混着放 jpg 与 txt也不用慌YOLO 的 dataloader 只认「图像与标签同名、扩展名分别为 .jpg 和 .txt」这个约定把脚本里的路径变量指向正确位置就能跑。真正要核对的是标签文件里面的内容。每一行 txt 对应一张图上的一个标注框固定五个字段class_id、x_center、y_center、width、height。这里要特意强调一点YOLO 标注格式里x_center、y_center、width、height 通通是相对图像宽高的归一化小数取值范围 0 到 1。比如某一行是3 0.52 0.43 0.18 0.22意思是类别编号为 3 的目标中心点位于图像横向 52%、纵向 43% 处框宽占整张图宽度 18%、框高占高度 22%。不是像素坐标也不是中心点加两个角点的写法这是从 LabelImg、X-AnyLabeling 这类常用标注工具导出后最容易搞混的地方。提示classes.txt 是类别命名的唯一真源训练前先打开看一眼并备份。2.2 classes 文件与 8 个类别的编号映射YOLO 标签里的 class_id 是纯数字数字背后对应哪个杂草类别完全由 classes.txt 或训练配置里的 names 列表决定。这份数据的 8 个类别如下class_id类别代码0RAD21RAD32RAD43RAD14RD25RAD236RDA37RA1RAD、RD、RA 这些缩写大概率是田间杂草调查登记时用的物种代码后缀数字可能是生育期或采集批次。具体物种对应关系以数据集自带的 classes 文件为准不要在文档里拍脑袋猜。我自己在类似数据上吃过一次亏图省事手敲 names 列表把 RAD23 和 RDA3 的顺序写反训练了 80 轮 loss 正常下降验证集 mAP 却始终上不去。最后用可视化脚本逐一核对才发现类别标签集体错位。从那以后所有读类名的脚本我都坚持从 classes.txt 读文件而不是手动硬编码这个习惯能直接杜绝一整类低级错误。2.3 文件名信息与同源划分风险文件名本身写满了元信息以camden_wheat_radish1_45_20210421_11-33-07_crop.jpg为例camden # 场地代号 wheat_radish1 # 试验小区/处理组 45 # 地块编号 20210421 # 拍摄日期 11-33-07 # 拍摄时刻 crop # 从原图裁出的小块这段命名暴露了一个很重要的隐患这批图是从田间定点拍摄的大图上裁出来的同一张大图裁出的多张小图内容高度相关、背景几乎重叠。如果按默认方式随机划分 train/val极可能出现同一拍摄时间的一对相邻裁剪一张进了训练集、一张进了验证集验证 mAP 虚高得离谱。判断方法也简单把验证集图像按文件名日期分组如果某个日期在验证集里出现得非常集中说明随机划分把同源图打散了更合理的做法是按日期做分组划分让同一个拍摄时段的图尽量进同一个集合。第 5 章我会给一个具体的划分处理方案这里先记住「按时间分组划分」这个原则它比任何调参都更能保证评估结果可信。3. 数据可视化与标注体检show 脚本跑通后的三个检查点3.1 改造 show.py让可视化在当前环境跑通数据集说明里提到自带 show 脚本。包里的脚本一般按作者本机路径写死换环境直接跑容易报 FileNotFoundError。我通常不去翻原脚本逻辑而是直接写一个通用的几分钟就能验证整个数据集import cv2 import os IMG_DIR images/train # 改成实际解压路径 LABEL_DIR labels/train CLASSES [RAD2, RAD3, RAD4, RAD1, RD2, RAD23, RDA3, RA1] for img_name in os.listdir(IMG_DIR): if not img_name.endswith(.jpg): continue img cv2.imread(os.path.join(IMG_DIR, img_name)) if img is None: print(读取失败:, img_name) continue h, w img.shape[:2] txt_name os.path.splitext(img_name)[0] .txt txt_path os.path.join(LABEL_DIR, txt_name) if not os.path.exists(txt_path): print(缺少标签:, txt_name) continue with open(txt_path) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(标签格式异常:, txt_name, line) continue cls_id int(parts[0]) if cls_id len(CLASSES): print(类别编号越界:, txt_name, cls_id) continue xc, yc, bw, bh map(float, parts[1:]) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, CLASSES[cls_id], (x1, max(y1 - 5, 15)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow(check, img_name) cv2.waitKey(0) cv2.destroyAllWindows()逻辑说明脚本遍历图像目录对每张图取同名 txt按 YOLO 格式把归一化坐标换算成像素坐标然后画矩形框和类别名。换算公式就四行x1 (xc - bw/2) * wy1 (yc - bh/2) * hx2 (xc bw/2) * wy2 (yc bh/2) * h。参数说明IMG_DIR、LABEL_DIR 按实际解压路径改CLASSES 列表顺序必须和 classes.txt 里 class_id 的映射一致。脚本在终端按任意键切下一张。这个版本额外加了四类保护——图像读取失败提示、标签缺失提示、格式异常提示、类别编号越界提示跑一轮就能把数据集底层问题暴露得差不多。3.2 标注体检的三个检查点可视化不是看个热闹我一般按顺序查三个点。第一类别与框的对应关系。图上画出的类别名应该和肉眼判断一致。如果出现「框里明显是萝卜苗标签却是另一类」这类系统性错位说明源标注就有问题训练前必须先修不然模型会在错误映射上学得头头是道。第二框的尺度分布。田间杂草刚出苗时目标很小正常框面积占整图的 1%~10% 是合理的。如果发现大量框占图面积 30% 以上要怀疑是不是把整丛杂草群体框进去了这种标注对单株检测模型是负样本级别的干扰。第三边缘截断框。目标被图像边缘切开时标注框贴边的比例很高这类样本在训练中容易让模型学到「框可以被截断」的错觉。对这种框可以选择保留或剔除取决于应用场景——无人机巡田时边缘目标很重要保留更合适。3.3 顺手检查图像与标签是否一一对应一个简单的 shell 命令就能统计出孤儿标签for f in labels/train/*.txt; do imgimages/train/$(basename $f .txt).jpg [ -f $img ] || echo 孤儿标签: $f done反过来也要查图像缺少标签的情况。YOLO 训练对没有标签的图默认跳过但会拖慢数据装载还会让「标注率」和实际参与训练的图数对不上。这份数据如果已经清洗过通常不会有太多孤儿文件但每份数据集到手都跑一遍这个检查成本极低、收益稳定值得养成习惯。4. 训练实战YOLOv5 数据配置、超参数与结果评估4.1 补一份数据集 yaml 配置数据集自带类别文件和 show 脚本但训练还需要一份数据配置。这是我惯用的写法保存为 wheat_weed.yamlpath: /home/user/wheat_weed # 数据集根目录绝对路径 train: images/train # 相对 path 的训练图目录 val: images/val # 相对 path 的验证图目录 nc: 8 # 类别总数 names: # 顺序必须与 classes.txt 一致 - RAD2 - RAD3 - RAD4 - RAD1 - RD2 - RAD23 - RDA3 - RA1参数说明path 指向数据集根的绝对路径train 和 val 是相对 path 的子目录nc 是类别总数 8names 的顺序必须和 classes.txt 完全一致。这行是最容易被忽略又影响最大的一处配置——顺序写反模型不报错只是所有框的类别都错位。改用 YOLOv8 时配置结构相同只是读取入口从 train.py 换成 yolo 命令。注意yaml 里的 names 顺序不一致时训练不会报任何错误只会让 mAP 对不上号。4.2 训练命令与关键参数取舍python train.py \ --data wheat_weed.yaml \ --weights yolov5s.pt \ --img 640 \ --batch-size 16 \ --epochs 100 \ --cache逐项说明我的选择理由。预训练权重这部分我用的是官方仓库的 yolov5s.pt。别从零开始1400 张图从零训练backbone 学不出足够的纹理特征杂草和土壤的区分会差很多。预训练权重不是玄学它把大规模数据集上学到的通用特征迁移过来对农业小数据是决定性的。输入尺寸我选 --img 640这是 YOLOv5 的默认值。这份数据的 crop 图本身如果是几百像素的小图640 够用如果原图分辨率还有富余小目标杂草可以考虑 1280代价是显存占用接近翻倍、训练时间变长需要自己权衡。batch size 我设为 16单张 3080 或 V100 级别显卡的稳妥值。显存 8G 就降到 8再低要小心训练中 BN 崩溃具体见第 5 章。--cache 参数会把 1400 张图全部缓存进内存每个 epoch 的数据装载从磁盘 IO 变成内存读取速度提升非常明显。这点数据量完全 cache 得起不开白不开。训练结束后模型权重、验证集预测图、混淆矩阵、PR 曲线默认输出到 runs/detect/exp/ 目录按时间排序最后一次训练通常就是序号最大的那个目录。4.3 评估指标这轮训练到底行不行光看总 mAP 不够田间杂草数据我按四步评估。先看 mAP0.5。杂草检测对框位置精度要求没那么苛刻mAP0.5 能上 0.9 说明检出率不错。再看 mAP0.5:0.95这个指标对框的精确度敏感小目标杂草通常会把整体平均拉低单类 50% 以上就说明模型学到了东西。接着看每类 AP八个类别里 RAD2、RAD23 这类个体小的往往最低这是正常现象重点要看有没有类别 AP 高到离谱——高得反常往往不是模型强而是类别划分有问题。最后打开混淆矩阵图 confusion_matrix.png看哪两个类别互相误检最多RAD1 和 RA1 如果频繁混淆多半是标注时两类本就长得像与其调模型不如先回头核对标注。这份数据也正好适合拿来验证 YOLOv5 改进结构带来的收益。5. 避坑指南四次翻车记录与排查方法5.1 类别编号错位loss 正常下降 mAP 却不涨**现象**训练 100 轮训练 loss 从 0.07 降到 0.02趋势完全正常但 val mAP 始终卡在 0.3 上下。可视化验证集时发现 RAD2 的框全部标成了 RAD3整批错位一个位置。**原因**修改数据 yaml 里 names 的顺序或拼接多个数据集的 labels 目录时没有统一 classes.txt导致 class_id 与类别名错位。YOLO 训练不会因为这个报错loss 照常下降黑匣子把问题完全吞掉。**解决**训练前把验证集每一张图跑一遍可视化人眼确认类别名和框内容一致更稳的做法是全部脚本从 classes.txt 读类别列表禁止手写硬编码。5.2 同源图划分泄漏验证 mAP 虚高到部署露馅**现象**随机划分训练集和验证集后mAP0.5 到 0.96模型拿到另一块田地的视频上推理漏检明显变多。**原因**图像是从大图上裁出来的 crop 块同一拍摄时刻的相邻裁剪内容几乎一样。随机划分把同一张大图的碎片分进两个集合验证集测的是「见过相似背景」的能力不是泛化能力。**解决**按文件名里的日期和地块编号分组划分同一天同一时刻的 crop 进同一个集合。常见做法是按日期做 GroupKFold或者干脆用 20210421 的图做训练、20210514 的图做验证指标才有参考价值。5.3 训练中 BN 崩溃loss 突然变 NaN**现象**训练到第 40 轮loss 突然变成 nanval mAP 归零后续 epoch 全部白跑。**原因**batch size 太小或学习率峰值太高BatchNorm 统计量发散小数据集配合 mosaic 增强会加剧这一抖动。**解决**batch size 提到 16 以上初始学习率从 0.01 降到 0.001开启 warmup 让学习率缓慢爬升还不收敛就冻结 backbone 前 10 层先训 10 轮再解冻给 BN 一个稳定先行的窗口。5.4 小目标被下采样抹掉指标还行但单株小草检不出**现象**验证 mAP 尚可但实际画面里只有 20×20 像素的小草苗经常漏检。原因--img 640 训练时下采样倍数高小草目标经过 stride 32 的特征图后只剩几个像素检测头拿不到足够的语义信息。**解决**应用场景要求识别小苗期杂草时用 --img 1280 重训一版推理阶段用 SAHI 这类切片工具把大图切成小块分别预测再拼回结果。评估时按目标尺寸分层统计指标只看整体 mAP 容易被大目标的多检出掩盖小目标问题。6. 进阶类别不均衡与小目标杂草的增强方案6.1 先做类别统计再决定增强策略八个杂草类别在图像里的目标数通常不均衡RAD2、RAD23 这类个体小、数量少RAD1、RA1 这类群体性明显的类型数量大。训练前先跑一次统计决定要不要做类别补偿from collections import Counter import os label_dir labels/train counter Counter() empty 0 for f in os.listdir(label_dir): with open(os.path.join(label_dir, f)) as fp: lines [ln for ln in fp.read().strip().splitlines() if ln.strip()] if not lines: empty 1 continue counter.update(int(ln.split()[0]) for ln in lines) print(各类别目标数:, dict(sorted(counter.items()))) print(空标签文件数:, empty)逻辑说明遍历训练集每个标签文件按 class_id 累加目标个数顺带统计空标签文件数。输出按类别编号排序一眼看出少数类是谁。参数上如果少数类只有几十个框做数据增强意义不大这个数量级应该先检查标注质量而不是靠算法补偿。6.2 两个在杂草场景里管用的增强第一个是 mosaic。YOLOv5 默认 1.0 概率触发四张图拼一张训练等于变相缩小目标在输入图中的尺寸分布对小目标检出提升真实有效。第二个是面向少数类的 copy-paste把 RAD23 这类少数类目标框裁剪下来粘贴到另一张图像的空旷区域同步在标签文件里追加一行。这个方法在农业检测里远比随机翻转有效因为杂草的位置和尺度都受真实物理约束翻转不增加新信息。6.3 验证划分的最终建议这份数据的关键在划分而不是增强。从那次在小麦田部署翻车之后我每次拿到任何一份目标检测数据集第一步永远是跑数据体检读 classes.txt、可视化抽样、统计类别分布、按拍摄时间分组划分然后才谈训练。这套流程后来帮我避开了好几份同类农业数据的坑也建议你把「划分必须按拍摄批次」写进项目规范。这份小麦杂草数据标注格式规范、类别划分清晰值得下下来完整跑一轮。希望帮到你。本文还有配套的精品资源点击获取
返回列表