简介:面向需要训练YOLO模型的目标检测开发者和学习者,这份中草药图像数据集按YOLOv5目录结构整理,已划分训练集、验证集和测试集,可直接用于YOLO检测训练。数据包含8个类别(如Cardamom、Cumin、Neem等),采用YOLO相对坐标标注,每张图片对应一个txt标签文件,内容为类别和归一化后的边界框坐标。整套资源共2000个文件,主要包括txt标注文件和一个可视化py脚本,压缩包约209.51MB。其中训练集约2600张,验证集约190张,测试集约100张,规模适中,适合快速跑通实验。附带的可视化脚本无需任何修改,随机传入一张图片即可绘制并保存标注框图像,便于直观检查标注质量或用于展示。目前已有1017人学习下载,适合作为中草药目标检测课程设计、论文实验或入门练习的数据基础。
1. 中草药 YOLO 目标检测数据集:我把它当做一个「能直接跑通」的标注样本集
很多做农业识别、药材检测的团队,卡在第一步不是模型选型,而是标注数据的数量和质量。这份中草药图像目标检测数据集按 YOLOV5 文件夹规范保存,训练集约 2600 张、验证集约 190 张、测试集约 100 张,包含 Cardamom、Cumin、Neem 等 8 个类别,自带类别 class 文件和数据可视化脚本。你把它下载解压后,不用改目录结构,写好一个 yaml 就能丢进 YOLOv5 训练。
我拆过这个资源后发现,它真正的价值不在「数据集有多大」,而在「标签格式是否干净、可视化能否快速验证」这两件事。很多从 Roboflow 导出的数据集都有标签文件名错位、类别索引漂移的问题,这份数据从文件名前缀.rf.来看同样来自 Roboflow 渠道,所以拿到的第一步不是训练,而是先解析标签内容,确认坐标是不是合理范围,再谈跑模型。下面我从目录结构、可视化检查、训练配置到验证排查,完整走一遍。
2. 解析数据集结构与 YOLO 标签格式:classes 文件、相对坐标与目录坑
2.1 目录结构:为什么 images/labels 分离更利于训练
先看整个数据集的保存方式,它遵循 YOLOv5 的标准布局:
datasets/ ├── images/ │ ├── train/ # 约 2600 张 │ ├── val/ # 约 190 张 │ └── test/ # 约 100 张 ├── labels/ │ ├── train/ # 与 train 同名 .txt │ ├── val/ │ └── test/ ├── classes.txt # 8 个类别,一行一个 └── show.py # 可视化脚本images 和 labels 分开存放,是 YOLOv5 从 v5.0 开始推荐的做法。训练器通过图片路径推断 label 路径,规则是:把/images/替换为/labels/,把图片后缀.jpg/.png替换为.txt。这里有两个坑值得注意。
第一,train/val/test 三个集合必须在 images 和 labels 下都有同名目录,哪怕某个集合为空,也要建目录,否则脚本扫描时容易报dataset not found或label not found。第二,数据集里给出的 test 集标注本身是存在的,但很多 YOLOv5 自定义数据集流程里 test 不被使用,我们通常把 val 当作验证集,test 留作最终评估。
2.2 class 文件与标签文件的对应关系
classes.txt的内容是 YOLO 格式中类别索引的唯一依据。以本数据集为例,打开后大约是:
Cardamom Cumin Neem ...每一行对应一个类别,行号从 0 开始,所以 Cardamom 的 class_id 是 0,Cumin 是 1。标签文件里每一行格式是:
class_id x_centre y_centre width height例如2 0.531997 0.639688 0.216372 0.304687,表示该目标属于索引为 2 的类别,边界框中心点在图片相对坐标的 (0.532, 0.640) 处,宽高分别占图片的 21.6% 和 30.5%。这里全部采用相对坐标,取值范围应在 0 到 1 之间。
我一般拿到标签后,第一件事是按类别统计数量,确认没有类别断层。比如有 8 个类别,但标签里的 class_id 最大是 7,如果出现 8 或 -1,大概率是 classes.txt 顺序和标注顺序没对齐导致的。常见做法是用一条 Python 脚本扫描所有 label:
import os from collections import Counter label_root = "datasets/labels/train" class_counter = Counter() invalid_files = [] for name in os.listdir(label_root): if not name.endswith(".txt"): continue path = os.path.join(label_root, name) with open(path) as f: for line in f: parts = line.strip().split() if len(parts) != 5: invalid_files.append((name, "field error")) continue cls_id = int(parts[0]) if cls_id < 0 or cls_id >= 8: invalid_files.append((name, f"class {cls_id} out of range")) else: class_counter[cls_id] += 1 print("类别统计:", dict(sorted(class_counter.items()))) print("异常文件:", invalid_files[:10])这段脚本会把所有标签聚合到 8 个桶里,同时找出字段数不对、类别索引越界的文件。判断标准很简单:class_id 必须在[0, 7]区间,坐标值必须在[0, 1]区间(允许边界为 1,但大于 1 就是越界)。如果出现大量class_id为 0 且集中在某几个文件,很可能是导出时 classes 文件损坏导致顺序错位,需要重新对照源标注。
2.3 重点:验证标签坐标是否越界
YOLO 相对坐标的越界问题,在 Roboflow 导出的数据集中比较常见。原因通常是图片经过缩放或自动定向后,原始标注没跟着变换。检测方法很简单:
def check_bounds(label_path): errors = [] with open(label_path) as f: for i, line in enumerate(f): cls, x, y, w, h = map(float, line.split()) if not (0 <= x <= 1 and 0 <= y <= 1): errors.append((i + 1, "center out of range")) if w <= 0 or h <= 0 or w > 1 or h > 1: errors.append((i + 1, "size out of range")) return errors运行后把异常的 label 记录到列表,再结合可视化脚本逐张确认。对于轻微越界(比如 x 为 1.02),YOLOv5 的 dataloader 在训练时会自动 clip 到 1.0,但这会造成目标框与原标注不一致;对于严重越界(比如负值),则会在计算 loss 时产生异常梯度,轻则 mAP 下降,重则 loss 变成 nan。所以这一步不能省。
3. 用数据可视化脚本检查标注质量:先看懂 show.py 再改业务
3.1 脚本运行方式与「随机取一张」的实现思路
数据集自带的show.py设计成「随机传入一张图片即可绘制边界框,并保存在当前目录」。它的循环逻辑大概是:读取指定图片,解析同名 txt 中的每一行,用 OpenCV 或 PIL 在图上绘制矩形,最终保存结果图。核心代码思路如下:
import cv2 import numpy as np import random import glob img_path = random.choice(glob.glob("datasets/images/train/*.jpg")) txt_path = img_path.replace("images", "labels").replace(".jpg", ".txt") img = cv2.imread(img_path) h, w = img.shape[:2] colors = [(0, 0, 255), (0, 255, 0), (255, 0, 0)] # 示例颜色 with open(txt_path) as f: for line in f: cls, x, y, bw, bh = map(float, line.split()) # 还原为像素坐标 cx, cy = int(x * w), int(y * h) box_w, box_h = int(bw * w), int(bh * h) x1, y1 = cx - box_w // 2, cy - box_h // 2 x2, y2 = x1 + box_w, y1 + box_h cv2.rectangle(img, (x1, y1), (x2, y2), colors[cls % 3], 2) cv2.putText(img, str(cls), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, colors[cls % 3], 1) cv2.imwrite("show_output.jpg", img)这段代码的核心在于把 YOLO 相对坐标反算成像素坐标:中心点乘图片宽高得到像素位置,再以中心点为基准偏移宽高的一半。脚本注释里写「无需更改」,确实可以直接运行,但它有个隐藏限制:如果没有设置随机种子,每次运行结果都不一样,不好复现问题图片。我建议运行前先random.seed(42),保证排查时多次运行能看到同一张图。
3.2 改成指定目录批量检查的常见做法
原脚本只随机看单张,适合快速确认数据集不是全黑或全空。但要系统检查几百张验证集的标注质量,我会改成「按目录扫描 + 输出拼图」的方式:
import os import cv2 def draw_one(img_path, txt_path, out_dir): img = cv2.imread(img_path) if img is None: return False h, w = img.shape[:2] with open(txt_path) as f: boxes = [list(map(float, line.split())) for line in f] for cls, x, y, bw, bh in boxes: x1 = int((x - bw / 2) * w) y1 = int((y - bh / 2) * h) x2 = int((x + bw / 2) * w) y2 = int((y + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) basename = os.path.basename(img_path) cv2.imwrite(os.path.join(out_dir, basename), img) return True # 遍历验证集 for name in os.listdir("datasets/images/val"): if not name.endswith(".jpg"): continue img_path = os.path.join("datasets/images/val", name) txt_path = os.path.join("datasets/labels/val", name.replace(".jpg", ".txt")) draw_one(img_path, txt_path, "vis_val")这样批量生成可视化图后,随便挑几十张混排,就能看出标注框是否贴合药材边界。批量检查的目的不是在屏幕前一张张看,而是快速发现「空标注」和「异常框」两类问题。
3.3 从可视化里能看出的常见标注意外
第一类是标签存在但图片里几乎看不到目标,这在中草药图片里很常见,因为很多图是白底商品图,目标颜色和背景非常接近;第二类是同一个目标被重复标注,比如一片 Neem 叶子被标了 3 个框,训练时会让模型对同一区域输出重叠预测;第三类是目标过小,框只有十几个像素,这类样本占比过高会拉低整体 mAP@0.5:0.95。
我用这个数据集时,发现它的 photo studio 风格图片比较多,背景干净、光线均匀,模型在验证集上容易跑出高 mAP,但放到真实货架或野外拍摄场景,性能会明显回落。可视化脚本只能验证「标注对不对」,不能验证「数据够不够真实」。这也是后面训练时要把数据增强加足的原因。
4. 把数据集接入 YOLOv5 / YOLOv8 训练:yaml 配置与命令行参数
4.1 from「按 YOLOV5 文件夹保存」到训练数据的目录映射
这份数据集既可以直接在 ultralytics/YOLOv5 仓库里用,也能迁移到 YOLOv8。区别只在于 yaml 的写法。YOLOv5 的train.py期望传入一个.yaml路径,里面写明train、val、test的绝对或相对路径,以及names列表。目录结构不需要调整,但建议把整个数据集放到与yolov5/同级的目录下,保持相对路径稳定。
4.2 编写 herbl.yaml 并核对 classes 顺序
在数据集根目录创建herbl.yaml,内容如下:
train: datasets/images/train val: datasets/images/val test: datasets/images/test nc: 8 names: [ 'Cardamom', 'Cumin', 'Neem', 'Turmeric', 'Holybasil', 'Fenugreek', 'Ginger', 'Cinnamon' ]注意names里的顺序必须和classes.txt完全一致,否则模型训练的类别和标注的 class_id 会错位。一个最常见的翻车写法是:为了方便把所有类别名写成了拼音或中文,导致训练代码在加载预训练权重时类别数对不上,报错num_classes mismatch。本数据集的类别名是英文,直接复制 classes.txt 即可。test字段可选,如果只做训练和验证,test 那一行可以删掉。
4.3 训练命令、超参与显存取舍
在 YOLOv5 仓库目录下,训练命令是:
python train.py --data herbl.yaml --weights yolov5s.pt --img 640 \ --batch-size 16 --epochs 100 --project runs/train --name herb_run推荐用yolov5s.pt作为预训练权重而不是从零训练。中草药数据只有 2600 张,属于小规模数据集,迁移学习能显著降低对样本量的需求。参数含义如下:
--img 640:训练时缩放图像尺寸。图片分辨率越高,模型能学习到更多细节,但显存占用成倍增长。--batch-size 16:单卡 16 是中等配置,8GB 显存建议降到 8,6GB 以下建议用--batch-size 4加梯度累积。--epochs 100:小数据集训练 100 轮足够收敛,继续增加容易过拟合。--project和--name:指定保存目录,方便多个实验对比。
| 显存 | batch-size | 模型 | 建议 |
|---|---|---|---|
| 6GB | 4 | yolov5s | 开启--cache预加载数据 |
| 8GB | 8 | yolov5s | 默认即可 |
| 12GB | 16 | yolov5s / yolov5m | 可用--cos-lr提升收敛 |
| 24GB | 32 | yolov5m | 可加--multi-scale |
如果改用 YOLOv8,则命令变化不大:
yolo detect train data=herbl.yaml model=yolov8s.pt imgsz=640 batch=16 epochs=100YOLOv8 会要求 yaml 路径、图片路径都是绝对路径,或者在当前终端使用相对路径时确保datasets目录能被模型包识别。一个快速替代方案是把datasets目录移动到 YOLOv8 安装包内部的datasets文件夹下,避免路径解析问题。我习惯在项目根目录写一个软链接,而不是复制整个数据集,节省磁盘空间:
ln -s $(pwd)/datasets /path/to/ultralytics/datasets这里处理的重点不是命令本身,而是数据规模。2600 张图片对 8 类目标来说,平均每类只有 300 多张,训练时--augment参数默认开启的 mosaic、flip、hsv 变换会起到很大作用。不要因为验证集 mAP 高就关闭增强,否则在真实场景中退化明显。
5. 模型跑通的最后一公里:验证集指标、标签错位与 loss 异常的排查
5.1 验证集指标怎么看:mAP@0.5 与 PR 曲线
训练完成后,runs/train/herb_run/下会生成results.png、confusion_matrix.png和val_batch*.jpg。重点关注mAP@0.5是否超过 0.9,以及mAP@0.5:0.95是否在 0.7 以上。如果 mAP@0.5 很高但 0.95 很低,说明模型对目标定位不够精细,框偏大或偏小,这时应检查标签框和真实目标边缘的贴合度。
验证集指标我一般配合测试集使用:
python val.py --data herbl.yaml --weights runs/train/herb_run/weights/best.pt --task test用自己的测试集而不是训练时的验证集,才能评估真实泛化能力。中草药数据集里的测试集只有 100 张,统计波动大,最好同时记录每次实验的混淆矩阵,看看是哪些类别之间互相误检。
5.2 标签、索引错位,loss 为 nan,图片无目标时的排错
训练中最常见的三类问题:
第一,loss 直接为 nan。原因通常是标签里有越界坐标或某些类别样本量极少,导致局部梯度爆炸。解决方法是按 2.3 的脚本扫描所有标签,同时检查每个类别的样本数,样本数小于 10 的类别要慎重参与训练。
第二,训练能跑起来但 mAP 为 0。优先检查 yaml 里nc是否正确。如果 classes.txt 有 8 类但herbl.yaml里误写为 5,训练过程不会报错,只是输出结果全部错位。我通常用一个简单脚本打印预测结果里的类别索引:
results = model(img) print(results.boxes.cls.unique())如果输出的是 0-4 但数据集里实际是 0-7,基本就是 nc 错了。
第三,验证时提示No labels found in ...,多半是 labels 目录下的子目录名与 images 不一致,或者 label 扩展名不是.txt。用find datasets/labels -name "*.txt" | wc -l对照图片数量即可定位。
5.3 用 val.py 导出错例做反向修正
最后分享一个我拿到这类小数据集时必做的动作:把验证集预测结果保存成带标签的图,再人工快速扫一遍。
python val.py --data herbl.yaml --weights runs/train/herb_run/weights/best.pt \ --save-txt --save-conf --project runs/val_inspect在runs/val_inspect/下的labels/里,每个 txt 文件保存了预测的类别、置信度和坐标。我把这些 txt 和 Ground Truth 并排对比,找出漏检最严重的图片,如果同一张图连续多轮都漏检,就回到可视化脚本重新看标注,很多时候是标注框覆盖了目标的一半,导致模型学到错误的边界。这种从预测到标注的反向修正,在小样本数据集上带来的收益比调整损失函数更明显。
本文还有配套的精品资源,点击获取