简介:YOLO格式的鲜花分类数据集,覆盖康乃馨、玫瑰、向日葵、雏菊等14种常见花卉,面向目标检测与图像分类的学习者,适合用于模型快速验证、性能评估和小数据集分类训练。资源以JPG图像为主体,共2000个文件、压缩包约844.66MB,其中1994张图片按训练集和验证集分别存放,并附带4个JSON标注文件、1个Markdown说明文档及1个gitattributes配置,便于对照查看数据组织方式与标注细节。数据集采用train、val两层目录结构,同一类别的图片集中在一个子文件夹中,同时提供与标注ID对应的classname.txt类名文件,可快速建立标签与类别映射,降低上手门槛。目前已有387人学习下载,适合刚接触YOLO系列模型、需要一份分类清晰的小型数据集做练习或算法验证的开发者。
1. 为什么一张「yolo鲜花分类数据集」能卡住整个项目一周
看到「yolo鲜花分类数据集」这个标题,很多人的第一反应是:去网上下载一个现成的花卉图片包,扔给YOLO训练,然后等着模型能数出画面里有几朵玫瑰。真这么做,大概率翻车——公开的花卉数据集大多是分类任务格式,一张图正中一朵大花,直接喂给检测模型,模型学到的其实是「画面中央带花瓣的风景」,一到花店实拍、公园远景、花束拥挤的场景就全线崩坏。这背后的差距不在模型结构,而在数据集的定义方式:你需要的不是「能训练的数据」,而是一套标注规范、目录结构、划分策略都贴合 YOLO 训练流程的样本集。本文面向想用检测模型做鲜花识别、又不满足于公开数据集效果的同学,讲清楚这类数据集应该怎么造、怎么验、怎么把坑提前踩平。
2. 公开花卉集为什么喂不饱 YOLO:任务格式与数据难度的双重错位
2.1 YOLO 吃的不是图片,是归一化边界框
YOLO 的 detect 任务,训练样本是「图片 + 标签 txt」。每个 txt 文件里每一行代表一个目标,包含类别编号和归一化后的中心坐标、宽高。这和分类数据集的「文件夹名即标签」完全是两套体系。
如果你的需求是「一张图里有且只有一种花,只要告诉我是什么花」,那可以直接走 YOLO 的 cls 模式,数据集按文件夹组织,训练命令也简单。但只要你需要知道「画面里哪几朵是玫瑰、哪几朵是雏菊」,就必须走 detect 模式,为每一朵花画一个框。
这个区别是标题歧义的根源。很多人搜「鲜花分类数据集」,拿到的却是一堆图片集,最后训练时才发现标签没法用。先想清楚自己的交付物是「类别」还是「框」——这一步想不清楚,后面全部白做。
2.2 公开花卉数据集的四个不合身
公开的花卉图片集,常见问题可以归成四类。
第一,标签粒度不统一。有的按品种分,有的按颜色分,有的把月季和玫瑰当成两个类,而生物学上它们的关系非常复杂。YOLO 训练最怕类别定义漂移,同一个物种今天叫 rose 明天叫 rosa 之类,标注的人自己都会标乱。
第二,图片构图太「正」。公开集里大量是单花特写,目标居中、占比大、背景干净。这种图训练出来的模型,权重里「花」和「背景」的边界特别牢,遇到真实场景里的遮挡、逆光、多目标重叠,检测置信度直线下降。
第三,类别不平衡严重。好看的品种样本多,冷门品种可能只有几十张,训练时小类的梯度被大类的 loss 淹没,最后 mAP 还行,单独看某个冷门品种的召回率几乎为零。
第四,格式需要二次转换。就算你拿到的是 Pascal VOC 格式的标注,也要把它转成 YOLO 的归一化 txt。转换本身不难,难的是转换过程中常见的坐标越界、类别 ID 错位、图片和标签文件名对不上这些坑。
2.3 鲜花检测比通用检测多出的三类坑
鲜花不是「桌子」「椅子」那种结构固定的刚体。第一坑是类间相似度高,菊科一堆花长得像同一个模子,绣球和某些伞形科花卉在远处看几乎一样,模型需要非常细的纹理特征才能区分。第二坑是类内差异大,同一品种的绣球有蓝、粉、紫多个色系,盛花期和凋谢期的形态也差很多。第三坑是尺度与遮挡,花海远景里一朵花可能只有十几个像素,而近景特写里一束花有二十几个互相重叠的框。
这三类坑决定了鲜花数据集不能照搬通用目标检测的数据增强策略。比如随机裁剪就很容易把花冠裁掉一半,模型学到的训练样本是一堆残花。后面的数据增强参数,我会在训练前的小模型验证环节专门说明怎么调。
3. 搭建一套 YOLO 可训练的鲜花分类数据集:目录、标签与标注规范
3.1 一图一标:目录结构从哪开始
我一般会在项目根目录下建一个干净的 data 目录,把图像和标签分开管理,然后按 train / val / test 划分。这里的关键不是目录名,而是和 dataset.yaml 里的配置保持一致。
mkdir -p flower_dataset/{images/{train,val,test},labels/{train,val,test}}注意 labels 目录下的子目录结构必须和 images 完全对应。YOLO 训练时会自动根据图片路径寻找对应的 txt 标签,默认规则是把 images 替换为 labels、把图片后缀替换为 .txt。如果目录结构不一致,训练时会疯狂报「label not found」的警告,虽然训练不中断,但你这批数据的有效标签数可能是 0。
划分比例上,我通常用 8:1:1 或者 7:2:1。样本总量少于 3000 张时建议 8:1:1,因为验证集太薄的话,指标波动会非常大,你很难判断一次改动到底是变好还是变坏。
3.2 标签 txt 与 dataset.yaml:一个样本走完喂给 YOLO 的全链路
假设你要做 5 种花:玫瑰、向日葵、郁金香、雏菊、兰花。类别编号从 0 开始,dataset.yaml 这样写:
path: ../flower_dataset train: images/train val: images/val test: images/test nc: 5 names: 0: rose 1: sunflower 2: tulip 3: daisy 4: orchidpath是数据集根目录,train和val是相对于path的路径。nc是类别总数,names里的顺序就是标签文件里类别编号的顺序。这里最大的坑是:如果你在标注工具里设定的类别顺序和 names 不一致,模型会把玫瑰学成向日葵,而且你不会第一时间发现,因为 loss 照样在下降。
再看一张图的标签 txt。假设图片宽 1920、高 1080,其中一朵玫瑰的框是左上角 (200, 300)、右下角 (600, 750)。归一化计算方式如下:
x_center = (200 + 600) / 2 / 1920 # 0.2083 y_center = (300 + 750) / 2 / 1080 # 0.4861 w = (600 - 200) / 1920 # 0.2083 h = (750 - 300) / 1080 # 0.4167对应的 txt 一行就是:
0 0.2083 0.4861 0.2083 0.41670是 rose 的类别编号,后面依次是中心点 x、中心点 y、宽度、高度,全部是 0 到 1 之间的浮点数。YOLO 官方实现里不允许 w 或 h 为 0,也不允许中心点在图片外。这些数值如果出现越界,训练时会被过滤掉,代价是你可能某张图一个目标都没留下。
我习惯在标注完成后用一小段脚本把所有标签扫描一遍,而不是等到训练时让模型去「沉默地跳过」。这个脚本会在第四章给出。
3.3 标注工具选型与花朵框的标注习惯
单人小批量标注,我推荐 labelImg,轻量、装好就能用,快捷键 W 画框、A 切上一张、D 切下一张。团队协作、需要多人同时标注时,CVAT 是更合适的选择,它在浏览器里跑,可以分配任务、审核标注结果。如果你有 GPU 且想省时间,x-anylabeling 这类带 SAM(分割模型)插件的工具可以先自动生成候选框,人工微调,效率提升明显。
无论用哪个工具,花朵框都有几个约定俗成的习惯。第一,框只包住花冠主体,尽量不包含花茎和叶子;如果花是侧面的,可以包含一点点花托,但不要一路框到底把整根茎都圈进去。第二,可见部分占整朵花的 50% 以上才标,比如一把花束里被完全挡住的、只能看到一小片花瓣的,不标。第三,严重模糊的图不要删得太干净,保留一部分作为困难样本,但占比控制在总样本的 5% 以内,否则会把模型训得疑神疑鬼。
标注速度上,单人一天大概能标 300 到 500 个框,如果花束场景一图十几个目标,速度会掉到 100 张以内。预算不够又赶时间的话,可以先标 500 张训一版模型,再用模型去预打标,这个流程在第六章展开。
4. 数据集清洗、分层划分与格式自检:训练前必做的三件事
4.1 第一轮清洗:重复图、损坏图与模糊图批量排查
很多人拿到一批图就直接开标,标到一半发现有两百多张是同一场景换了个角度,白白浪费人力。我一般先做三轮机械排查。
先把完全重复的图去掉。计算文件的 MD5,比对哈希值,能干掉一模一样的副本。但手机连拍的多张照片往往只是相似、不是完全相同,这时可以用感知哈希(pHash),把每张图缩到小尺寸、转灰度、算哈希,汉明距离小于 5 的视为重复候选,人工扫一眼确认。
然后是损坏图。有的图后缀是 .jpg 但实际解码失败,训练时读到一半会报错。用 OpenCV 批量读一遍,读不出来的直接移入 discard 目录。
最后是模糊图。这里给一个可跑的脚本思路,用拉普拉斯方差给每张图打分,分数越低越可能模糊。
import cv2 from pathlib import Path def score_blur(img_path, threshold=50): img = cv2.imread(str(img_path), cv2.IMREAD_GRAYSCALE) if img is None: return None, False var = cv2.Laplacian(img, cv2.CV_64F).var() return var, var < threshold for p in Path("raw_images").rglob("*.jpg"): var, is_blur = score_blur(p) if is_blur: print(f"{p}: {var:.1f}")threshold不是固定值,跟你的拍摄设备、压缩质量都有关系。我的习惯是先只打印不处理,跑一遍看分数的分布,再取分数最低的 5% 人工过一遍。不要脚本说模糊就删,有些花天生纹理弱,拉普拉斯分数就是低,强行删掉会误伤正常样本。
4.2 按类别与拍摄批次分层划分 train / val / test
随机划分是最容易犯的错。假设你某一天在植物园拍了一整片郁金香,同一个画面连续拍了 10 张,随机划分时这 10 张可能 5 张进训练集、5 张进验证集。训练时模型相当于「见过」验证集的同一朵花的另一半角度,验证指标会虚高到让你误以为模型已经很强。
正确的做法是双层分层:先按类别分层,保证每个类别在三个集合里的比例和总体一致;再按拍摄批次分组,同一时间段、同一地点的照片只能整体进入其中一个集合。
import random import shutil from pathlib import Path random.seed(42) def split_by_group(groups, train_ratio=0.8, val_ratio=0.1): groups = list(groups) random.shuffle(groups) n_train = int(len(groups) * train_ratio) n_val = int(len(groups) * val_ratio) return { "train": groups[:n_train], "val": groups[n_train:n_train + n_val], "test": groups[n_train + n_val:], } # 假设 groups 是 dict: {组名: [图片路径列表]} # 组名可以按日期+地点拼接,例如 "20241001_植物园"这里最关键的是随机种子固定,方便复现。另一个容易被忽略的点是:如果某个冷门类别只有 30 张,按 8:1:1 划分后验证集只有 3 张,指标波动会非常大。这种情况我一般把该类别的最小验证样本量卡到 10 张,不足的话训练集和验证集适当交叉,或干脆对这个类别不做 test 划分。
4.3 标签合法性自检:越界、缺文件与空标签
标注完、划分完,训练之前一定要跑一遍标签自检。常见问题包括:标签文件里类别 ID 大于 nc-1、坐标出现负数或大于 1、w 或 h 为 0、图片存在但标签缺失、标签文件是空的。
from pathlib import Path def check_dataset(label_root, num_classes=5): problems = [] for label_file in label_root.rglob("*.txt"): lines = label_file.read_text().strip().splitlines() if not lines: problems.append((str(label_file), "空标签")) continue for line in lines: parts = line.strip().split() if len(parts) != 5: problems.append((str(label_file), "字段数不为5")) continue cls = int(parts[0]) x, y, w, h = map(float, parts[1:]) if cls >= num_classes: problems.append((str(label_file), f"类别ID越界: {cls}")) if not (0 <= x <= 1 and 0 <= y <= 1 and 0 < w <= 1 and 0 < h <= 1): problems.append((str(label_file), f"坐标非法: {line}")) return problems for p, reason in check_dataset(Path("flower_dataset/labels"), num_classes=5): print(f"{p}: {reason}")num_classes必须和 dataset.yaml 里的nc一致。跑完自检后,有问题的文件要么修要么移除,不要带着问题进训练。YOLO 对非法标签的处理是「静默跳过」,这意味着你可能训了 100 个 epoch 才发现某类图片实际上一张都没参与训练,浪费一整个晚上的算力。
4.4 先训一个最小的模型,把数据集跑通
数据集做完,先不要上大模型。用最小的预训练权重(比如 YOLOv8n)先训 30 个 epoch,目的不是出成绩,而是验证「数据链路通不通」:图片能不能读、标签能不能解析、loss 能不能正常下降。
yolo detect train data=flower_dataset/dataset.yaml model=yolov8n.pt epochs=30 imgsz=640 batch=16imgsz=640是训练输入尺寸,算力紧张时可以降到 512,但对于花这种小目标多的场景,降分辨率会丢掉细节,不建议低于 512。batch=16根据显存调整,显存不够就降到 8。我看到很多新手用 3080 以上的卡也会把 batch 拉到 64,然后训练时显存溢出、中断,其实 batch 16 在这个阶段足够稳定地反映 loss 趋势。
跑完后看两个东西。第一是 loss 曲线:训练 loss 前 10 个 epoch 应该持续下降,val loss 可能会震荡但整体趋势向下。如果训练 loss 从第一个 epoch 开始就横在 3 左右不动,多半是标签和图片对不上,或者 dataset.yaml 里路径写错。第二是可视化预测结果:随便抽几张 val 图片看预测框,如果框的位置明显偏离花的中心,说明标签格式转换时坐标出了问题,而不是模型训练不到位。
这套最小模型跑通后,再决定是直接调大模型训练,还是先做预打标扩样本。大部分情况下,你会发现样本量不够,需要回到标注环节再补一批。
5. 鲜花分类数据集训练避坑:现象、原因与解决办法
5.1 类别严重不平衡,小类永远学不会
现象:训练结束后,大类(比如玫瑰)的 mAP50 到了 0.9,小类(比如蒲公英)只有 0.3,推理时蒲公英几乎不输出框。
原因:样本量差距大,模型在反向传播时,大类贡献的 loss 占绝对主导,小类的梯度被淹没。这不是学习率问题,是数据分布问题。
解决:先尝试对小类做过采样,也就是把蒲公英的样本复制几份进训练集。注意不要复制同一张图多次——模型会对这张图过拟合,正确做法是复制后配上不同的数据增强。如果过采样后小类还是起不来,给 loss 加类别权重,具体做法是把每个类的样本数倒数归一化后传给训练脚本。最有效的做法还是回去补拍,哪怕一个小类只补 100 张,效果也比调参强。
5.2 数据泄漏:同一朵花出现在训练集和验证集
现象:验证集准确率很高,但拿到真实场景一测就崩,检测框乱飞,置信度普遍低于 0.4。
原因:同一朵花的不同角度、不同光线下的照片,被随机划分拆进了训练集和验证集。模型在训练时已经「背过」验证集里的目标,验证指标失真。
解决:按拍摄批次分组划分,同一时间、同一地点的一组照片只能整体进入 train / val / test 的一个集合。分组信息可以在采集时就写入文件名,比如20241001_植物园_玫瑰_001.jpg这种格式,此后做任何划分都基于这个前缀。我见过某开发者在这上面栽过跟头,重划分后同一套模型真实场景的表现涨了十几个点。
5.3 标注框把整株植物都框了进去
现象:训练 loss 正常下降,但 val 的 PR 曲线异常,有些类别的框明显偏大,预测框往往比花大两三倍。
原因:标注员为了省事,把花连同茎、叶、旁边的花苞一起框了。这样模型学到的正样本里包含大量绿色背景,特征被稀释。
解决:回到标注规范,框只包住花冠主体。这个问题的隐蔽性在于:如果所有标注框都偏大,模型也能收敛,它学的是「一坨花+叶」的整体模式,等部署时遇到只有花没有叶的场景,召回率骤降。我处理这种问题的方法是,抽查每个类别的标注框可视化,把框的尺寸分布拉出来看,如果某一类框的宽高比集中在 2.0 以上,说明大概率框进了茎叶。
5.4 网络图片混入训练集,loss 震荡不止
现象:训练 loss 曲线上下跳动,滤波后也不平滑,前 5 个 epoch 尤其明显,且 val 指标一直上不去。
原因:从搜索引擎或公开数据集里抓的图,带有水印、压缩噪声、拼接式长图,甚至还有插画和真实照片混在一起。模型在这些人造特征上反复横跳,学不到稳定的花的特征。
解决:严格限定训练集来源,自己拍的优先;确实需要外部图补充类别多样性,至少过滤掉带水印的图,并用 4.1 的模糊脚本筛掉低质量样本。水印很影响花朵的局部纹理,模型很容易把「水印」当成「花瓣特征」的一部分,推理时见到任何带纹理的区域都会误报高置信度。
5.5 类别名称不统一,标注错乱直到训练结束才发现
现象:训练跑完,输出 confusion matrix 时发现「rose」和「月季」两个类互相误判率极高,或者某个类别名称在标注工具里叫简称、在 dataset.yaml 里叫全称,同一物种被拆成两个类。
原因:多人协作标注时没有统一的词汇表。一个人标「rose」,另一个人标「rosa」,模型被逼着把同一个物种学成两个类。
解决:动笔标注前,先建一张类别定义表,指定每个类别在数据集里的英文 ID 和中文备选名,标注工具里只用 ID 或固定的英文名,不允许标注员自由发挥。这张表还要包含「常见混淆项处理规则」,比如「单瓣月季一律归入 rose,即使它的商品名叫玫瑰」。类别定义是数据集的宪法,改它的成本远高于重标几百张图。
6. 让数据集越用越强:预打标、主动补样与验收习惯
6.1 用已有模型做预打标,把标注成本压到最低
整套数据做完且首版模型训好之后,你会有大量未标注的新图片。常见做法是拿当前最好的权重去做批量推理,把预测结果转成 YOLO 标签,再让标注员只做「确认和微调」而不是从零画框。
yolo detect predict model=runs/detect/train/weights/best.pt source=unlabeled_images save_txt=True conf=0.3save_txt=True会为每张图输出一个同名 txt 标签,conf=0.3控制置信度阈值。低于这个阈值的检测不会生成标签。注意这里有个权衡:置信度阈值调得过低,伪标签里全是噪声框,人工复核反而更费劲;调得过高,漏检的目标需要标注员自己补充,效率提升有限。
我的习惯是先跑几个阈值,比如 0.3 和 0.5,对比伪标签的数量,选一个人工修改量最小的组合。预打标能把单张标注时间从 30 秒压到 5 秒左右,但它引入了「标注员默认模型是对的」的心理惯性,所以漏检比误检更危险——模型没看到的目标,标注员很容易跟着没看到。复核前要明确提醒:格外注意没出框的区域。
6.2 让模型告诉你该补哪些样本
模型训练完后,除了看指标,拿它去跑一遍未标注的候选图片,把「低置信度样本」和「误检样本」挑出来,这些就是模型最困惑的数据。你给这些困难样本做标注并补充进训练集,比随机补拍一批场景图的效果好得多。
具体做法是:对候选图片批量推理,输出每张图最高置信度的类别和分数,把所有最高置信度低于 0.5 的图片汇总成一份清单。有两种情况。一种是无检测框,说明模型没认出来,这些图往往是新场景或新角度。另一种是有框但置信度只有 0.2 到 0.4,说明模型觉得「像花但不像任何一种已知花」,这些图往往对应类间相似的品种。把这两类都补标进去,训练迭代的效率立刻就能看出来。
补数据的原则是一次补 200 到 500 张,训一版,看指标变化,而不是攒了 2000 张才训一次。花的类别差异大,可能补 200 张困难样本,某个小类的召回率就从 0.5 跳到 0.8。
6.3 每次训练完固定做一次可视化工序
训练不是看 loss 降了就收工。我现在养成的习惯是,每一版模型跑完,必做三件事:第一,从验证集每个类别里随机抽 30 张图做推理可视化,打印成网格图,肉眼扫一遍检测框的位置和置信度,这能揪出标签错乱这类指标看不出来的问题。第二,看混淆矩阵,尤其关注相似品种间的误判率——月季和玫瑰如果混淆率超过 5%,回去查类别定义而不是调模型。第三,用验证集扫描一遍置信度阈值,画出 PR 曲线,给业务方一个明确的阈值建议,而不是让他们默认用 0.25。这三个习惯看着笨重,但每一条都帮我省过重新标注的力气——尤其是可视化抽查,它是最便宜的数据质量审计方式。
如果你也在做类似方向,建议每次都把固定随机种子、训练命令、最终可视化结果存档。等三个月后数据集扩到一万张,这些记录就是你判断「这次改动到底有没有变好」的唯一依据。希望帮到你。
本文还有配套的精品资源,点击获取