YOLOv5实战:小麦杂草检测数据集解析与训练全流程指南

📅 发布时间:2026/10/19 5:36:06
YOLOv5实战:小麦杂草检测数据集解析与训练全流程指南
简介面向小麦田杂草检测与农业视觉识别任务这份数据集已整理为YOLO标注格式包含约1400份已标注数据覆盖RAD1、RAD2、RAD23、RDA3、RA1等8个杂草类别适合用于YOLOv5、YOLOv8等目标检测模型的训练、验证与算法改进。包内共2000个文件含1414个txt标注、585张jpg原图和1个py可视化脚本压缩包约276.19MB文件命名保留了拍摄角度与采集时间便于按场景筛选样本。数据集已按训练集和验证集划分运行show脚本可快速查看标注效果便于核对标签质量、类别分布与边界框位置类别定义可直接参考classes文件。对刚接触杂草目标检测的初学者可借此理解YOLO标签组织方式对开展智慧农业、作物病虫草害监测或除草机器人视觉模块的开发者也可作为现成的数据基准与效果对比素材。目前已有101人学习下载可直接用于实验复现与模型迭代。1. 小麦杂草检测数据别被 1400 张的规模劝退难点在 8 类划分与小目标第一次看到这份数据时我第一反应是“才 1400 张能练出什么名堂”。但真正打开标签目录后想法变了——它把小麦地里最常见的杂草按 8 个类别做了细粒度标注每张图还保留了 0 度和 45 度两种拍摄角度。对做 YOLO 系列改进实验、课程设计、或者想快速跑通目标检测全流程的开发者来说这个数据量配合规范的 YOLO 标签反而比动辄上万张未清洗的数据集更容易出效果。数据已按训练集、验证集划分好配合自带的 show 脚本可以直接做可视化校验省去最枯燥的数据整理环节。它能解决的问题很聚焦杂草检测的类别区分、小目标漏检分析、以及数据增强策略的验证。适合想省掉标注时间、直接进入训练和调参阶段的从业者。2. 从文件名读到采集细节解读 camden_wheat_radish 系列图片的隐含信息拿到数据集的第一步不是急着训练而是把文件名和目录结构读懂。这批数据的文件名看似冗长其实每条信息都对建模选择有直接影响。比如camden_wheat_radish1_45_20210421_11-33-07_crop.jpg拆开来看camden_wheat_radish1是地块与作物代号45是拍摄角度20210421和11-33-07是采集日期与时间crop表示该图由原始大图裁剪而来。理解了这层结构就能解释为什么同一场景会出现两组特征差异明显的样本也会影响后续数据增强的策略选择。2.1 拍摄角度 0 与 45同一杂草为什么需要两种视角数据集中文件名第二段出现了0与45两个值。按常见农业采集设备的设置0 度是垂直向下俯拍杂草呈俯视形态叶片遮挡少但目标轮廓可能收缩45 度是倾斜拍摄杂草的侧向形态和叶片伸展特征更完整但会出现透视形变和相互遮挡。这个差异对 YOLO 训练来说是个隐藏的增强同一类别在两种角度下呈现出不同的形态特征模型被迫学到更鲁棒的语义特征而不是简单记住某一类杂草的轮廓模板。实际训练中我一般会把 0 度和 45 度样本混合得均匀一些避免全部 0 度样本进了训练集而 45 度样本集中在验证集那样验证指标会异常偏高或偏低。2.2 classes.txt 的 8 个类别编号体系的逻辑与建模含义数据集共标注 8 个类别按摘要中列出的编号存于 classes.txt 中。类别并不是以杂草中文名命名的而是采用 RAD、RD、RA 加序号的编码体系。按字面推断RAD 系列大概率是同一类阔叶杂草的不同生长阶段或亚种编号RD 系列可能对应另一类禾本科杂草RA 系列则可能是伴生杂草或特定环境下的变种。具体对应关系必须以项目自带的 classes.txt 文件为准不要凭文件名猜测。从目标检测建模角度8 个类别中 RAD2、RAD3、RAD4、RAD1 这四个编号相近的类别更容易混淆因为它们形态上可能存在连续性变化。如果你的模型训练后 F1 分数不低但混淆矩阵里 RAD 系列互相串就要考虑是否把形态接近的类别合并为单一类别重新训练。类别编号可能含义建模注意点RAD1阔叶杂草某阶段或亚种与 RAD2 形态可能存在重叠RAD2阔叶杂草某阶段或亚种标注量相对充足时优先保留RAD3阔叶杂草某阶段或亚种样本量不足优先做增强补偿RAD4阔叶杂草某阶段或亚种验证集分配时需确认有覆盖RD2窄叶/禾本科杂草类别纹理特征与 RAD 系列差异明显RAD23复合编号或过渡形态重点关注是否与 RAD2/RAD3 混淆RDA3其余杂草类别排序靠后样本量可能偏少RA1伴生杂草类别优先检查标注框是否完整2.3 YOLO 标签格式txt 文件结构、坐标归一化与边界校验YOLO 格式的每个 txt 标签文件与图片同名每行代表一个目标字段为class_id x_center y_center width height四组坐标值均为 0 到 1 的归一化相对值。别小看这个格式最容易翻车的就是坐标值写错或越界。比如0 0.512345 0.673210 0.142130 0.183452含义是类别 0 的目标中心点位于图片宽度 51.23%、高度 67.32% 处box 宽度为图片宽度的 14.21%高度为图片高度的 18.35%。拿到数据后第一件事我建议写一个最基础的校验脚本把每个 txt 的坐标范围扫一遍看看有无小于 0 或大于 1 的异常值。这个习惯能省下训练到一半才发现 loss 不收敛再去查数据的后悔药。import os label_dir labels/train for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname), r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f字段数异常: {fname} - {line.strip()}) continue cid, xc, yc, w, h parts xc, yc, w, h map(float, [xc, yc, w, h]) if not (0 xc 1 and 0 yc 1 and 0 w 1 and 0 h 1): print(f坐标越界: {fname} - {line.strip()})这里label_dir的值需要按实际目录换成labels/val再扫一遍。重点检查的不是坐标有效范围而是w和h是否为 0一旦出现 0 宽或 0 高的框训练时 loss 会直接爆 nan黑匣子式报错很难定位。如果扫描发现异常行优先回归到原始图片确认是否存在漏标或误标而不要自行修改坐标因为你并不知道原始标注者的标注规范。3. 数据划分与可视化验证集怎么拆、show 脚本怎么帮你检查标注质量YOLO 训练要求数据目录结构清晰图片与标签文件双轨并行。数据已配置好训练集、验证集划分但使用者应该自己重复一遍检查逻辑确认划分的合理性和类别的均衡性。3.1 目录结构约定images 与 labels 双轨并行以下是最常见的 YOLOv5 数据目录布局dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ ├── classes.txt ├── data.yaml └── show.py每一张images/train/xxx.jpg必须在labels/train/xxx.txt有一份对应标签文件名前缀完全一致否则训练时会出现找不到标签的警告。一个隐蔽的坑是如果使用 Windows 系统解压数据图片扩展名可能被系统改成大写.JPG而标签文件名是自动生成的小写.jpg前缀导致匹配失败。Linux 环境下则没有这个问题所以优先在 Linux 下解压和训练。3.2 训练集、验证集划分比例的复算逻辑数据自带划分但你可以重新统计一次确认划分是否合理。一个常见做法是用随机抽样按 9:1 或 8:2 切分但更稳妥的做法是分层抽样——先按类别统计每个类别在全部数据中的数量再按比例把每个类别的样本分散到训练集和验证集中防止某个样本量少的类别全跑进验证集。import os import random random.seed(42) image_dir images/train label_dir labels/train for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue image_path os.path.join(image_dir, fname.replace(.txt, .jpg)) if not os.path.exists(image_path): print(f缺少图片文件: {fname})这段代码是检查图片与标签是否一一对应的最小实现。实际做重新划分时需要先复制一份完整数据集再按random.shuffle打乱索引按比例截断生成新的 train/val 文件列表最后移动文件。一般不建议在原始数据集上原地调整划分因为破坏了原有划分就等于失去了和其他人对比实验时的参照系。3.3 show.py 可视化逻辑把归一化坐标还原到图像上数据自带的 show 脚本是验证标注质量最直接的工具。它做的事说起来很简单读取图片遍历对应 txt 文件中的每一行用cv2.rectangle把归一化坐标乘上图片宽高画框同时把类别 ID 打印在框上方。核心逻辑如下import cv2 import os def visualize(image_path, label_path, class_names): img cv2.imread(image_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cid, xc, yc, bw, bh parts cid int(cid) xc, yc, bw, bh map(float, [xc, yc, bw, bh]) 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, class_names[cid], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) return img核心在坐标还原这一步xc - bw / 2得到左上角 xyc - bh / 2得到左上角 y宽高直接乘以原图宽高。如果画出来的框明显偏离目标主体说明标签文件与图片不是一一对应的状态或者坐标字段顺序错位。另一个值得关注的是class_names[cid]这个索引如果cid大于len(class_names)说明标签里出现了未在 classes.txt 注册的类别 ID这个错误在训练时不会直接报错但会降低 mAP 上限。3.4 类别分布统计训练前先算一次每个类的样本数除了可视化我还会跑一段类别分布统计按 txt 里每一行计算每个类别的出现次数输出为柱状图数据或终端报告。这一步能快速发现极端不均衡的类别。比如如果 RA1 只有 12 个框训练时这个类别的 AP 大概率会是个位数那就要考虑要不要对 RA1 做复制粘贴增强、Mosaic 加权或者干脆在报告中说明该类别属于长尾类不参与最终 mAP 平均。from collections import Counter counter Counter() for fname in os.listdir(labels/train): if not fname.endswith(.txt): continue with open(os.path.join(labels/train, fname), r) as f: for line in f: parts line.strip().split() if len(parts) 5: counter[int(parts[0])] 1 for cid, num in sorted(counter.items()): print(f类别 {cid}: {num} 个标注框)这段代码顺着labels/train目录逐行统计int(parts[0])取每行第一个字段即类别 ID。输出的数字如果和摘要里 8 个类别的预期落差大说明某个类别确实样本稀缺。处理长尾类别的实践方案有三种一是按类别比例做 oversample让模型每个 epoch 看到更多少样本类的图片二是用copy-paste增强把少样本类的目标粘贴到其他图片中三是调 loss 的类别权重例如在 YOLOv5 的hyp.yaml里调整cls_pw参数。具体选择取决于你的任务目标如果只是跑通基线先不做特殊处理也可以。4. 把数据集喂给 YOLOv5data.yaml、超参数与训练命令全实践数据确认无误后进入正式训练。这里以 YOLOv5 系列为例因为数据集的标注格式与目录结构对它天然友好。如果你用的是 YOLOv8 或 YOLOv11只需要把 data.yaml 的路径改一下模型结构参数按需调整标注格式完全兼容。4.1 data.yaml 的写法类别顺序必须与 classes.txt 严格一致data.yaml 是连接数据集与模型的桥梁写错一个类名顺序或路径训练出来的模型就会出现类别串位。下面是一个可以直接套用的配置模板train: ./dataset/images/train val: ./dataset/images/val nc: 8 names: 0: RAD1 1: RAD2 2: RAD3 3: RAD4 4: RD2 5: RAD23 6: RDA3 7: RA1关键约束是nc: 8必须与 classes.txt 的总行数一致names列表的索引位置必须与 classes.txt 里的行顺序一致。如果 classes.txt 第 1 行是 RAD1而 data.yaml 把第 1 行写成了 RAD2模型不会报错训练照常进行但推理时类别名称会错位这是最难排查的坑之一。我自己遇到过类似翻车模型训了 100 轮才发现验证集 AP 对应关系不对重新对齐后成绩立刻上升。4.2 训练命令与关键参数说明python train.py \ --data dataset/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --device 0这几个参数足够跑通第一个基线实验。--img 640是输入分辨率如果显存有限可以降到 512 或 416但代价是小目标召回率下降--batch 16在 8GB 以上显存即可运行低于 6GB 建议调到 8--epochs 100对 1400 张数据来说偏多一般 60 到 80 轮就能收敛。重点是 watch 训练曲线如果 val_loss 在 30 轮后不再下降且 mAP 停滞说明模型已经学不动了继续加大 epochs 只会过拟合不会提升泛化能力。4.3 锚框设置是否需要重新聚类YOLOv5 默认会从数据集中自动聚类锚框。虽然标注格式规范但小麦田间目标的尺寸分布与 COCO 数据集中常见的行人、车辆完全不同——大部分杂草框集中在图像中部且尺度偏小。建议训练时保留默认的自动锚框功能训练日志里会有AutoAnchor相关的提示不要手动指定 COCO 预训练权重里自带的锚框。如果训练日志显示AutoAnchor: 0.0意思是锚框聚类结果不理想。常见原因是图片中存在极端长宽比的目标比如 0 度俯拍下杂草叶片呈狭长形。解决办法是检查是否存在宽度或高度接近 1 像素的极端目标这类目标要么是标注误差要么是裁剪时把杂草切掉了一半保留它们会拉低整体 mAP。4.4 显存不足与训练速度的平衡策略1400 张图的体量对显存压力很小但如果你把--img提到 1280 且--batch提到 32老显卡一样会爆显存。下表是不同配置下的参考资源占用输入尺寸batch显存占用约适用显卡64084-6 GB6GB 级显卡640168-10 GB10GB 级显卡1280812-16 GB16GB 级显卡12801620-28 GB24GB 级及以上注意以上数值是 YOLOv5s 模型的大致范围换成 YOLOv5m 或 YOLOv5l 显存开销逐级递增约 60% 和 140%。一般第一轮实验先用 640 batch 16 跑通验证 loss 曲线正常后再用 1280 尺寸微调 20 轮提高小目标召回。小麦杂草这类小目标密集的场景升分辨率对 mAP 的提升往往比加深网络结构更明显。5. 部署训练时容易踩的坑四个典型问题与排查方案YOLO 格式数据集看起来简单真正跑起来才明白坑都在细节里。这一章把我在实际部署中最常遇到的四类问题按现象、原因、解决思路写清楚每一条都是真金白银的血泪经验。5.1 类别顺序错位导致 mAP 虚高但推理完全不可用现象训练 50 轮后验证集 mAP 达到 0.72看起来成果不错但把训练好的权重拿去跑单张图片推理预测出的框总把 RAD2 标成 RA1类别完全错乱。原因data.yaml 中的 names 列表顺序与 classes.txt 不一致。YOLO 训练只读 class_id 的数值如果第 0 行的类别定义在 data.yaml 中被写到了第 7 行训练时数值 0 对应的语义还是原位置的类别但推理时输出层的类别索引映射关系已经变了所以 mAP 数字正常实际映射却错位。解决训练前写一个自动对比脚本逐个读取 data.yaml 中的 names 值和 classes.txt 的行值确认完全一致后再训练。我一般会在启动命令前把这个对比写进启动脚本里不一致直接 abort。5.2 验证集出现训练时的过拟合假象现象训练 loss 持续下降验证 loss 先降后升但 mAP 曲线还在缓慢爬升看似一切正常实际模型已经开始记住训练集中的噪声特征。原因1400 张的数据规模偏小而 YOLOv5s 参数量在 700 万左右在 60 轮之后模型很容易从高频纹理特征中提取对验证集无效的信息。杂草图像中叶脉纹理、土壤颗粒这些背景特征在小目标检测中很容易被误当成判别依据。解决不要只看 mAP 指标同时观察验证集上每个类别的 PR 曲线。如果 RAD2 和 RAD3 的 PR 曲线在高置信度区间出现了明显的锯齿说明模型对它们的区分依据不是形态而是纹理噪声。此时优先使用更强的增强策略例如大幅提高 mosaic 和 mixup 概率并考虑引入 early stopping在验证 loss 连续 10 轮不下降时提前终止训练。5.3 标注框偏移导致小目标漏检率奇高现象模型对面积小于 32×32 像素的杂草目标几乎全部漏检可视化时发现部分框的中心点明显偏移到目标边缘IoU 与真实目标重合度很低。原因这批图像的crop后缀说明图片由原始大图裁剪而来裁剪过程中如果标注工具未同步更新坐标就会出现框偏移。0 度俯拍图在裁剪时目标在原始大图中的位置需要按比例换算换算四舍五入导致的偏移在目标较小时会被放大。解决打开 show.py 逐个目测检查裁剪后图片的标注框偏移程度。如果偏移是整体性的比如所有框都向右下偏移 2%可以统一修正如果是零散的需要手动调整。另一个实用技巧是训练时使用--cache参数把图片预加载到内存配合--multi-scale让模型在多个尺度上学习目标特征能从一定程度上抵消标注框的小幅偏移带来的噪声影响。5.4 训练过程中 loss 出现 NaN现象训练跑到 10 轮左右终端输出 loss 变成nan后续所有指标均不可用重启训练也一样。原因YOLO 格式的标签中存在 width 或 height 为 0 的异常框或者存在 top-left 坐标大于 bottom-right 坐标的标注错误。这类数据在读取时会产生无效的 anchor 匹配梯度更新过程中计算到非法位置最终导致 loss 爆炸。另一个可能原因是数据中出现了一行只有 class_id 没有坐标信息的残留文本。解决回到第 2.3 节的边界校验脚本把所有 width 或 height 为 0 的行找出来逐一确认是漏标还是文本残留。确认是异常后直接删除对应行或丢弃该图片而不是试图修改坐标让它“看起来合法”。之后在训练命令加上--workers 4如果仍然出现 NaN就要考虑在数据加载阶段用try...except跳过异常文件或者把--batch调小排查是否为显存不足导致的随机错误。6. 用混淆矩阵找出杂草类别边界一个验证模型上限的实用技巧训练结束后大多数人只看一眼 mAP 就收工了但这远远不够。对 8 类杂草检测任务最有价值的一步是打印出混淆矩阵观察哪些类别在互相串扰这直接决定了后续的优化方向。上面提到的类别关系在代码中有直观体现。跑完val.py后YOLOv5 会在runs/val/exp/目录下生成confusion_matrix.png和confusion_matrix.png。首先看主对角线以外的非零区域密度最大的位置就是模型的混淆热点。以这个数据集为例我最关注的是 RAD1、RAD2、RAD3、RAD4 四者之间的互混情况以及 RAD23 与 RAD2/RAD3 之间的边界。如果 RAD23 大量被预测成 RAD2说明这个复合类别在形态上确实与 RAD2 难以区分要考虑把 RAD23 合并到 RAD2 里然后重新训练一次看 mAP 是否反而上升。另一种情况是 RD2 与 RAD 系列混在一起这通常意味着窄叶与阔叶特征在俯拍视角下区分度不够优先考虑的方案是把输入分辨率从 640 提升到 1280在更精细的尺度下捕捉叶片宽窄差异。除了混淆矩阵我还会做一次针对性的误差分析把验证集中所有漏检和误检的图片挑出来看它们在采集时间上的分布。这批次数据的文件名携带日期信息如20210421和20210514中间隔了近一个月。如果你发现误检图片集中在某个日期的批次中而模型训练时该日期批次图片较少说明数据存在批次偏差。解决方法是按采集日期做分层采样保证训练集和验证集中都包含不同日期的样本降低场景偏差对模型泛化能力的影响。还有一个小技巧值得养成分习惯每次训练前固定--seed参数。YOLOv5 的默认 seed 是 0但如果你复现别人的实验后想对比增强方案不固定 seed 会导致结果出现随机波动混淆矩阵里的细微变化可能只是噪声。固定 seed 之后重复训练得到的混淆矩阵才会有真正的对比意义。从那以后我每次拿到新数据集不管标注说明写得多完整都会强制走一遍坐标边界扫描、类别顺序对比、按批次分层划分这三步并记录到实验文档里。这三步前两章提过做下来只需十几分钟但能避开至少半天的调试时间。希望帮到你让你能把精力花在模型设计而不是数据集翻车排查上。本文还有配套的精品资源点击获取