news 2026/9/10 12:52:27

中草药YOLO目标检测数据集:从标签解析到训练验证全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
中草药YOLO目标检测数据集:从标签解析到训练验证全流程

简介:面向需要训练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 foundlabel 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路径,里面写明trainvaltest的绝对或相对路径,以及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模型建议
6GB4yolov5s开启--cache预加载数据
8GB8yolov5s默认即可
12GB16yolov5s / yolov5m可用--cos-lr提升收敛
24GB32yolov5m可加--multi-scale

如果改用 YOLOv8,则命令变化不大:

yolo detect train data=herbl.yaml model=yolov8s.pt imgsz=640 batch=16 epochs=100

YOLOv8 会要求 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.pngconfusion_matrix.pngval_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 并排对比,找出漏检最严重的图片,如果同一张图连续多轮都漏检,就回到可视化脚本重新看标注,很多时候是标注框覆盖了目标的一半,导致模型学到错误的边界。这种从预测到标注的反向修正,在小样本数据集上带来的收益比调整损失函数更明显。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 12:52:07

Python流程控制核心技巧与实战应用

1. 为什么流程控制是Python编程的基石刚接触Python的新手常会陷入一个误区——把编程语言简单理解为"写命令的语法"。但真正要写出有用的程序&#xff0c;关键在于控制代码的执行逻辑。这就好比做菜时只知道食材和调料还不够&#xff0c;必须掌握火候控制和步骤顺序才…

作者头像 李华
网站建设 2026/9/10 12:51:01

Sunshine 自托管游戏串流服务器教程:把 PC 游戏串到任何设备

Sunshine 自托管游戏串流服务器教程&#xff1a;把 PC 游戏串到任何设备 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine Sunshine 是一款免费开源的自托管游戏串流服务器&#xff…

作者头像 李华
网站建设 2026/9/10 12:50:42

CANN/GE数据依赖形状自定义算子样例

Data Dependent Shape 自定义算子(三类算子)样例 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。…

作者头像 李华
网站建设 2026/9/10 12:48:52

Qt/C++ TCP多线程客户端:收发分离与粘包状态机实现

简介&#xff1a;这是一份面向C与Qt开发者的高并发TCP通信实战资源&#xff0c;聚焦多线程客户端设计&#xff0c;解决网络编程中收发阻塞、TCP粘包拆包、数据包自动成型等核心难点&#xff0c;适用于物联网终端、实时通信中间件及工业控制客户端开发等场景。资源共121个文件&a…

作者头像 李华