简介:一套面向YOLO目标检测任务的高清番茄数据集及完整标注文件,适合计算机、电子信息、数学等专业学生在课程设计、期末大作业或毕业设计中直接使用。图片均经过人工重新打标,与站内已有番茄数据集不同,清晰度高,标注框准确,配合YOLO格式的txt文件即可快速进入模型训练阶段。资源包共303个文件,以150张jpg高清图像和150个对应的txt标注文件为主体,另含2个cache缓存文件和1个xml说明文件,整体体积387.26MB,目录结构简洁,便于按序读取与训练。目前已有325人学习下载,适合需要高质量标注数据或希望省去手动标注时间的开发者。作者为资深算法工程师,多年从事YOLO与计算机视觉仿真,数据标注思路清晰、注释明确,具备较强的实用性和参考价值。
1. YOLO目标检测+番茄高清数据集:标注完整,拆开就能上手跑训练
做目标检测课程设计或毕业设计,最烦的不是模型调参,而是数据集不能用。网上下到的图片资源常常没有标注文件,自己标又得花两三天;下到的标注数据集往往分辨率低、标签错位,跑出来的效果惨不忍睹。这份番茄高清标注数据集的卖点在于「已经标注且标签都是作者自己打的」,图片是高清JPG,检测目标(番茄)特征清晰,配合YOLO格式的txt标注文件,解压后就能直接进训练管线。适合计算机视觉方向课设、期末大作业和毕设的在校生,也适合做果蔬识别或农业视觉demo的工程师把它当迁移学习的起点。下面按实际拆包、验证、训练的顺序,把文件结构、yaml配置、标注检查、常见翻车点完整拆解一遍。
2. 先看清资源里有什么:从图片序号到cache文件的完整读法
2.1 图片与标注文件怎么对应
解压后你看到的是一组IMG_1335.JPG、IMG_1399.JPG、IMG_1394.JPG这类高清图片,加上train.cache、val.cache两个非图片文件。这里的核心资源是图片和它们对应的YOLO格式txt标注。YOLO标注文件每行5个字段,分别表示类别id、归一化后的中心x、中心y、宽、高。拿一张1920x1080的图举例,某个番茄框左上角在像素(400,300)、右下角在(700,600),换算出来的txt内容应该是这样的:
0 0.286458 0.416667 0.156250 0.277778后四个数字全是0到1区间的比例值,不是像素值。这样做的好处是标注与图像分辨率解耦,无论训练时imgsz设640还是1280,模型读到的是比例坐标,再按当前尺寸换算。文件名必须严格同名,IMG_1335.JPG对应的标签文件必须是IMG_1335.txt,多一个空格、大小写不一致都不行。拿到包后我建议先统计一下图片数量和txt数量是否对得上,这是验证资源完整度最快的办法。如果有图片没标签,训练时那张图会被自动当作无目标样本,不仅浪费数据,还会在验证时拉低recall。
2.2 train.cache 和 val.cache 是运行时缓存,不是标注数据
很多刚接触YOLO的同学会误以为train.cache里存着什么特殊标注信息,甚至有人把它当训练数据读进来。实际上,cache文件是Ultralytics YOLO首次运行数据加载器时生成的扫描缓存,记录的是「图片路径、尺寸、文件是否完整」这类元信息。它的作用是让第二次启动训练时省掉全量I/O扫描,直接加载缓存,缩短启动时间。
这个文件对模型精度没有任何影响。它的体积通常只有几十到几百KB,取决于图片数量。删除后下次训练会重新扫描并生成。有一个场景值得注意:压缩包里的cache文件如果是作者机器上打包的,路径和你解压后的路径对不上,训练启动时会触发校验失败并自动重建。这是正常现象,不用处理,也不要为了保留cache强行去对齐路径。
2.3 训练前把目录结构调整成YOLO约定结构
Ultralytics YOLO对数据集目录结构的约定是images/train/、images/val/和labels/train/、labels/val/四件套。拿到压缩包后,我一般会整理成下面这个结构:
tomato_dataset/ ├── images/ │ ├── train/ │ │ ├── IMG_1335.JPG │ │ ├── IMG_1399.JPG │ │ └── ... │ └── val/ │ ├── IMG_1381.JPG │ └── ... ├── labels/ │ ├── train/ │ │ ├── IMG_1335.txt │ │ ├── IMG_1399.txt │ │ └── ... │ └── val/ │ ├── IMG_1381.txt │ └── ... └── tomato.yaml图片后缀是大写.JPG没有关系,读取时大小写不敏感。真正要紧的是同名图片和同名txt必须待在同一个子集里,IMG_1335.JPG在images/train/,那IMG_1335.txt就得在labels/train/。出现图片在train、标签在val的情况,训练时不影响启动,但评估时模型会在这张图上失去监督信号。
train和val的划分比例,数据量不大时我一般用8:2。如果总量很少,比如不到50张,建议少留一点val,保证训练样本够用。另一种方案是干脆先不划分,用带交叉验证的Ultralytics模式整体跑,等确认数据没问题再分最终版本。
2.4 压缩包没分好train/val,用脚本一步拆开
不少资源站打包时会把所有图片平铺在一个目录里,标签放在另一个目录。这不是数据集有问题,只是没按训练约定组织。这时候不需要手动一个个拖文件,写个短脚本按比例拆分即可。下面是我常用的拆分脚本,图片和同名txt一起拆:
import os import random import shutil img_src = "tomato_images" # 原始图片目录 lbl_src = "tomato_labels" # 原始标签目录 out_root = "tomato_dataset" val_ratio = 0.2 # 验证集比例 random.seed(42) img_list = [f for f in os.listdir(img_src) if f.lower().endswith(".jpg")] random.shuffle(img_list) val_num = int(len(img_list) * val_ratio) val_set = set(img_list[:val_num]) train_set = set(img_list[val_num:]) for subset, names in [("train", train_set), ("val", val_set)]: img_out = os.path.join(out_root, "images", subset) lbl_out = os.path.join(out_root, "labels", subset) os.makedirs(img_out, exist_ok=True) os.makedirs(lbl_out, exist_ok=True) for name in names: shutil.copy(os.path.join(img_src, name), os.path.join(img_out, name)) lbl = os.path.splitext(name)[0] + ".txt" if os.path.exists(os.path.join(lbl_src, lbl)): shutil.copy(os.path.join(lbl_src, lbl), os.path.join(lbl_out, lbl))脚本里val_ratio控制验证集比例,改0.3就是7:3;random.seed(42)保证每次拆分结果一致,方便复现。跑完后建议加一行输出统计图片总数和缺失标签数,这是后面排查数据问题的第一手依据。我自己的习惯是拆完后数一遍txt,发现缺失就先补齐再进训练。
3. 把数据集跑进YOLO训练流程:写yaml与调参的关键几步
3.1 data.yaml 怎么配才不报错
目录就位后,训练入口就是数据集描述文件tomato.yaml。YAML文件里最容易出错的是path、train、val三者的相对位置,以及nc和names跟txt标签的class_id一致性。下面这个模板可以直接用:
path: D:/datasets/tomato_dataset # 数据集根目录,建议绝对路径 train: images/train val: images/val nc: 1 names: 0: tomato如果你的yaml文件放在tomato_dataset根目录下,path也可以写相对路径,但更稳妥的是写绝对路径,避免切换工作目录后出现train not found这类问题。nc和names必须和标注文件里的class_id对齐:只有一个类别时nc=1,class_id只能是0。如果txt里出现class_id=1,而yaml里只声明了1个类别,加载时就会直接报错。如果你后续想扩展成番茄和坏果两个类别,把nc改成2、names补成两个条目即可。
有一个排查习惯值得养成:别直接闷头训练,先跑一个epoch验证数据链路是否通:
yolo detect train model=yolov8n.pt data=tomato.yaml epochs=1终端输出里会出现类似「All 100 images found in train」的提示,确认图片数量正确后再停掉,进入正式训练。这一步能过滤掉一大半目录结构问题。
3.2 训练命令与关键参数怎么选
小数据量不建议直接从随机初始化开始训练,更不要第一轮就上yolov8x。下面这条命令是我针对这份番茄数据集常用的起步配置,用yolov8n做基线:
yolo detect train model=yolov8n.pt data=tomato.yaml epochs=100 imgsz=640 batch=16 lr0=0.001 device=0参数说明:
model=yolov8n.pt: 加载COCO预训练权重,即便类别不同,backbone上学习到的边缘和纹理特征对番茄这种小数据集也很有用,收敛速度远快于随机初始化。epochs=100: 这份数据量不大,通常40轮左右loss就进平台期,100轮够用。如果数据总量很小,再加早停回调,避免过拟合。imgsz=640: 默认尺寸。如果原始图是高清大图且小目标较多,可以试800或960,代价是显存占用和训练时间增加。batch=16: 24GB显存下很宽裕,8GB显存降成8。lr0=0.001: 基于预训练权重微调的安全学习率。如果是随机初始化,建议回到0.01。
显存不够时,Ultralytics支持在低batch下开启梯度累积变相扩大batch。核心是先把batch降到一个不OOM的值,比如4,再按显存余量逐步往上提,不要一上来就是32。
3.3 训练日志出现哪些信息说明要停下来
训练启动后终端会滚动输出每个epoch的box_loss、cls_loss、dfl_loss,以及precision、recall、mAP50。新手容易只看mAP50,忽略loss曲线。对于这份小样本数据集,我一般看三个信号。
第一,前10个epoch的box_loss应当稳步下降。如果前5轮不降反升,先停掉,检查学习率是不是偏大,以及标签框是否有大量越界的情况。第二,训练日志里输出的图片总数应当和你实际拆分的数量一致。第三,注意不同版本YOLO的参数差异。YOLOv8和YOLO11的命令格式基本一致,但如果你按老教程装的是YOLOv5环境,命令是另一套写法。
3.4 不同YOLO版本环境下命令的兼容写法
Ultralytics 8.x系列的YOLOv8和YOLO11都支持yolo detect train这种命令形式,只是内部模型结构有差异。如果你用的是YOLOv5,则命令格式不同,参数名也不一样:
python train.py --data tomato.yaml --weights yolov5s.pt --batch 16 --epochs 100 --img 640注意YOLOv5里图片尺寸参数是--img而不是--imgsz,权重参数是--weights而不是--model。两套命令混着用会直接报unknown argument。我遇到不少同学在同一个环境里同时装了yolov5和ultralytics的yolov8,两个包的入口互相覆盖,命令行怎么敲都报缺参数。遇到这种情况,用pip list确认当前环境实际装的是哪个包,或者干脆分两个conda虚拟环境独立管理,不要混装。这个坑处理不好,会浪费掉至少半天时间。
4. 标注质量验证:训练前把标签画回图上,避免盲训
4.1 用OpenCV把YOLO标签可视化
作者标注的质量再高,也要亲手验证过才放心。把自己变成数据集质检员,训练前把标签画回原图看一遍,这一步我认为是不可跳过的。下面的脚本读取images/train/下的图片和labels/train/下的同名txt,把框画回图上输出到visualized/:
import cv2 import os img_dir = "images/train" lbl_dir = "labels/train" out_dir = "visualized" os.makedirs(out_dir, exist_ok=True) class_names = {0: "tomato"} img_names = [f for f in os.listdir(img_dir) if f.lower().endswith(".jpg")] for img_name in sorted(img_names): img_path = os.path.join(img_dir, img_name) lbl_path = os.path.join(lbl_dir, os.path.splitext(img_name)[0] + ".txt") if not os.path.exists(lbl_path): print(f"[MISSING] {img_name} 没有对应txt") continue img = cv2.imread(img_path) if img is None: print(f"[FAILED] {img_name} 读取失败,可能是损坏图片") continue h, w = img.shape[:2] with open(lbl_path, "r") as f: lines = f.readlines() if len(lines) == 0: print(f"[EMPTY] {img_name} 的txt为空") for line in lines: parts = line.strip().split() cls = int(parts[0]) xc, yc, bw, bh = map(float, parts[1:5]) 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(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names.get(cls, "cls_" + str(cls)), (x1, max(0, y1 - 8)), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) cv2.imwrite(os.path.join(out_dir, img_name), img) print(f"processed {img_name}: {len(lines)} boxes")画完图后,快速翻一遍框的位置。重点关注两类:框明显没有贴住番茄边缘的,以及框太小或贴近图像边缘的。前者是标注质量问题,后者在letterbox缩放时容易被过滤,相当于白丢标签。这两个点比检查精度指标更直观。
4.2 class_id一致性检查,揪出隐藏的干扰类别
除了可视化,我还会顺手统计整个数据集所有txt里的class_id分布,确认没有混入未知类别。以下脚本遍历labels目录并统计每个类别出现的次数:
import os label_dir = "labels" stats = {} for subset in os.listdir(label_dir): sub_path = os.path.join(label_dir, subset) if not os.path.isdir(sub_path): continue for f in os.listdir(sub_path): if f.endswith(".txt"): with open(os.path.join(sub_path, f)) as fh: for line in fh: cls = line.strip().split()[0] stats[cls] = stats.get(cls, 0) + 1 print(stats)单类别数据集输出应该是{'0': N}这种形态。只要出现了1、2这类key,标注文件里就存在非0值的class_id。这种情况在别人打标的数据集里并不罕见,可能是框误标、可能是工具导出问题。如果不处理,训练时yaml声明1个类别、数据里读出类别1,轻则自动丢弃,重则直接报错。别让这种隐藏的类别变成你调试路上的黑匣子,先揪出来再说。
4.3 高清图带来的两个隐藏细节
这份资源主打高清,但高清本身会带来两个容易忽略的问题。第一,如果某些图片分辨率特别高,超过4000像素,Ultralytics读取时会先等比缩放再做letterbox。标签是按比例归一化的,缩放本身无损,需要担心的是贴近边缘的目标在letterbox填充后坐标仍有效,但在某些增强策略下,边缘框容易被裁切。第二,检查是否有过度曝光的图片。番茄是红色主体,如果某张图高光严重,番茄颜色已经发白,即使标注准确,网络也学不到有效纹理。我的做法是直接剔除这种图,数据少了不心疼,模型学得更干净。
5. 避坑记录:cache报错、路径中文、类别偏移的排查清单
这章内容来自我实际调试学生项目时遇到的真实问题,按「现象 → 原因 → 解决」排列,可以直接对照排查。
5.1 训练提示cache corrupt或反复重建
现象:启动训练时日志提示cache校验失败,每次启动都花几分钟重新扫描图片,训练进度反复被打断。
原因:压缩包里的train.cache是作者机器上生成的,路径记录和你本机不一致,Ultralytics校验后判定失效,自动重建。如果反复重建,说明images/目录下混入了非图片文件,比如Thumbs.db或隐藏的.DS_Store,每次扫描都在重复处理这些文件。
解决:直接删除train.cache和val.cache,让Ultralytics自己重新扫描生成。这不是你的错,也不是资源有问题,是跨机器迁移的必然结果。同时把images/目录下的隐藏文件清干净,避免每次启动都触发不必要的重扫。
5.2 可视化时画不出框,或大量报MISSING
现象:跑可视化脚本,输出一整片MISSING或EMPTY,连一张正常的框都没画出来。
原因:最常见的是labels目录路径不对,或图片与txt虽然同名但不在同一个子集划分下。另一个典型原因是用Excel或WPS打开过txt后另存,导致分隔符变成了制表符或行尾带了特殊字符,split后字段数不等于5。
解决:先确认labels目录下实际有几个txt,和图片数量对不对得上。然后抽查三张图,直接读txt文件,确认每行是空格分隔的5个数字。如果是因为编码或制表符导致的问题,用Python统一清洗一遍格式再进训练。
5.3 训练日志显示类别数比预期多一个
现象:yaml里明明声明了nc=1,训练日志却输出nc=2,而且训练后的模型分类结果错乱。
原因:数据集的txt里出现了非0的class_id,最常见的是作者打标时多标了一个误点框,或导出的标签里残留了其他类别的索引。yaml声明和真实数据不一致。
解决:先跑上面4.2的统计脚本,找出所有非0的class_id。数量少就手工修改txt,把误标的class_id改回0;数量多就写脚本批量重映射。改完删除cache,重新启动训练。
5.4 loss不降反升,越跑越离谱
现象:前10个epoch的box_loss和cls_loss一路上扬,或者在某个batch出现巨大的loss尖峰,mAP50始终归零。
原因:学习率过高的可能性最大。数据量小的时候,一个batch里如果凑到全是困难样本,loss也会产生尖刺。还有一个隐蔽原因是标签框大量越界,比如框的坐标计算错误导致宽高为0或负数。
解决:先把lr0压到0.0005试一轮,确认loss曲线是否恢复下降趋势。如果恢复,说明是学习率问题。如果仍然上升,检查txt里是否有宽高为0的异常框,用脚本筛查一遍,把这些坏标注剔除掉。不要一上来就换模型结构,先把数据和训练参数稳住。
5.5 Windows下路径或文件名带中文导致读取失败
现象:训练到中途报FileNotFoundError,或者某几张图片读出来是None,换一台机器跑同样的代码又正常。
原因:OpenCV的imread对中文路径兼容性不好。项目目录、数据集目录、Windows用户名目录里只要出现中文字符,就可能触发这个怪问题。
解决:整个工程和数据放到纯英文路径下,不要出现「数据集」「训练」这类中文目录名,连桌面路径都尽量避开中文用户名。这条看起来基础,但实际项目中至少一半的磁盘读取报错和它有关。顺手把文件名里的中文字符也改掉,换成英文或数字命名。
6. 进阶玩法:用小样本番茄数据集做迁移学习与效果验证
6.1 用预训练权重做迁移学习微调
数据量小的项目,最忌讳从零初始化的随机权重开始训练。从yolov8n.pt开始,通常几十个epoch就能得到可用的权重。如果显存有余量,可以换yolov8s或yolov8m做微调,它对小目标的框回归精度比n版好一些。核心是model参数换成对应预训练权重,其他参数保持稳定。
微调时还有一个技巧:前20轮把backbone冻结,只训练检测头,等loss降下来后再解冻整个网络做细调。Ultralytics支持在训练中配置冻结层数,但更简单的做法是先用小学习率跑一轮,确认loss在下降,再正常全量微调,避免一上来就破坏backbone在COCO上学到的特征。
6.2 用验证脚本确认mAP真实水平
训练日志里的mAP是参考,选型还要看验证集上的独立评测结果。下面这个命令对训练好的权重做验证:
yolo detect val data=tomato.yaml model=runs/detect/train/weights/best.pt输出表格里重点看四个值:precision、recall、mAP50和mAP50-95。结合这份番茄小数据集的实际情况,我给一个参考范围:
| 指标 | 含义 | 小样本合理范围 |
|---|---|---|
| precision | 检测出的框里真正目标的占比 | 0.85以上较好 |
| recall | 真实目标里被检测出的占比 | 0.8左右可接受 |
| mAP50 | IoU阈值为0.5时的平均精度 | 0.8以上较稳 |
| mAP50-95 | 多IoU阈值综合平均精度 | 数据小,0.5以上即可 |
如果precision高但recall低,说明模型偏保守,推理时置信度阈值往上调;反过来就降阈值。实际部署时用conf参数控制,我一般从0.4起步,再根据场景调整:
yolo detect predict model=tomato_best.pt source=tests/ imgsz=640 conf=0.4我自己有一个固定习惯:从那次被cache和中文路径双重折磨之后,每次拿到别人的标注数据集,第一件事永远是跑一遍可视化脚本,把框亲手画出来翻一遍再进训练管线。这个流程多花二十分钟,但能省掉后面至少一整天的排错时间。尤其对这种个人打标的数据集,边界情况和误标很难完全避免,不要盲信描述里的「准确」两个字,亲手验证过的数据才谈得上可靠。希望帮到你。
本文还有配套的精品资源,点击获取