简介本资源为玻璃瓶缺陷检测数据集聚焦瓶盖cap类缺陷的识别任务面向从事工业质检、机器视觉算法开发与深度学习模型训练的研究人员及学生。包内共255个文件以125张jpg图像和121个xml标注文件为核心另含少量txt说明文件压缩包约48.31MB图像与标注一一对应可直接用于目标检测模型的训练与验证。数据集覆盖多种瓶盖缺陷样本适合作为YOLO、Faster R-CNN等检测框架的实战素材帮助读者快速搭建缺陷检测流程、验证算法效果并完成模型调优。目前已有201人学习下载可作为工业缺陷检测入门与算法对比的参考数据。1. 玻璃瓶缺陷检测里125 张 cap 缺陷图到底能干什么先泼一盆冷水125 张图放在常规目标检测任务里连一个像样的训练集都算不上。但如果你手上拿到的就是这么一个压缩包缺陷类型只标了 cap瓶口/瓶盖区域缺陷那它大概率不是让你从零训一个大模型的而是让你验证一条小样本缺陷检测链路能不能跑通。玻璃瓶产线上的 cap 缺陷典型表现是瓶口崩边、盖体歪斜、密封圈缺失、压盖不到位这些缺陷在整瓶图像里占比极小属于典型的小目标 小样本叠加场景。我见过太多人拿到这种数据集第一反应是直接上 YOLO 开训结果 mAP 卡在 0.3 上下然后开始怀疑人生。问题不在模型在于 125 张图连划分验证集都捉襟见肘更别说覆盖产线上光照、角度、瓶型的真实分布。所以这篇笔记要讲清楚的是125 张 cap 缺陷图正确的用法是搭一套可复现的基线流程把数据增强、迁移学习、评估口径这三件事做扎实先跑出一个能解释的结果再谈要不要扩数据。适合正在做玻璃瓶质检、小样本工业缺陷检测的从业者尤其是手上数据量不大、又必须给出可行性结论的人。2. 小样本 cap 缺陷检测的技术选型为什么不能直接套通用检测器2.1 cap 缺陷的成像特点决定了预处理优先级玻璃瓶瓶口区域是曲面高反光材质cap 缺陷在图像里往往表现为边缘的暗裂、局部高亮异常或盖体轮廓的几何偏移。这意味着两件事第一缺陷信号高度依赖打光方式如果数据集里的成像条件不统一模型学到的很可能是光照伪影而不是缺陷本身第二缺陷区域相对整图极小直接缩放到 640×640 输入一个瓶口崩边可能只剩几个像素。我一般会先做一轮数据体检而不是急着写训练脚本。体检的核心是看三件事图像分辨率分布、瓶口区域在画面中的位置是否稳定、缺陷标注框的宽高分布。这三项直接决定你后面是走整图检测还是先做 ROI 裁剪。import os import cv2 import numpy as np # 遍历数据集统计图像尺寸和标注框尺度分布 img_dir dataset/images label_dir dataset/labels sizes [] box_areas [] for name in os.listdir(img_dir): img cv2.imread(os.path.join(img_dir, name)) h, w img.shape[:2] sizes.append((w, h)) # YOLO 格式标注class cx cy bw bh归一化 label_path os.path.join(label_dir, name.rsplit(., 1)[0] .txt) if os.path.exists(label_path): with open(label_path) as f: for line in f: parts line.strip().split() if len(parts) 5: bw, bh float(parts[3]), float(parts[4]) box_areas.append(bw * bh) # 归一化面积 print(图像尺寸种类:, set(sizes)) print(标注框归一化面积: 最小 %.5f 中位 %.5f 最大 %.5f % ( np.min(box_areas), np.median(box_areas), np.max(box_areas)))这段脚本的作用是先摸清家底。sizes集合如果只有一两种尺寸说明采集设备固定预处理可以统一如果五花八门就得考虑 letterbox 还是直接 resize。box_areas的中位数尤其关键——如果归一化面积中位数低于 0.01说明缺陷框在整图里占比不到 1%这时候整图训练基本是浪费算力应该先裁 ROI。参数上bw * bh用的是归一化宽高所以面积是无量纲的方便跨分辨率比较。2.2 迁移学习是小样本场景唯一理性的起点125 张图从零训练卷积核根本学不出有判别力的特征。常见做法是用在通用数据集上预训练过的检测骨干做初始化然后只微调检测头加部分高层。选型上如果追求部署轻量我会优先考虑 YOLO 系列的小模型变体如果缺陷更偏纹理异常而非明显几何目标也可以考虑基于特征重建的异常检测思路但那条路对标注依赖低、对成像一致性要求极高。这里有个反直觉的点小样本下模型容量不是越大越好。大骨干在小数据上过拟合的速度快得惊人训练 loss 一路降验证集 mAP 纹丝不动甚至掉。我一般会把骨干冻结前若干层只训后段和检测头学习率也压到常规的十分之一量级。# 以 YOLO 风格配置为例说明冻结策略与学习率设置思路 # 冻结前 10 层骨干只训练剩余层与检测头 freeze_layers 10 lr0 0.001 # 初始学习率小样本下比常规 0.01 低一个量级 warmup_epochs 3 # 小数据 warmup 不宜过长否则有效训练轮次太少 batch_size 8 # 125 张图batch 太大一个 epoch 只有十几步梯度噪声反而有益 epochs 150 # 小样本需要更多轮次观察收敛但要配合早停参数说明freeze_layers是经验值骨干浅层学的是边缘、颜色这类通用特征小数据没必要动lr0压到 0.001 是为了避免预训练权重被少量样本带偏batch_size取 8 是在显存允许下尽量保留梯度噪声小样本训练里适度的噪声反而有正则效果epochs给大是因为每轮迭代步数少但必须配早停否则后面全是过拟合。2.3 数据增强要针对 cap 缺陷的物理特性设计通用增强里颜色抖动、随机裁剪、马赛克这些对小样本有帮助但用在 cap 缺陷上要小心。瓶口缺陷很多是几何性的崩边、歪盖水平翻转通常安全垂直翻转就可能把瓶口朝向搞反制造出产线上不存在的样本。颜色抖动幅度也要克制因为玻璃反光对亮度极敏感抖太狠会让缺陷信号淹没在光照变化里。我的习惯是分两层增强几何层只做小幅平移、缩放、水平翻转光度层做轻微亮度对比度扰动幅度控制在 ±15% 以内。另外针对小目标可以额外做一次以缺陷框为中心的局部裁剪放大人为提升缺陷像素占比。import random import cv2 import numpy as np def augment_cap_defect(img, boxes, brightness0.15): # 几何增强仅水平翻转 小幅平移缩放 if random.random() 0.5: img cv2.flip(img, 1) h, w img.shape[:2] boxes [[1 - x - bw, y, bw, bh] for x, y, bw, bh in boxes] # 翻转后框坐标同步 # 光度增强亮度对比度微扰 alpha 1.0 random.uniform(-brightness, brightness) # 对比度 beta random.uniform(-20, 20) # 亮度偏移 img cv2.convertScaleAbs(img, alphaalpha, betabeta) return img, boxes逻辑说明翻转时标注框的 x 坐标要按1 - x - bw重算这是 YOLO 归一化坐标下最容易写错的地方漏改就会导致框和图像对不上训练直接崩。alpha控制对比度beta控制亮度两者都限制在小范围是为了模拟产线光照的正常波动而不是制造极端样本。这段增强函数应该挂在数据加载流程里每个 epoch 随机触发而不是离线把数据集扩增几倍存盘——离线扩增会让验证集也被污染。3. 用 125 张图跑通训练与评估的完整步骤3.1 数据集划分125 张怎么分才不至于评估失真125 张图如果按 8:1:1 分验证集只有 12 张测试集 12 张评估结果的方差大到没有参考意义。我的做法是训练集留 100 张验证集 25 张先不单独切测试集用交叉验证的思路多跑几轮看 mAP 的波动范围。等流程稳定后再固定一份划分做最终报告。划分时必须做分层保证 cap 缺陷的几种亚型崩边、歪盖、缺圈在训练和验证里都有分布。如果某类缺陷只有个位数样本那评估时就要单独看它的召回而不是只看总体 mAP。# 按 8:2 划分并保证每类缺陷在验证集中至少出现一次 python split_dataset.py \ --images dataset/images \ --labels dataset/labels \ --val_ratio 0.2 \ --seed 42 \ --stratify_by_class--seed 42固定随机种子保证划分可复现--stratify_by_class是分层的关键没有这个参数随机划分很可能把某类稀有缺陷全分进训练集验证集里一个都没有召回率就没法算。划分脚本跑完后务必人工抽查验证集里是否每类缺陷都有代表样本。3.2 训练配置与启动命令配置文件的每一项在小样本场景下都要比常规更保守。除了前面说的冻结和学习率还要特别注意锚框anchor的设置。如果你的 cap 缺陷框普遍很小默认锚框尺度可能完全不匹配导致正样本匹配不上训练直接不收敛。# data.yaml path: ./dataset train: images/train val: images/val nc: 1 names: [cap_defect] # hyp.yaml 关键项 lr0: 0.001 lrf: 0.01 # 最终学习率为初始的 1% momentum: 0.937 weight_decay: 0.0005 warmup_epochs: 3 box: 7.5 # 框回归损失权重小目标可适当调高 cls: 0.5 anchors: [[4,5, 8,10, 13,16]] # 针对小缺陷重设的小尺度锚框锚框那行是重点。默认锚框往往覆盖 32×32 以上的目标而 cap 缺陷归一化后可能只有十几像素必须用聚类的方式在你自己数据集的标注框上重新算一组锚框。box权重调高是为了让模型更关注定位精度因为小缺陷的分类信号本来就弱定位不准分类也跟着错。启动训练python train.py \ --data data.yaml \ --hyp hyp.yaml \ --weights pretrained_backbone.pt \ --img 640 \ --batch 8 \ --epochs 150 \ --freeze 10 \ --patience 30 \ --project runs/cap_defect--patience 30是早停耐心值验证集 30 轮不提升就停避免在 125 张图上无限过拟合。--freeze 10对应前面的冻结策略。--img 640是输入尺寸如果你的缺陷框特别小可以考虑提到 960 甚至 1280但显存和速度要权衡。3.3 评估口径小样本下别只看 mAP125 张图的验证集mAP 的小数点后第二位基本没有统计意义。我更关注三个指标召回率漏检在质检里代价最高、误检率过杀会导致产线频繁停机、以及不同缺陷亚型的召回分布。评估时把置信度阈值调低一点看召回上限再调高看误检下限找到业务能接受的平衡点。from collections import defaultdict # 按缺陷亚型统计召回而不是只看总体 def eval_by_subtype(preds, gts, iou_thr0.5): stats defaultdict(lambda: {tp: 0, fn: 0}) for img_id, gt_list in gts.items(): pred_list preds.get(img_id, []) matched set() for gt in gt_list: hit False for i, pred in enumerate(pred_list): if i in matched: continue if iou(gt, pred) iou_thr: matched.add(i) hit True break key gt[subtype] # 崩边/歪盖/缺圈 stats[key][tp if hit else fn] 1 return {k: v[tp] / max(v[tp] v[fn], 1) for k, v in stats.items()}这段代码的核心是把召回拆到亚型维度。iou_thr取 0.5 是检测任务的常规口径但小目标下可以放宽到 0.3 观察趋势。matched集合防止同一个预测框被多个真值重复匹配。跑完你会看到很可能总体召回 0.7但某一类缺陷召回只有 0.3——这才是真正要回去补数据或调增强的方向。4. 玻璃瓶 cap 缺陷检测的避坑清单5 个我踩过的坑4.1 坑一验证集 mAP 很高上线全漏检现象本地验证集 mAP 跑到 0.85换一批新采集的图漏检率飙升。原因125 张图大概率来自同一批次、同一光照条件验证集和训练集同分布模型学到的是这批图的特定成像特征不是缺陷本质。解决划分验证集时尽量按采集批次或时间段切分而不是纯随机如果数据里有多批次信息务必按批次分层。上线前一定要留一批完全没参与训练的图做盲测。4.2 坑二锚框没重设训练 loss 不降现象训练几十轮box loss 几乎不动模型输出全是背景。原因默认锚框尺度和 cap 缺陷框严重不匹配正样本匹配阶段就没有足够的 anchor 被激活。解决用 k-means 在你自己标注框上聚类出锚框尺度替换配置里的 anchors。聚类时用归一化宽高聚类数取 3 到 5 组即可小数据集不需要太多。4.3 坑三增强过度缺陷被增强没了现象加了马赛克和大幅随机裁剪后训练集 loss 正常但验证集表现反而下降。原因cap 缺陷本身占比极小马赛克拼接和大幅裁剪很容易把缺陷区域裁掉或拼到边缘模型看到的大量样本其实不含有效缺陷信号。解决小目标场景慎用马赛克随机裁剪要保证裁剪后缺陷框仍完整保留可以写个过滤逻辑裁剪后缺陷框面积小于原面积 50% 的样本直接丢弃。4.4 坑四类别不平衡被忽略现象某一种 cap 缺陷召回极低其他类正常。原因125 张图里各类缺陷数量天然不均稀有类可能只有几张损失被多数类主导。解决在损失里给稀有类加权或者用重采样让每个 batch 里稀有类出现概率提升。加权系数不要拍脑袋按类别频率的倒数来设再裁剪到合理范围避免权重过大导致训练震荡。4.5 坑五置信度阈值照搬默认值现象评估时按默认 0.25 阈值误检一堆调到 0.5漏检又上来了。原因小样本模型输出的置信度分布和在大数据集上训出来的模型完全不同默认阈值不适用。解决在验证集上画 PR 曲线按业务对漏检和过杀的容忍度选阈值。质检场景通常优先保召回阈值可以压到 0.15 到 0.2再用后处理规则过滤明显误检。5. 从 125 张到可落地小样本缺陷检测的进阶技巧把基线跑通只是第一步真正决定这套方案能不能落地的是你怎么处理数据永远不够这件事。我自己的习惯是先用 125 张图跑出一个可解释的基线把召回和误检的数字摆出来然后拿这个基线去反推需要补什么样的数据。比如评估发现歪盖缺陷召回只有 0.3那就针对性地去采集歪盖样本而不是盲目扩到一千张。一个具体技巧是主动学习式的迭代用当前模型对未标注的产线图做推理挑出置信度处于中间区间比如 0.3 到 0.6的样本这些是模型最犹豫的人工标注后加进训练集信息量最大。这比随机标注新样本的效率高得多。下面这个筛选逻辑可以直接用def select_hard_samples(preds, low0.3, high0.6): # 挑选模型置信度处于中间区间的样本优先送标 hard [] for img_id, pred_list in preds.items(): for p in pred_list: if low p[conf] high: hard.append((img_id, p[conf])) break # 按置信度升序越接近决策边界的越优先 return sorted(hard, keylambda x: abs(x[1] - (low high) / 2))low和high的区间不是固定的取决于你模型当前的置信度分布。如果模型整体置信度偏低区间要相应下移。排序用abs(conf - 中点)是为了把最靠近决策边界的样本排前面这些样本对模型改进的价值最大。另一个值得做的验证是跨批次稳定性测试。把不同时间段采集的图各留一小批分别评估看召回波动有多大。如果波动超过 15 个百分点说明模型对成像条件太敏感这时候与其继续调模型不如先回去统一打光和拍摄角度——工业质检里成像稳定性带来的收益往往比换模型大得多。最后说个我自己的教训早期做这类小样本缺陷检测我总想着一步到位把模型调到最好结果在 125 张图上反复调参调到最后连自己都不确定哪个配置是真的好。后来改成先固定一套保守配置跑出基线把每次改动的效果记录清楚反而推进得更快。小样本场景下可复现的流程比单次的高指标值钱得多。希望帮到你。本文还有配套的精品资源点击获取