ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

273张食品数据集下的YOLO系列目标检测训练实战解析

2026/10/7 6:06:08 拓冰建站 浏览量
273张食品数据集下的YOLO系列目标检测训练实战解析 简介一套面向YOLO系列算法的食品目标检测数据集聚焦鸡蛋、冰箱、米饭等常见物品适合目标检测初学者学习模型训练流程也适合开发者快速搭建食品识别应用。压缩包共547个文件包含273张jpg图像、273个配套txt标签和1个yaml配置文件标签按YOLO标准格式写入每行依次为类别索引、归一化中心点x坐标、y坐标、框宽度和框高度数值均在01之间数据划分与类别设置已就绪可在YOLOv5至YOLO11多个版本中直接调用也可按要求转换为VOC格式。整体大小仅13.7MB轻量、易下载训练迭代速度快能有效省去自行采集标注与分配数据集的步骤。目前已有56人学习下载。这套数据适合作为YOLO模型效果对比实验的数据基础也可以作为食品识别、智慧零售等项目的迁移学习素材帮助完成从数据准备到模型验证的完整目标检测任务。1. 一份273张的食品数据集yolo系列算法训练够不够用先给结论273张图、带YOLO格式标签的食品数据集放在yolov5到yolo11全系列上都能直接开训前提是你别指望一张图塞五六个密集小目标还能一次收敛。这份资源覆盖的场景很明确——鸡蛋、冰箱里的食材、米饭这类“餐厨食品目标检测”标签是标准的class x_center y_center width height归一化坐标已经划分好训练集、验证集和测试集少了自己写split脚本这一步。适合两类人一类是刚开始接触yolo系列目标检测想拿一份干净、标签没毛病的现成数据集跑通yolov5或yolov8全流程另一类是要做食品识别、餐饮视觉相关原型验证手里没有标注数据先拿这份资源起底做可行性测试。我的建议是把它当“跑通流程的基准数据集”用而不是当“生产级训练集”用。273张的体量决定了它更适合验证模型结构、调试训练参数、熟悉标签格式真正上线前你大概率还得自己扩数据。接下来我会从数据集结构、标签校验、实际训练、增广策略到避坑记录把这份资源从头到尾拆一遍。2. 数据集结构与YOLO标签格式先拆目录再谈训练2.1 目录结构与文件对应关系解压后你会看到典型的YOLO数据集布局images目录下分train、val、test三个子目录labels目录下的子目录结构与之完全对应。文件名像img_0132_5.jpg和img_0132_5.txt这样一一配对前缀相同、后缀不同。这种“同名不同扩展名”的映射是yolo系列训练器读取数据的基础images/train/img_0132_5.jpg对应的标签文件必须位于labels/train/img_0132_5.txt路径层级错一层都读不到。我自己拿到这类资源的第一件事不是急着配yaml文件而是先写一段脚本统计每个集里图片和标签的数量是否一致。由于这份数据集的划分已经做好了少了这个检查训练时大概率会撞上Image not found或者Label not found的中断。import os for split in [train, val, test]: img_dir fimages/{split} lbl_dir flabels/{split} imgs {f.split(.)[0] for f in os.listdir(img_dir) if f.endswith(.jpg)} lbls {f.split(.)[0] for f in os.listdir(lbl_dir) if f.endswith(.txt)} only_img imgs - lbls only_lbl lbls - imgs print(f[{split}] images{len(imgs)} labels{len(lbls)}) if only_img: print(f 缺标签的图片: {list(only_img)[:3]}) if only_lbl: print(f 缺图片的标签: {list(only_lbl)[:3]})这段脚本用集合差集找出“有图无标签”和“有标签无图”的文件逻辑很简单先把文件名去掉扩展名做成集合再求差集。任何一边查到非空集合都说明这份资源的文件配对有问题需要手动补齐或删除多余文件。正常情况是两边数量完全相等only_img和only_lbl都为空集。2.2 YOLO标签格式逐项拆解与代码校验摘要里写得很清楚标签格式是class x_center y_center width height五个字段全部是空格分隔坐标值归一化到0到1之间。class是类别索引从0开始x_center和y_center是目标框中心点相对于图像宽高的比例width和height是目标框宽高相对于图像宽高的比例。注意这里存的是相对值不是像素绝对值所以同一张图缩放后标签依然有效。光看格式定义没用我习惯抽查几个标签文件把归一化坐标换算回像素值看一眼确认有没有越界或明显的标框错误。下面这段脚本会读取某一张图的宽高把标签里的归一化中心点和宽高换算成像素坐标再用OpenCV画出来直接目检。import cv2 img_path images/train/img_0132_5.jpg label_path labels/train/img_0132_5.txt img cv2.imread(img_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f.readlines(): parts line.strip().split() cls int(parts[0]) x_center float(parts[1]) * w y_center float(parts[2]) * h box_w float(parts[3]) * w box_h float(parts[4]) * h x1 int(x_center - box_w / 2) y1 int(y_center - box_h / 2) x2 int(x_center box_w / 2) y2 int(y_center box_h / 2) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(cls), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imwrite(check_visual.jpg, img)这段脚本把一行标签拆成五个字段前两个坐标乘以图像宽高得到像素坐标宽高同样换算后画矩形框。画出来的框如果明显偏离目标本体说明标签本身有问题如果框边界超出图像范围说明归一化坐标计算时把宽高和中心点搞混了。这种目检对任何yolo数据集都是第一道质检工序花两分钟能省下后面几小时的无效训练。2.3 类别统计与数据平衡性检查273张图听上去不多但如果三个类别的样本分布极不均衡——比如鸡蛋出现200次而米饭只出现20次——训练出来的模型会产生严重的类别偏向。yolo系列训练时会根据每个batch里的目标数量计算损失类别样本少的那一类损失贡献小训练结果往往是对多数类过拟合、对少数类几乎不检测。from collections import Counter cls_counter Counter() for split in [train, val, test]: lbl_dir flabels/{split} for name in os.listdir(lbl_dir): with open(os.path.join(lbl_dir, name), r) as f: for line in f.readlines(): cls_id int(line.strip().split()[0]) cls_counter[cls_id] 1 print(cls_counter)运行结果会告诉你每个类别ID在整份数据集里的目标框总数。假如类别分布悬殊可以在data.yaml里调整weight参数或者针对少数类单独做增广。食品检测场景里鸡蛋、冰箱、米饭这三类目标形态差异大但鸡蛋这种小而圆的物体最容易因为标注框偏移一两像素导致AP值波动明显类别统计是提前发现风险的手段。这套流程走完数据集本身的状态你心里就有底了。3. YOLOv8实战训练配置、命令与损失函数观察3.1 环境准备与data.yaml配置训练之前先把data.yaml配好。yolo系列从v5到v11的配置文件格式基本一致核心就是path、train、val、test四个路径和nc、names两个字段。这份资源已经划分好直接把路径指到对应的三个目录即可。path: ./food-items-gldps train: images/train val: images/val test: images/test nc: 3 names: 0: egg 1: fridge 2: rice路径最好填绝对路径或相对path字段的路径不要填train: ./food-items-gldps/images/train这种把完整路径写死的形式。yolo训练器在读取时会把path和train拼接成完整路径写重复了会报AssertionError。nc的值要和标签文件里出现过的类别索引最大值1保持一致比如标签里只出现过0、1、2nc就是3。names的排列顺序会决定模型预测输出的类别名称随便起名可以但顺序不能乱。环境方面yolov5和yolov8的依赖差异不大核心就是torch和torchvision。建议先装CPU版跑通流程再用GPU版训练避免一上来就撞CUDA版本不匹配的坑。同样一份数据集在RTX 3060上yolov8n训练300轮大概50分钟纯CPU可能要三四个小时图省事可以先减少轮数验证流程。3.2 训练命令与参数选择拿yolov8为例最短的训练命令一行就够yolo detect train datadata.yaml modelyolov8n.pt epochs100 imgsz640 batch16 patience20modelyolov8n.pt这里有个习惯性操作直接用官方预训练权重做迁移学习而不是从零开始训练yolov8n.yaml。273张图的数据量从零训练几乎不可能收敛迁移学习是唯一务实的选择。epochs100对这份数据来说是合理区间再多就很容易过拟合。imgsz640是yolo系列的黄金分辨率食品目标不算极小640够用。patience20控制早停如果验证集mAP连续20轮不涨就自动终止这个参数对防止过度训练很有用。如果换成yolov5命令会变成python train.py --data data.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100yolov5和yolov8在参数命名上有差异v5用--imgv8用imgszv5的--weights对应v8的model。这份数据集的标签格式两者通用不需要额外转换。yolov7的命令行参数风格又不同走的是train.py加--data、--weights、--batch的方式。无论哪个版本标签文件始终是那份五个字段的txt这正是资源“适用yolo系列算法”这句话的实际含义。3.3 训练过程中的损失函数观察训练日志里有两组关键指标box_loss、cls_loss、dfl_loss以及验证集的mAP50和mAP50-95。yolo系列最新版本的损失函数组合一般是分类损失BCE加上边界框回归损失CIoU或DFL三者加权重合成最终loss。训练时如果看到box_loss从2.x降到0.9左右、cls_loss从1.x降到0.3以下说明模型在学习如果cls_loss降到0.1以下但mAP还是拉胯问题大概率在标签本身不在损失函数。yolo detect train datadata.yaml modelyolov8n.pt epochs100 imgsz640 batch16 patience20 projectruns namefood_exp加project和name参数可以让每次实验的输出放到独立目录避免后续实验覆盖掉之前的权重和曲线。训练结束后模型权重保存在runs/food_exp/weights/best.pt和last.pt前者是验证集指标最优的权重后者是最后一轮的权重。我的习惯是优先用best.pt做推理测试last.pt留作过拟合对比。验证集曲线里的mAP50-95对这张273张的数据集而言能在0.7以上已经算不错食品检测场景mAP50更有参考价值它只要求在50% IoU下检测到目标就算命中更贴近实际应用的判定标准。4. 把273张用出2730张的效果增广策略与小数据集训练技巧4.1 yolo内置增广与超参数设置273张图要训练出能用的检测模型必须靠数据增广撑场。yolo系列最核心的增广手段是mosaic把四张图随机拼接成一张再参与训练变相扩大了单次训练看到的样本组合和目标尺寸分布。yolov8的超参数里默认已经开启mosaic但针对食品这类目标边缘清晰的场景可以进一步把hsv_h、hsv_s、hsv_v调高因为餐厨环境的灯光色温变化本来就大鸡蛋在黄光下和白光下的颜色差异显著augment的颜色抖动越强模型对光照变化的鲁棒性越好。# hyp.scratch.yaml 片段只列调整项 mosaic: 1.0 hsv_h: 0.02 hsv_s: 0.8 hsv_v: 0.6 fliplr: 0.5 scale: 0.5mosaic: 1.0代表每轮训练都有mosaic增广参与但对小数据集来说mosaic有个副作用如果参与拼接的图中目标特别少拼出来的图大半区域是背景模型学到的背景特征会增多。scale: 0.5控制随机缩放比例这个值不宜过大否则鸡蛋这种小目标被缩到十几个像素就彻底失去检测意义。我的经验是食品数据集里scale调到0.4到0.6之间最稳。4.2 外部增广与类别平衡手段内置增广做的是锦上添花外部增广才是把小数据集盘活的真正手段。常见做法是用albumentations对原始图片做离线增广生成扩增后的图片和标签再并入原数据集。具体操作是把原图做水平翻转、随机亮度和对比度调整、轻微模糊每张图生成两到三个变体标签坐标随之同步变换。import albumentations as A import cv2 import os transform A.Compose([ A.HorizontalFlip(p0.5), A.RandomBrightnessContrast(p0.8), A.GaussNoise(p0.2), ], bbox_paramsA.BboxParams( formatyolo, label_fields[class_labels] )) out_img_dir images/train_aug out_lbl_dir labels/train_aug os.makedirs(out_img_dir, exist_okTrue) os.makedirs(out_lbl_dir, exist_okTrue) for img_name in os.listdir(images/train): if not img_name.endswith(.jpg): continue img_path os.path.join(images/train, img_name) lbl_name img_name.replace(.jpg, .txt) lbl_path os.path.join(labels/train, lbl_name) image cv2.imread(img_path) with open(lbl_path, r) as f: lines f.readlines() boxes [] labels [] for line in lines: parts list(map(float, line.strip().split())) labels.append(int(parts[0])) boxes.append(parts[1:]) for i in range(2): augmented transform(imageimage, bboxesboxes, class_labelslabels) aug_img augmented[image] aug_out f{img_name.split(.)[0]}_aug{i}.jpg cv2.imwrite(os.path.join(out_img_dir, aug_out), aug_img) with open(os.path.join(out_lbl_dir, aug_out.replace(.jpg, .txt)), w) as f: for bbox, label in zip(augmented[bboxes], augmented[class_labels]): f.write(f{label} { .join(map(str, bbox))}\n)关键点在于bbox_params里的formatyolo。albumentations要求框坐标必须和输入图片的格式声明一致这里声明yolo格式它内部会先转换成像素格式做变换再转换回归一化坐标输出。增广过程中boxes里的坐标顺序是[x_center, y_center, width, height]和YOLO标签字段顺序完全一致不需要调整。生成的新图放在train_aug对应目录后再把images/train_aug和labels/train_aug并入data.yaml的train路径即可训练时原图加增广图混合参与。4.3 小数据集训练的轮数与早停策略外部增广做完后数据集可能从273张扩到600张上下。这时候epochs可以适当增加但不要无脑拉满。我的经验是300轮以内配合早停足够再多的轮数对这个小规模数据只会让模型把训练集里的噪声细节背下来验证集指标反而下滑——典型的过拟合信号是train_loss持续下降而val_loss开始回升mAP曲线同步掉头。看到这种情况直接取best.pt做模型即可。数据增广的有效性可以用一个简单实验验证同一份数据集分别关闭和开启增广训练对比验证集mAP50的差距。如果差距小于0.05说明原始数据已经足够丰富增广只是锦上添花如果差距在0.1以上说明增广是这份资源训练成功的决定性因素。食物类目标的边缘和颜色特征比较鲜明增广效果通常很显著。5. 常见问题与避坑换模型、转格式、小样本的五个坑5.1 换yolov5时标签索引从1开始导致类别错乱现象用yolov5训练这份数据集时模型推理输出所有目标的类别索引都比实际大1鸡蛋被识别成冰箱或者输出类别ID直接越界。原因Kaggle等平台导出的数据集经常沿用VOC标注习惯类别索引从1开始而YOLO格式的class必须从0开始。如果前一个使用者手动改了标签文件里的一些索引或者数据集原始标注里0号类别是背景类没被清掉就会出现这种错位。解决写脚本遍历labels下所有txt文件把class字段整体减1或加1。关键是先看data.yaml里names定义的类别顺序再对照标签文件里实际出现的类别ID最大值。如果标签里出现3而names只有3个类别几乎可以确定是从1开始计数的残留问题。5.2 直接用文本编辑器打开标签文件看到的是乱码或全0现象用记事本打开标签txt发现内容不是预期的五个数字而是一行乱码或者全是0。原因YOLO标签文件是纯文本正常情况用任何编辑器打开都是可读的数字。如果出现乱码可能是文件本身以UTF-8带BOM格式保存BOM头干扰了训练器的读取如果全是0大概率是标注工具导出时把归一化坐标写成了整数0真正的坐标值丢失了。解决检查文件编码用sed -i s/^\xEF\xBB\xBF//去掉BOM头或者在Python里统一按utf-8-sig编码读入再重新写回。全0的文件直接删除并在训练前重新标注不要试图修复因为原始坐标信息已经不存在了。5.3 273张小数据集直接训练300轮mAP反而下降现象损失函数一路下降训练集mAP接近1但验证集mAP在100轮后不升反降推理结果出现大量漏检。原因这是典型的过拟合。273张图的信息量撑不起300甚至500轮高容量模型的训练需求模型到后期在背诵训练集图像的背景纹理和目标摆放位置而不是学习通用的“鸡蛋特征”。尤其冰箱这类背景占比大的场景模型很容易把特定角度、特定光线的冰箱局部当作特征记住。解决把epochs控制在100左右开启patience20提前终止同时用4.2节的外部增广扩充数据量。还有一个实操技巧如果mAP下降来得特别早比如30轮内先检查是不是学习率衰减策略太激进把lr0从默认0.01调低到0.005重训一轮对比。5.4 训练时报错找不到类别ID对应的名称现象data.yaml里nc设置为3但训练日志在读取某个标签时抛出class index out of range的错误。原因标签文件里存在大于nc-1的类别ID。这份资源里如果出现某个class字段等于3而配置的nc3合法索引只有0、1、23就超界了。常见原因是数据集混入了其他来源的标签文件或者原始标注里把某类标成了不存在的ID。解决跑一遍2.3节的类别统计脚本把标签里出现的所有类别ID列出来找出超界的那几个文件手动改到正确类别。检出超界文件本身就能说明数据集里混入了不属于这份资源的标签稳妥起见直接删除该文件并重新标注。5.5 验证集样本太少一个batch全是一个类别的图现象验证集指标忽高忽低同一份权重在两次评估间的mAP波动超过0.1。原因原始划分的验证集可能只有30到40张图分布不均某个类别在某些验证batch里完全没出现导致mAP计算时该类别AP为0拉低整体指标。食品数据集类别少这个问题更明显。解决训练时把val的batch设成1让每张验证图单独参与评估或者干脆把测试集并进验证集用。用yolov8可以直接在训练命令里加batch1配合val评估虽然会慢一些但指标更稳定。如果验证集确实太小另一个办法是重新划分训练验证集按类别分布做分层采样保证每类在验证集里至少有5到8个目标实例。6. 从训练到落地的最后一公里混淆矩阵、单张推理与模型导出训练完不等于交付完。我拿到一份新训练好的食品检测权重第一件事永远是打开训练输出目录里的confusion_matrix.png。别急着看mAP数字混淆矩阵能告诉你模型真正分不清什么。食品场景里最容易出问题的往往是鸡蛋和米饭的误检——鸡蛋在特定光照下表面反光颜色接近米饭的米白色如果混淆矩阵里这两个类别的交叉格子数值偏高说明特征区分度不够需要回到增广阶段给这两个类别单独加对比度和饱和度扰动。from ultralytics import YOLO model YOLO(runs/food_exp/weights/best.pt) results model.predict(sourceimages/test/img_0812_75.jpg, conf0.25, saveTrue)conf0.25是一个经验值。yolo系列的置信度阈值默认是0.25但食品检测这种误检代价低的场景可以调到0.2把召回率提上来如果部署到工业质检场景则要调到0.4以上宁可漏检不能错检。saveTrue会把带框的结果图存到runs/detect/predict目录直接肉眼检查检测框和标签的重合程度。我一般会在测试集里挑一张目标密集的图片和一个低对比度场景各跑一次推理前者看漏检后者看过检。模型导出到ONNX是整个流程最容易翻车的环节但也是部署到边缘设备的必经之路yolo export modelruns/food_exp/weights/best.pt formatonnx opset12 imgsz640opset12是为了兼容老设备。如果你用的是yolo11或yolov10的新权重部分算子可能要求opset更高但食品检测场景用不到那些新特性opset12能覆盖大多数边缘推理框架。导出完成后顺手跑一遍onnxruntime推理验证导出的图和PyTorch原模型输出是否一致差距在0.01以内就说明导出成功。从那以后我每次做完小样本训练都强制走一遍“混淆矩阵→单张推理→ONNX导出”的三步流程不做完不交付。这份273张的食品数据集能帮你跑通yolo全流程但真正的检测效果还得看你在数据增广和阈值调参上下的功夫。希望这份拆解能让你少走几步弯路早日跑出自己满意的模型。本文还有配套的精品资源点击获取