6847张带标签杂草图像YOLO数据集:从数据检查到yolov8训练全流程
简介:这是一份面向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,说明数据或配置有硬伤,别浪费时间跑全量。这个习惯帮我省过至少三次通宵排查。希望帮到你。
更多推荐

所有评论(0)