☰
从零训练水果检测模型:YOLO目标检测入门实战指南
2026/10/21 5:15:24 网站建设 项目流程

第一百零九篇了。这个系列写到现在,从零基础到能跑通训练流程,也算是陪着不少人从“练气期”一路走到现在。今天这篇,还是老规矩,用“东方仙盟”的壳,聊点硬核的东西——Python 水果检测模型训练。如果你正卡在入门目标检测这一步,不知道怎么选模型、不知道数据集怎么整、不知道训练完那堆指标到底怎么看,这篇就是给你准备的。

1. 项目整体设计与思路拆解

1.1 为什么选“水果检测”当练气期第一课

初学者最容易犯的错误,就是一上来就挑战高难度任务。比如有人第一课就想训练一个自动驾驶的交通标志识别系统,还有的人直接去搞人脸关键点检测。这就像刚进仙门的练气期弟子,还没学会运转周天,就想着修炼元婴期的功法,不把自己练出心魔才怪。水果检测之所以适合作为入门项目,原因很直白。

第一,数据集好找。公开的水果图像数据集中,最常见的水果类别——苹果、香蕉、橙子、葡萄这些,在各类开放数据集里都有现成的标注。你不需要自己爬图、自己标注,省下大把时间用来理解模型本身。第二,类别区分度高。苹果和橘子虽然有大有小,但形状、颜色、纹理的差异很明显,模型不需要学习特别微妙的特征就能取得不错的检测效果。换句话说,你更容易获得正反馈——“哦原来真的能检测出来”——这种正反馈对坚持学下去特别重要。第三,应用场景直观。水果检测做好了,你可以直接套到“超市自动结账”“水果分拣机器人”这些实际场景里去想问题。

而且水果检测涉及的目标检测核心流程非常完整:数据准备、标注格式转换、模型配置、训练调参、指标评估、推理部署,一个不落。也就是说,你把水果检测搞明白了,换一个场景去检测别的东西,无非是换数据集、调参数,底层思路完全一样。这门功法一旦练成,受益的是后续所有目标检测任务。

1.2 目标检测和图像分类,到底差在哪

很多刚入门的人会把“图像分类”和“目标检测”搞混。图像分类做的事情是:给一张图,判断整张图属于哪一类。而目标检测要做的是:给一张图,把图里每一个目标都找出来,告诉你是“什么”(类别)以及“在哪里”(位置框)。

举个具体的例子。一张桌子上放着三个苹果、两根香蕉,旁边还有一杯橙汁。图像分类模型只能告诉你“这是一张包含水果的图片”,具体几个苹果、几个香蕉、它们在哪个位置,它完全不知道。而目标检测模型会给出结果:苹果1、苹果2、苹果3各自的位置框,香蕉1、香蕉2各自的位置框,橙汁的框也会标出来,每个框都有坐标和类别。

这就是核心区别。目标检测 = 分类 + 定位,两者一起做。也因此,目标检测的数据标注要复杂得多。分类任务只需要给每张图一个类别标签,目标检测需要给出每个目标的类别和边界框坐标。这也是为什么很多初学者在数据准备阶段就翻车了,因为标注格式搞错了,后面根本训练不出来。

1.3 技术选型:为什么是YOLO而不是Faster R-CNN

确定要做目标检测之后,下一个问题是:用什么模型?“东方仙盟练气期”这个定位,决定了选型思路必须先考虑“跑得起来”。当年我刚开始学的时候,也想过直接上Faster R-CNN,毕竟很多论文里都提到它,影响力大、精度高。但真等到跑起来,新手村的电脑就有点吃不消了。

Faster R-CNN是两阶段检测器,先通过区域提议网络(RPN)生成候选区域,再对每个候选区域进行分类和回归。它的优点是精度高,但缺点是速度慢、显存占用大、代码结构相对复杂,训练一轮的时间够YOLO跑好几轮。对练气期选手来说,训练一次等半天,调个参又要等半天,心态分分钟崩掉。

YOLO(You Only Look Once)走的是另一条路线。它把目标检测当成一个回归问题,图像一次前向传播就能同时预测所有目标的类别和位置,一阶段完成。速度优势非常明显,在同等条件下,YOLO的推理速度可以达到Faster R-CNN的十倍甚至更多。而且YOLO系列迭代到现在,最新版本的精度已经完全不输两阶段模型,甚至常年在各类检测榜单上霸榜。

考虑到“跑得动”“学得会”“用得上”这三点,YOLO是当之无愧的首选。具体到版本,练气期推荐新版框架自带的轻量级版本,比如YOLOv8n,模型体积小、推理速度快,在CPU上勉强也能跑,有独立显卡更是毫无压力。先把流程跑通,再去追求精度不迟。

2. 环境准备与数据集构建

2.1 炼丹炉配置:Python环境与依赖安装

“练气期”有一个很现实的问题:很多人连环境都没配置好,就已经在到处问“为什么报错”了。我见过有人在自己电脑上同时装了三个Python版本,环境变量乱成一锅粥,最后连pip都用不了。所以环境这块,别嫌麻烦,一次性配好,后面能省一半痛苦。

第一步,建议用虚拟环境。不管是conda还是venv,都行。给这个项目单独建一个环境,避免和别的项目互相污染依赖版本。我第一次训练模型的时候图省事,直接装在全局环境里,结果后来另一个项目需要的依赖版本冲突,折腾了一晚上才把环境清理干净,从那以后就老老实实建虚拟环境了。

conda create -n fruit_detect python=3.9 conda activate fruit_detect

第二步,检查硬件。训练深度学习模型,显卡和驱动是影响体验最大的因素。在命令行里输入nvidia-smi,确认能看到显卡信息。如果显示不出来,说明CUDA驱动有问题,或者根本没装显卡驱动,后面PyTorch的GPU版就用不了,只能退而求其次用CPU训练。CPU训练不是不行,只是慢,而且慢得让人怀疑人生。练气期选手如果有条件,租个云GPU环境甚至比自己电脑跑还要省事。

第三步,安装依赖框架。我用的新版YOLO框架是PyTorch生态的,所以先把PyTorch装上,这里有个小坑:从官网安装的时候一定要选择对应当前CUDA版本的安装命令。很多人直接pip install torch,装的是CPU版,后面训练的时候发现速度极慢,还以为是模型问题,其实是装错了。

pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118

装完PyTorch,再装YOLO框架本身就很简单了:

pip install ultralytics

装完之后,验证一句话:

import torch print(torch.cuda.is_available())

输出True,说明GPU可用,心头大石可以放下。如果是False,优先检查驱动和PyTorch版本是否匹配。

2.2 数据集从哪来:公开数据集与自建标注

数据集是整个训练中最基础的部分,也是最容易让人忽视的部分。很多论文和教程都会强调模型结构多重要、调参多重要,但一个脏乱差的数据集,再好的模型也救不回来。数据比模型重要,这句话在工程实践中一点也不夸张。

对于练气期选手,我不建议一开始就自己造数据集。自己爬图、自己清洗、自己标注,这套流程太耗时了,而且很容易因为标注质量参差不齐导致模型训练效果差,最后你根本分不清是自己的问题还是数据的问题。更合理的路径是先用公开数据集把流程跑通,训练出来一个能用的模型,建立自信之后再去考虑自建数据集。

公开水果数据集有很多,我用的是一个包含几十种水果的公开图像数据集。下载下来之后,你会看到它按照类别分好了文件夹。这里又要说一个重点:这个数据集是为图像分类任务准备的,并不是目标检测数据集。它只有“这张图是什么水果”,没有“每个水果在哪里、边界框是多少”。要用于目标检测,还需要做一步转换或者另外找附带标注的检测数据集。

如果你找到的公开数据集纯粹是分类数据,有一个取巧的办法:自己写一个小脚本,把每张图片按固定比例随机抠出一部分区域作为训练样本。但这个办法不够严谨,实际项目里最好不要这么做。我的建议很简单:去找自带检测标注的数据集。目前有很多开放平台提供了带边界框标注的水果检测数据集,下载之后得到的格式一般是COCO格式或VOC格式,后面可以进行格式转换。

如果确实找不到合适的数据集,也完全可以自己造一个小的。找三四种水果,每类拍几十张照片,然后用标注工具手工标注。水果检测的必备数据量没有想象中那么大,每类一两百个实例,练气期绰绰有余。自己标注有一个额外的好处:你能深切理解标注质量对模型的影响,这比看任何教程都管用。

2.3 标注格式详解:YOLO的txt标签到底怎么写的

我在训练营里见过太多人栽在这里:数据和标注都搞好了,结果训练的时候模型警告“found no labels”,根本读不到标注信息。原因基本都是标注格式搞错了。

也就是说,YOLO的数据标注格式要求每个txt对应一张图片,文件名和图片保持一致,后缀换成.txt。txt文件的每一行代表一个目标,格式固定为:

class_id x_center y_center width height

这里的x_center、y_center、width、height,全部是相对于图片宽高的归一化值,都在0到1之间。注意,x_center和y_center是中心点坐标,不是左上角坐标。这一点非常容易搞错,很多初学者拿着左上角坐标直接填进去,训练出来的模型位置预测全是歪的。

举个例子。一张宽800、高600的图片,里面有一个苹果目标,目标框左上角坐标是(100, 200),右下角是(300, 400)。那么:

# 原始坐标 x1, y1, x2, y2 = 100, 200, 300, 400 # 宽高 w = x2 - x1 # 200 h = y2 - y1 # 200 # 中心点 x_center = x1 + w / 2 # 200 y_center = y1 + h / 2 # 300 # 归一化 img_width, img_height = 800, 600 x_center_norm = x_center / img_width # 0.25 y_center_norm = y_center / img_height # 0.5 width_norm = w / img_width # 0.25 height_norm = h / img_height # 0.333

那么这一行就是“0 0.25 0.5 0.25 0.333”,其中0代表苹果这个类别ID。

用LabelImg或者label-studio这类标注工具直接导出YOLO格式,可以省去手动转换的麻烦。但无论如何,自己得能看懂这个格式,因为后续排查问题经常需要直接打开txt看内容。

数据集准备好之后,正确的目录结构应该是这样的:

datasets/ fruit/ images/ train/ 001.jpg 002.jpg val/ 003.jpg labels/ train/ 001.txt 002.txt val/ 003.txt

这里有个必须注意的点:images和labels文件夹的名字不能乱改,因为框架约定就是这个名字。另外,train和val下面每个图片必须有一个对应的txt文件,没有标注的图片也要放一个空的txt文件,否则后续训练会警告但不报错,影响数据读取效率。

3. 模型训练实操全流程

3.1 配置文件逐项拆解

数据集准备完成之后,接下来是建立一个配置文件,告诉训练框架“数据在哪、有几类、叫什么名字”。新版YOLO框架用的是yaml配置文件,内容非常简洁。

path: /path/to/datasets/fruit # 数据集根目录 train: images/train # 训练集图片路径,相对于path val: images/val # 验证集图片路径,相对于path names: 0: apple 1: banana 2: orange

这里有三点要提醒。第一,path建议写绝对路径,避免因为当前工作目录不一致导致找不到数据。我犯过的错就是写了相对路径,结果换了一个终端启动,路径就失效了。第二,names是类别名列表,索引顺序必须和标注文件中的class_id一致,否则模型会把苹果当橙子来学,效果直接崩掉。第三,这种配置文件一般还支持设置test路径,练气期阶段可以把test省略,val就够用了。

配置好之后,先别急着训练。用一个最基础的验证方法:运行一条可视化标注命令,把数据集的标注画到图片上看看是否正确。

from ultralytics import YOLO model = YOLO('yolov8n.yaml') model.val(data='fruit.yaml')

如果输出界面能画出正确的水果框,说明数据链路是通的。这个时候再进入正式训练流程。

3.2 启动训练:参数选择背后的逻辑

万事俱备,只欠东风。训练命令本身并不复杂,核心是在理解参数含义的前提下把值设置合理。

yolo train data=fruit.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16

逐项拆解一下我为什么这么设置。

model=yolov8n.pt是预训练权重。有一个常见误区:既然我是训练自己的数据集,为什么不从零开始?原因在于,从头训练一个深度学习模型需要海量数据和足够的算力,练气期阶段根本扛不住。使用预训练权重相当于“传承功法”,YOLOv8n在大型公开数据集上已经学到了通用的特征提取能力,比如边缘、纹理、颜色等基础特征的识别。迁移到水果检测任务时,只需要在它的基础上“微调”,收敛速度快得多,最终精度也更高。这在计算机视觉里叫迁移学习。

epochs=100是训练轮数。100轮是练气期阶段的合理值。如果数据集很小,几十轮就能收敛,100轮足够了。如果你发现训练到后面损失还在明显下降,说明还没收敛,可以加大到200甚至300。如果训练到一半损失已经平了,动态停止机制会帮你自动保存最佳模型,不需要手动干预。

imgsz=640是输入图像尺寸。YOLO会把输入图片等比缩放到640×640再送进网络。尺寸越大能保留的细节越多,小目标检测效果越好,但显存占用和计算量也越大。练气期阶段先用640,跑通流程后可以试试看提高尺寸会不会带来精度提升。

batch=16是每批次样本数。这个值直接由显存决定。显存不够的时候,模型会直接报CUDA out of memory,这时候别犹豫,把batch减半。我记得第一次训练的时候,电脑只有6G显存,batch设了32,直接爆了,改到8才勉强跑起来。训练速度会慢一些,但至少能跑。

训练启动后,框架会自动在项目目录下创建训练输出文件夹,模型权重、日志、预测示例都会放在这里。训练过程中会实时更新一些核心指标,需要重点关注的有loss(损失)、Precision(精确率)、Recall(召回率)、mAP50(平均精度均值)。看到它们别慌,新手最容易在这个环节乱了阵脚。

3.3 训练过程监控与指标解读

练气期的关键课之一,就是学会看训练输出。很多人训练跑起来之后,看着屏幕上滚动的数据完全不知道什么意思,只觉得数字在动就很安心,直到最后发现模型效果一塌糊涂,才回头研究这些指标是什么。

loss是损失函数值,衡量模型预测结果与真实标注之间的差距。训练的过程就是不断降低这个值。它分两部分:分类损失(区分这个目标是什么)和边界框回归损失(预测的位置框有多准)。正常情况下的表现是:随着epoch数增加,loss逐渐下降,曲线趋于平缓。

Precision(精确率)的含义是:模型预测出来“这个框里有苹果”的结果里,有多少是真的有苹果。如果模型经常把橙子误判成苹果,精确率就会低。

Recall(召回率)的含义是:图片里真正的苹果中,有多少被模型找出来了。如果模型漏检,比如图里有三个苹果,它只检出来两个,召回率就会下降。

mAP50是目标检测里最常用的综合指标。它的含义是:在不同置信度阈值下,以IoU(预测框和真实框的重叠比例)超过0.5为判定标准,计算精确率和召回率综合曲线下的面积。说白了,就是“检测结果到底多靠谱”的综合评分。mAP50的值越接近1越好。练气期训练一个水果检测模型,mAP50达到0.9以上是合理的预期,达到0.93以上说明数据、模型、训练参数都很健康。

这几项指标通常是联动的。一个非常容易踩的坑:训练结束后看着loss降得很低,以为万事大吉,结果验证集上mAP惨不忍睹。这说明模型过拟合了——把训练集的“特征细节”(包括噪声)全背下来了,换一批没见过的数据就不会泛化了。如果训练loss持续下降而验证loss上升,大概率就是过拟合。解决思路在下一章细说。

训练结束之后,框架会自动保存两个权重文件:最后一次训练的last.pt,以及验证集上表现最好的best.pt。实际使用和后续推理,一定用best.pt。那个last.pt的唯一价值是万一中断了可以从这里继续训练。

3.4 模型评估与推理测试

训练完不等于结束。最后一步是用训练好的模型跑一下推理,看真实效果。推理命令很简单:

yolo predict model=best.pt source=test_images/fruit.jpg

这里source可以直接指向一张图片、一个文件夹,甚至一段视频。框架会自动画出每一个检测到的水果框,标注类别和置信度分数。

首次推理时,有两个结果示例需要仔细看。第一个是水果都被正确地框出来了,框的位置贴合水果,类别正确,置信度分数也高。这是理想状态。第二个是有些水果没被框出来,或者框的位置很偏,又或者把不是水果的东西当成了水果检测出来,这就是“漏检”和“误检”,需要结合上一节讲的指标和数据分析原因。

一个容易被忽视的技巧是尝试调整置信度阈值。predict命令默认只输出置信度高于0.25的框。如果框太多了(很多误检),可以调高阈值,比如0.5,只保留足够有把握的结果。如果框太少了(很多漏检),可以调低阈值,比如0.1,让模型多输出一些候选框看看它“到底认为这有多像水果”。这是一个很好的debug手段,能帮你直观理解模型的“内心想法”。

4. 常见问题与排查技巧实录

4.1 训练损失不下降:最经典的翻车现场

训练跑起来,loss明明在刚开始的时候还有下降趋势,结果跑到几十轮就彻底平坦了,甚至变成一条水平线。这种问题我遇到太多次了,原因通常归结为几类。

学习率设置不合适是首因。学习率太大,损失函数会在最优解附近来回震荡,表现为loss剧烈波动不下降。学习率太小,模型更新幅度非常微小,表现在loss曲线上就是“缓慢爬坡”,小幅下降然后停滞。新版框架默认的学习率对大多数任务来说是可以的,但如果你修改过优化器参数,建议先恢复到默认值。

数据标注错误是隐藏的重灾区。检查一下标注文件,看看有没有类别ID越界、坐标归一化后大于1或者小于0、或者某个类别在数据集中数量极少(比如橙子只出现了几次)。这些都可能导致loss异常或模型根本无法学到有效特征。

还有一个很隐蔽的问题:数据集里的图片本身可能有问题,比如所有训练图片都是同一张图的重复缩放,或者图片内容过于单调,比如所有苹果都是从同一个角度拍的。这时候模型没有足够的信息量去泛化,loss自然很难降。可以把训练集随机抽出几张用上面提到的标注可视化方法检查,确认数据本身没有低级错误。

4.2 显存溢出:CUDA out of memory的解题思路

显存溢出是新手村出现频率最高的报错之一,尤其在batch设大、图片分辨率设置高的时候。第一次遇到这个报错,我第一反应是“我是不是要把所有数据都删掉重来”,后来才知道根本不用那么紧张,这是有标准解题套路的。

第一步,降低batch size。这是最直接的手段。如果batch=16爆了,改8,还爆,改4。代价仅仅是训练速度变慢,精度基本不受影响。

第二步,降低imgsz。把640改成512或者416。代价是模型在分辨率较低的输入上训练,小目标检测能力会有所下降,但练气期阶段影响不大。

第三步,检查是不是有别的程序占着显存。我之前就犯过这个错,一个没关干净的浏览器页面,硬生生把显存吃掉了几个G。训练前打开任务管理器看一眼GPU占用情况,确保显存是干净的。

第四步,如果以上都试过还是爆,那说明当前训练配置对你的硬件来说太奢侈了。要么换更小的模型,比如YOLOv8n是最小的,改yolov8m甚至s模型都不会得到更好的精度;要么考虑用云GPU,花点钱买时间,性价比比自己纠结硬件配置高得多。

4.3 检测效果差:漏检、误检的排查思路

训练结束后做了测试,发现效果很差。怎么判断是哪儿出问题?我建议按照下面这个顺序排查。

先看图,再谈心。拿出数据集的标注可视化结果,逐一核对每一张验证集图片的标注框是否准确。如果标注框本身就是歪的或漏标了,那模型再怎么训练也学不会——它认为“没有框的地方就是没有水果”,结果测试的时候自然会漏检。这一步没问题之后,再去看训练日志。

如果训练过程中Precision和Recall都还不错,但实际推理时效果差,那问题往往出在置信度阈值上。预测时阈值太高,很多低置信度的真实目标会被过滤掉,造成漏检。阈值太低,误检增多。建议画一下置信度分布图,看看你的模型对测试图片的置信度分布情况,再选择一个平衡阈值。

最后才考虑模型容量问题。如果数据集很小(几十张图),最大可能的瓶颈是数据量,数据增强策略(比如随机翻转、旋转、缩放)能在一定程度上缓解,但治标不治本。最好的方案永远是增加数据量,尤其是增加同一场景不同角度、不同光照、不同背景的多样性。

4.4 踩过的坑清单(升级版)

最后分享一份“练气期特供”避坑清单,全是血泪经验。

第一,路径和名字一定要规范。数据集文件夹、图片、标注文件不要出现中文、空格、特殊符号。有些工具对中文路径不友好,训练时莫名其妙报错,排查到最后发现是路径惹的祸。图片名不要用01、02、03,尽量用001、002这种补零后的编号,否则排序错乱、匹配混乱,都是潜在的地雷。

第二,标注文件必须和图片文件一一对应。训练框架默认每张图片都会有一个同名txt标签文件。如果缺少对应txt,这一张图片就被自动跳过;如果txt里没有标注内容,就是一个空文件,也要放进去。不要自作主张地删掉没标注的图片的txt,否则训练集里图片数不一致,日志和指标会对不上。

第三,类别名称一旦定下来就别乱改。你确定数据结构时用的是apple、banana、orange,写配置文件时就别改成apples、banana、orange。哪怕是后面纯粹“想统一一下大小写”的改名,都可能导致所有标注文件的类别ID映射错乱,之前训练的一切推倒重来。

第四,部署和训练的环境尽量保持一致。练气期阶段可能经常会在本机和云环境之间切换,注意检查两个环境的依赖库版本、显存配置、绝对路径是否都匹配。最蛋疼的一次经历是:模型在本机训练一切正常,换到云环境做推理就各种报错,最后发现是两边的路径分隔符不一样(Windows是反斜杠,Linux是正斜杠)导致数据文件找不到。就为了这个,白白排查了一下午。

第五,学会看输出的日志。训练过程中输出的loss、mAP等指标,不是看一眼就完事的。把一张训练完成后输出的指标曲线截图保存下来,和后续调参后的曲线做对比,你能直观地感受“加了轮数”和“调了batch”到底带来了什么变化。这种体感,比任何教程里的经验总结都来得实在。

我个人在实际操作中的体会是:练气期最大的瓶颈往往不是技术,而是“流程感”。你需要的不是最前沿的模型,而是把一整套流程——数据、配置、训练、评估、推理——完完整整跑通的那个过程。这个过程跑通了,你才算真正跨过了“目标检测”这道门槛。尤其是当你第一次看到测试图片上画着漂亮的水果检测框、旁边标着正确的类别名和置信度时,那种感觉,还真有一点点“筑基成功”的意思。水果检测搞明白之后,后续可以试着往这些方向扩展:增加更多水果类别、换不同环境采集数据、把检测结果接到一个简单的计数功能上,甚至把它包装成一个可以演示的实时摄像头小程序。这套思路一旦掌握,它就不只是水果检测了,它是你自己造的“检测法宝”,想往哪儿用,就往哪儿造。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询