简介:面向厨房安全智能告警与目标检测训练需求,这份数据集提供了积水(water)与泡沫(foam)两类目标的标注样本,共88张真实厨房环境图片,可用于训练厨房积水检测、地面湿滑预警等视觉模型,也适合目标检测初学者练习VOC/YOLO格式的数据组织与训练流程。数据包含两种主流标注规范,xml文件对应Pascal VOC格式,可直接用于VOC系模型;txt文件对应YOLO格式,便于接入YOLO系列训练框架;jpg为原始图像,另含2个ini配置文件。压缩包共268个文件,整体仅18.23MB,轻量易下载。所有标注使用labelImg完成矩形框绘制,累计目标框562个,其中foam类272个、water类290个,类别分布较为均衡。数据集提供准确合理的标注,可快速开展模型训练与验证,目前已有161人学习使用,适合作为厨房场景视觉感知项目的前期数据基础。
1. 厨房积水检测:88 张图的小数据集,怎么撑起一个落地模型
如果你在找“厨房积水检测数据集 VOC+YOLO 格式 88 张 2 类别”,多半是要做后厨地面积水识别:地面积水导致滑倒风险,或者需要联动排水告警。我拿到这类小数据集的第一反应是:88 张图、2 个类别,数量不大,但足够验证一条检测流程能不能跑通。它同时给了 VOC 和 YOLO 两种标注格式,意味着你既可以用传统两阶段检测路线,也可以直接把图片丢进 YOLOv8 训练,省掉手工转换的步骤。适合刚入目标检测、显卡显存 8GB 上下、想在本机快速出第一版模型的人,也适合做算法可行性验证。不过提前说清楚,88 张是小样本,训练出的模型只能证明“这条路走得通”,离直接上生产还有距离;这篇就是帮你把这段路最短、最稳地走完。
2. 拆开 88 张图的厨房积水数据集:VOC 与 YOLO 格式必须先看懂
2.1 目录结构:VOC 侧和 YOLO 侧各放什么
一个.7z压缩包解压后,常见做法是里面有两个相对独立的目录,一侧遵循 Pascal VOC 标注规范,一侧是 YOLO 格式目录。Pascal VOC 是目标检测领域沿用多年的标准之一,结构固定为三个核心目录:
Annotations/:每张图片对应一个 XML 标注文件,记录图片尺寸、文件名和每个目标的类别与边界框。JPEGImages/:原始图片,一般是 JPG 格式。ImageSets/Main/:存放train.txt、val.txt这样的划分文件,内容是图片文件名(不带扩展名)。
YOLO 侧目录则是给现代训练框架准备的,典型结构如下:
images/train/与images/val/:训练和验证图片。labels/train/与labels/val/:与图片同名的 TXT 标注文件。classes.txt或data.yaml:类别名列表。
我习惯先看classes.txt,再抽 3 到 5 个标注文件打开对照,而不是先翻图片。原因很简单:标注决定了训练上限,图片看再多也看不出标注错没错。
2.2 XML 与 TXT 标注的字段差异
VOC 的 XML 标注是绝对像素坐标,一个典型的积水目标长这样:
<annotation> <folder>JPEGImages</folder> <filename>kitchen_012.jpg</filename> <size> <width>640</width> <height>480</height> <depth>3</depth> </size> <object> <name>water</name> <bndbox> <xmin>120</xmin> <ymin>90</ymin> <xmax>260</xmax> <ymax>210</ymax> </bndbox> </object> </annotation>这段 XML 的含义是:图片kitchen_012.jpg里有一个类别为water的目标,检测框左上角在像素坐标(120, 90),右下角在(260, 210)。xmin/ymin/xmax/ymax全部是整数像素值,读取时不需要做归一化。
YOLO 侧同一个目标在 TXT 里是这样一行:
0 0.296875 0.312500 0.218750 0.250000四个数字依次是:类别ID 中心点x 中心点y 宽度 高度。其中中心点和宽高都做了归一化,公式是:
x_center = (xmin + xmax) / 2 / width y_center = (ymin + ymax) / 2 / height box_width = (xmax - xmin) / width box_height = (ymax - ymin) / height拿上面 XML 举例,图片宽 640、高 480,代入后就是x_center = (120 + 260) / 2 / 640 = 0.296875,和 TXT 里的值一致。可以看到,VOC 转 YOLO 的本质就是把绝对坐标换成相对坐标,同时把类别名映射成整数 ID。如果两张标注对同一个图的数据对不上,排查就从这里开始。
2.3 2 个类别的判定边界
标题写了 2 个类别,解压后classes.txt或data.yaml里能看到具体名称。以常见的厨房积水检测为例,两个类别大概率是“积水区域”和“反光干扰”——这也是这个场景里最容易标错的地方。厨房地面的瓷砖在强光下会有明显反光,肉眼看像一滩水,模型也容易混淆。如果你用的是预标注数据,我建议把每个类别的样本拿出来看一遍,确认反光样本没有被标成积水。边界判定不干净,后面损失函数怎么调都白搭。
3. 训练前先做标注体检:校验、清洗与划分
3.1 用脚本校验 XML 和 TXT 是否对得上
小数据集最容易出的问题不是算法不行,是标注文件错位:XML 里写了 5 个框,TXT 里只有 4 行;或者某个框的边界超过图片尺寸。这类脏数据放进 YOLO 训练,轻则 loss 曲线异常,重则训练直接崩掉。我每次解压完数据集,第一件事就是跑一遍校验脚本。下面这段 Python 代码同时检查 VOC 侧和 YOLO 侧的一致性:
import os import xml.etree.ElementTree as ET voc_root = "VOC" # VOC 侧根目录 yolo_root = "YOLO" # YOLO 侧根目录 image_ext = ".jpg" def check_voc(): ann_dir = os.path.join(voc_root, "Annotations") img_dir = os.path.join(voc_root, "JPEGImages") for xml_name in os.listdir(ann_dir): if not xml_name.endswith(".xml"): continue # 检查同名图片是否存在 img_name = xml_name.replace(".xml", image_ext) img_path = os.path.join(img_dir, img_name) if not os.path.exists(img_path): print(f"[VOC] 缺图: {img_name}") continue # 检查 bbox 是否越界 tree = ET.parse(os.path.join(ann_dir, xml_name)) root = tree.getroot() w = int(root.find("size/width").text) h = int(root.find("size/height").text) for obj in root.findall("object"): box = obj.find("bndbox") xmin = int(box.find("xmin").text) ymin = int(box.find("ymin").text) xmax = int(box.find("xmax").text) ymax = int(box.find("ymax").text) if xmin < 0 or ymin < 0 or xmax > w or ymax > h: print(f"[VOC] 越界: {xml_name}, box=({xmin},{ymin},{xmax},{ymax})") def check_yolo(): img_dir = os.path.join(yolo_root, "images", "train") label_dir = os.path.join(yolo_root, "labels", "train") for img_name in os.listdir(img_dir): label_name = img_name.replace(image_ext, ".txt") label_path = os.path.join(label_dir, label_name) if not os.path.exists(label_path): print(f"[YOLO] 缺标注: {img_name}") continue img_line_count = sum(1 for _ in open(label_path)) if img_line_count == 0: print(f"[YOLO] 空标注: {label_path}") check_voc() check_yolo()这段脚本干了两件事:在 VOC 侧确认每张图片都有 XML、框没有超出图片边界;在 YOLO 侧确认训练集里每张图都有对应的 TXT,且 TXT 不是空文件。os.listdir列目录、ET.parse读 XML,逻辑很直白。如果你发现标注越界的框,处理方式不是删掉,而是用np.clip把坐标裁剪到图像尺寸范围内,再重新写回 XML 和 TXT。
3.2 训练集/验证集划分:88 张怎么分才不虚
88 张图做划分,常见做法是 75 张训练、13 张验证,比例大约 85% / 15%。验证集太小会导致 mAP 波动很大,所以划分时尽量保证两个类别的样本都出现在验证集里。更稳的做法是分层抽样:按类别统计图片,再从每个类别里各抽一部分进验证集。如果两张图是同一个厨房场景的连续帧,要避免同时进训练集和验证集,否则会引入泄漏,评估结果虚高。
划分脚本直接操作文件列表生成train.txt和val.txt:
import os import random from sklearn.model_selection import train_test_split random.seed(42) img_dir = "VOC/JPEGImages" all_imgs = [f.replace(".jpg", "") for f in os.listdir(img_dir) if f.endswith(".jpg")] # 假设 img_names.txt 记录了每个样本对应的类别,做分层划分 # 如果没有类别信息,直接随机划分 train_names, val_names = train_test_split(all_imgs, test_size=0.15, random_state=42) with open("VOC/ImageSets/Main/train.txt", "w") as f: f.write("\n".join(train_names)) with open("VOC/ImageSets/Main/val.txt", "w") as f: f.write("\n".join(val_names)) print(f"训练集 {len(train_names)} 张,验证集 {len(val_names)} 张")train_test_split的test_size=0.15表示验证集占 15%,random_state=42固定随机种子,保证每次运行结果一致。如果你的 YOLO 侧目录已经按images/train和images/val分好了,那这一步可以跳过。没有分层信息时用纯随机,后果是某个类别可能从验证集里消失,训练时看不出来,评估时类别的 AP 直接算不出来。
3.3 小数据集的数据增强取舍
88 张图训练,增强策略要保守。强烈建议开启 YOLOv8 自带的mosaic=1.0增强吗?我的意见是别开满。Mosaic 会把四张图拼在一起训练,对小数据集很有效,但厨房积水这种目标纹理相近的场景,拼接边界会产生大量“伪积水”干扰。我更推荐只用轻微的马赛克概率,比如mosaic=0.3,同时把翻转、亮度、对比度调整打开。积水表面有高光,亮度抖动能让模型学到更多光照变化下的特征。HSV 增强里hsv_h=0.01, hsv_s=0.5, hsv_v=0.4是 YOLOv8 的默认值,对积水这种低纹理目标不用调太大,hsv_s降到 0.3 可以避免颜色过饱和导致的误检。
4. 用 YOLOv8 跑通厨房积水检测的最小训练流程
4.1 写 data.yaml:路径与类别映射
YOLOv8 训练前需要一个data.yaml描述数据集位置。参考 YOLO 侧目录结构,我一般这样写:
# kitchen_data.yaml path: /path/to/kitchen_dataset # 数据集根目录 train: images/train val: images/val nc: 2 names: 0: water 1: reflectionpath是 YOLO 侧目录的绝对路径,train和val相对于path写。nc是类别数,names的 ID 顺序必须和labels里 TXT 文件的类别 ID 一一对应。
这里有个隐藏坑:数据集作者给的classes.txt里类别顺序可能和 YOLOv8 预训练权重 COCO 80 类的顺序没关系,你只要保证names列表和 TXT 里的整数 ID 一致就行。很多翻车现场都是names写反了,比如把水渍标成 0、反光标成 1,代码里却把 0 当成反光,训练完推理结果语义全反。
4.2 训练命令与参数:小数据集怎么设
在 PyCharm 里跑 YOLOv8,最简单的方式是直接用ultralytics库的 Python API,也可以走命令行。我倾向在脚本里控制全部参数,方便复现:
from ultralytics import YOLO model = YOLO("yolov8n.pt") # 加载预训练权重 model.train( data="kitchen_data.yaml", epochs=100, imgsz=640, batch=8, patience=20, lr0=0.01, mosaic=0.3, device=0, name="kitchen_water", )各个参数在小样本场景下的含义:
model="yolov8n.pt":用 nano 版本预训练权重,88 张图扛不住 larger 模型,n 或 s 足够。epochs=100:小数据集训练 100 轮属于合理范围,配合patience=20做早停。imgsz=640:保持 YOLO 默认输入尺寸,不盲目调高。调太大显存不够,调太小积水小目标容易丢。batch=8:8GB 显存跑 nano 大约能吃下 8 到 16,先给 8 比较保险。patience=20:验证集指标连续 20 轮不提升就早停。lr0=0.01:预训练迁移学习的初始学习率,0.01 是稳妥起点。数据量小,学习率太大会直接震荡。mosaic=0.3:马赛克增强概率从默认 1.0 降下来,防止拼接伪目标。
训练结束后,runs/detect/kitchen_water/下会生成weights/best.pt和weights/last.pt。best.pt是验证集分数最好的权重,后续推理导出都用它,不要用last.pt。
4.3 盯住损失函数:loss 曲线怎么读
YOLOv8 训练日志里主要有三个损失:box_loss是边界框回归损失,cls_loss是分类损失,dfl_loss是分布焦点损失。小数据集训练我自己最常看box_loss和cls_loss两条曲线。正常走势是前 20 轮快速下降,之后缓慢收敛。如果cls_loss在训练集上降得很好、验证集却不降,说明模型开始记样本了,过拟合信号出现,应该停掉或增强正则化。box_loss如果震荡明显,优先怀疑标注框本身不齐,而不是梯度问题。
5. 避坑:小样本积水检测的 5 个翻车点
5.1 现象:训练 loss 不降反升
训练前 10 轮cls_loss从 1.2 涨到 2.0,验证集 mAP 为零。原因通常是学习率过大或标签错乱。先确认lr0没有误设成 0.1 以上,然后抽 5 张训练图的标注 TXT,打印出来人工对照图片里的目标位置。我碰到过一次是 YOLO 侧里有两张图的 TXT 文件名和图片名大小写不一致,Linux 下img_01.JPG和img_01.jpg被当成两个文件,训练时找不到标注,整张图变成背景样本,loss 自然混乱。解决:校验脚本里把所有文件名统一成小写再对比。
5.2 现象:验证集 mAP 一直是 0
训练正常结束,results.csv里metrics/mAP50(B)保持 0。这大概率是data.yaml里val路径指向的目录里没有标注文件,或者验证集和训练集有重叠但类别 ID 映射不齐。用yolo val detect model=best.pt data=kitchen_data.yaml验证时,如果提示找不到 label,先去labels/val看有没有 TXT 文件。另一个常见原因是验证集图片里根本没有正样本,所有目标都被当成背景,AP 自然为 0。
5.3 现象:显存不足,OOM 直接中断
8GB 显卡跑imgsz=640 batch=16很容易被 OOM。优先降batch,从 16 降到 8 再降到 4。如果 4 还爆,就把imgsz降到 480。注意imgsz降到 480 会影响小目标检测,但作为跑通流程的临时手段没问题。Ultralytics 还支持batch=-1自动探测,但小显存卡上探测结果经常不准,我都是手动定。
5.4 现象:水渍框小,模型漏检率高
厨房地面积水在画面里往往只占几十个像素,imgsz=640下很容易漏。处理办法有三个优先级:第一,检查原图分辨率,如果原图超过 1920,可以按 16 的倍数裁切成子图再训练;第二,把imgsz提到 960,前提是显存扛得住;第三,用 YOLOv8 的切片推理,把推理输入切块后合并结果。数据量只有 88 张,重标一份“大图切小图”的代价不算高,我实测这种方式对漏检改善最直接。
5.5 现象:过拟合严重,训练集 mAP 近乎满分、验证集差
88 张图过拟合几乎是必然,不是 bug,是样本量决定的。解决手段按有效程度排:降低模型复杂度(nano 别换 large)、打开更强的正则化(dropout=0.1)、降低mosaic以外的增强强度、或者干脆引入外部厨房场景图片补充训练。有一种玄学做法是冻结主干只训练检测头,在预训练基础上用小数据量只微调 head,能减缓过拟合,代价是收敛变慢,冻结层数设freeze=10左右即可。
6. 从 88 张图到现场验证:导出 ONNX 与实测建议
训练完拿到best.pt,可以顺手导出 ONNX 格式,方便判断模型部署到边缘设备上的兼容性:
yolo export model=runs/detect/kitchen_water/weights/best.pt format=onnx imgsz=640导出后建议先用onnxruntime跑一遍推理,确认输出张量维度符合预期。现场验证时,置信度阈值别用默认 0.25,厨房地面反光会带来大量低置信度误检,我把阈值提到 0.35 到 0.4,宁漏勿错。如果你手上有 Intel RealSense D435i 这类深度相机,可以额外用深度信息过滤反光:积水在深度图上没有明显深度突变,反光则是镜面反射,深度值与周边差异大,把两者交叉验证能明显降低误报。在 AGX Orin 这类边缘设备上搭建完 YOLO 环境后,我习惯写一个一键部署脚本,把 ONNX 模型、配置文件和测试图片绑定在一起,现场只需要跑一个deploy.sh就能出结果;这种流程留给交付很省心。
我自己做这类小数据集项目时,习惯把每一次yolo train的results.csv保留下来,后面再跑新版本就对比同一份验证集的 mAP。88 张图本身不能证明绝对精度,但不同方案之间的相对提升是有参考价值的。希望这篇内容能帮你把厨房积水检测的第一版模型跑起来,少走几趟我走过的弯路。
本文还有配套的精品资源,点击获取