
简介这份面向深度学习物体检测任务的香蕉数据集包含三千张带标注图片同时提供YOLO与VOC两种通用标注格式适合计算机视觉学习者、算法工程师及科研人员训练香蕉识别模型或验证检测算法。压缩包共两千个文件主要包含PNG图像、TXT标签和XML标注文件整体约七百九十四兆字节其中TXT对应YOLO格式的边界框信息XML遵循PASCAL视觉目标类别挑战标准可直接衔接主流检测框架已有一千四百人学习下载。数据集中每个香蕉目标均以边界框精确框出无需额外整理即可开始训练兼容YOLO系列与Faster R-CNN等模型便于对比不同算法效果也可配合随机旋转、缩放等数据增强手段提升模型泛化能力是入门目标检测或专项调优非常实用的素材之一。1. 香蕉数据集这件事3000张图能省掉两周标注时间做目标检测的都知道模型训练本身不值钱值钱的是数据。香蕉数据集是这么一套资源3000张带标注的香蕉图片VOC和YOLO两种格式同时打包解压就能直接喂给YOLO训练省掉的是采集图像和手工标注这两件最耗时间的事。适合两类人刚接触深度学习目标检测、想拿真实数据练手的新手以及做农业视觉、果蔬分拣预研需要快速验证检测流程的工程师。我拿到这套数据后没有急着开训练而是先把两套标注核对了一遍这一步帮我避掉了后面好几个坑下面按这个顺序拆给你看。2. 双格式数据集拆解VOC和YOLO标注长什么样2.1 目录结构两套格式是怎么并存的很多数据集只给一种标注格式这套比较省心VOC和YOLO都给了。VOC格式面向Pascal VOC生态的老工具链YOLO格式面向Ultralytics YOLO这类直接训练的主流框架。两份并存的含义不是重复而是同一批图片的两种等价表达坐标体系完全不同。下载解压后典型的目录长这样banana_dataset/ ├── VOC/ │ ├── JPEGImages/ # 3000张jpg原图 │ ├── Annotations/ # 3000个同名xml标注 │ └── ImageSets/ │ └── Main/ # train.txt / val.txt / test.txt └── YOLO/ ├── images/ │ ├── train/ # 训练集图片 │ ├── val/ # 验证集图片 │ └── test/ # 测试集图片 └── labels/ ├── train/ # 与images/train一一对应的txt ├── val/ └── test/VOC这边靠文件名配对banana_0001.jpg对应banana_0001.xmlYOLO这边靠images和labels两个平行目录配对同样靠文件名。下载后第一件事是核对两边的文件名集合是否完全一致不一致的挑出来单独处理否则训练时会报label not found或者image not found这类报错看着像路径问题实际是文件缺失。文件级核对用一条命令就能完成把两个目录的文件名列出来做差集差集为空才继续往下走。2.2 两种标注的核心差异绝对坐标对归一化坐标VOC的标注写在XML里用的是像素绝对坐标。打开任意一个XML核心内容长这样annotation folderJPEGImages/folder filenamebanana_0001.jpg/filename size width640/width height480/height depth3/depth /size object namebanana/name bndbox xmin120/xmin ymin85/ymin xmax420/xmax ymax310/ymax /bndbox /object /annotationsize字段里的宽高是关键后面所有归一化计算都以它为准。如果XML里写的宽高和实际图片尺寸对不上转换出来的YOLO标注全是偏的而且这种错非常隐蔽训练时loss照样下降预测框就是整体偏移。YOLO的txt每行一个目标格式是类别id加归一化后的中心点x、中心点y、宽、高0 0.421875 0.411458 0.468750 0.468750拿上面的XML框来算x中心(120420)/2/6400.421875y中心(85310)/2/480约等于0.411458宽(420-120)/6400.46875高(310-85)/4800.46875。YOLO格式完全丢掉了图片尺寸信息所有数值都在0到1之间好处是换分辨率不用改标注坏处是一旦除以的尺寸搞错整份标注集体作废。两种格式的差异汇总成一张表维度VOC(XML)YOLO(TXT)坐标形式像素绝对坐标归一化相对坐标目标表示xmin, ymin, xmax, ymaxx_center, y_center, width, height是否依赖图片尺寸依赖size字段不依赖多目标写法多个object元素每行一个目标2.3 单类别检测的数据特点与适用场景这份数据集标注的是单一类别banana这是它最朴素的定位类别少、目标形态相对固定、背景不算太杂乱。3000张的体量对单类别物体检测任务来说够用YOLOv8n在单卡上跑60个epoch能看到比较收敛的曲线。我一般把它当三类用途验证YOLO训练环境是否配通、测试自己写的标注转换工具是否正确、以及对比不同检测模型在单类别场景的基线精度。判断这份数据值不值得直接下我有个快速办法打开Annotations目录随机挑10个XML如果超过2个存在框明显偏大、漏标或类别名混乱说明原始标注质量一般需要清洗后才能用如果10个都干净直接进训练流程。需要注意的边界是如果后续要做多类别复杂场景比如香蕉和苹果、橙子一起检这份数据只能当底料类别和场景都得自己补。另外下载后先看一眼name字段有的版本会把ripe、unripe分开标注如果实际是两到三个类CLASSES映射就按你看到的真实字段来别按单类直接写死。3. 自己写转换脚本用代码核对两套标注的一致性3.1 批量转换XML到TXT的完整脚本数据集虽然自带YOLO格式我仍然建议自己跑一遍转换目的不是多此一举而是核对。所谓双格式数据集很多是脚本半自动生成的个别文件漏转、转错的情况并不少见。拿VOC原始XML重新生成一份YOLO标注和数据集自带的labels逐文件对比能直接暴露问题。import xml.etree.ElementTree as ET from pathlib import Path CLASSES [banana] # 以XML里实际出现的name为准顺序就是类别id def voc_xml_to_yolo(xml_path: Path, out_txt: Path) - int: tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASSES: print(f[跳过] {xml_path.name} 出现未定义类别: {name}) continue cls_id CLASSES.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 归一化坐标 x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h # 裁剪到[0,1]防止个别越界标注让训练崩掉 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) out_txt.parent.mkdir(parentsTrue, exist_okTrue) out_txt.write_text(\n.join(lines) \n) return len(lines) xml_dir Path(VOC/Annotations) out_dir Path(YOLO/labels) for xml_file in sorted(xml_dir.glob(*.xml)): voc_xml_to_yolo(xml_file, out_dir / (xml_file.stem .txt))脚本逻辑不复杂先拿XML里size的宽高作为分母再遍历每个object元素取bndbox四个值换算成归一化的中心点加宽高最后拼成一行写入txt。几个参数值得说明CLASSES的顺序就是类别id映射定下来以后不要改动训练用的names.yaml必须和它保持一字不差否则全体错位格式化用的.6f保留六位小数对YOLO训练足够不需要更多精度。转换完不能直接开训先做一轮标签体检检查每个txt的行数、每行字段数、类别id范围from pathlib import Path label_dir Path(YOLO/labels) bad [] for txt in sorted(label_dir.glob(*.txt)): lines txt.read_text().strip().splitlines() if not lines: bad.append((txt.name, 空文件)) continue for line in lines: parts line.split() if len(parts) ! 5: bad.append((txt.name, f字段数不对: {line})) break if not (0 int(parts[0]) len(CLASSES)): bad.append((txt.name, f类别id越界: {parts[0]})) break if bad: print(体检未通过前10个问题文件:) for name, reason in bad[:10]: print(name, reason) else: print(f体检通过: {len(list(label_dir.glob(*.txt)))} 个标签文件全部合法)这段体检脚本成本不到一分钟每次换数据集我都跑一遍挡住过好几次训练到一半突然报IndexError的尴尬。体检脚本输出的问题文件清单就是后续修数据的入口比训练时被报错打断再回头定位高效得多。3.2 归一化坐标的边界为什么要裁剪刚才脚本里做了clip到[0,1]的处理有同行问过会不会把本来错的标注洗成对的。我的习惯是裁剪是为了训练不崩但裁剪完要统计被裁剪的比例超过1%就要回头查原始XML。常见的原因是标注工具导出时框稍微出了图像边缘这类轻微越界裁剪掉完全没问题如果大面积越界说明XML的size字段和真实图片尺寸不一致问题出在源头而不是数值上。还有一种情况是目标太贴近边缘中心点坐标接近0或1裁剪后变成0.000000看起来合法但实际语义丢了。我一般会单独挑出这类样本看一眼确认是真实遮挡还是标注画偏标注画偏的直接修XML再重新转换不修会让模型学到错误的边缘分布。统计被裁剪比例很简单转换函数里把clip前后不一致的样本单独记到一个列表最后print出数量一眼就能判断是偶发还是系统性问题。3.3 重新划分数据集固定随机种子下载下来的train/val/test划分不一定符合你的需求比如test只有几十张或者你想按自己的比例重新划分。重划分脚本很短但有一个细节不能省——随机种子import random from pathlib import Path random.seed(42) imgs sorted(Path(VOC/JPEGImages).glob(*.jpg)) random.shuffle(imgs) n len(imgs) n_train int(n * 0.8) n_val int(n * 0.1) train_imgs imgs[:n_train] val_imgs imgs[n_train:n_train n_val] test_imgs imgs[n_train n_val:] for split, items in [(train, train_imgs), (val, val_imgs), (test, test_imgs)]: Path(fsplit/{split}.txt).write_text(\n.join(p.stem for p in items) \n) print(split, len(items))seed固定为42保证每次运行得到完全相同的划分。这个细节直接影响实验可比性不固定seed两次训练用的数据分布不一样指标上的差异说不清是模型改出来的还是数据换出来的。划分完我还会顺手统计每个集合里的目标框数量确认训练集和验证集的目标数占比大致均匀避免某个集合恰好全是复杂场景导致验证指标失真。0.8/0.1/0.1的比例是单类别检测的常用起点如果你的验证集只有几十张说明标注分布有偏建议先看清楚再动手。4. 避坑清单这五个坑我几乎每次换数据集都会遇到4.1 坑一类别id顺序错位检测框全乱现象损失正常下降训练也顺利完成但预测时类别名对不上香蕉被标成别的名字或者个别框的类别id直接越界。原因YOLO标签里存的是数字id它对应names.yaml里的顺序。如果names.yaml写的是[banana]标签里却出现id1直接越界反过来如果命名文件和生成标签时用的CLASSES顺序不一致所有框都会整体错位到别的类别上。香蕉数据如果只有单类最容易翻车的是你把它和其他数据合并训练时两个数据集的类别id没有重新映射。解决训练前跑一遍第3章的体检脚本统计labels目录里出现的最大id确认它不超过len(names)-1再抽查三五个txt手动算一下中心点坐标是否落在对应图片的目标区域里。这一步同时验证了归一化尺寸有没有用错一举两得。4.2 坑二路径写死换台机器就崩现象在自己电脑上训练一切正常把数据拷到服务器训练直接报No such file or directory但文件名明明都在。原因数据集的yaml配置或某些脚本里写的是发布者的绝对路径比如/home/xxx/banana_dataset/images/train换环境后前缀失效。我还在一些数据集里见过XML的folder字段写了绝对路径YOLO训练不打紧但如果后续要转成其他格式这个字段会把工具搞蒙。解决拿到数据集第一件事把yaml和脚本里的绝对路径全部改成相对路径。检查手段是全局搜索绝对值前缀一条命令把问题行全揪出来grep -rn /home/\|C: banana_dataset/*.yaml banana_dataset/**/*.py搜出来的行逐条改成相对路径。banana.yaml里path用./banana_datasettrain和val写相对path的路径这样整份数据集拷到任何机器都能直接跑不用再改配置。4.3 坑三图片尺寸差异大直接resize把目标拉变形现象loss曲线很漂亮但验证集mAP一直上不去小目标几乎全漏召回率明显低于预期。原因数据集的图片来源杂手机竖拍、横拍、截图都有长宽比从3:4到16:9都有。如果训练时不处理直接强制缩放到640x640几何形状被粗暴拉伸香蕉的弯形特征全部变形模型学到的是一堆畸变图推理时自然框不准。解决保持Ultralytics默认的letterbox逻辑用灰边补齐到统一尺寸不要关。判断自己的数据到底吃不吃这个亏先统计一下全部图片的尺寸分布如果只有一两种尺寸就无所谓如果长宽比超过三四种开训练前可以专门拿变形前后的对比图看一眼确认letterbox在正常工作。真要提速就开rect模式让batch内按各自尺寸处理但先抽几张极端长宽比的图确认不会出问题。4.4 坑四mosaic增强把目标切没了现象训练初期mAP涨得很快后段卡住不动小目标场景的检测效果比预期差一截。原因YOLOv8默认开mosaic增强四张图拼一张。香蕉数据集目标占比忽大忽小拼接时原本就小的目标被进一步缩小甚至裁出画布模型被迫学了一堆半个香蕉指甲盖大的香蕉对正常尺寸的反而迟钝。解决调低mosaic概率并在训练最后阶段关闭。命令里加mosaic0.5 close_mosaic10含义是全程50%概率拼接最后10个epoch彻底关闭让模型用真实分布做微调。这个改动在目标偏小的数据集上通常能让mAP50涨一到三个点代价几乎为零。如果你观察到loss后段来回震荡不收敛优先怀疑mosaic而不是学习率。4.5 坑五验证集和训练集泄题现象训练时mAP50到了0.95你觉得模型很强拿自己随手拍的香蕉一试效果明显拉胯。原因数据划分没按场景去重。香蕉数据集的图不少是从视频抽帧来的同一串香蕉的连续帧几乎一样如果这些帧一部分进train一部分进val验证集考的是记忆而不是泛化指标自然虚高。解决按视频片段或拍摄批次划分不纯随机。先看文件名规律banana_0001、banana_0002这种连续编号大概率来自同一段视频划分时按文件名前缀分组保证同一组的图片只进一个集合。用第3章的划分脚本时把随机shuffle改成按前缀分组代价是多写几行字典分组代码换来的是验证指标的含金量。5. 跑通一次验证训练命令、指标口径和一次推理5.1 训练命令与yaml配置banana.yaml只需要五件事数据集根路径、train目录、val目录、类别数、类别名。path: ./banana_dataset train: images/train val: images/val nc: 1 names: [banana]启动训练一条命令yolo detect train databanana.yaml modelyolov8n.pt epochs60 imgsz640 batch16 mosaic0.5 close_mosaic10model用yolov8n.pt做迁移学习比随机初始化收敛快得多mosaic和close_mosaic对应第4章的增强策略batch16是8G显存的安全值显存更大可以提到32加快收敛速度。5.2 指标口径训练完打开runs/detect/train/results.csv看mAP50、mAP50-95、precision。mAP50是IoU阈值0.5下的平均精度框大致位置对就算命中mAP50-95把0.5到0.95逐档求平均对定位精度要求苛刻得多。香蕉数据集标注干净的话mAP50到0.9以上不稀奇mAP50-95比它低0.1到0.2是正常现象。precision和recall要放在一起看precision高recall低说明模型宁缺毋滥漏检多反过来说明误检多两者差值大的时候优先查标签而不是调参。5.3 一次推理验证yolo detect predict modelruns/detect/train/weights/best.pt sourcetest_images/ conf0.5看可视化结果时重点挑三类图单根香蕉、一串香蕉互相遮挡、暗光环境。单根都框不准就回头查标签遮挡场景框重叠严重可以调NMS但别指望单类别数据能根治遮挡漏检。从那以后我每次拿到新数据集都强制走一遍这个流程先核对文件名和标签完整性再扫类别id范围最后确认划分没有泄题全套通过才开训练。这套检查不到十分钟帮我省掉的排查时间超过十倍。希望帮到你。本文还有配套的精品资源点击获取