简介:面向深度学习初学者与目标检测开发者的西红柿检测项目,基于YOLO11算法和Python/PyTorch环境实现,覆盖数据标注、格式转换、模型训练与可视化识别全流程。压缩包共一千三百二十二个文件,大小约一百四十四兆,包含六百五十六张西红柿图像、三百二十六个YOLO格式文本标注、三百二十一个XML标注、三个Python脚本、三个权重文件、三个YAML配置,以及训练产生的结果表格与标签缓存等记录。目录结构清晰,便于按模块学习或二次开发。目前已有102人学习下载,适合入门目标检测完整流程。包内第一个脚本可将图片与XML标注转为YOLO格式并生成训练集、验证集和配置文件;第二个脚本用于启动模型训练;第三个脚本提供可视化界面,加载图片即可完成识别,既支持重新训练,也可直接调用现成权重快速体验。环境依赖在文本文件中说明,按提示自行配置PyTorch即可运行。
1. 用 yolo11 训练自己的西红柿检测模型:这份资源包里到底有什么
做目标检测的人都有过这种经历:网上找的公开数据集要么太大、要么跟自己的场景对不上,最后只能自己拍照片、自己标注。但当你真的拿一批西红柿图片开始跑 yolo11 时,才会发现坑远比想象的多——标签格式、数据集划分、训练参数、可视化界面,每一步都能卡住你半天。这份「yolo11 西红柿目标检测」资源包正好把这条路走完了:它包含已标注的西红柿数据集、划分脚本、训练脚本和 PyQt 可视化界面,从数据集转成 YOLO 格式到最终鼠标点几下完成识别,整条链路是通的。它适合两类人:一类是刚接触 yolo 系列、想拿一个小数据集把训练流程完整跑一遍的新手;另一类是自己手里有标注数据、想快速迁移到 yolo11 做验证的工程师。我拆完这份资源后,最直观的感受是:数据集不大,但流程完整,参数细节值得逐行看。
2. 从原始图片到 YOLO 格式:划分脚本与 data.yaml 的生成逻辑
2.1 先看资源包里真实存在的东西
解压这个 zip 之后,你会看到一批 JPG 图片和几个关键文件。很多人一上来就急着跑训练,结果被文件搞懵——这里先把资源包里的东西对照着讲清楚。
events.out.tfevents.1733742028.zzg.7648.0:TensorBoard 的事件文件。说明这个项目在训练时启用过 TensorBoard 回调,训练过程的 loss 曲线、mAP 曲线都记录在里面。labels.cache:跑过一次验证或训练后,YOLO 会把每张图片的标签路径和哈希缓存起来,下次加载时不用重新解析所有标注文件。如果你的图片或标注文件有变动,这个缓存会导致「图片和标签对不上」的诡异问题,后面避坑章会细说。results.csv:训练过程中每个 epoch 的指标记录,包括 train/loss、val/loss、mAP50、mAP50-95、precision、recall 等。这个东西比 TensorBoard 更直观,Excel 直接打开就能看曲线趋势。- 若干
*.jpg:就是西红柿的原始图片,文件名有重复的,是因为资源包作者把训练集中的样例图一起放进来了,不影响训练。
这个资源的实际训练集数量不大,属于「小数据集跑通流程」的典型规模。你下载后建议先做一件事:把所有图片统一重命名成纯数字或纯英文,不要带中文、不要带特殊字符。Windows 路径 + 中文 + YOLO 的组合经常出编码问题,这是血泪经验。
2.2 01 划分数据集.py 做了什么
作者给的说明是:运行python 01划分数据集.py,会把数据集转成 YOLO 格式的 txt,同时生成 train.txt、val.txt 和 data.yaml。这个脚本是整条流程的起点,也是最容易被跳过、但实际影响最大的一个环节。很多从 VOC 或自己标注工具导出的数据,原始标签是 XML 或 JSON,必须转成 YOLO 的 txt 格式才能喂给 yolo11。
YOLO 格式的标注 txt 每行代表一个目标,格式是:
class_id x_center y_center width height注意:这里的x_center y_center width height全部是归一化到 0~1 之间的相对坐标,用框的绝对像素值除以图片宽度和高度。这是最容易翻车的地方——如果你直接填了像素坐标,loss 会直接爆掉,训练出来的模型什么都检测不出来。
划分脚本的核心逻辑,我按常见实现思路给你还原一下:
import os import random from pathlib import Path from tqdm import tqdm def convert_to_yolo(txt_path, img_width, img_height): # 假设你的标注是从 LabelImg 导出的 XML 格式 # 这里只展示坐标转换的核心思想 boxes = [] for obj in objects: x_center = (xmin + xmax) / 2 / img_width y_center = (ymin + ymax) / 2 / img_height box_width = (xmax - xmin) / img_width box_height = (ymax - ymin) / img_height boxes.append(f"0 {x_center:.6f} {y_center:.6f} {box_width:.6f} {box_height:.6f}") return boxes def split_dataset(image_dir, train_ratio=0.8, seed=42): random.seed(seed) image_paths = list(Path(image_dir).glob("*.jpg")) # 支持 jpg/png/jpeg random.shuffle(image_paths) train_count = int(len(image_paths) * train_ratio) train_paths = image_paths[:train_count] val_paths = image_paths[train_count:] for split, paths in [("train", train_paths), ("val", val_paths)]: txt_file = f"{split}.txt" with open(txt_file, "w") as f: for img_path in paths: f.write(str(img_path.resolve()) + "\n")逻辑说明:脚本先是读取图片宽高,把框坐标做归一化转换;然后按比例切分训练集和验证集,切分时固定了随机种子(seed=42),保证每次划分结果一致——这一点非常关键,否则你每次跑训练用的数据分布都不一样,对比实验就没意义了。
参数说明:train_ratio=0.8是个合理的默认值,对西红柿这种类别单一、目标分布相对均匀的小数据集来说够用。如果你的数据量很少(比如只有一两百张),建议把比例调到 0.9,让训练集更充分;如果后续要加数据增强,0.8 就合适。seed一定要写死,不要省。
2.3 data.yaml:训练配置的「黑匣子」打开给你看
划分脚本还会自动生成 data.yaml,内容是:
path: ./ # 数据集根目录,相对路径 train: train.txt # 训练集图片路径列表 val: val.txt # 验证集图片路径列表 nc: 1 # 类别数,西红柿只有一类 names: ['tomato'] # 类别名称列表这里有三个容易踩坑的点。第一个:path字段用相对路径时,YOLO 会把它当作相对于当前工作目录的路径。你如果在别的目录下运行训练命令,就会报dataset not found。我一般习惯path直接写绝对路径,一劳永逸。第二个:train.txt里存放的是图片的完整路径,如果图片移动过位置,txt 里的路径就失效了。第三个:nc是 1 没问题,但如果你后面想加类别(比如还检测茄子和辣椒),这里就要改成 3,同时names要对应加上——很多人忘了改这里,训练出来的模型类别索引全乱了,推理时框看到的类别名是错的。
3. 环境配置与 requirement.txt:PyTorch 版本、CUDA 与依赖项对齐
3.1 这份资源跑起来需要什么环境
作者在摘要里说得很明确:代码基于 Python + PyTorch 环境,requirement.txt在压缩包里,环境需要自己配置。这意味着你下载之后还要花 20~30 分钟装环境,但这是所有深度学习项目的常态,不要指望解压即用。
深度学习环境的本质是:CUDA → PyTorch → torchvision → ultralytics(YOLO 的官方库)。这个依赖链条里任何一个版本不对,都会以各种奇怪的方式报错。最常见的崩溃组合是 PyTorch 编译时用的 CUDA 版本和显卡驱动不匹配,出现CUDA error: no kernel image is available这种提示。
一个常规的 requirement.txt 内容大概是:
torch>=2.0.0 torchvision>=0.15.0 ultralytics>=8.0.0 pyside6 numpy pillow opencv-python matplotlib tensorboard pandas tqdm这里重点说两个:ultralytics是必须的,因为 yolo11 的模型定义、训练逻辑全在这个库里,版本太低会没有yolo11这个模型入口;pyside6是 03pyqt.py 的界面依赖,如果只想跑训练不跑界面,这个可以不装,节省不少时间。
3.2 一个规避版本纠结的安装路径
如何避免在 PyTorch 安装上反复折腾?这里给一条我的经验路径。先确认显卡驱动支持的 CUDA 版本,然后装对应版本的 PyTorch。如果你机器上没有 NVIDIA 显卡,那只能装 CPU 版,训练速度会慢到让你怀疑人生——我拿一个 1000 张的数据集在 CPU 上跑过 yolo11n,一个 epoch 差不多要 15 分钟,后来果断换 GPU。下面是安装命令的示例:
# 查看显卡驱动支持的 CUDA 最高版本 nvidia-smi # 以 CUDA 11.8 为例安装 PyTorch(按官网命令替换) pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装 ultralytics 和其余依赖 pip install ultralytics pyside6 numpy opencv-python # 验证安装,能正常输出版本即成功 python -c "import torch; print(torch.__version__)" python -c "import ultralytics; print(ultralytics.__version__)"参数说明:nvidia-smi输出里的 CUDA Version 是驱动支持的最高版本,不代表你要装这么高的 PyTorch,尽量选低一点的兼容版本更稳,比如驱动支持 12.4 就装 cu118 或 cu121 的 PyTorch。--index-url指定了 PyTorch 的官方源,比默认 PyPI 源版本更全,能直接选到带 CUDA 的版本。装好之后通过两个 print 语句验证,能输出版本号说明环境基本通了。
3.3 最常见的一个环境坑:requirements 装不进去
pip install -r requirement.txt装了一半报错,是每个项目群都会有人问的问题。报错分两种:一种是某个库编译失败(比如opencv-python在老版本 Python 上会出这类问题),解决方法是先升级 pip 再装;另一种是相互冲突(比如 2.0.0 的 torch 和 0.15.0 的 torchvision 不配套),这个看 torch 官网的版本匹配表手动指定。
我自己的习惯是:手边常备一个已装好 PyTorch 的环境镜像,不管是 Conda 还是 venv,遇到新项目只复制环境不改系统环境。因为这类项目每个的依赖版本都略有差异,你在 A 项目里pip install torch升级了版本,B 项目可能就出现 CUDA 相关玄学报错。环境隔离是深度学习项目的第一条纪律,没有之一。
4. 运行 02train.py 开始训练:epochs、batch、imgsz 与结果解读
4.1 从命令行到真正的训练执行
环境就绪、数据集划分完毕后,02train.py做的事情就是加载 yolo11 预训练权重,在自己的西红柿数据集上做微调(fine-tune)。这是整个资源里最耗时、最消耗耐心的一个步骤,但也是最核心的环节——模型能不能检测出西红柿,全看这里。
运行训练很简单:
python 02train.py脚本内部的核心逻辑,按常见写法展开是这样的:
from ultralytics import YOLO # 加载 yolo11n 预训练权重,首次运行会自动下载 model = YOLO('yolo11n.pt') # 开始训练 results = model.train( data='data.yaml', # 上一步生成的配置 epochs=100, # 总训练轮数 batch=16, # 每批图片数量 imgsz=640, # 输入图片分辨率 device=0, # GPU 索引,CPU 则填 'cpu' workers=4, # 数据加载线程数 patience=20, # 早停等待轮数 cache=True, # 缓存图片到内存 )逻辑说明:model = YOLO('yolo11n.pt')这行会加载 COCO 预训练权重,这是迁移学习的关键——你的西红柿数据量小,从头训练几乎不可能收敛,但有了 COCO 上学到的通用特征(纹理、边缘、颜色),只要少量数据就能快速适配到西红柿这个类上。model.train()里传递的是一系列训练超参,这些参数直接决定了训练速度、显存占用和最终精度。
参数说明:epochs=100对小数据集来说够用,一般训练到 50~70 轮时 loss 就趋于平稳了,多出来的轮次主要被早停机制拦截掉。batch=16在 8GB 显存以下的显卡上跑 yolo11n 是安全的;如果你的显卡是 4GB 显存,把 batch 降到 8,否则会直接CUDA out of memory。imgsz=640是 YOLO 系列的默认输入分辨率,西红柿目标在画面里往往占比较大,640 够用;如果你的图片里西红柿很小(远距离拍摄),可以改成 960 或 1280,但训练时间会翻倍。patience=20表示连续 20 轮验证集指标不提升就自动停止,这是省时间的利器。cache=True表示把图片一次性加载进内存,减少磁盘 I/O 等待,如果你的显存不紧张就开着,训练速度快一截。
4.2 results.csv 和 events.out.tfevents 怎么读
训练过程中,ultralytics 会在runs/detect/train/目录下持续写入结果。用 Excel 打开results.csv,你会发现每一列对应一个关键指标:train/box_loss是边框回归 loss,train/cls_loss是分类 loss,metrics/precision是精确率,metrics/recall是召回率,metrics/mAP50(B)是 IoU 阈值 0.5 时的平均精度,metrics/mAP50-95(B)是更严格的综合评价指标,也是论文里比较常用的一个。
怎么看这个表格?小数据集上不要追求 mAP50-95 特别高,那是数据量极为充足的场景下才可能做到的事。你要看的是两条趋势:第一,train 和 val 的 loss 是否都在同步下降——如果 train loss 降了但 val loss 不动甚至反升,说明模型过拟合了,数据量太小 + 训练轮数太多就会这样;第二,mAP50 是否稳定在某个值附近——西红柿这种形态规整的目标,mAP50 达到 0.9 以上基本就能用在生产场景了。
TensorBoard 的事件文件events.out.tfevents.*对应的是训练过程中的可视化日志,在项目根目录用命令启动查看:
tensorboard --logdir ./然后浏览器打开终端里输出的http://localhost:6006就能看到曲线。对新手来说,把每个 epoch 的指标存下来的results.csv其实比 TensorBoard 实用,因为不用启动服务,Excel 直接就能筛选排序。我和同事的日常习惯是:训练完先看 results.csv 里最后一个 epoch 的 mAP50 和 precision,再拉出损失曲线看有没有尖刺,两头一天三遍。
4.3 这篇文章里一定会有的一个坑:训练集和验证集数据分布不一致
前面01划分数据集.py里设置了seed=42,目的就是保证切分的随机性可控。但如果你的图片是连拍的——同一株西红柿从不同角度连续拍了几十张——随机切分后,训练集和验证集里可能会出现「同一颗果实的相似角度照片」,验证集指标虚高,相当于作弊。这一点在很多小数据集里普遍存在,并不是这个资源包的问题,但要提醒你留意数据采集时的多样性:不同光照、不同角度、不同成熟度,图片差异越大,验证集的参考价值越高。
5. 避坑与排查:西红柿小目标检测的五条踩坑记录
5.1 训练 loss 为 nan 或直接爆炸
现象:训练一开始,loss 直接变成nan,或者前几个 epoch loss 从几万开始往下掉,最后模型什么都检测不出来。
原因:最普遍的三个来源——一是标签 txt 里的坐标没有做归一化,绝对值直接进网络导致梯度爆炸;二是图片路径是中文或包含空格,数据集加载时图片读不到,标签却还在,数据对不齐;三是学习率设置过高,在 batch 很小的情况下直接冲出了合理的收敛区间。
解决:先用labels.cache对应的原始标签文件,抽样做一次datasets验证。用 Python 写一个快速脚本,读一张图片和对应 txt,把标注框画出来比对位置是否合理。归一化坐标时,框的 x_center 除以图片宽度、y_center 除以图片高度、宽高也都对应除,四点中漏一项模型就会在学习率稍高时立刻爆炸。学习率方面,模型默认是 0.01,如果小数据集上反复 NaN,改成 0.001 再试。
5.2 labels.cache 导致的「图片和标签对不上」
现象:你明明把一张新图片加到数据集目录里,也标注好了,但训练时这张图片像是被忽略了一样,验证结果也没有它的任何指标。
原因:ultralytics 第一次运行时会生成labels.cache文件,里面记录了图片路径和标签文件的哈希对应关系。你在那之后往数据集目录加了新文件,下次训练时 ultralytics 还会用旧缓存去匹配,导致新增数据没被纳入。
解决:出现这种「明明加了数据但训练没变化」的情况时,先删除根目录下的labels.cache文件再重新训练。删除后会自动重新扫描并重建缓存。从那以后,我每次改数据集后第一件事就是删 cache,已经形成一个不会忘的习惯。
5.3 密集遮挡场景下漏检严重
现象:单个西红柿在图片里很清晰,检测框也很准确;但一串西红柿堆叠在一起时,模型只能检出最外层的几个,被遮挡的漏掉了,而且置信度普遍走低。
原因:这是目标检测在密集小目标场景下的通病,本质上是训练数据里缺少密集遮挡的样本。yolo11 的 NMS(非极大值抑制)会在两个重叠度很高的候选框之间只保留置信度高的一个,如果模型没见过这种密集场景,它输出的候选框本身就不可靠。
解决:第一种方法是数据增强——用 Mosaic 增强,把多张图片拼接成一张,模拟密集分布的场景;yolo11 默认开启 Mosaic,但如果有数据清洗环节把这一步关了,要检查是否恢复。第二种是调 NMS 参数,在推理时把iou阈值从默认的 0.45 降到 0.3,允许更高度重叠的框保留下来。第三种是从数据端解决——单独把你认为最难检测的密集图片放到训练集里复制几份,相当于过采样,让模型多学几轮这些困难样本。
5.4 CPU 训练慢到无法忍受
现象:用 CPU 跑训练,一个 epoch 用了十分钟以上,整整一天下来几个 epoch 都没跑完,最终放弃。
原因:目标检测的矩阵运算量大,CPU 上做卷积运算本来就是逆水行舟,加上workers数据加载线程数设置太小,数据准备时间甚至超过了计算时间。
解决:不用换机器的情况下,有两招可以用。第一,把batch调到 4、imgsz降到 320,牺牲精度换训练速度,先跑通验证流程——注意只是验证流程,不是最终模型。第二,买或租一块云 GPU,现在很多云平台的入门级 GPU 按小时计费也不贵,把数据和代码传上去跑,比自己机器快了不止二十倍。记住一个原则:CPU 只配用来做最后的推理演示,不配用来做训练迭代。
5.5 PyQt 界面加载图片后检测卡死未响应
现象:运行03pyqt.py后,点击加载图片按钮选了一张大图,界面马上变白、卡住不动,标题栏显示「未响应」,过一会才恢复或者直接崩溃。
原因:这是典型的界面线程被耗时操作阻塞了。目标检测在 GPU 上推理一次也需要几十毫秒到几百毫秒,加上加载高清大图的解码耗时,UI 事件循环被阻塞,系统就会判定程序未响应。如果图片分辨率特别高(比如相机拍的原图 4000×3000),检测前的图像预处理更是雪上加霜。
解决:把图像加载和推理放到一个独立线程里执行,UI 主线程只负责接收操作事件。具体来说就是给按钮绑定一个槽函数,槽函数内部用QThread或threading.Thread包装推理逻辑,推理完成后通过信号机制把结果传回界面线程更新显示。另外,在加载图片后先做一次resize,把最长边缩到 1280 以内再传入检测网络,既保证检测效果又省显存。界面端还有一个优化点:加载图片后用QPixmap缩放显示,不要把原图直接贴到 label 上,省内存也减少刷新卡顿。
6. 从单张图片到实时视频流:自定义检测脚本与置信度阈值校准
以 03pyqt.py 的加载图片检测为起点,把它的内部逻辑抽象出来——实际上 ultralytics 的predict方法已经把大部分细节封装好了,你要关心的两个核心变量是conf(置信度阈值)和iou(NMS 阈值)。针对西红柿这种目标,我习惯把 conf 设在 0.25,iou 设在 0.45,这是 COCO 预训练模型的标准配置;但如果你实际应用场景里对漏检零容忍(比如分拣产线上不想漏掉任何一个坏果),把 conf 降下来到 0.1,代价就是一些误检框的出现,需要人工在界面里二次确认。
把单图检测扩展成摄像头视频流,核心代码只有几行:
from ultralytics import YOLO model = YOLO('runs/detect/train/weights/best.pt') # 摄像头实时检测,0 表示第一个摄像头 results = model.predict(source=0, show=True, conf=0.25, imgsz=640) # 视频文件检测,输出到本地 results = model.predict( source='demo.mp4', save=True, conf=0.25, imgsz=640, project='output', # 输出目录 name='video_result', # 输出子目录 )逻辑说明:model.predict一次调用完成从图片解码、预处理、推理、NMS、后处理到可视化的全部流程。source=0是摄像头输入,source='demo.mp4'是视频文件输入;show=True会弹出实时窗口,save=True会把检测结果保存为视频文件。这里用到的best.pt不是last.pt——训练结束后 ultralytics 会自动保存两个权重文件,best.pt是验证集上指标最好的那一轮的权重,last.pt是最后一轮的;永远用best.pt做推理,这是所有做过目标检测实战的人都会反复强调的一点,因为训练后期模型可能出现过拟合,最后一轮的权重已经不是最优解。
验证模型是否真正能用的一个有效技巧是:取训练时的那几张样例图片,先跑一次推理,然后逐一人工检查检测框相比真实标注的偏移量。如果框的中心点偏了超过 5%,说明模型对边界的拟合还不够充分;如果框的大小普遍偏大或偏小,可能是训练时 imgsz 和实际推理时不一致导致的——这个坑我踩得很深,训练时用的 640,到了推理时为了追求速度把输入调成了 320,结果大面积漏检。
另外还有一个被人忽略但很实用的校准方式:把测试集图片批量跑一遍推理,统计每张图的检测数量和平均置信度,和 labels 里标注的真实目标数量做个对比。如果检测数量系统性少于标注数量,说明阈值过高或模型欠拟合;如果检测数量远超标注数量,说明有大量误报。根据这个差值来精确调整 conf 值,比凭感觉调要科学得多。
我在实际跑这类资源时还有一个习惯:拿到资源包后不会一上来就把 01、02、03 三个脚本依次跑完,而是先用最少的图片(比如 10 张)跑一遍完整的训练-推理闭环,确认代码、数据、环境三者全部正确后,再放开全部数据和完整 epoch 数。这个习惯帮我省下来大量的时间——很多时候脚本有序执行到最后才发现环境里的某个依赖版本不对,回头改完再重新从第一步跑,代价非常大。希望帮到你。
本文还有配套的精品资源,点击获取