简介:本资源面向深度学习与计算机视觉方向的开发者及学生,提供一套基于YOLO11目标检测算法识别西红柿的完整代码与数据集,帮助读者快速上手目标检测项目的训练与推理流程。压缩包共1322个文件,约143.79MB,包含656张jpg图像、321个xml标注、326个txt标签文件,以及yaml配置、pt权重、py脚本和csv训练日志等,覆盖数据、模型与代码全链路。已有102人学习下载。资源内含数据集划分、模型训练与PyQt可视化推理三个脚本,运行01划分数据集.py可将数据转为YOLO格式并生成train.txt、val.txt与data.yaml,02train.py用于重新训练模型,03pyqt.py则提供图形界面,点击加载图片与检测按钮即可完成识别。适合希望掌握YOLO11实战、复用预训练权重或二次开发检测界面的读者参考。
1. 西红柿检测这件事,难的不是模型而是数据
去年帮一个做智慧农业的朋友看他的番茄分拣线,他上来就问“YOLO11 能不能直接跑”,我让他先把手机里拍的 200 张棚内照片发过来。结果一看,逆光、遮挡、青红果混叠、背景全是藤蔓,直接推理的漏检率超过四成。这就是西红柿目标检测最真实的起点:算法本身早就不是瓶颈,YOLO11 目标检测算法在通用数据集上已经足够成熟,真正卡住落地的是“你的西红柿数据长什么样”。这个标题对应的方案,本质是给你一套从零构建西红柿检测能力的完整路径——包含数据集、训练脚本和推理代码。它适合三类人:想入门目标检测但缺真实场景练手的开发者、做农业视觉分拣需要快速验证可行性的工程师、以及带学生做课程设计但不想在数据采集上耗掉半学期的导师。接下来我会把数据组织、YOLO11 训练参数、推理部署和踩坑记录一层层拆开,让你拿到这套东西能直接复现,而不是对着一个跑不通的压缩包发呆。
2. 西红柿数据集怎么组织才能让 YOLO11 吃得下
2.1 目录结构与标注格式的硬性约定
YOLO11 沿用了 YOLO 系列一贯的数据组织方式,但很多人第一次跑翻车就翻在目录层级上。常见做法是建一个tomato_dataset根目录,下面分images和labels两个子目录,各自再分train、val、test。注意images/train里的abc.jpg必须对应labels/train/abc.txt,文件名(不含扩展名)严格一致,差一个字符这条数据就被静默跳过,训练日志里不会报错,你只会发现 loss 降不下去。
标注文件是每行一个目标的纯文本,格式为class_id x_center y_center width height,后四个值都是归一化到 0~1 的浮点数。西红柿检测通常只设一个类别,class_id就是 0。如果你拿到的原始标注是 VOC 的 XML 或者 LabelMe 的 JSON,需要先转换。下面这个脚本处理 VOC 到 YOLO 的转换,我一般会把它放在数据集根目录下跑一次:
import os import xml.etree.ElementTree as ET # VOC 标注目录与输出目录 voc_dir = "./annotations_xml" out_dir = "./labels/train" os.makedirs(out_dir, exist_ok=True) # 西红柿只有一类,类别名到 id 的映射 class_map = {"tomato": 0} for xml_file in os.listdir(voc_dir): if not xml_file.endswith(".xml"): continue tree = ET.parse(os.path.join(voc_dir, xml_file)) root = tree.getroot() # 读取图像宽高,用于归一化 size = root.find("size") img_w = float(size.find("width").text) img_h = float(size.find("height").text) lines = [] for obj in root.findall("object"): name = obj.find("name").text.strip().lower() if name not in class_map: continue bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 转为中心点加宽高并归一化 x_c = (xmin + xmax) / 2.0 / img_w y_c = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{class_map[name]} {x_c:.6f} {y_c:.6f} {w:.6f} {h:.6f}") if lines: txt_name = os.path.splitext(xml_file)[0] + ".txt" with open(os.path.join(out_dir, txt_name), "w") as f: f.write("\n".join(lines))这段代码的关键点有三个:一是class_map只保留西红柿,其他类别直接丢弃,避免训练时出现未定义类别;二是归一化用图像真实宽高,不能用固定值,否则不同分辨率图片的框会错位;三是坐标保留六位小数,YOLO11 对精度不敏感,但太短的浮点在某些版本里会被解析成 0。转换完记得抽查几条,用cat labels/train/xxx.txt看一眼数值是否都在 0~1 之间,出现大于 1 的基本就是宽高读反了。
2.2 数据划分与增强策略的参数选择
数据集划分比例没有绝对标准,但西红柿检测我一般按 8:1:1 走,训练集至少保证 500 张以上,否则 YOLO11 的预训练权重优势发挥不出来。如果原始数据只有两三百张,优先做的是补充采集而不是调模型,因为小样本下任何增强都是拆东墙补西墙。
YOLO11 训练时自带在线增强,配置文件里几个参数直接决定模型对西红柿的泛化能力。hsv_h、hsv_s、hsv_v分别控制色调、饱和度、明度扰动,西红柿从青到红的颜色跨度大,hsv_h可以给到 0.015,hsv_s给 0.7,hsv_v给 0.4,让模型不依赖单一颜色。degrees旋转角度建议 10 以内,因为棚内拍摄的西红柿基本是正立或轻微倾斜,旋转太猛会引入不存在的姿态。flipud上下翻转设 0,fliplr左右翻转设 0.5,符合自然场景。mosaic设 1.0 能显著提升小目标召回,但最后 10 个 epoch 要关掉,否则框的分布和真实场景偏差太大。
提示:增强参数不是越大越好。我见过把
degrees开到 45 的训练,模型在验证集上 mAP 很高,一到实际分拣线就疯狂误检,因为真实摄像头根本不会那样拍。
2.3 用 YOLO11 命令行快速验证数据可用性
在正式训练前,先跑一次数据检查,确认 YOLO11 能正确读到所有图片和标签。假设你已经装好 ultralytics 包,在数据集根目录下建一个tomato.yaml:
path: ./tomato_dataset train: images/train val: images/val test: images/test names: 0: tomato然后执行:
yolo detect train data=tomato.yaml model=yolo11n.pt epochs=1 imgsz=640 batch=4只跑 1 个 epoch,目的是看日志里train和val的图片数量是否和你预期一致。如果val显示 0,说明val路径下没有图片或者路径写错;如果训练中途报No labels found,回去检查 labels 目录的层级和文件名。这一步花两分钟,能省掉后面半小时的无效训练。
3. YOLO11 训练参数怎么调才不浪费显卡
3.1 模型尺寸选择与显存匹配
YOLO11 提供 n、s、m、l、x 五个尺寸,西红柿检测这种单类别、目标形态相对固定的任务,yolo11n和yolo11s就够用。我实测在 5000 张棚内西红柿数据上,yolo11s比yolo11n的 mAP50 高约 2.3 个点,但推理速度慢 40%。如果你的部署端是边缘设备,直接选yolo11n;如果是服务器批量分拣,yolo11s性价比最高。yolo11m以上更适合多类别、小目标密集的场景,西红柿检测用不上,纯属浪费显存。
显存方面,imgsz=640、batch=16时,yolo11n大约占 4GB,yolo11s占 6GB,yolo11m要 10GB 以上。如果你显卡只有 8GB,把batch降到 8,同时把accumulate设成 2,等效 batch 还是 16,梯度更新次数不变,只是多花一点时间。这个技巧在显存吃紧时非常实用。
3.2 学习率与优化器的实操配置
YOLO11 默认用 SGD,初始学习率lr0=0.01,最终学习率lrf=0.01,也就是余弦退火到初始值的 1%。西红柿数据集如果超过 5000 张,这个默认值可以直接用;如果只有 1000 张左右,lr0要降到 0.005,否则前期 loss 震荡剧烈,容易把预训练权重带偏。momentum保持 0.937,weight_decay保持 0.0005,这两个值在 YOLO11 里已经调得很稳,不要轻易动。
训练轮数epochs设 100 到 300 之间。判断何时停的标准不是看 epoch 数,而是看验证集 mAP 是否连续 20 个 epoch 不再提升。YOLO11 自带patience=50的早停机制,但实际用下来 20 到 30 就够了,设太大只是浪费电。warmup_epochs设 3,让模型在前几个 epoch 用极小的学习率预热,避免一开始就大梯度冲击。
下面是一份我常用的训练命令,参数都标了注释:
yolo detect train \ data=tomato.yaml \ model=yolo11s.pt \ epochs=200 \ imgsz=640 \ batch=16 \ lr0=0.005 \ lrf=0.01 \ warmup_epochs=3 \ patience=30 \ hsv_h=0.015 \ hsv_s=0.7 \ hsv_v=0.4 \ degrees=10 \ fliplr=0.5 \ mosaic=1.0 \ close_mosaic=10 \ device=0 \ project=runs/tomato \ name=exp1close_mosaic=10表示最后 10 个 epoch 关闭 mosaic 增强,让模型在接近真实分布的数据上收尾。device=0指定第一块 GPU,多卡用device=0,1。project和name决定日志和权重的保存路径,建议每次实验换个name,方便回溯。
3.3 训练过程监控与指标解读
训练启动后,终端会实时打印每个 epoch 的box_loss、cls_loss、dfl_loss和验证集的mAP50、mAP50-95。西红柿检测重点看mAP50,因为它对框的位置容忍度更高,更贴近分拣线“框住就行”的需求。如果box_loss持续下降但mAP50不动,大概率是学习率太小或者数据增强过猛;如果cls_loss很快降到接近 0,说明类别太单一,模型闭着眼都能分类,这时候要把精力放在框的回归上。
训练结束后,runs/tomato/exp1目录下会有weights/best.pt和weights/last.pt。best.pt是验证集 mAP 最高的权重,推理用它;last.pt是最后一个 epoch 的权重,一般不用。results.csv里记录了所有指标,用 pandas 读出来画个曲线,能直观看到有没有过拟合。如果训练集 loss 还在降但验证集 mAP 开始掉,就是过拟合了,减少 epochs 或者加大weight_decay。
4. 推理部署与效果验证的完整链路
4.1 用 Python 脚本跑单张与批量推理
训练完拿到best.pt,下一步是验证它在真实图片上的表现。YOLO11 的 Python 接口很简洁,但有几个参数直接影响结果。下面这段代码同时处理单张和整个文件夹:
from ultralytics import YOLO import os # 加载训练好的权重 model = YOLO("runs/tomato/exp1/weights/best.pt") # 单张推理,conf 控制置信度阈值,iou 控制 NMS 重叠阈值 results = model.predict( source="test_images/one.jpg", conf=0.35, iou=0.5, imgsz=640, save=True, project="infer_out", name="single" ) # 批量推理整个文件夹 results = model.predict( source="test_images/", conf=0.35, iou=0.5, imgsz=640, save=True, project="infer_out", name="batch" ) # 打印每张图的检测框数量和类别 for r in results: boxes = r.boxes print(os.path.basename(r.path), "detected:", len(boxes))conf=0.35是我在西红柿场景下的经验值。设太高会漏掉被叶片遮挡的果子,设太低会把红色反光误判成西红柿。iou=0.5用于非极大值抑制,如果同一串西红柿挨得很近,可以降到 0.4 让框更分散。save=True会把画了框的图片存到infer_out目录,方便肉眼核对。
4.2 评估指标与业务指标的换算
YOLO11 验证时输出的mAP50是算法指标,但分拣线关心的是“漏检率”和“误检率”。漏检率等于没被框住的真实西红柿数量除以总数,误检率等于框错的果子数量除以总框数。这两个指标和conf直接相关,我一般会跑一组不同conf的测试,画一条漏检-误检曲线,找业务能接受的平衡点。
具体做法是准备 200 张有标注的测试图,用model.val()得到预测结果,然后写个小脚本统计。如果漏检率要求低于 5%,conf通常要降到 0.25 左右,但误检率会升到 10% 以上。这时候要么补充遮挡样本重新训练,要么在业务侧加一道人工复核。没有免费的午餐,参数调优只是在两个错误之间找平衡。
4.3 导出 ONNX 与推理速度实测
如果部署端不是 Python 环境,可以把best.pt导出成 ONNX:
yolo export model=runs/tomato/exp1/weights/best.pt format=onnx imgsz=640 simplify=Truesimplify=True会做一层图优化,去掉冗余算子。导出后在目标机器上用 onnxruntime 加载,实测yolo11n在 CPU 上单张推理约 80ms,yolo11s约 150ms;在 GPU 上分别降到 8ms 和 12ms。如果分拣线节拍是每秒 5 个果子,yolo11n加 GPU 完全够用。导出时注意imgsz要和训练时一致,不一致会导致框的坐标偏移。
5. 西红柿检测避坑记录:从翻车到稳住
5.1 标注框贴边导致训练发散
现象:训练前几个 epoch loss 正常下降,到第 10 个 epoch 突然变成 NaN。原因:部分标注框的坐标正好等于 0 或 1,归一化后出现边界值,YOLO11 在计算宽高损失时对 0 取了对数。解决:写个脚本遍历所有 label 文件,把小于 0.001 的值改成 0.001,大于 0.999 的改成 0.999。改完重新训练,loss 平稳下降。
5.2 验证集 mAP 虚高但实际漏检严重
现象:验证集mAP50到 0.92,但拿棚里新拍的照片测试,漏检一半以上。原因:训练集和验证集来自同一批照片的随机划分,同一颗西红柿的不同角度可能同时出现在训练和验证里,模型记住了背景而不是果子。解决:按拍摄批次划分数据集,同一批次拍的照片要么全在训练集,要么全在验证集。重新划分后mAP50掉到 0.78,但这才是真实水平。
5.3 推理时框重叠严重
现象:一串西红柿被框了七八个框,密密麻麻叠在一起。原因:iou阈值设太高,NMS 没有把重叠框抑制掉。解决:把推理时的iou从 0.7 降到 0.45,同时把conf从 0.25 提到 0.4。如果还有少量重叠,可以在后处理里加一道按面积排序的过滤,只保留置信度最高的框。
5.4 青果被系统性漏检
现象:红果检测很准,青果几乎全漏。原因:训练数据里红果占 90% 以上,模型学到了“红色等于西红柿”的捷径。解决:补充青果样本,至少让青红比例到 3:7。如果青果样本实在难采,可以在增强里加大hsv_h扰动,让红色样本的色调偏移到青绿色区间,强迫模型学形状而不是颜色。
5.5 导出 ONNX 后框坐标偏移
现象:PyTorch 推理正常,ONNX 推理的框整体偏移几十个像素。原因:导出时imgsz和推理时预处理的分辨率不一致,或者 letterbox 的填充方式不同。解决:导出和推理统一用imgsz=640,并且在 ONNX 推理前自己做 letterbox,保持和 ultralytics 一致的缩放加填充逻辑。如果懒得写,直接用 ultralytics 的model.predict加载 ONNX,它内部会处理。
6. 把西红柿检测推到可交付水平的两个进阶习惯
第一个习惯是建立自己的“错题本”。每次在真实场景发现漏检或误检,把那张图单独存到一个hard_cases文件夹,标注好之后加入训练集重新微调。我一般每积累 50 张错题就重训一次,三轮下来模型在棚内的漏检率能从 15% 压到 4% 以内。这个做法比盲目加数据有效得多,因为错题代表的是模型当前的盲区,针对性最强。
第二个习惯是固定一套推理参数并记录版本。西红柿检测的conf、iou、imgsz三个值一旦在业务侧调好,就写进配置文件,不要每次手动改。同时把对应的best.pt权重文件用日期加版本号命名,比如tomato_yolo11s_20240612_v3.pt。我吃过亏,有一次分拣线换了新批次西红柿,效果突然变差,查了半天才发现是有人把推理脚本里的conf从 0.35 改成了 0.5,权重没换,参数变了。从那以后所有参数都进版本管理,改任何一项都要走记录。
还有一个技巧是善用 YOLO11 的model.val()做定期体检。每周拿最新采集的 100 张图跑一次验证,看mAP50有没有掉。如果掉了超过 5 个点,说明数据分布漂移了,需要补样本重训。这个习惯花不了多少时间,但能避免模型在不知不觉中退化。希望帮到你。
本文还有配套的精品资源,点击获取