简介:目标检测是计算机视觉的核心任务之一,其落地效果高度依赖数据质量与模型训练策略。在实际工程中,公开数据集往往与真实场景存在偏差,导致模型部署后性能下降。本文以一套包含东鹏特饮、红牛、可乐等常见饮料的罐装饮料识别数据集为例,系统解析YOLOv8在真实数据上的完整训练流程。从数据集目录结构、标注格式与质量评估入手,讲解小目标、遮挡、类别不平衡等典型问题的处理思路;随后介绍环境搭建、data.yaml配置、超参数调优与训练监控方法;再通过mAP、精度、召回率等指标评估模型,并导出ONNX或TensorRT格式用于部署。针对实际运行中的解压报错、显存不足、模型效果不佳等问题,也给出了系统化排查方案。这套方案不仅适用于饮料识别,也可迁移至零售商品检测等场景,为开发者提供从数据到部署的可复现参考。 做目标检测的同行应该都有过这种经历:想训练一个贴近真实场景的识别模型,却总在数据集上卡壳。要么是公开数据集太干净、太理想化,部署到实际环境就露馅;要么是类别对不上业务需求,还得重新标注。最近整理手头资料的时候,翻出一套之前用过的罐装饮料识别数据集,一千多张图片,带完整yolov8格式标注,覆盖了东鹏特饮、红牛、芬达、养乐多、可乐、雪碧这些便利店冷柜里的高频单品,还顺带标了一批薯片袋装。这套数据虽然不算海量,但胜在场景真实、类别贴近日常零售场景,对于想跑通目标检测全流程的新手,或者需要一个基座数据来微调的开发者,都挺有参考价值。
这篇文章就围绕这套数据集展开,从数据集的目录结构、标注质量、类别分布,到yolov8训练环境搭建、参数配置、训练调优、模型评估和部署,再到实际跑数据时容易踩的坑,一步一步拆开讲。所有内容都基于我实际跑过的流程,可以直接照着操作。
1. 项目背景与数据集解读
1.1 数据集规模与覆盖场景分析
先说整体情况。这套数据集压缩包解压之后,完整目录大概是这样的:
罐装饮料识别数据集/ ├── images/ │ ├── train/ # 训练集图片 │ └── val/ # 验证集图片 ├── labels/ │ ├── train/ # 训练集标注txt │ └── val/ # 验证集标注txt ├── data.yaml # 数据集配置文件 └── 类别说明.txt图片总数在一千三百张左右,其中训练集约一千一百张,验证集约两百张。这个比例大约是8.5比1.5,符合常规的划分习惯。图片分辨率大多数在1080p级别,部分来自手机拍摄,部分来自商超监控视角,还有一小部分是网络公开图片。手机拍摄的图片通常比较清晰,构图也比较随意;监控视角的图片则带有俯视角度,光照条件不稳定;网络图片的后期处理痕迹明显,颜色饱和度高。这三种来源混合在一起,反而让模型的泛化能力更好,因为推理阶段遇到的真实场景往往就是这种"脏乱差"输入。
需要说明的是,数据集除了罐装饮料之外,还包含薯片这个类别。一开始我也觉得奇怪,薯片和罐装饮料有什么关系?后来看标注图片才明白,很多拍摄场景就是便利店货架、超市收银台或者办公室茶几,薯片和饮料经常摆在一起。如果只标饮料不标薯片,那些摆在一起的零食会成为背景干扰,模型在特征提取时容易被误导。所以数据集作者把薯片也纳入了标注范围,这个思路在实际项目里挺常见的——标注的类别选择往往不是按"这是什么",而是按"哪些物体容易和目标一起出现"来考虑。
1.2 标注格式与标签文件结构
这套数据集用的是yolov8原生支持的YOLO文本格式,也就是每个图片对应一个同名txt文件,放在labels目录下。txt文件名和图片文件名完全一致,只是扩展名不同,比如:
images/train/IMG_20250103_142353.jpg labels/train/IMG_20250103_142353.txt每个txt文件里的每一行对应图片中的一个目标物体,格式是:
class_id x_center y_center width height所以最需要留意的是:所有坐标都是相对图片宽高的归一化数值,范围在0到1之间,不是像素坐标。比如图片是1920x1080,一个物体的像素边界框是xmin=960、ymin=540、width=480、height=270,中心点就是(960+240)/1920=0.625、y_center=(540+135)/1080=0.625,width=480/1920=0.25,height=270/1080=0.25。写入txt就是:
0 0.625 0.625 0.25 0.25类别编号从0开始。这套数据集的类别定义应该是:
0: crisps 薯片 1: dongpeng 东鹏特饮 2: redbull 红牛 3: fenda 芬达 4: yangleduo 养乐多 5: cola 可乐 6: sprite 雪碧 7: wanglaoji 王老吉一共8个类别。有些标注工具导出的时候类别顺序可能不一样,所以拿到数据后第一步不是急着训练,而是打开几个txt文件看看类别编号对应的实际物体,避免标签错位。
提示:训练前务必检查labels里是否有文件是空的(0KB),空标签文件会导致训练时报错或者这部分图片没有参与训练,建议直接过滤掉。
2. 数据集标注质量与预处理
2.1 标注质量评估的几个关键点
拿到数据集之后,我习惯先不急着训练,而是花半小时左右抽查标注质量。因为标注质量直接决定模型效果上限,模型再强也学不出标注里不存在的信息。这次抽查发现整体标注质量还不错,但仍存在几个需要注意的现象。
一是小目标占比高。便利店货架上的饮料瓶通常只占画面的百分之几到十几,尤其是一整排商品陈列的场景,单个瓶子可能只有30x80像素。小目标对检测模型的挑战很大,因为特征提取层下采样多次后小目标的有效特征可能只剩一两个像素点,很容易漏检。如果训练出来的模型对远距离小瓶子漏检明显,可以考虑把推理分辨率从640提升到960或者1280,或者换用P5层输出更强的模型变体。
二是遮挡和密集排列。货架上的饮料瓶前后重叠、瓶身贴在一起是常态,但我在抽查中发现有些红色瓶身挨在一起时,标注框画得比较随意,框住了半个相邻瓶子。这种标注噪声对模型训练影响不算致命,但如果数量较多,会让模型学习到错误的边界特征,建议用标注工具微调一下。实际操作中我筛选出了大约三四十张有明显标注偏差的图片,花了一个小时手工修正,后期训练收敛速度明显变好。
三是类别不平衡。可乐、雪碧这类最常见的饮料样本数量最多,养乐多、王老吉样本相对少。如果按默认参数训练,少量类别容易被多数类别压制。缓解的办法有三种:第一,对样本少的类别做数据增强,比如复制粘贴到更多背景中;第二,在loss计算中提高小类别的权重;第三,最简单也最有效,就是补充更多的该类图片。如果不方便采集原始图片,可以用已训练好的模型做半自动标注来扩充数据,人工审核后再加入训练集。但要注意,半自动标注会引入模型自身的bias,审核环节不能省。
2.2 数据预处理与数据集划分
虽然数据集作者已经划分好了train和val,但实际使用中还是要检查划分是否合理。最常见的问题是同一场景的连续帧图片被同时分入训练集和验证集,比如一段视频抽帧得到的图片中,第10帧在train、第11帧在val,这种情况下验证集的指标会虚高,因为验证集图片和训练集图片太像了,模型"记住"了场景而不只是学到了类别特征。
这种问题不容易用代码直接发现,只能靠肉眼抽查。我抽查的方式是:把val里的图片按文件名排序,然后快速浏览缩略图,如果发现连续多张图片内容高度相似,就确认划分有问题。如果遇到这种情况,需要按场景而不是按文件随机划分,比如以拍摄时间或地点为维度,同一场景的图片全部归入训练集或验证集。
数据预处理方面,我做了两件常规操作。一是统一图片格式,把所有jpg、png、bmp等格式统一转成jpg,避免某些格式在训练时解码报错。二是清理损坏图片,用Python脚本检查图片完整性,删除无法解码的文件。脚本大概是这样的:
import os from PIL import Image image_dir = 'images/train' for filename in os.listdir(image_dir): filepath = os.path.join(image_dir, filename) try: img = Image.open(filepath) img.verify() except Exception as e: print(f'损坏文件: {filepath} - {e}') os.remove(filepath)保险起见,我还会把labels目录里找不到对应图片的txt文件也删掉,以及把图片存在但txt为空或txt格式错误的样本标记出来,在data.yaml里用exclude参数排除或者直接移出目录。
3. YOLOv8训练环境搭建与配置
3.1 环境依赖安装
环境配置是新手最容易卡壳的环节。YOLOv8基于PyTorch,Python版本建议3.8到3.10之间,PyTorch版本1.8以上即可,新版本2.x也兼容。硬件方面,NVIDIA显卡只要有CUDA支持就基本都能跑,显存不低于6GB就可以训练yolov8n和yolov8s,如果只有CPU,训练速度会慢很多但也不是完全不能跑。
创建虚拟环境,我习惯用conda:
conda create -n yolo python=3.9 conda activate yolo然后安装PyTorch。这里有个常见的坑:直接pip install torch默认安装的是CPU版本,虽然能跑但速度极慢。建议到PyTorch官网选择对应CUDA版本的安装命令,比如CUDA 11.8:
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118安装完后可以用下面这行命令验证GPU是否可用:
import torch print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))如果输出True和显卡型号,说明环境没问题。接着安装ultralytics:
pip install ultralytics这一步会把yolov8的所有依赖,比如opencv-python、pandas、matplotlib这些一并装好,不需要再单独安装。装完后在Python里执行import ultralytics验证,能正常导入就说明环境准备完毕。
注意:ultralytics包更新很频繁,不同版本的默认参数和行为可能有细微差别。如果你安装的是最新版,但代码是照着老教程写的,训练参数可能不生效。建议训练前用
yolo --help查看当前版本支持的参数。我这次用的版本是8.1.x系列。
3.2 编写data.yaml配置文件
环境准备好之后,下一步就是编写data.yaml。这是YOLOv8训练时必需的数据集配置,内容非常简单:
# data.yaml train: /home/user/datasets/beverage/images/train val: /home/user/datasets/beverage/images/val nc: 8 names: 0: crisps 1: dongpeng 2: redbull 3: fenda 4: yangleduo 5: cola 6: sprite 7: wanglaojitrain和val路径最好写绝对路径,相对路径虽然YOLOv8也能解析,但在不同目录下执行训练命令时容易找不到数据集报错。names字典里的类别顺序必须和标注txt文件中的class_id一一对应,这点非常重要。如果把cola的id写成5,训练出来的模型预测id=5时就会显示sprite,部署到应用里就全乱了。
4. 模型训练实操
4.1 模型选型与训练参数设定
YOLOv8提供了5个预训练模型:yolov8n(nano)、yolov8s(small)、yolov8m(medium)、yolov8l(large)、yolov8x(xlarge)。它们的参数量和检测精度依次递增,推理速度依次递减。对于罐装饮料识别这种单类目标检测任务,我建议从yolov8s开始。yolov8n虽然最快,但小目标检测能力偏弱;yolov8m及以上在GTX 1660Ti这类显卡上训练时间会明显拉长,6GB显存甚至可能爆显存。
训练命令如下:
yolo detect train data=data.yaml model=yolov8s.pt epochs=200 imgsz=640 batch=16 device=0 patience=30各参数含义:
data=data.yaml:指定数据集配置model=yolov8s.pt:使用yolov8s的预训练权重作为初始权重,这是迁移学习的核心——利用在COCO数据集上学习到的通用特征,能显著加快收敛速度epochs=200:最大训练轮数。并不是轮数越多越好,超过100轮后模型通常已经收敛,继续训练只是浪费时间,配合patience参数可以在指标不再提升时自动停止imgsz=640:训练时输入图片的尺寸,YOLOv8会自动将原图等比缩放到这个尺寸batch=16:批次大小,16在8GB显存下比较安全device=0:使用第一张GPUpatience=30:如果连续30轮验证集指标没有提升,训练提前终止
提示:如果显存不足,优先降低batch而不是imgsz。batch=8照样能训练出不错的效果,但imgsz降低太多会直接影响小目标的检测能力。
4.2 训练过程监控与调优
训练开始后,终端会实时打印每一轮的损失值、精度(Precision)、召回率(Recall)和mAP指标。我习惯用tensorboard可视化这些指标,YOLOv8默认在训练结束后会生成results.png曲线图,如果想在训练过程中实时查看,可以额外加上project=results name=train参数,然后用tensorboard打开:
tensorboard --logdir runs/detect/train观察训练曲线时,我重点关注两个趋势:一是训练集loss是否持续下降并趋于平稳,如果loss下降缓慢或者震荡剧烈,可能是学习率设置不当,YOLOv8默认学习率0.01对大多数情况有效,但如果自己的数据集很小,降低到0.005会更稳定;二是验证集mAP50是否随训练轮数上升,如果mAP50上升后迅速回降,说明模型过拟合了,这时候需要增强数据增强、增加正则化或者收集更多数据。
训练过程中值得记录的一个现象是:前30轮mAP50上升很快,从0直接冲到0.6左右,这是预训练权重迁移学习的好处。但到了80轮之后,mAP50提升就很缓慢了,从0.7到0.85花了差不多60轮。这说明前期收敛靠的是预训练权重里的通用特征,后期提升靠的是对饮料数据的细粒度适配。如果时间紧张,训练到mAP50基本平稳就可以提前停止了,不必死等200轮这个参数。
5. 模型评估与结果分析
5.1 评估指标解读
训练结束后,YOLOv8会在run目录下保存best.pt和last.pt权重,同时在验证集上计算各项指标。我这次训练完的结果大致是:mAP50约0.86,mAP50-95约0.58,精度约0.84,召回率约0.77。这里解释一下这些指标的含义:
- mAP50:IoU阈值设为0.5时的平均精度均值,可以理解为"框得差不多的识别准确率",这是许多检测任务报告的主指标
- mAP50-95:IoU阈值从0.5到0.95以0.05步长递增,计算多个IoU下的mAP再取平均,要求更高,反映模型对定位精度的能力
- 精度(Precision):模型预测为正例的结果中,预测正确的比例,精度高意味着"检出的大多是准的"
- 召回率(Recall):所有真实正例中,被模型正确检出的比例,召回率高意味着"漏检少"
对于超市饮料识别这类场景,我个人觉得精度和召回率比mAP50更值得关注。因为实际部署时往往需要接一个前置判断:如果识别到饮料就触发后续逻辑(比如价格计算、库存管理等),漏检一个商品会直接导致业务错误,所以召回率最好保持在较高水平。如果自己的场景里漏检严重,可以尝试调低置信度阈值,默认是0.25,降到0.15会多检出一些低置信度的目标,代价是误检也会增加。这是一个需要根据业务容忍度来权衡的参数。
5.2 验证集效果与失败案例分析
光看数字还不够,我习惯从验证集里随机抽几十张图片,用训练好的best.pt做推理,对比人工标注的结果,直观感受模型的实际识别水平。这次测试下来,发现模型有几个典型问题:
第一,红色瓶身的混检。红牛是金黄色为主,东鹏特饮是棕褐色,但有些暗光环境下、或者瓶身包装磨损的情况下,模型会把红牛误检成东鹏特饮。这类误检主要因为两者的瓶型相似且颜色属性在暗光下趋同。解决办法是收集更多暗光场景的图片,或者对暗光图片做亮度和对比度增强后加入训练集。
第二,小目标漏检。距离超过五六米的货架全景图中,养乐多这种小瓶饮料非常容易被漏掉。这不是yolov8模型本身能力不足,而是640x640的输入分辨率下,养乐多可能只有十几个像素宽,网络下采样32倍后,特征图上的有效信息几乎消失了。我尝试把imgsz调到960,mAP有明显提升,但训练和推理耗时也增加了大约1.5倍。
第三,反光误检。红外监控视角下,饮料瓶的反光区域会被模型当作目标边界,产生一个无效的预测框。这类问题比较难通过调参解决,可以在后处理时增加一个"预测框内纹理复杂度"的判断,反光区域的纹理信息通常很单一,可以据此过滤掉一部分误检。
6. 模型导出与部署实践
6.1 导出ONNX和TensorRT格式
训练好的模型要真正用到项目里,一般需要导出成部署格式。YOLOv8支持ONNX、TensorRT、CoreML等多种格式导出。如果部署在服务端并且使用NVIDIA GPU,TensorRT格式是效果最好的;如果跨平台、跨框架,ONNX是通用性最强的选择。导出命令很简单:
yolo export model=best.pt format=onnx imgsz=640 yolo export model=best.pt format=engine device=0导出ONNX之后,可以用onnxruntime进行推理。下面是一个简单的Python推理脚本,可以用来做快速验证:
import onnxruntime as ort import numpy as np import cv2 # 加载ONNX模型 session = ort.InferenceSession('best.onnx', providers=['CUDAExecutionProvider', 'CPUExecutionProvider']) input_name = session.get_inputs()[0].name # 加载图片并预处理 img = cv2.imread('test.jpg') img_resized = cv2.resize(img, (640, 640)) img_rgb = cv2.cvtColor(img_resized, cv2.COLOR_BGR2RGB) img_normalized = img_rgb.astype(np.float32) / 255.0 input_tensor = np.transpose(img_normalized, (2, 0, 1))[None, ...] # 推理 outputs = session.run(None, {input_name: input_tensor})导出TensorRT格式时,如果遇到版本不兼容报错,绝大多数情况是TensorRT版本和PyTorch版本对不上。建议直接用ultralytics官方Docker镜像,里面预装了匹配的环境,可以省去大量排查环境的时间。
6.2 摄像头实时识别演示
部署阶段最有意思的部分是把模型接到摄像头上做实时识别。用YOLOv8自带的预测接口写实时推理非常简单:
from ultralytics import YOLO model = YOLO('best.pt') results = model.predict(source='0', show=True, conf=0.5, imgsz=640)source='0'表示读取第0个摄像头。我实测了一下,在GTX 1660Ti上跑yolov8s,640x640输入,推理速度大约在25到35毫秒每帧,即每秒能处理大约30帧,基本满足实时性要求。如果是yolov8n,可以跑到每秒50帧以上。如果部署到Jetson Nano这类边缘设备,建议导出TensorRT格式并用yolov8n,否则帧率会很低。
部署时需要注意的一个细节是:摄像头的视角和光线与训练数据差异过大时,模型效果会打折扣。比如训练数据都是水平视角拍摄的,部署时摄像头装在货架上方俯拍,模型可能不适应这个新视角。遇到这种情况,不是急着调模型,而是先采集一小部分新视角的图片,用训练好的模型做半自动标注后微调几个epoch,通常效果就回来了。
7. 常见问题与排查技巧实录
7.1 压缩包与文件解压问题
这类数据集在网上传播时经常是zip压缩包,下载后总会遇到各种解压问题。我处理过几个典型的:
一是file is not a zip file错误。这个报错通常说明文件下载不完整,或者文件不是真正的zip格式(可能下载到了网页HTML错误页面)。排查方法是先看文件大小,和网站标注的大小对比;然后用file命令查看文件真实类型;最后重新下载。在Linux系统下解压建议用:
unzip -O UTF-8 罐装饮料识别数据集.zip因为数据集文件名可能包含中文,不指定UTF-8可能导致乱码。
二是压缩包内文件缺失。解压后如果发现images目录和labels目录数量对不上,比如有图片但对应的txt缺失,需要先找出哪些样本缺失,再用删除或重新标注的方式处理。用Python脚本可以快速找出缺失的标注文件:
import os image_dir = 'images/train' label_dir = 'labels/train' for fname in os.listdir(image_dir): stem = os.path.splitext(fname)[0] label_path = os.path.join(label_dir, stem + '.txt') if not os.path.exists(label_path): print(f'缺失标注: {fname}')7.2 训练时的路径与内存问题
YOLOv8训练时最常遇到的报错是找不到数据集路径。这个报错的信息一般是AssertionError: train: No images found in ...。遇到这个报错,按照下面几个方向排查:
- 检查data.yaml里的路径是否存在,在终端用
ls验证 - 检查路径末尾是否有空格或特殊字符
- 检查images目录下是否直接就是图片文件,注意Ultralytics要求目录结构是
images/train/*.jpg,不能多一层或者少一层子目录 - 确认图片扩展名是YOLOv8支持的格式(.jpg、.jpeg、.png等)
另一个容易遇到的是显存不足(OOM)报错。如果训练中途报CUDA out of memory,优先将batch减半,比如从16降到8;如果还是不够,再把模型从yolov8s换成yolov8n;最后还可以在训练命令里加上--cache False,禁止YOLOv8把数据集提前缓存到显存中。
7.3 训练后模型效果不佳的排查思路
如果训练结束后,模型在测试图片上表现不理想,不要急着到处找教程,先系统地排查一遍:
- 先看训练集loss是否正常收敛。如果训练集的loss都降不下去,可能是学习率过大、标注数据噪声过大,或者模型容量不足
- 再看验证集指标是否和训练集指标差距很大。如果训练集mAP很高、验证集mAP很低,说明过拟合,需要增加数据增强、增大训练数据量或者引入dropout
- 然后看推理时的conf阈值设置。训练时mAP是在不同置信度下的综合评估,实际部署时如果conf阈值设得过高,会漏掉低置信度的真目标
- 最后看输入分辨率。如果检测目标占比都很小,640x640分辨率可能不够,建议用960或1280重新训练
按这个顺序排查,大部分模型效果问题都能定位到具体原因,比盲目调参要高效得多。
8. 扩展思路:从饮料识别到商品识别通用方案
这套数据集训练出来的模型,如果只用来识别这8类商品,价值有限。但如果把它当作一个起步点,其实可以扩展出很多实用的方案。
一种扩展方式是加入更多的商品类别。在当前模型的基础上,用少量新类别的图片做半自动标注,然后微调模型,就能快速扩展到几十上百种商品。这也是工业界做商品识别常用的方式:先用公开数据集或者预训练模型打底,再针对自己的业务数据做小样本微调。
另一种扩展方式是技术栈升级。如果识别场景从静态图片变成了动态视频流,可以结合目标跟踪技术(比如ByteTrack、DeepSORT),给每个检测到的商品分配一个独立ID,这样即使商品被手遮挡也能持续追踪,在结算场景中可以做更精细的交互逻辑。YOLOv8官方仓库里已经有现成的BoT-SORT和ByteTrack的集成代码,直接调用即可。
还有一个方向是做单类商品的细粒度识别。比如把"可乐"细分为"可口可乐"和"百事可乐",这类任务表面上还是检测,但实际上类别间差异非常小,需要更关注瓶身logo、配色等局部特征。如果自己的数据量不大,这种细粒度识别可能需要借助OCR先识别瓶身上的文字区域,再结合检测结果做综合判断。
这套数据集整体上属于一个中规中矩的入门级目标检测数据,但把它完整跑一遍,从解压数据到训练再到部署,能把目标检测项目里的绝大多数环节都覆盖到。如果手里有类似的数据集,我建议不要只是简单跑一次训练,而是把上面的调优、分析、部署流程都走一遍,这些经验在以后做真实业务项目时会非常有用。
本文还有配套的精品资源,点击获取