简介:这是一份面向YOLO系列算法目标检测任务的杂草检测数据集,适合从事农业智能识别、无人机植保或图像检测方向的开发者与研究者使用,可直接用于模型训练与验证测试。数据集共包含6847张图像并配有完整标注,压缩包内文件总数达2000个,以xml标注文件为主要类型,对应VOC格式的目标框信息,同时提供YOLO格式的txt标签,两种格式分别存放于独立文件夹,便于按需调用。包体整体约238.8MB,并附带data.yaml配置文件,兼容yolov5、yolov8、yolov9、yolov7、yolov10及yolo11等主流版本,数据已预先划分好训练与验证集。YOLO标签采用类别索引与归一化中心点、宽高比例值表示,方便直接接入训练流程。目前已有308人学习下载,读者可借此快速搭建杂草检测实验环境,省去数据采集与标注环节,将精力集中于模型调优与效果对比。

1. 6847 张杂草图像带标签:这份 YOLO 数据集到底能不能直接开训

田里长草这件事,做农业视觉的人都懂——杂草和作物在幼苗期长得几乎一样,人工背着药桶打药,打多了伤苗、打少了减产。我去年帮一个做植保无人机的团队看模型,他们卡在数据上整整两个月:自己拍了两千张,标完发现类别不均衡,背景单一,模型一换地块就崩。后来换了一份现成的杂草检测数据集,三天跑通 baseline,一周出可用的 mAP。这次拆的这份资源就是同类东西——6847 张带标签的杂草图像,YOLO 格式和 VOC 格式双份标签,配好了 data.yaml ,从 yolov5 到 yolo11 都能直接吃。

它解决的不是"有没有数据"的问题,而是"数据能不能直接进训练管线"的问题。目录里那些 img_0100_937.xml 、 img_0100_5624.xml 是 VOC 标注,对应的 txt 是 YOLO 归一化坐标,两套标签同源,省掉了自己写转换脚本的环节。适合三类人:刚接触 yolo 训练想找个完整数据集练手的新手、做农业视觉需要快速验证算法改进的工程师、以及要对比不同 yolo 版本在杂草场景下表现的调参党。不适合想直接拿去做商业部署的人——6847 张的体量够验证,不够覆盖真实田间的全部光照和遮挡变化。

2. 拆开压缩包先看什么:目录结构、标签格式与 data.yaml 的对应关系

拿到一个数据集压缩包,我习惯先不急着解压训练,而是把目录结构和标签格式摸清楚。这一步花十分钟,能省掉后面几小时的报错排查。这份资源的组织方式比较规整,VOC 和 YOLO 两套标签分开放,图像统一命名, data.yaml 直接给好了路径和类别。

2.1 目录树与文件命名规律

解压后典型的结构是这样(不同版本可能略有差异,但核心目录不会变):

weed_dataset/
├── images/
│   ├── train/          # 训练集图像
│   └── val/            # 验证集图像
├── labels/
│   ├── train/          # YOLO 格式 txt 标签
│   └── val/
├── annotations/        # VOC 格式 xml 标签
│   ├── train/
│   └── val/
└── data.yaml           # 数据集配置文件

图像命名是 img_XXXX_YYYY.jpg 这种形式, XXXX 是批次或场景编号, YYYY 是帧序号。标签文件名和图像文件名严格一一对应,只是扩展名不同—— img_0100_937.jpg 对应 img_0100_937.txt 和 img_0100_937.xml 。这个对应关系是后面排查"标签丢失"问题的关键,先记住。

常见做法是解压后先跑一条命令统计文件数量,确认图像和标签是否配对:

# 统计训练集图像和标签数量
echo "train images: $(ls weed_dataset/images/train/*.jpg | wc -l)"
echo "train labels: $(ls weed_dataset/labels/train/*.txt | wc -l)"
echo "train xml:    $(ls weed_dataset/annotations/train/*.xml | wc -l)"

三个数字应该一致。如果 labels 比 images 少,说明有图像没标或标签丢失;如果 xml 和 txt 数量不一致,说明两套标签生成时有一边漏了。我一般会再写个 python 脚本做精确配对检查,比 shell 更可靠:

import os

img_dir = "weed_dataset/images/train"
lbl_dir = "weed_dataset/labels/train"

imgs = {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith(".jpg")}
lbls = {os.path.splitext(f)[0] for f in os.listdir(lbl_dir) if f.endswith(".txt")}

missing_lbl = imgs - lbls   # 有图无标签
missing_img = lbls - imgs   # 有标签无图

print(f"图像总数: {len(imgs)}, 标签总数: {len(lbls)}")
print(f"缺标签的图像: {len(missing_lbl)}")
print(f"缺图像的标签: {len(missing_img)}")
if missing_lbl:
    print("示例:", list(missing_lbl)[:5])

这段脚本用集合差集找出不配对的文件。 missing_lbl 非空意味着这些图像在训练时会被 yolo 跳过或报 warning, missing_img 非空则是脏数据,建议直接删掉对应标签。参数上没什么可调的,路径改成你自己的解压位置即可。

2.2 YOLO 格式与 VOC 格式的字段对照

这份资源同时给了两种标签格式,很多人会纠结用哪个。先把两种格式的字段含义对齐,再决定。

YOLO 格式每行一个目标,五个字段:

<class> <x_center> <y_center> <width> <height>

<class> 是类别索引,从 0 开始;后四个都是相对图像宽高的归一化值,范围 0 到 1。比如 0 0.523 0.441 0.112 0.203 表示第 0 类目标,框中心在图像宽度 52.3%、高度 44.1% 的位置,框宽占图像宽度 11.2%,框高占 20.3%。

VOC 格式是 xml,关键字段是 <bndbox> 里的 xmin/ymin/xmax/ymax ,单位是像素绝对值:

<object>
    <name>weed</name>
    <bndbox>
        <xmin>412</xmin>
        <ymin>298</ymin>
        <xmax>530</xmax>
        <ymax>441</ymax>
    </bndbox>
</object>

两种格式的换算关系是: x_center = (xmin + xmax) / 2 / img_width , width = (xmax - xmin) / img_width ,y 方向同理。这份资源两套标签已经生成好了,不需要你自己转。但如果你后续要用 labelImg 补标,注意 labelImg 默认存 VOC 格式,切到 YOLO 模式才会存 txt——这个切换在界面左侧工具栏,很多人第一次用会漏掉。

对比项 YOLO txt VOC xml
坐标单位 归一化比例值 像素绝对值
每文件目标数 多行,每行一个 多个 <object> 节点
类别表示 数字索引 类别名称字符串
直接可用性 yolo 系列直接读 需转换或换数据加载器
适合场景 训练 标注、可视化、跨框架

2.3 data.yaml 里每个字段怎么改

data.yaml 是 yolo 训练入口配置文件,内容大致如下:

path: ./weed_dataset
train: images/train
val: images/val
nc: 1
names: ['weed']

path 是数据集根目录, train 和 val 是相对 path 的子路径。 nc 是类别数, names 是类别名列表。这份资源是单类别杂草检测,所以 nc: 1 。如果你要合并其他数据集做多类别,改 nc 和 names 就行,但注意类别索引要和标签里的 <class> 对应——标签里全是 0,你加类别时新类别的索引从 1 开始,别把顺序搞乱。

常见坑是 path 用了相对路径,训练时工作目录一变就找不到数据。我一般改成绝对路径,或者确认训练脚本的 cwd 和 yaml 同级。另外 val 如果指向的是验证集,训练时 --val 会自动读;如果你想把验证集也并进训练,改 train 指向即可,但那样就没有独立验证了,不推荐。

3. 从零跑通第一次训练:yolov8 环境配置与命令行参数

环境配置这块,新手最容易在版本冲突上翻车。yolo 系列迭代快,yolov5 和 yolov8 的依赖不兼容,yolov8 和 yolo11 的 ultralytics 包版本要求也不同。我的建议是每个项目单独建 conda 环境,别在 base 里装。

3.1 conda 环境与 ultralytics 安装

以 yolov8 为例,python 3.9 或 3.10 都行,3.11 部分依赖还没跟上:

conda create -n yolo_weed python=3.10 -y
conda activate yolo_weed

# 安装 pytorch,按你的 CUDA 版本选,这里以 CUDA 11.8 为例
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118

# 安装 ultralytics,yolov8 及之后版本统一用这个包
pip install ultralytics

装完验证一下:

import torch
from ultralytics import YOLO

print("CUDA available:", torch.cuda.is_available())
print("CUDA version:", torch.version.cuda)
model = YOLO("yolov8n.pt")   # 会自动下载预训练权重
print("model loaded:", model.model_name)

torch.cuda.is_available() 返回 False 的话,先查显卡驱动和 CUDA 版本是否匹配,别急着往下训——CPU 训 6847 张能跑到你怀疑人生。 YOLO("yolov8n.pt") 这行会从官方源拉预训练权重,如果网络不通,手动下载后把路径换成本地文件。

提示:yolov5 用的是 requirements.txt 那套,和 ultralytics 包不兼容。如果你要跑 yolov5,单独建环境,从 yolov5 仓库 clone 后 pip install -r requirements.txt ,别和 yolov8 混装。

3.2 训练命令与关键参数含义

环境好了,训练就一行命令的事:

yolo detect train \
  data=weed_dataset/data.yaml \
  model=yolov8n.pt \
  epochs=100 \
  imgsz=640 \
  batch=16 \
  lr0=0.01 \
  patience=20 \
  project=runs/weed \
  name=exp1

逐个说参数。 data 指向你的 yaml; model 是预训练权重, yolov8n 是最小的 nano 版,显存不够就换 n,够就上 s 或 m; epochs=100 是训练轮数,杂草检测这种单类别任务,100 轮通常够收敛; imgsz=640 是输入分辨率,和预训练权重一致,改了要重新适配; batch=16 看显存,8G 显存跑 640 分辨率大概能到 16,不够就降到 8; lr0=0.01 是初始学习率,ultralytics 默认会做 warmup 和余弦退火,一般不用大改; patience=20 是早停,20 轮验证指标不升就停,省时间。

训练过程中终端会打印每轮的 box_loss、cls_loss、mAP50、mAP50-95。重点看 mAP50 有没有稳步上升,如果前 10 轮就平了,大概率是学习率太大或数据有问题。 runs/weed/exp1/ 下会存权重、训练曲线和验证结果图, weights/best.pt 是验证集上最好的权重,后面推理用它。

3.3 验证与推理:确认模型真的学到了杂草

训完别急着高兴,先跑验证:

yolo detect val \
  model=runs/weed/exp1/weights/best.pt \
  data=weed_dataset/data.yaml \
  imgsz=640

输出里看 mAP50 和 mAP50-95 。单类别杂草检测,mAP50 能到 0.85 以上算正常,低于 0.7 就要查数据或训练配置。然后拿几张图做推理,肉眼确认:

yolo detect predict \
  model=runs/weed/exp1/weights/best.pt \
  source=weed_dataset/images/val \
  conf=0.25 \
  save=True

conf=0.25 是置信度阈值,低于这个值的框不显示。杂草场景里,如果漏检多就降到 0.15 试试,误检多就升到 0.4。结果存在 runs/detect/predict/ ,打开图看框有没有框到作物上——框错了说明标签质量有问题,回去查对应图像的 txt。

4. 换 yolo 版本、改类别、调置信度:几个高频改动怎么做

baseline 跑通只是开始,实际项目里你大概率要换版本、改类别或调推理阈值。这几件事看着简单,但每个都有坑。

4.1 从 yolov8 切到 yolov5 或 yolo11

这份资源标称兼容 yolov5 到 yolo11,但"兼容"指的是数据格式兼容,不是代码兼容。切版本要换环境和训练脚本。

yolov5 的训练命令是:

python train.py \
  --data weed_dataset/data.yaml \
  --weights yolov5s.pt \
  --epochs 100 \
  --img 640 \
  --batch 16

注意 yolov5 的 data.yaml 里 path 字段可能不认,它更习惯 train: 和 val: 直接写绝对路径。如果报找不到数据,把 yaml 改成:

train: /abs/path/weed_dataset/images/train
val: /abs/path/weed_dataset/images/val
nc: 1
names: ['weed']

yolo11 用 ultralytics 包,命令和 yolov8 几乎一样,只是模型权重换成 yolo11n.pt :

yolo detect train data=weed_dataset/data.yaml model=yolo11n.pt epochs=100 imgsz=640

yolo11 在 ultralytics 8.3 以后版本才支持,装之前确认 pip show ultralytics 的版本号。版本低了就 pip install -U ultralytics 。

4.2 多类别扩展与标签索引对齐

单类别改多类别,比如区分"阔叶杂草"和"禾本科杂草",要做三件事:改 data.yaml 的 nc 和 names ;把标签里的 <class> 按新索引重新映射;确认 VOC xml 里的 <name> 和 names 列表对应。

假设原来全是 0,现在要把其中一部分标成 1,写个脚本批量改:

import os

lbl_dir = "weed_dataset/labels/train"
# 假设文件名里带 broadleaf 的归为类别 1
for fname in os.listdir(lbl_dir):
    if not fname.endswith(".txt"):
        continue
    path = os.path.join(lbl_dir, fname)
    with open(path) as f:
        lines = f.readlines()
    new_lines = []
    for line in lines:
        parts = line.strip().split()
        if len(parts) == 5:
            # 根据你的规则改类别索引,这里示例:全部改成 1
            parts[0] = "1"
            new_lines.append(" ".join(parts))
    with open(path, "w") as f:
        f.write("\n".join(new_lines))

这段脚本把指定目录下所有标签的类别索引改成 1。实际用时把判断条件换成你的业务规则,比如按文件名前缀、按图像内容分类结果。改完记得同步改 data.yaml 的 nc: 2 和 names: ['broadleaf', 'grass'] ,顺序不能错——索引 0 对应 names[0]。

4.3 置信度与 NMS 阈值对杂草检测的影响

推理时两个阈值最关键: conf 和 iou 。 conf 控制哪些框保留, iou 控制 NMS 合并重叠框的力度。

yolo detect predict \
  model=best.pt \
  source=test_images/ \
  conf=0.3 \
  iou=0.5 \
  save=True

杂草密集区域,框容易重叠, iou 设太低(比如 0.3)会把相邻杂草的框合并成一个,漏检;设太高(0.7)又会出现同一株草多个框。我一般从 0.5 起步,看结果微调。 conf 对农业场景更敏感——打药决策宁可多框几株也别漏,所以 conf 可以压到 0.2 左右,代价是误检率上升,需要后处理过滤。

参数 典型范围 调低效果 调高效果
conf 0.15 ~ 0.5 召回升,误检增 精度升,漏检增
iou 0.4 ~ 0.7 重叠框合并多 重叠框保留多
imgsz 416 ~ 1280 速度快,小目标漏 精度升,显存增

5. 避坑与排查:标签、路径、显存这三类问题最常翻车

这一章是我自己踩过的坑,按"现象 → 原因 → 解决"写,你遇到报错先来这里对号入座。

现象:训练启动就报 No labels found ,或者 loss 一直是 0。 原因: data.yaml 里的路径不对,或者标签目录结构和 yolo 预期的不一致。yolo 默认在图像同级目录找 labels 文件夹,或者按 yaml 里的 path 拼接。这份资源把 labels 单独放,如果 yaml 没写对,yolo 就找不到。 解决:确认 data.yaml 的 path 是绝对路径, train 和 val 指向图像目录,然后 yolo 会自动把 images 替换成 labels 去找标签。如果目录名不是 images ,要么改名,要么在 yaml 里显式指定标签路径(部分版本支持 train_labels 字段)。最稳的办法是跑一遍第 2 章的配对检查脚本,确认标签文件真实存在。

现象:训练中途报 CUDA out of memory 。 原因: batch 或 imgsz 超过显存容量。6847 张图里如果有高分辨率原图,yolo 会先 resize 到 imgsz ,但数据加载和增强也会占显存。 解决:先把 batch 减半,还不行就降 imgsz 到 416。另外 ultralytics 默认开 cache=True 把图像缓存到内存,显存不够时设 cache=False 。如果显卡本身小于 6G,建议用 yolov8n 或 yolov5n 这种 nano 模型,别硬上 s 以上。

现象:验证集 mAP 很高,但拿新拍的田间图推理,框全错。 原因:数据集里的图像和你的实际场景分布不一致——光照、拍摄角度、杂草种类不同。6847 张看着多,但如果都是同一批次、同一相机拍的,泛化性有限。 解决:先做数据增强,ultralytics 训练时默认开 mosaic、HSV 抖动、翻转,可以再加 degrees 、 translate 参数。更根本的办法是补拍目标场景的图,标几百张加进训练集。我一般会留 10% 的真实场景图做测试,不参与训练,专门用来暴露这个问题。

现象:标签里坐标超过 1 或为负数,训练报 normalized coordinates out of range 。 原因:VOC 转 YOLO 时,如果框超出图像边界,归一化后就会越界。这份资源的两套标签是预生成的,但如果你自己补标或转换,容易出这个问题。 解决:写脚本检查所有 txt,把越界坐标裁剪到 [0, 1]:

import os

lbl_dir = "weed_dataset/labels/train"
for fname in os.listdir(lbl_dir):
    if not fname.endswith(".txt"):
        continue
    path = os.path.join(lbl_dir, fname)
    with open(path) as f:
        lines = f.readlines()
    fixed = []
    for line in lines:
        parts = line.strip().split()
        if len(parts) != 5:
            continue
        cls = parts[0]
        coords = [min(max(float(x), 0.0), 1.0) for x in parts[1:]]
        fixed.append(f"{cls} " + " ".join(f"{c:.6f}" for c in coords))
    with open(path, "w") as f:
        f.write("\n".join(fixed))

min(max(x, 0.0), 1.0) 把每个坐标夹到 0 和 1 之间。注意这会把越界框压扁,如果越界严重,更好的做法是丢弃该目标而不是裁剪。

现象:换 yolo 版本后,同样的数据训练结果差很多。 原因:不同版本的默认超参、数据增强策略、损失函数都不同。yolov5 和 yolov8 的 anchor 机制、标签分配策略差异很大,不能直接比 mAP。 解决:换版本时把 epochs 、 lr0 、 batch 对齐,数据增强参数也尽量一致,再比结果。另外预训练权重必须用对应版本的,别拿 yolov5 的权重去初始化 yolov8,会报 key 不匹配。

6. 把 6847 张用到极致:数据增强策略与训练结果验证技巧

数据集体量固定,但你能通过增强和验证手段把它的价值榨干。这一章讲两个具体技巧:一是怎么配增强参数让单类别杂草检测更鲁棒,二是怎么验证模型不是"背答案"。

先说增强。ultralytics 训练时默认开 mosaic,把四张图拼成一张,这对小数据集很有效,能变相扩大样本量。但杂草检测有个特殊点——mosaic 拼接后,不同图的杂草尺度混在一起,模型可能学到"拼图边界"这种伪特征。我的做法是训练后期关掉 mosaic,让模型在真实单图上微调:

yolo detect train \
  data=weed_dataset/data.yaml \
  model=yolov8s.pt \
  epochs=150 \
  imgsz=640 \
  batch=16 \
  mosaic=0.5 \
  close_mosaic=20 \
  degrees=15 \
  hsv_h=0.015 \
  hsv_s=0.7 \
  hsv_v=0.4

mosaic=0.5 表示 50% 概率做 mosaic,比默认的 1.0 温和; close_mosaic=20 是最后 20 轮关掉 mosaic,让模型适应真实图像; degrees=15 做 ±15 度旋转,杂草在田间不会总是正着长; hsv_h/s/v 是色调、饱和度、亮度抖动,模拟不同光照。这几个参数是我在农业数据集上试出来的,不一定最优,但比默认值稳。

再说验证。mAP 高不代表模型可用,我习惯做两件事。第一件是混淆矩阵,看模型把多少背景误判成杂草:

yolo detect val \
  model=runs/weed/exp1/weights/best.pt \
  data=weed_dataset/data.yaml \
  plots=True

plots=True 会在输出目录生成混淆矩阵和 PR 曲线。单类别任务看矩阵右上角和左下角——右上角是背景误检,左下角是漏检。农业场景漏检比误检严重,如果左下角数值大,说明模型对某些形态的杂草不敏感,回去查那部分图像的标签。

第二件是拿训练集里的图做推理,看模型是不是"记住"了。如果训练集 mAP 接近 1.0 但验证集只有 0.7,就是过拟合。对策是加 dropout、减模型容量或加更多增强。我一般会留一份完全没参与训练的图,训练全程不碰,最后只跑一次推理,这个结果才是真实水平。

注意:验证集和测试集要分开。这份资源已经划分了 train 和 val,但 val 在训练中被用来选 best.pt,严格说它算验证集不算测试集。如果你要发论文或做正式评估,从 val 里再切一半出来当 test,训练时完全不看。

从那以后我每次拿到新数据集,都强制走一遍"配对检查 → 格式抽检 → 小样本过拟合测试 → 全量训练"这个流程。小样本过拟合测试是拿 50 张图训 200 轮,如果 mAP 上不到 0.9,说明数据或配置有硬伤,别浪费时间跑全量。这个习惯帮我省过至少三次通宵排查。希望帮到你。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

更多推荐