简介:面向目标检测与农业智能化场景,YOLO杂草检测数据集适用于训练和验证YOLOv5/v7/v8/v9/v10/v11等系列模型,帮助学习者快速上手农业环境下的目标识别与杂草定位。压缩包共2000个文件,以xml标注文件为主要形式,整体大小约236.6MB,数据已划分为训练、验证等子集,并附带可直接修改的data.yaml配置。标注同时提供YOLO格式(txt)和VOC格式(xml)两套文件,分别存入独立目录;YOLO格式记录类别索引及归一化后的中心点、宽高比例,VOC格式则保留标准边界框信息,便于在主流检测框架中切换使用。已有201人学习下载。数据整体适合模型训练、精度对比以及农业自动化项目的前期验证,可降低数据准备成本;无论是首次接触YOLO的初学者,还是需要快速验证模型效果的开发者,都能基于该数据集直接开展实验。
1. 杂草检测数据集:拿到6849张图后的第一件事不是训练
做yolo系列算法目标检测数据集踩坑的人都有一个共同体验:找数据集不难,难的是拿到手后还要清洗标签、统一格式、手动划分训练验证集。这份杂草检测数据集把这几步都省了,6849张图像连同yolo格式和voc格式的标签一起打包,train和val已经切好,data.yaml也配置好了。它要解决的是你从零开始训练一个杂草识别模型最耗时间的标注与格式问题,适合正在做农业场景检测、想用yolov5到yolo11快速验证算法效果的开发者。但别急着把压缩包丢给训练脚本,我接下来说的几件事会直接决定你是3小时跑出可用模型,还是花一整天在debug标签格式。
2. 数据集的真实构成:两种标签格式、目录结构与data.yaml逐项拆解
2.1 解压之后先看目录结构
这份资源解压后的标准布局是下面这个样子,我建议你先对照自己的目录复核一遍,别急着往训练工程里塞:
. ├── data.yaml ├── images/ │ ├── train/ │ │ ├── img_0631_937.jpg │ │ ├── img_0631_942.jpg │ │ └── ... │ ├── val/ │ │ ├── img_0631_3137.jpg │ │ └── ... ├── labels/ │ ├── train/ │ │ ├── img_0631_937.txt │ │ └── ... │ ├── val/ │ │ ├── img_0631_3137.txt │ │ └── ... ├── voc_labels/ │ ├── train/ │ │ ├── img_0631_937.xml │ │ └── ... │ └── val/ │ └── ...img_0631_937.xml 这类文件对应的就是 images 下同名 jpg 的 Pascal VOC 标注,txt 则是同内容的 yolo 标注。重点在于:train 和 val 在 images、labels、voc_labels 三个目录里是同步划分的,xml 和 txt 的文件名必须与 jpg 完全一致,后缀不同而已。
常见做法是训练时只用 labels 下的 txt 文件,voc_labels 留作参考或二次校验。不要同时把两个标签目录都指给训练脚本,否则 yolo 会读进重复标签,轻则警告重则训练崩。我一般会先写一行命令统计标签数量,确保 txt 与 jpg 一一对应:
find images/train -name "*.jpg" | wc -l find labels/train -name "*.txt" | wc -l如果两边数量不一致,说明解压或划分环节出了问题。常见坑是压缩包嵌套了一层目录,导致脚本按相对路径找不到文件。先把目录结构理顺,后面训练才不折腾。这一类数据集下载资源里,目录层级是最容易被忽视的隐性坑,很多人栽过。
2.2 两种标签格式逐行对照:yolo 归一化坐标与 voc 绝对坐标
打开任意一个 txt 文件,内容大概是这样的三行:
0 0.511719 0.361547 0.153385 0.401042 0 0.289844 0.575000 0.231250 0.636667 0 0.724219 0.851042 0.203385 0.270417每行表示一个目标框:class 类别索引(从0开始)、x_center、y_center、width、height。所有坐标值都是相对图像宽高的比例,范围在0到1之间。比如第一个框,中心点x是图像宽度的51.17%,也就是约一半位置;框宽是图像宽度的15.34%。
对应的 xml 里长这样:
<annotation> <folder>images</folder> <filename>img_0631_937.jpg</filename> <size> <width>1280</width> <height>720</height> <depth>3</depth> </size> <object> <name>weed</name> <bndbox> <xmin>342</xmin> <ymin>143</ymin> <xmax>531</xmax> <ymax>438</ymax> </bndbox> </object> </annotation>xml 里是绝对像素坐标,xmin、ymin 是左上角,xmax、ymax 是右下角。从 voc 转 yolo 需要做一次归一化,核心公式就四句:
x_center = (xmin + xmax) / 2 / width y_center = (ymin + ymax) / 2 / height w = (xmax - xmin) / width h = (ymax - ymin) / heightwidth 和 height 取自 xml 的 size 节点。很多人会把 xmin、ymin 直接当成 x_center、y_center 用,或者忘记除以宽高,训练时 loss 直接不收敛。这份资源把两种格式都备齐了,正好可以做交叉验证:用上面的公式把 xml 转成 txt,看是否和你手里的 txt 一致,既练了格式转换又验了数据质量。
2.3 data.yaml 里的关键配置项逐个说
data.yaml 是 yolov5、v7、v8、v9、v10、yolo11 共用的数据配置入口。打开它,你看到的是这样一个模板:
path: /path/to/your/weed_dataset # 数据集根目录路径 train: images/train val: images/val test: images/test # 可选字段 names: 0: weed先用最简单的一句话解释这套结构的约定:yolo 训练时会根据 train 字段找到图片目录,然后在同级的 labels 目录下寻找对应 txt。也就是 images/train 对应 labels/train。如果你的 labels 目录名不是 labels,或者被嵌套了一层,就会报 no labels found。
三个字段的值要留意。path 是根目录,我一般写成绝对路径,尤其是多人协作或跨机器跑的时候,相对路径很容易因为工作目录不同而全部失效。Windows 用户不要直接复制反斜杠路径填进 yaml,建议把 path 写成正斜杠的绝对路径,比如 D:/weed_dataset。train 和 val 是从 path 出发的相对路径,写法只有 images/train,没有斜杠开头或盘符。
names 这个字典是重点:索引必须与 txt 文件里的 class 数字严格对齐。这份数据集只有一个类别 weed,索引0对应杂草。如果你的复制版本里还有作物类,names 会写成 0: weed, 1: crop,这时 txt 里第二个字段如果是1,就代表作物,训练前务必确认索引语义。
使用这份数据集前,我建议你手动加载一下 yaml,打印出 train、val 的实际长度,确认数据量符合预期。这一步花半分钟,能避免后面训练跑了几十分钟才发现 val 是空的:
import yaml from pathlib import Path with open('data.yaml', 'r', encoding='utf-8') as f: cfg = yaml.safe_load(f) base = Path(cfg['path']) for split in ['train', 'val']: img_dir = base / cfg[split] count = len(list(img_dir.glob('*.jpg'))) print(split, count)如果 val 打印出来是0,要么是划分时没放文件,要么是 yaml 里 val 路径写错了。224张这类故障,我看到过太多次。数据集已经划分好的好处就在这里,你只需要验证不用自己切分,但验证这一步不能跳过。
3. 用yolov8实测训练:环境准备、标签校验与训练参数明细
3.1 环境安装与工程目录
我一般在中专门跑yolo训练,避免把核心环境弄乱。基于这份数据集,推荐用 python3.10 建环境,然后安装固定版本的 ultralytics:
conda create -n weed python=3.10 -y conda activate weed pip install ultralytics==8.2.0为什么指定 8.2.0 而不是 latest?因为新版本对 yolo 模型结构有过调整,某些小版本会把预训练权重格式改掉,旧权重报 key mismatch,导致你从coco迁移来的 pretrain 失效。固定一个验证过的版本,配合 yolov8n.pt 和 yolov8m.pt 都不会出问题。如果手里是显卡比较普通的机器,yolov8n 是起步配置,m 适合有8G以上显存的情况,逐个试。
数据集不需要拷到 ultralytics 工程里,只要 data.yaml 的 path 写对,训练脚本就能找到。常见的做法是把数据集放在项目同级目录:
mkdir weed_project cd weed_project # 把解压好的 weed_dataset 放到这个目录下 tree -L 1然后你看到的 weed_project 里应该有 data.yaml、images、labels、voc_labels 四个顶层内容。这样写路径时不容易产生层级错乱,也方便随时回头看 voc 标签。
3.2 训练前强制做一次标签内容校验
解压后的数据不代表完全不存在脏数据。我习惯写一段一次性校验脚本,把标签缺失、字段数不对、坐标越界三类问题都扫出来:
from pathlib import Path base = Path('weed_dataset') for split in ['train', 'val']: label_dir = base / f'labels/{split}' img_dir = base / f'images/{split}' for label in sorted(label_dir.glob('*.txt')): # 图片缺失检查 img = img_dir / label.with_suffix('.jpg') if not img.exists(): print(f'missing img: {img}') # 标签行格式检查 with label.open() as f: for line_no, line in enumerate(f, 1): parts = line.strip().split() if len(parts) != 5: print(f'bad column: {label}:{line_no}') continue cls = int(parts[0]) x_center, y_center, w, h = map(float, parts[1:]) if cls < 0: print(f'negative class: {label}:{line_no}') if w <= 0 or h <= 0 or w > 1 or h > 1: print(f'bbox out of range: {label}:{line_no}')这段脚本会逐个检查 labels/train 和 labels/val 下的所有 txt。注意 w 或 h 大于1的情况,意味着 bbox 的宽度或高度超过了图像本身,通常是 voc 转 yolo 时没有除以 width 和 height,或者除以反了。如果发现 out of range,返回 voc_labels 里对应的 xml,重新按 2.2 的公式转,不要直接在 txt 里做小修补。
这项校验是这条数据集资源里最值得做的复现步骤。因为 xml 和 txt 两套格式都存在,你完全可以随机抽200个文本,用公式手工核算坐标,确认资源质量。反正它已经划分好,你训练出来的模型好坏,数据决定一半。我见过有人拿到相同的数据集,没做校验直接训,结果发现6000多张里只有80%的图像有标签,剩下的空跑一遍,花费的时间没法后悔。
3.3 训练命令与关键参数明细
校验通过后,可以开始训练。以 yolov8n 为例:
yolo train data=data.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16 device=0 patience=20 save_dir=runs/weed这条命令的参数逐个拆开说。data=data.yaml 指向刚才校验过的配置;model=yolov8n.pt 是从coco预训练权重开始的迁移学习,杂草这类目标与常规物体差异较大,但底层特征依然能复用,比从头训练快很多。epochs=100 是上限,配合 patience=20 表示连续20个epoch在验证集上mAP没提升就早停,实际跑完可能只到60个epoch左右。imgsz=640 是标准输入尺寸,如果你的图像是1280宽,建议把imgsz也提到1280,但要相应调小 batch。batch=16 在8G显存下跑n没问题,m或l模型要降到8或4。device=0 指定GPU,如果只有CPU则改成 device=cpu,但训练速度会慢到难以接受。save_dir 要把每次实验输出放到独立目录,避免 later 混淆不同参数下的结果。
如果希望在 jupyter 或 python 脚本里控制训练,等价写法是:
from ultralytics import YOLO model = YOLO('yolov8n.yaml').load('yolov8n.pt') results = model.train( data='data.yaml', epochs=100, imgsz=640, batch=16, patience=20, save_dir='runs/weed', )注意这里用的是 yolov8n.yaml 初始化网络结构,再 load 预训练权重。普通新手最容易写错的版本是直接写YOLO('yolov8n.pt'),这样会把预训练权重里的80个类别结构带进你的杂草任务,到训练时报类别数不匹配,或者输出头多余。用 yaml 初始化再到 pt 里加载权重,模型输出头会自动变成你的类别数,也就是 names 里定义的1类。
训练过程中的增强配置也值得调。杂草检测常见的问题是叶片遮挡、光照变化,可以在训练参数里显式打开:
yolo train ... fliplr=0.5 flipud=0.0 mosaic=1.0fliplr=0.5 表示水平翻转50%概率,flipud 不建议开,因为农田里杂草不会倒着长,开多了干扰语义。mosaic=1.0 在 yolo 里默认开启,它把四张图拼成一张训练,能在数据集有限的情况下极大丰富小目标样本组合,对杂草这类密集小目标很有用。
3.4 训练日志里的关键曲线怎么看
训练启动后,ultralytics 会在 save_dir 下生成 weights/best.pt 和 weights/last.pt,同时不断写出 results.csv。我习惯随时用命令看尾部:
tail -f runs/weed/results.csvcsv 里核心字段与含义可以按下表速查:
| 字段名 | 含义 | 我关注的点 |
|---|---|---|
| train/box_loss | 训练集边框损失 | 应逐步下降,不降说明学习率或数据问题 |
| val/box_loss | 验证集边框损失 | 若先降后升,说明开始过拟合 |
| val/cls_loss | 验证集分类损失 | 类别少时该值应很快接近0 |
| metrics/mAP50(B) | IoU=0.5 的平均精度 | 我评估模型可用的首要指标 |
| metrics/mAP50-95(B) | 更严格的 mAP | 追求精度时关注,波动大是正常的 |
如果你发现 mAP50 始终为0,但 loss 在下降,优先怀疑标签类别索引对不上 names。其次检查数据集中是否有大量超出图像边界的框,这些框会让网络学不到正样本。翻车现场非常多,细节会在第5章展开。
这一轮训练跑完,你会拿到 best.pt。接下来要做的不是直接部署,而是先评估它在 val 上的具体表现,找到漏检的重灾区是哪些图像,这决定了你的阈值怎么选、需不需要换更大模型。
4. 交叉验证与模型评估:mAP的读法、混淆矩阵形态与多版本切换注意点
4.1 评估指标速读与阈值选择
训练结束后,用 best.pt 对 val 集做一次推断并产生指标报告:
yolo val model=runs/weed/weights/best.pt data=data.yaml结果里会看到 mAP50、mAP50-95、precision、recall。杂草检测这类农田场景,我第一看 mAP50,因为杂草框不需要覆盖到像素级,只要框住主要叶片区域,后续机械除草或喷洒都能用。mAP50-95 更严格,它要求预测框与真实框的 IoU 从0.5到0.95逐步提高,对框位置的精确度很敏感,用于比较算法调优时更有效。
对于这份数据集,如果 mAP50 在0.7以上,属于可用的状态。0.5到0.7说明存在较多漏检或误检。0.5以下需要回到数据层面排查。conf 阈值也会影响最终表现,yolo val 默认 conf=0.001 是极端宽松的评估方式,适合计算 mAP;如果你部署时用 conf=0.5,实际看到的数量会少很多。我一般会在 val 上多跑几个阈值对比:
yolo val model=best.pt data=data.yaml conf=0.25 yolo val model=best.pt data=data.yaml conf=0.5观察阈值提高后 precision 和 recall 的平衡点。杂草密集时 bias 是 recall 优先,适当放宽到 conf=0.25,宁可多框几个冗余区域,不能漏掉重度杂草区。
4.2 混淆矩阵与预测图案例排查
ultralytics 会在验证输出目录生成 confusion_matrix.png。对于单类别检测,你会发现矩阵只有4个象限:真阳性、假阴性、假阳性、背景误检。这个矩阵能直接告诉你漏检高不高。如果左下角的假阴性数值明显偏高,说明大量真实杂草框没被预测出来,可能原因一是小目标太多,二是框太小在缩放到640时特征消失。
然后看 val 集对应的预测图保存,通常在 save_dir/val_batch0_pred.jpg。这些图上绿色框是真实标注,红色或蓝色框是预测。对比两套框的位置,你会直观看到问题集中在密集小草区域还是叶片重叠区域。我通常会挑选20张预测图翻一遍,这个步骤虽然原始,却比任何指标分析都能发现问题。
4.3 换yolov5、v7、v10、yolo11时的三个注意点
这份数据集因为同时提供 yolo 和 voc 两种标签,实际在迁移到不同版本时都具备基础条件,但换版本有三个坑值得先说。
第一个是训练入口差异。yolov5 官方仓库用 train.py 加 --data data.yaml,不带 yolo 命令;yolov8/v10/yolo11 用 ultralytics 的 yolo train 命令。yolov7 则要求标签目录必须严格包含 train 和 val 子目录,并且不自动在 labels 下类推,如果你的路径层级不同,它会直接报错找不到标签。
第二个是预训练权重的类别数对齐。从 yolov8 迁移到 yolov11 时,直接用 yolo11n.pt 迁移没问题,但若把 yolov8 训练得到的 best.pt 交给 yolov7 加载,会涉及结构改动,基本没意义。换版本后应重新加载官方预训练权重,从零迁移学习,不要指望跨版本权重直接晋升。
第三个点是 anchor 配置。yolov5 和 v7 里有默认 anchor,针对coco数据粒度设计的。杂草目标普遍小,默认 anchor 可能不合适。v8 及以后是 anchor-free,基本不用管。如果你用 v5/v7 训练后发现框偏大或偏移,优先检查 imgsz:把输入尺寸从640提到960或1280,小目标框对齐会有显著改善。这就是为什么我总说数据准备好,算法版本的迁移成本集中在超参数调整。
完成评估和版本选择后,模型基本稳定了。接下来要说的这些避坑经验,是你在实际使用时最容易踩到、且会白白消耗时间的部分。
5. 避坑:杂草检测数据集最容易误的事故现场
5.1 现象:txt 文件存在但训练时一直报 no labels found
训练一启动就报“找不到标签”,去 labels 目录看文件明明都在。原因通常是目录结构和 ultralytics 的约定不一致。它默认在图片目录同级的 labels 下找标签,也就是 images/train 对应 labels/train。如果你把标签放在了 images/train_labels 这类自定义目录里,即使 data.yaml 只写了 images/train,它也不会去找自定义路径。解决的办法是先按第2章的标准目录重新整理,或者把 data.yaml 里的 train 改成指向包含标签结构的父目录,并同时修改 labels 路径。最常见的做法是把 labels 作为 images 的同级目录,保持默认约定。我从那次之后,任何数据集到手第一眼就是看 labels 目录位置,根本不看那些花哨的 README。
5.2 现象:loss 正常下降但 mAP 始终为零,预测框全部空白
训练过程看起来很正常,损失也降了,但到 val 阶段 mAP 是零,输出的预测图上一片空白。核查标签时才发现 txt 里的类别索引写的是 1,而 data.yaml 的 names 只有 0: weed。原因是类别索引没有从0开始。yolo 里的类别索引是绝对的,names 字典定义了0对应weed,你却给了1,于是所有样本都落在一个模型根本不认识的类别上。解决方法是把 txt 里的第一个字段统一改成0,或保留1但 names 里同时补上1的空类别。最好用脚本批量改成0,因为这本来就是单类别问题。养成训练前统计类别索引的习惯:
awk '{print $1}' labels/train/*.txt | sort | uniq -c看到最大索引超过 names 长度再减一,就该停手检查了。
5.3 现象:voc 格式转换后部分 bbox 位置偏移,跑到图像外面
你在二次开发时想重新生成 txt,把 xml 里的 xmin、ymin、xmax、ymax 代入公式后发现部分框的中心坐标大于1或宽高大于1。原因是原始 xml 的部分边界框超出了图像尺寸,比如 xmax 写的是 1290,但图像宽只有 1280。标注软件允许画出图像边缘外的框,转换脚本却没有做裁剪。解决是在转换时对边界做裁剪,确保 xmax 不超过 width-1、ymax 不超过 height-1:
xmax = min(xmax, width - 1) ymax = min(ymax, height - 1)之后再归一化。这份数据集本身已经转换好了,大概率不会出现这种问题,但如果你自己扩展标注,或从其它资源合并图像时,一定记得这一步。边界框超出图像范围,在训练时会让模型学习到错误的坐标分布。
5.4 现象:训练中途报 CUDA out of memory
显存不足是训练时最常见的报错,特征很明显,训练几个 batch 后直接崩掉。原因多数是 imgsz 和 batch 的组合超出了显卡显存。用这份数据集的图像是 1280 宽,你强行用 imgsz=1280 且 batch=16,8G显卡直接扛不住。解决思路是先减 batch,从8降到4,再不行降到2。降到2后还崩,才考虑换更小模型或减小 imgsz。另一个补救是同时打开训练参数:
yolo train ... batch=8 accumulate=2accumulate=2 表示每2个batch累计一次梯度,等效于 batch=16 的效果,但显存占用只有 batch=8 的水平。这个参数很多人不知道,遇到显存不足先想到缩小 batch,而不是调整累计步数。
5.5 现象:训练完评估时 val 集是空的
数据集已经划分好,但你训练时发现验证后指标全无。经验总会出现 val 目录下文件数量为0。原因可能是解压时 val 文件夹没被正常解压出来,或者 data.yaml 里 val 路径指向了 test 的目录。解决方法是进入 images/val 和 labels/val 分别数文件,确认数量大于0。如果在 Windows 下解压 zip,偶尔会出现某些长文件名无法解压的问题,比如 img_0631_5586.xml 这类文件名超长容易被截断,导致对应 jpg 找不到标签。解决办法是用解压工具兼容的短路径重解压,或者把数据集放到磁盘根目录,例如 D:/weed_dataset,避免前面叠加太深的目录前缀。
6. 从检测到实用:杂草小目标的切片推理优化技巧
训练得到最佳模型之后,拿整张原图直接推理往往表现就不错,但当杂草目标比较小,比如图像里每株草只占二三十像素时,把整幅图缩放到 640 输入,小目标基本被压缩到模糊。我验证过多次,把 1280 宽的大图直接丢给模型,与切成四块分别推理再合并结果,后者在小目标上的召回率明显更高。
具体实现不复杂,核心是把原图切块推理后再做坐标还原:
from ultralytics import YOLO import cv2 model = YOLO('runs/weed/weights/best.pt') img = cv2.imread('field_sample.jpg') h, w = img.shape[:2] crop_size = 640 stride = 480 # 重叠区域,避免目标被切分 detections = [] for y in range(0, h, stride): for x in range(0, w, stride): crop = img[y:y+crop_size, x:x+crop_size] results = model(crop, conf=0.25, iou=0.45) for box in results[0].boxes.data.cpu().numpy(): x1, y1, x2, y2, score, cls = box detections.append([x + x1, y + y1, x + x2, y + y2, score, cls])切片尺寸取训练时的 imgsz,这里用 640。步长设为 480,让相邻切片有160像素的重叠,保证横跨切分线的大目标在两边都完整出现。推理时每个切片独立调用模型,得到的是局部坐标,所以还原时要加上切片起点坐标,也就是代码里的 x 和 y。合并结果后,可以用 NMS 把重叠区域的重复框合并,简单方式是对同一类别的检测框做贪心去重,或者把结果直接存入列表后统一应用 cv2.dnn.NMSBoxes。
这种切片策略有两个适用前提:一是原图分辨率显著大于训练尺寸,二是目标占图像面积比例小且呈均匀分布。如果你的场景是整幅图像本身就不大,或目标已经占了半个画面,切片反而会破坏语义,让目标变形,此时不建议用。我实际跑农田航拍图时用的比较多,地块大的没边,3600万像素的图缩到640,杂草直接小成一个点,切片是唯一有效解。
这个方法还有个副产品:你能看到模型在每个局部区块的独立预测置信度,从而快速定位哪些区域是难例。比如长期阴影下的杂草,单独切出来的置信度总是低于0.2,那就知道下一轮训练要补充阴影条件下的样本,或者调高图像增强里的亮度扰动强度。从那以后,我每次部署杂草检测模型都强制走一遍切片推理验证,先跑几十张代表性场景图,确认小目标召回率不存在明显洼地,再进正式流程。整套数据资源和切片验证脚本配合起来,模型从训练到落地的时间能压缩一大截。希望帮到你。
本文还有配套的精品资源,点击获取