YOLO食品检测实战:273张小数据集训练全流程与避坑指南
简介面向YOLO系列算法学习与目标检测实战的食品图像数据集包含273张带标签的日常食品图片覆盖鸡蛋、冰箱、米饭等常见物品适用于yolov5、yolov7、yolov8、yolov9、yolov10、yolo11等主流检测框架。标签采用YOLO标准格式每个txt文件记录目标类别、归一化中心点坐标及宽高可直接用于模型训练、验证与测试也支持转换为VOC格式。压缩包共547个文件以273张jpg图像、273个txt标注文件和1个yaml配置文件为主整体仅13.7MB数据规模轻量适合教学演示、算法对比与快速迭代。数据集已预先划分好训练集和验证集免去手动整理标签的麻烦目前已有56人学习下载对于刚接触目标检测或想快速跑通YOLO流程的开发者是一份低门槛的入门练习资源。1. 273张图训练YOLO食品检测到底靠不靠谱“yolo算法-食品数据集-273张图像带标签-鸡蛋冰箱米饭food-items-gldps.zip”这个名字看起来像个一次性数据集压缩包但它真正的问题是只有273张带标签图像YOLO能训出能用的食品检测模型吗我的答案是能但前提是你把它当“启动数据集”而不是“最终数据集”。这套数据覆盖鸡蛋、冰箱里的常见食材、米饭等food-items类别适合快速验证YOLO训练流程、对比算法选型、给边缘设备做原型。本文从标签格式核对、目录规整、训练参数配置一直写到小数据集最常见的翻车现场和排查手段全程按可复现步骤来。2. 先看懂food-items-gldps数据集标签格式与类别分布2.1 解压之后先看什么目录结构与标签文件拿到zip包别急着开训。我一般先解压并列出目录结构确认图像和标签是否分离、标签是YOLO的txt格式还是VOC的xml格式。YOLO训练器默认只认与图像同名的txt文件如果包里有的是xml得先转换。unzip food-items-gldps.zip -d food-items find food-items -maxdepth 2 -type d | sort find food-items -maxdepth 2 -type f | head -50-d指定解压目录第一个find看目录层级重点找images和labels是否分开放第二个find抽样看文件类型。常见的数据包结构是images/和labels/平级里面再分train和val。如果所有文件和图片混在一起说明需要自己划分后面第三章就干这件事。看到txt标签后用head抽查内容。YOLO格式每行固定5列类别编号、中心点x、中心点y、框宽、框高后四列都是相对图像宽高的归一化值。检查一下就知道标注工具是否导出错误。head -5 food-items/labels/train/0001.txt wc -l food-items/labels/train/0001.txthead看前5行是否符合“class_id x_center y_center width height”的顺序wc -l统计这个文件里有几个目标。如果一个图像里明明有3个鸡蛋却只标了1行说明标注缺失训练时模型会学着漏检。还要注意文件名后缀jpg对应txtpng对应txt后缀不一致时YOLO会直接跳过标签报一堆“no labels found”。2.2 273张图能训几个类类别平衡与样本量下限273张图听起来少但如果每张图平均3个目标就有约800个实例单类检测绰绰有余3类以内也能训出能看的模型。问题在于类别数不能拍脑袋定。像food-items-gldps这种命名通常包含鸡蛋、米饭、冰箱里的包装食材但实际标签可能有5个甚至更多类。多类很可能是标注者把“蛋壳碎片”“蛋黄”“水煮蛋”都拆成了独立类实例数被摊薄。我一般先统计每个类别的实例数用一条awk命令看分布。for f in food-items/labels/*.txt; do awk {print $1} $f done | sort | uniq -c | sort -nawk取每行第一列即类别编号sort | uniq -c统计每个编号出现次数最后的sort -n按数量升序排列。结果类似“1 18, 0 122, 2 45”。如果某个编号少于20个实例这一类在验证集上基本会翻车因为YOLO很难从十几个框里学到稳定的特征。我的经验底线单类检测最少50个实例多类检测每类至少30个否则就合并相似类。比如把“蛋黄”和“鸡蛋”合并成egg把“白米饭”和“炒饭”合并成rice。273张图还有另一个隐含风险相似图像可能重复。冰箱食材数据集经常连拍同一个苹果在不同角度、不同光照下的照片视觉上高度相似训练集和验证集会互相泄漏。后面划分时要先做图像去重否则验证集指标虚高。2.3 标签格式核对坐标越界与类别编号打开标签文件最常见的问题是坐标没归一化。很多标注工具导出时默认给像素坐标中心点和宽高都大于1YOLO读到这种框会当作无效目标直接丢弃。损失照常降但mAP永远为零。用一段Python脚本扫描所有标签先把问题框找出来。import os label_dir food-items/labels bad [] for fn in os.listdir(label_dir): if not fn.endswith(.txt): continue with open(os.path.join(label_dir, fn)) as f: for line in f: parts line.strip().split() if len(parts) ! 5: bad.append((fn, 列数不对, line.strip())) continue cid, cx, cy, w, h int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) if not (0 cx 1 and 0 cy 1 and w 0 and h 0): bad.append((fn, 坐标越界, line.strip())) print(len(bad), 条问题标签) for b in bad[:10]: print(b)这段脚本扫描全部txt检查两件事行结构是否满足5列中心点坐标是否在0到1之间。w和h大于1也算越界。发现像素坐标需要批量转换中心x等于(xmin加xmax除以2再除以图像宽)框宽等于(xmax减xmin)除以宽y方向同理。转换后图像和标签要一一对应不能只改标签不改文件名。提示类别编号是整数且从0开始。如果数据包里有个names.txt它的第一行对应编号0第二行对应编号1顺序错位会让模型把鸡蛋学成米饭而且训练过程毫无报错。3. 把数据集接进YOLO训练从目录规整到配置文件3.1 标准目录规整与分层划分YOLO训练器期望的目录结构很固定最好一次到位food-items/ images/ train/ val/ labels/ train/ val/ data.yaml如果下载下来的数据只有images和labels两个平级目录没有train和val子目录需要手动划分。273张图我建议按8:2划分train约218张val约55张。不要随机划分因为少数类可能全被分进val训练时模型见都没见过这一类。按类别做分层采样更稳。import os, shutil, random from collections import defaultdict img_dir food-items/images label_dir food-items/labels random.seed(42) imgs [f for f in os.listdir(img_dir) if f.endswith((.jpg, .png, .jpeg))] by_class defaultdict(list) for im in imgs: stem im.rsplit(., 1)[0] lbl os.path.join(label_dir, stem .txt) if not os.path.exists(lbl): continue try: first open(lbl).readline().split()[0] except Exception: continue by_class[first].append(im) train_imgs, val_imgs [], [] for cid, lst in by_class.items(): random.shuffle(lst) cut max(1, int(len(lst) * 0.8)) train_imgs lst[:cut] val_imgs lst[cut:] for split, files in [(train, train_imgs), (val, val_imgs)]: os.makedirs(ffood-items/images/{split}, exist_okTrue) os.makedirs(ffood-items/labels/{split}, exist_okTrue) for im in files: stem im.rsplit(., 1)[0] shutil.move(f{img_dir}/{im}, ffood-items/images/{split}/{im}) if os.path.exists(f{label_dir}/{stem}.txt): shutil.move(f{label_dir}/{stem}.txt, ffood-items/labels/{split}/{stem}.txt) print(train:, len(train_imgs), val:, len(val_imgs))逻辑说明先读每张图标签文件的第一行第一列确定它属于哪个类别然后按类别分组。每组内部随机打乱后取八成进train其余进val。random.seed(42)固定随机序列保证你复现时划分结果一致。cut max(1, ...)防止某类只有1张时train为空。最后把图像和同名标签一起移动进子目录。注意这里只按首行类别做分层没有去重。如果数据里有连拍帧比如同一个鸡蛋在0.1秒内拍了3张它们会分别落进train和val验证集分数虚高。严谨做法是先算图像感知哈希把相似度超过阈值的归到同一组整组只进一个split。这个步骤在273张图上成本不高但收益很大。3.2 写data.yaml类别映射与路径规范目录规整好之后创建data.yaml。它是YOLO训练器唯一需要的数据配置入口路径写错比标签写错更隐蔽。path: /absolute/path/to/food-items train: images/train val: images/val names: 0: egg 1: rice 2: fridge_itempath必须是绝对路径不能写~/food-items或相对路径Ultralytics在部分版本里对相对路径解析有坑。train和val是相对path的子路径这里只写到images/train训练器会自动去labels/train找同名txt。names的键是类别编号必须和标签txt第一列的编号一致。如果你的数据包里原本有8个类我建议先从names.txt读出原始类别名再决定合并策略。比如“fridge_item”本来分成了“milk”“juice”“yogurt”实例数都不够合并成一个父类能显著提升稳定性。合并标签时要同时改txt第一列和names两边不一致时训练不报错但验证时类别显示错乱。3.3 最小训练命令用预训练权重跑通流程配置写好后用YOLOv8的官方包开训。YOLOv11的命令格式相同只是模型名换成yolo11n.pt。第一次跑通不要追求精度先把流程走完。pip install ultralytics yolo detect train \ modelyolov8n.pt \ datafood-items/data.yaml \ epochs100 \ imgsz640 \ batch16 \ patience20 \ projectruns/food \ nameexp1modelyolov8n.pt会从官方源下载预训练权重。这个参数对小数据集是续命关键模型带着COCO上学的特征迁移到食品检测收敛速度和精度都比从零训练强很多。epochs100配合patience20连续20个epoch验证集没有提升就早停避免过拟合。batch16在显存不够时降到8。project和name决定输出目录最后权重在runs/food/exp1/weights/best.pt。训练结束后用best.pt跑一次快速验证确认mAP不是零yolo detect val \ modelruns/food/exp1/weights/best.pt \ datafood-items/data.yaml如果val的mAP50低于0.3先别急着调参回到第2章重新核对标签。我见过太多案例参数调了一周最后发现是标签类别编号整体大1。4. 小数据集训练的5个必调参数不调等着过拟合4.1 epoch、batch与imgsz怎么配273张图最忌讳的就是把epoch拉满。有人习惯COCO那一套300轮训练但那是百万级数据量小数据集跑到100轮以后模型开始背训练集鸡蛋的纹理、碗边的阴影、冰箱里的反光全部记进权重验证集表现反而下滑。我一般用epochs100加上patience30让早停决定实际轮数。batch能大则大。小数据集的梯度噪声大batch8时loss曲线像心电图batch32时平滑很多。显存不够就降imgsz但imgsz对检测效果影响更大。冰箱里的鸡蛋、米饭团在640分辨率下可能只有二三十像素属于小目标。如果验证时发现漏检严重把imgsz提到960或1280能明显改善显存占用大概翻两到四倍。训练和推理的imgsz必须一致否则框偏。4.2 数据增强参数小数据集靠增强续命YOLO默认开启马赛克、随机翻转、HSV扰动这些对食品场景大部分是友好的。马赛克能凭空增加场景多样性让模型看到鸡蛋放在不同位置、不同背景的组合。但有两个坑一是马赛克在最后阶段要调低否则模型学到拼接缝二是垂直翻转对食品是灾难现实里米饭碗不会倒扣在桌上。yolo detect train \ modelyolov8n.pt \ datafood-items/data.yaml \ epochs100 \ batch16 \ imgsz640 \ mosaic0.5 \ fliplr0.5 \ flipud0.0 \ hsv_h0.015 \ hsv_s0.7 \ hsv_v0.4mosaic0.5表示一半训练样本走四图拼接另一半保持原图YOLOv8内部默认是做动态概率调度你手动固定0.5即可。fliplr0.5水平翻转保留flipud0.0关掉垂直翻转。hsv_h是色调偏移幅度0.015是经验值超过0.05会让白米饭变成蓝米饭鸡蛋变成紫色引入假阳性。hsv_s和hsv_v控制饱和度与亮度扰动0.7和0.4能模拟厨房里不同光照。如果你用YOLOv5增强参数写在hyp.scratch.yaml里改文件而不是改命令行。4.3 损失函数与anchor在食品小目标上的表现YOLO损失函数由框回归损失、置信度损失、分类损失三部分构成。YOLOv8把框回归换成了DFL加CIoU的组合对鸡蛋这种椭圆目标、米饭这种密集小目标回归精度比YOLOv5好。但对于20x20像素以下的鸡蛋anchor-free检测头的下采样步长会直接把特征丢失DFL也救不回来。实际项目中我会做两件事。第一把验证阈值调低看模型到底学到了什么yolo detect val \ modelruns/food/exp1/weights/best.pt \ datafood-items/data.yaml \ conf0.05 \ iou0.5conf0.05让模型输出大量低置信度框再靠NMS去重。这样能同时看到真阳性和假阳性判断漏检是阈值问题还是特征问题。如果conf降到0.05后召回率明显提升说明模型其实学到了目标只是置信度低如果召回率没变化说明该学的特征没学到问题在数据或增强。第二如果验证发现小目标漏检集中在某个尺度可以把imgsz固定成960让检测头在更大特征图上工作。这个调整对冰箱场景特别有效。注意小数据集上千万不要一开始就换大模型。yolov8n只有约300万参数yolov8x有6800万273张图喂大模型过拟合几乎不可避免。先从最小的n模型跑通后面加数据再换大模型。5. 273张图训练的常见问题排查从loss不降到mAP为05.1 现象loss降了但mAP为0训练日志里loss稳步下降验证结果全零框一个都画不出来。先别质疑模型九成是标签类别编号和names对不上。YOLO的类别编号从0开始有些标注工具从1开始导出的txt第一列全是1、2、3。names里只有3类模型读到编号3就当成越界框丢掉于是纯背景训练loss当然降因为模型学到“什么都不检测”就是最优解。解决重跑第二章的标签核对脚本打印最大类别编号。如果比len(names)-1大1执行批量减一import os for split in [train, val]: label_dir ffood-items/labels/{split} for fn in os.listdir(label_dir): p os.path.join(label_dir, fn) if not fn.endswith(.txt): continue with open(p) as f: lines f.readlines() with open(p, w) as f: for line in lines: parts line.split() cid int(parts[0]) - 1 f.write(f{cid} .join(parts[1:]) \n)这段脚本把每个txt的第一列减一适用于且仅适用于“编号整体偏移1”的情况。改完先跑10个epoch看val是否有非零mAP。还要检查有没有框宽或框高为0的坏标注YOLO会把它们过滤结果同样导致mAP为0。5.2 现象训练集mAP高、验证集全漏训练集上mAP50接近0.9验证集只有0.1这是过拟合和泄漏的混合症状。小数据集最常见的泄漏是相似图像同时进了train和val。冰箱数据集大量连拍同一个目标视觉几乎一样模型在训练时见过这张“复制品”验证时自然认得出。真实部署时新场景光照变了立刻漏检。解决划分前去重。用感知哈希或简单像素差异计算相似度相似度超过0.95的只保留一张整组进同一个split。另外检查增强参数mosaic和mixup配置过猛会破坏验证图像语义模型被迫记训练图像的细节。把mosaic降到0.3关掉mixup通常能显著缓解验证集暴跌。5.3 现象蛋黄和米饭团分不清这是类别混淆不是模型故障。煮鸡蛋蛋黄和米饭团都是浅黄色圆形团块特征空间重叠严重。我处理过类似case解决方向有三个。第一合并类别如果业务不要求区分蛋黄和米饭团直接把蛋黄并入egg第二补难例从验证集挑出模型置信度在0.3到0.6之间的预测框单独人工精标加进训练集第三检查标签中是否有大面积“框内混类”——比如一个框同时盖住蛋黄和米饭强迫模型在一张框里学两个类标签分类头会无所适从。如果你用的是YOLOv5或YOLOv8类别混淆主要靠分类损失学特征。小数据集上不要迷信更大的模型重点是把混类样本拆开一个框只对应一个类别。5.4 现象导出ONNX或TensorRT后检测框漂移训练时画框正常导出ONNX或TensorRT后框的位置偏了半个身位。原因几乎总是预处理不一致。训练阶段Ultralytics会自动做letterbox把原图等比缩放到640x640剩余部分补灰边。导出部署后如果工程代码直接用cv2.resize压到640x640没有补灰边长宽比被拉伸坐标全偏。解决在推理代码里显式实现letterbox再把模型输出的归一化框映射回原图坐标。常见的做法是记录缩放系数scale和灰边填充量pad还原公式为import cv2 import numpy as np def letterbox(img, new_shape640, color(114, 114, 114)): shape img.shape[:2] r min(new_shape / shape[0], new_shape / shape[1]) new_unpad int(round(shape[1] * r)), int(round(shape[0] * r)) dw, dh new_shape - new_unpad[0], new_shape - new_unpad[1] dw, dh dw // 2, dh // 2 img cv2.resize(img, new_unpad, interpolationcv2.INTER_LINEAR) top, bottom dh, dh (new_unpad[1] % 2) left, right dw, dw (new_unpad[0] % 2) img cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, valuecolor) return img, r, left, top逻辑说明letterbox先按比例缩放再在两侧补灰边返回缩放系数和左上角偏移。推理时拿模型输出的中心点和宽高减去left和top的偏移再除以r就得到原图像素坐标。这个坑在食品检测部署里特别常见因为在边缘设备上大家普遍用TensorRT加速预处理代码常常是工程师手写的。5.5 现象训练时提示no labels found日志里出现no labels found in food-items/labels/train但文件明明存在。按顺序排查第一labels目录路径是否拼错Ultralytics使用data.yaml里的path加train路径确认最终拼接结果第二图像扩展名和标签格式是否匹配jpg对应jpgpng对应png如果图像是jpeg但标签叫同名.jpg匹配失败第三标签文件是否为空。一个命令查空标签for f in food-items/labels/train/*.txt; do if [ ! -s $f ]; then echo 空标签: $f fi done-s判断文件非空。空文件在训练时不会报错但对应图像会被当作背景图大量空标签会让mAP难以提升。如果只有几张空标签直接删除对应图像和标签如果空标签占比超过10%回到标注工具重新导出。6. 验证与进阶用一份混淆矩阵决定要不要加数据6.1 用val模式做一次体检训练完不要急着部署。先跑一次带图的验证生成混淆矩阵yolo detect val \ modelruns/food/exp1/weights/best.pt \ datafood-items/data.yaml \ splitval \ plotsTrue这次验证会输出混淆矩阵和PR曲线保存在runs/food/exp1/下。重点看两个格子rice被错认成egg的比例以及fridge_item的漏检率。如果错认格子占比超过20%说明这两个类在特征上不可分加数据不如合并类。如果漏检集中在某一类别下一步补数据就补这一类而不是平均分配。6.2 从273张到2000张主动学习与难例挖掘273张验证流程足够但要做产品级食品检测数据量还不够。我的做法是先用这个模型对一批未标注图像做批量预测把置信度在0.3到0.6之间的预测框列为难例人工精标再并回训练集。这个区间是模型“说不准”的区域比随机抽图标注值钱得多。每轮标注量控制在100到200张三轮下来数据集涨到500到800张mAP通常能拉开10个点以上。第二件事是去真实环境补拍。厨房里的光照、冰箱内壁的反光、碗的遮挡角度和公开数据分布差异很大。现场拍100张比你网上爬1000张更有效。补数据时保持每类实例数相对均衡不要因为鸡蛋好拍就一直拍鸡蛋米饭和冰箱食材很快会成为瓶颈类。最后说一个我早期踩过的坑拿到这种小数据集总想一口气调出漂亮指标结果把大量时间花在loss曲线和增强参数上。后来改成“先跑通、再体检、再补数据”的循环效率翻倍。食品检测的瓶颈从来不在模型结构而在数据分布和标签质量。先用这份273张的数据集把pipeline跑通再按混淆矩阵指的方向补数据比盲目堆参数更靠谱。希望帮到你。本文还有配套的精品资源点击获取