尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

葡萄图像数据集目标检测实战:YOLO格式转换与迁移学习要点

发布时间:2026/9/28 1:27:05

资讯中心
01
ARTICLE

葡萄图像数据集目标检测实战:YOLO格式转换与迁移学习要点

葡萄图像数据集目标检测实战:YOLO格式转换与迁移学习要点
简介面向葡萄生长阶段识别与目标检测任务的图像数据集包含v0.1和v0.2两个版本图片统一为1280×720分辨率分别提供1212张和2099张图像并配有3993个与6641个边界框标注。数据覆盖不同生长阶段可支撑深度学习模型训练、农业视觉算法验证及教学实验等多种场景。压缩包整体约314.1MB共2000个文件主要文件类型为jpg图像、txt标注文件与xml标注文件分别对应原图、归一化坐标标注及Pascal VOC格式标注便于直接接入YOLO、Pascal VOC等主流框架。目前已有180人学习下载适合计算机视觉初学者、农业智能化研究人员及算法工程师使用可作为葡萄长势监测、成熟度判断等任务的直接训练数据。1. 葡萄图像数据集拿它跑目标检测前先把这两个版本的差异搞清楚做农业视觉检测的人手头最缺的往往不是模型而是干净、带框、能直接喂给 YOLO 的数据。这套葡萄不同生长阶段的图像数据集包含 v0.1 和 v0.2 两个版本v0.1 有 1,212 张图、3,993 个边界框v0.2 有 2,099 张图、6,641 个边界框统一 1280x720 分辨率。换句话说它覆盖了从幼果、转色到成熟的不同发育阶段每张图平均 3 个以上的框密度适中不会出现一张图上几十个框挤在一起的情况。适合做葡萄检测模型的微调、农业场景的迁移学习或者拿来当作物图像数据集的基准测试。但注意文件是以 txt 清单形式给出的不是现成的图片文件夹第一步必须先把数据清单理顺否则直接当普通数据集用会翻车。2. 数据格式与目录结构txt 清单背后是标注信息的入口2.1 两个版本的文件组织逻辑拿到手是一串 UUID 命名的 .txt 文件每个文件名对应一个文本索引。常见做法是先写一个脚本把这些 txt 逐行读出来看里面是图片路径、标注信息还是两者的混合。我的经验是先不要猜直接跑一段探测代码把文件内容打印出来确认结构后再决定用什么方式解析。import glob txt_files sorted(glob.glob(./*.txt)) for tf in txt_files[:3]: with open(tf, r, encodingutf-8) as f: lines f.readlines() print(f文件: {tf}, 行数: {len(lines)}) for line in lines[:5]: print(line.strip())这段代码的作用是快速查看前三个文件的前五行内容。glob.glob(./*.txt)匹配当前目录下所有 txt 清单文件encodingutf-8是为了防止中文路径或注释乱码。如果行数差异很大比如有的文件几百行、有的就几行说明清单分配不均匀后续需要合并处理如果每行都是图片路径加空格加坐标信息那就是标准的目标检测标注格式。2.2 理解边界框与图像尺寸的匹配关系数据集标注了边界框边界框数量与图片数量不相等——因为一张图上有多个葡萄簇。假设标注格式是class_id x_center y_center width heightYOLO 格式那么归一化坐标必须基于 1280x720 的尺寸。为什么强调这一点因为如果标注文件里存的是绝对像素值转 YOLO 格式时需要除以宽高如果是归一化值则直接可用。两个版本之间这个细节可能不一致——v0.1 和 v0.2 采集时间不同标注工具也可能不同常见做法是先统一成归一化坐标再进训练管线。在写转换脚本前我先做一步统计看一下每个类别的框数量分布。如果某个类别的框非常少训练时容易欠拟合需要加大该类别的 loss 权重或者做针对性增强。import numpy as np counts {} with open(label_example.txt, r) as f: for line in f: parts line.strip().split() cls int(parts[0]) counts[cls] counts.get(cls, 0) 1 print(counts.items()) for k, v in counts.items(): print(f类别 {k}: {v} 个框)这段代码统计每个类别出现的次数。line.strip().split()按空白字符切分第一个字段是类别 ID后面是坐标信息。如果这里发现类别 0 有 3000 个框、类别 1 只有 500 个框那就是严重的数据不平衡训练时要注意cls_loss加权。我一般会把类别分布贴到终端里作为后续训练参数的依据。2.3 图片资源与标注资源的对应关系1,212 张图对应 3,993 个框说明图片和框不是一一对应的存在一图多框。这一步要做的是把 txt 里的图片路径与本地图片文件做匹配确认没有缺失或多余文件。常见坑是 txt 清单里写了train/images/xxx.jpg但实际目录结构是images/train/xxx.jpg路径前缀不一致导致加载时报 FileNotFoundError。import os img_root ./images with open(train_list.txt, r) as f: img_paths [line.strip() for line in f if line.strip().endswith(.jpg)] missing [p for p in img_paths if not os.path.exists(p)] print(f缺失图片数量: {len(missing)}) if missing: print(missing[:10])这段代码检查图片是否存在。endswith(.jpg)过滤出图片路径os.path.exists(p)判断相对或绝对路径是否有效。如果缺失数量大于 5%就要检查是清单路径前缀错误还是某些图片在采集时损坏被剔除了。检查完毕后把 img_paths 以换行写入train.txt作为后续训练的图片索引文件。3. 从 txt 到 YOLO 训练管线格式转换与训练参数调优3.1 把标注统一成 YOLO 格式拿到数据后第一件事不是训练而是把标注统一成 YOLO 能吃的格式。YOLO 格式要求是class x_center y_center width height全部是相对坐标。即使原始数据已经是归一化的我也建议用 OpenCV 或 PIL 读图验证一遍读取框坐标画出来看看能不能贴合葡萄区域。这一步很重要很多数据集标注质量不高框偏大或偏小直接训练会影响 mAP。import cv2 image cv2.imread(sample.jpg) h, w image.shape[:2] with open(sample.txt, r) as f: for line in f: cls, xc, yc, bw, bh map(float, line.strip().split()) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(visualized.jpg, image)这段代码把归一化坐标还原成像素坐标。(xc - bw / 2) * w将中心坐标转换为左上角 x 值宽高乘以图像的宽和高得到实际像素宽高。画框后如果框没有紧贴葡萄簇要么是标注本身有偏移要么是坐标中心点定义不同——有的标注工具用左上角有的用中心点这一步能直接暴露格式问题。3.2 划分训练集与验证集并生成 data.yaml划分时要注意同一张图像可能出现在 txt 的不同清单中必须先去重再按 8:1:1 或 9:1 划分。由于葡萄生长阶段在 v0.1 和 v0.2 中可能重叠同一棵葡萄在不同天拍的照片如果两个版本要一起训练先把图片的哈希值算出来删除跨版本的相同图片防止数据泄露导致验证集虚高。import hashlib import random import os def file_hash(path): with open(path, rb) as f: return hashlib.md5(f.read()).hexdigest() imgs glob.glob(./all_images/*.jpg) hash_map {} unique_imgs [] for imp in imgs: h file_hash(imp) if h not in hash_map: hash_map[h] imp unique_imgs.append(imp) random.seed(42) random.shuffle(unique_imgs) n len(unique_imgs) train_imgs unique_imgs[:int(n * 0.8)] val_imgs unique_imgs[int(n * 0.8):] for split, lst in [(train, train_imgs), (val, val_imgs)]: with open(f{split}.txt, w) as f: f.writelines([p \n for p in lst])这段代码用 MD5 去重random.seed(42)固定随机数种子保证每次运行划分结果一致便于复现。int(n * 0.8)取 80% 作为训练集剩下的 20% 作为验证集。如果算力允许可以改成 90% 训练、10% 验证因为该数据集属于小规模数据集验证集太大反而让训练数据减少。3.3 在 YOLOv8 上训练参数选择与停止条件YOLOv8 是当前物体检测常用的模型在农业场景中尤其是果实检测这类目标不算极小、背景复杂的任务v8n 和 v8s 是首选。如果显存有限我一般用yolov8n.pt作为预训练权重。训练时最关键的三个参数是imgsz、batch和epochs。这个数据集的图片是 1280x720直接输入 640x640 会丢失部分细节如果尺寸设为 1280显存占用大幅升高。折中方式是设成 768 或 896缩放比例接近整数信息损失可控。yolo detect train datadata.yaml modelyolov8n.pt epochs150 imgsz768 batch16 device0epochs150是因为数据量只有两千多张150 轮足够模型收敛而不至于严重过拟合。batch16在 8GB 显存的 GPU 上跑 768 分辨率接近上限如果显存不足就降为 8。训练过程中重点看验证集 loss 曲线如果 val loss 在 80 轮后不再下降而 train loss 持续下降说明开始过拟合应当停止训练。另一个观察点是mAP50指标农业检测一般不追求 mAP50-95 特别高mAP50 达到 0.85 以上基本能满足实际需要。3.4 类间混淆与大目标尺度问题葡萄在不同生长阶段的形态差异对检测模型有显著影响。幼果期葡萄果粒小、颜色与叶子接近转色期颜色变红但叶片遮挡多成熟期果串大但密集量大。模型容易把转色期的葡萄误检为成熟期因为颜色特征存在连续性。解决方式有两种一是训练时开启 mosaic 增强和 mixup增强模型对遮挡和颜色变化的鲁棒性二是如果发现 val 集在某一阶段如幼果期的 mAP 明显低于其他阶段需要单独提取该阶段的图片复制后做 HSV 色彩增强再放进训练集人为平衡各阶段比例。4. 避坑指南五个常见问题与排查方法4.1 训练时 loss 为 NaN现象训练到第 3 个 epoch 左右训练 loss 突然变成 NaN终端输出大量 warning。原因最常见的是学习率设置过高尤其是在加载预训练权重后模型前期梯度更新过大另一个原因是标注文件中存在非法坐标比如负值或大于 1 的归一化坐标导致 loss 计算出错。解决先下调学习率从默认的 0.01 降到 0.002再用脚本扫描所有标注 txt检查是否有坐标值不在 [0,1] 区间内包含则删除对应标注行。bad_count 0 with open(all_labels.txt, r) as f: for line in f: parts line.strip().split() coords [float(x) for x in parts[1:]] if any(c 0 or c 1 for c in coords): bad_count 1 print(f异常标注行数: {bad_count})检查坐标范围的代码。parts[1:]跳过类别 ID只看坐标部分。if 条件判断任一坐标超出归一化区间则该行标注非法。这种行如果不清理轻则 mAP 下降重则训练崩溃。4.2 训练完 mAP 低但训练集表现好现象训练集 loss 很低mAP 接近 0.9但验证集只有 0.5 左右。原因数据划分时没有把同一棵葡萄树、同一时间段的图片分开导致相似图片同时出现在训练集和验证集验证集失去真实性或者划分时随机种子未固定每次实验结果不可比。解决划分前先给每张图片打上拍摄时间戳或葡萄品种标签按时间分组保证同一天的图片都在同一集合中。如果 txt 中没有这些元数据可以按文件夹名称分组——比如名为2024-06-12的目录下所有图片全部进入训练集或验证集不允许拆分。4.3 多类别训练时某一类完全检测不到现象训练完成后用模型预测新图某一阶段比如幼果期的葡萄完全没有检出框。原因幼果期样本数量少且在数据集中占比远低于成熟期训练时模型倾向于学习样本量大的类别特征。另一个原因是幼果期的标注框过小YOLO 训练时下采样倍数导致小目标特征丢失。解决针对小目标可以开启 YOLOv8 的scale增强让训练时随机缩放图片模拟更小尺寸目标如果目标太小如 20x20 像素以下考虑使用 SAHI 切片推理把大图切成 512x512 的小块后分别推理再合并结果。4.4 图片路径包含中文导致加载失败现象报错UnicodeDecodeError或图片读取后为 None。原因数据清单中某些图片路径包含中文字符如“葡萄_成熟期_01.jpg”部分工具链在 Windows 环境下默认编码不一致。解决统一把图片重命名为纯英文加数字格式例如grape_01.jpg。写一个重命名脚本遍历所有图片文件用os.rename把旧路径映射成新路径同时更新标注文件中的文件名引用。import os img_paths glob.glob(./images/*.jpg) for idx, old_path in enumerate(img_paths): new_path f./images/img_{idx:06d}.jpg os.rename(old_path, new_path)img_{idx:06d}生成六位数的序号保证文件名长度一致且按字典序排列就是时间顺序。重命名后记得同步修改 txt 清单中的路径和标注文件中的对应字段。4.5 数据增强过度导致验证集失真现象训练时 mAP 稳定上升但实际部署到果园监控摄像头上效果比验证集差很多。原因训练时开了过强的 mosaic 和 copy-paste将多张图拼接、目标被复制到其他位置模型学到的是“拼接图”的特征而不是真实葡萄的生长特征。解决验证测试时把增强关闭确保验证集完全采用原始图片如果测试效果仍差需要减小增强概率把mosaic0.5、mixup0.2作为起点逐步调高而不是一上来就拉满。5. 迁移学习的正确姿势先用 v0.1 预训练再在 v0.2 上微调很多人拿到数据集后直接把两个版本合并训练其实这是一种浪费。v0.1 和 v0.2 是不同拍摄条件下采集的相当于两个域的图片。合并训练容易让模型同时面对两种光照和角度分布收敛变慢。更合理的方案是用 v0.1 训练一个基线模型然后冻结主干网络只在 v0.2 上微调检测头。这样 v0.1 学习到的通用特征葡萄轮廓、叶片纹理、果梗结构可以作为先验v0.2 的图片专门用于适应新域的光照和分辨率差异。微调的具体做法先用 v0.1 训练得到best_v1.pt然后把它作为预训练权重训练时在 YOLOv8 中设置freeze10冻结前 10 层。如果 v0.2 与 v0.1 的采集设备差异很大只微调 50 轮就够了如果差异小微调 30 轮即可。注意微调时的lr0要比从头训练低一个数量级——比如从头训练用lr00.01微调就用lr00.001这样模型不会在一个新域上快速覆盖掉已经学到的通用特征。验证迁移学习效果时我会在 v0.2 上同时跑两个实验一个是用yolov8n.pt从头训练另一个是用 v0.1 预训练权重微调。对比两者的 mAP50 和收敛速度。常见的结果是微调版本在 30 轮就达到从头训练 100 轮的效果且最终 mAP50 高 3% 到 5%。这个收益在数据量小的时候尤其明显——因为 v0.2 只有 2,099 张图单靠它的原始分布信息不足以学到足够深的特征。如果业务场景中图片数量长期无法扩充这个迁移学习方案几乎是用这套数据集的标准做法。调用微调的命令与普通训练差别不大只是权重路径换成 v0.1 的产物多一个冻结参数yolo detect train datadata_v2.yaml modelbest_v1.pt epochs50 imgsz768 batch16 freeze10 lr00.001freeze10表示冻结模型前 10 层——YOLOv8 的主干网络backbone主要集中在前几层负责提取边缘、纹理和颜色特征这些特征对葡萄本身依然有效不需要重新学习。lr00.001保证检测头在新数据上平滑适应不会出现 loss 振荡。微调完成后第一时间用验证集测试单张图看看在 v0.2 图像光照条件下是否有大量的误检框出现在叶片暗部。最后分享一个个人习惯每次训练结束后我都会从验证集里随机挑 20 张图把模型预测框和 GT 框画在一起按类别、目标尺寸分别统计漏检率。光看 mAP 是不知道模型在哪一类上翻车的——可能成熟期非常好幼果期一团糟。可视化这一步必须强制走一遍因为测试集里一张图的预测错误往往就对应着某个阈值的崩溃。从那以后我每次换数据集都会先在训练前把可视化脚本写好训练完直接出图而不是等推理阶段才发现模型对某个生长阶段完全没有反应。这套葡萄图像数据集的分辨率和边界框质量都比较理想转换脚本准备好后基本半小时内能跑通第一轮训练希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。