资讯详情

陶瓷盘表面缺陷检测:单类YOLO数据集制作与训练避坑指南

📅 2026/9/28 12:12:50 | 华诺云谱 👁 阅读
陶瓷盘表面缺陷检测:单类YOLO数据集制作与训练避坑指南
简介面向陶瓷盘表面缺陷检测的YOLO格式数据集包含划分好的训练/验证数据、类别class文件与数据可视化脚本适合学习目标检测或需要陶瓷质检场景样本的开发者直接使用。数据场景丰富图像以jpg存储标注文件采用txt格式通过class文件可查看唯一缺陷类别defect的定义可视化脚本基于Python编写无需修改即可随机读取图片绘制边界框并保存结果便于快速检查标注质量。整个资源共2000个文件其中1359个txt标注文件、640个jpg图像、1个py脚本压缩包为7z格式整体大小153.79MB结构清晰可直接接入YOLO训练流程。目前已有297人学习/下载对于需要快速获取陶瓷表面缺陷数据集进行算法验证或模型迭代的工程师而言是一份实用且低成本的数据补充。1. 陶瓷盘表面缺陷检测为什么单类 YOLO 最省心陶瓷盘表面缺陷检测是单类 YOLO 数据集最常见的落地场景之一。标题里的“1类”含义很直接所有缺陷——裂纹、缺釉、釉泡、针孔、磕碰——统一标成一个类别 defect模型只回答两个问题图里有没有缺陷缺陷框落在什么位置。不做缺陷分类粒度由图片采集和光源方案去兜底。对刚上线视觉质检的生产线这种做法的标注成本最低也最容易在当天跑出第一版结果。适合手里有几百张缺陷图、但还没有统一缺陷分类标准的团队也适合第一次接触 YOLO 数据集组织方式的学生和开发。这个方案自带三件可复用的东西已经划分好的 train/val/test 数据集、类别 class 文件data.yaml、以及数据可视化脚本。后面每一章都围绕这三件套展开先讲数据集怎么组织成 YOLO 需要的格式再讲可视化脚本能提前暴露哪些坑最后给训练命令、参数调整建议和验证技巧。模型选型没有特殊讲究YOLOv8n 起步显存和训练时间都友好。2. 把陶瓷盘缺陷图整理成 YOLO 格式目录、data.yaml 和标签文件2.1 先搭好 images 与 labels 的目录结构YOLO 训练不依赖数据库靠文件夹约定。根目录下必须有两个平行目录 images 和 labels各自内部再分 train、val、test。图片放在 images/train 下对应的标注文件放在 labels/train 下文件名保持一致只是扩展名从 .jpg 变成 .txt。train/val/test 的划分比例按 7:2:1 或 8:1:1 都行陶瓷盘缺陷属于样本量小的场景我一般给 val 留足 15% 到 20%验证集太小会让 mAP 波动剧烈调参时看不出真实变化。mkdir -p ceramic_defect/{images/{train,val,test},labels/{train,val,test}}这条命令一次建出九个目录。ceramic_defect 是数据集根目录后面 data.yaml 里的 path 会指向它。train、val、test 的相对路径写死在配置里不要改目录名否则训练时会报路径不存在。labels 里每个 txt 和 images 里每张 jpg 靠同名关联这是 YOLO 唯一的对应方式名字对不上就相当于这张图没有标签训练时会被跳过。划分数据时我习惯写一个可复现的 Python 脚本而不是手动拖文件。手动拖的缺点是没法记录哪些图进了哪个集合训练完想复盘、或者调整划分比例重训时前后结果没有可比性。固定随机种子能保证每次划分结果一致后续实验也说得清每一次效果差异来自模型改动还是数据改动。import os, random, shutil from glob import glob random.seed(42) images glob(raw_images/*.jpg) random.shuffle(images) train_cut int(len(images) * 0.7) val_cut int(len(images) * 0.85) for idx, img in enumerate(images): name os.path.basename(img).replace(.jpg, ) if idx train_cut: subset train elif idx val_cut: subset val else: subset test shutil.copy(img, fceramic_defect/images/{subset}/{name}.jpg) label_src flabels_all/{name}.txt if os.path.exists(label_src): shutil.copy(label_src, fceramic_defect/labels/{subset}/{name}.txt)先对图片列表做一次随机打乱按索引切出三个集合前 70% 做训练70% 到 85% 做验证最后 15% 做测试。labels_all 目录存放全部原始标签复制时检查文件是否存在防止某些图没有标注时脚本崩掉。随机种子固定为 42重跑脚本得到完全相同的划分。如果你的缺陷图比例本身就严重失衡比如裂纹占八成、其他缺陷只有零星几张划分前先按缺陷类型分层否则某类样本可能全部掉进测试集训练时一个都见不到。2.2 data.yaml类别 class 文件的字段含义data.yaml 就是标题里说的 class 文件YOLO 训练时通过它知道数据集路径和类别名。这个文件决定训练程序把标签里数字 0 翻译成 defect而不是随便一个看不懂的名字。写法上有个容易踩的点train 和 val 写的是相对路径相对的是 path 字段不要写成绝对路径换机器训练时改一处 path 就行。path: ../ceramic_defect train: images/train val: images/val test: images/test names: 0: defectpath 是数据集根目录train、val 是相对 path 的图片目录test 可选不参与训练只在验证阶段用。names 用字典而不是列表键是类别 ID从 0 开始。因为这里只有 1 类names 里只有 0: defect。常见翻车是把类别 ID 写成 1训练不会立刻报错但输出结果里类别名全部错位可视化脚本查不出来只能靠手动检查预测结果发现。写 data.yaml 时顺便确认三件事路径拼起来后真实存在labels 目录和 images 目录同级names 里的类别数和所有标签文件里的最大类别 ID 一致。这个检查不用写脚本命令行打两行 ls 就能确认。类别文件在团队协作时容易乱我一般会在 data.yaml 头部加一行注释说明数据来源和划分比例方便其他人接手。2.3 标签文件一行一个缺陷框坐标全部归一化标签 txt 每行代表一个缺陷框格式固定为五列类别 ID、框中心 x、框中心 y、框宽、框高。后四列全部归一化到 0 到 1 区间也就是除以图片的宽和高。一张图里有多个缺陷就有多行一个缺陷被标成两个框是标注质量问题会在训练时拉低定位精度。如果现有的标注是 VOC 格式的 XML转换时注意坐标系换算。import xml.etree.ElementTree as ET def xml_to_yolo(xml_path, img_w, img_h, out_txt): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.iter(object): class_name obj.find(name).text box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) x_center ((x1 x2) / 2) / img_w y_center ((y1 y2) / 2) / img_h width (x2 - x1) / img_w height (y2 - y1) / img_h lines.append(f0 {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(out_txt, w) as f: f.write(\n.join(lines))转换逻辑就是把 VOC 的左上角点和右下角点换算成中心点加宽高再统一除以图像宽高结果保留六位小数。img_w 和 img_h 必须来自原图实际尺寸不能拿缩略图的尺寸来算否则所有框都会偏移。class_name 字段在这个脚本里固定写死成 0因为单类模型不需要区分缺陷类型如果你的标注里混了多个类名先统一归并再转换。如果是从 COCO 格式转换逻辑类似但取数方式不同COCO 的 annotation 直接存的是 x、y、width、height其中 x、y 是左上角坐标不需要再解 bndbox。换算公式为 x_center (x width / 2) / img_w。无论来源是哪种格式转换完都要跑一遍标签合法性检查这个检查脚本在下一章的可视化部分给出。3. 训练前先看清数据可视化脚本提前暴露三类问题3.1 用 OpenCV 把标注框画回原图拿到划分好的数据集第一件事不是训练是把标注框画回原图上肉眼过一遍。标注文件里的坐标是归一化数字不转回像素坐标系根本看不出框是否贴住缺陷。可视化脚本的核心就几行读图、读 txt、把归一化坐标乘回宽高、画矩形。我会把输出单独放到 vis 目录不污染原始数据集。import cv2 import os from glob import glob os.makedirs(vis, exist_okTrue) for img_path in glob(ceramic_defect/images/train/*.jpg): img cv2.imread(img_path) if img is None: continue h, w img.shape[:2] txt_path img_path.replace(images, labels).replace(.jpg, .txt) if not os.path.exists(txt_path): continue with open(txt_path) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cx, cy, bw, bh map(float, parts[1:]) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.imwrite(vis/ os.path.basename(img_path), img)这段脚本从 train 目录读图把标签坐标反算回像素值缺陷框画成红色。运行后逐张翻阅 vis 目录重点看三种情况框是否紧贴缺陷边缘、有没有把整个盘子当成缺陷框进去、一张图里同一缺陷是否被拆成多个重叠框。第一种情况影响 mAP 上限第二种会让模型学到错误的目标尺寸第三种在 NMS 阶段会反复压制导致同一个真实缺陷有时出框有时不出。这类问题光看坐标数字发现不了画出来才直观。标注是人工做的每人标准不一样同一张缺陷图不同人标出的框可能差出 20% 的面积。可视化抽查就是要统一标注口径框住缺陷主体的紧密外接矩形不要包含过多背景。抽查量不用大20 到 30 张就能暴露普遍问题。3.2 统计样本数量与缺陷密度陶瓷盘缺陷图有个特点缺陷稀疏大片背景。统计每个子集的图片数和每张图的平均标注框数能提前判断训练是否会出现正样本不足的问题。缺陷密度过低时模型很容易学会“默认输出空白”因为预测无缺陷的损失比预测错位置小得多。import os from glob import glob from collections import Counter for subset in [train, val, test]: label_dir fceramic_defect/labels/{subset} txts glob(f{label_dir}/*.txt) box_count [] empty_count 0 for txt in txts: with open(txt) as f: n sum(1 for line in f if line.strip()) box_count.append(n) if n 0: empty_count 1 if txts: avg round(sum(box_count) / len(txts), 2) else: avg 0 print(subset, 图片数:, len(txts), 平均框数:, avg, 空标签数:, empty_count)输出三行统计train / val / test 各自的图片数、平均每张图的缺陷框数、空标签数量。陶瓷盘缺陷密度低平均框数在 0.5 到 3 之间都正常如果均值超过 5说明标注粒度可能过细一条长裂纹被截成了好几段。空标签图片不是废数据保留在训练集里能让模型学会区分正常盘和缺陷盘避免全盘误报。平均框数明显偏低时比如 100 张训练图只有 40 个框后续训练很容易陷入模型什么也不输出的状态。这时要么补充缺陷样本要么先降低置信度阈值观察 recall 再决定是否调整标注粒度。这个统计数字比 mAP 更早告诉你数据够不够用。3.3 标签合法性检查越界框、零面积框、字段缺失这是训练前最后一道关卡。标签文件里只要有一个框的中心点坐标大于 1训练时损失函数就会异常轻则 loss 震荡重则直接 NaN。分辨率不同的图片混在一起标注时最容易出现这种问题标注工具按某张图的尺寸保存坐标换一张分辨率不同的图就不适用。bad [] for txt in glob(ceramic_defect/labels/*/*.txt): with open(txt) as f: for i, line in enumerate(f): parts line.strip().split() if len(parts) ! 5: bad.append((txt, i 1, 字段数不为5)) continue cls, cx, cy, bw, bh parts if not (0 float(cx) 1 and 0 float(cy) 1): bad.append((txt, i 1, 中心点越界)) if float(bw) 0 or float(bh) 0: bad.append((txt, i 1, 框宽高为0)) print(问题数:, len(bad)) for item in bad[:20]: print(item)脚本遍历三个子集的所有标签文件逐行检查五个字段是否完整、中心点坐标是否落在 0 到 1 区间、宽高是否大于 0。输出前 20 条问题明细文件名和行号都有。越界和零面积框必须修掉否则训练的早期阶段就会暴雷而且这种问题定位起来费时间看起来像是模型不收敛实际上是数据脏。提示跑训练之前把第 3 章的三个脚本按顺序执行一遍花费不到五分钟能省掉后面至少两小时的排错时间。4. 用 YOLOv8 训练陶瓷盘缺陷检测最小命令与关键参数调整4.1 最小训练命令与输出产物环境装好 ultralytics 包之后训练就一条命令。模型选 yolov8n是 YOLOv8 系列里最小的版本参数量最少陶瓷盘缺陷检测这种单类任务不需要大模型n 级别的特征提取能力足够而且训练速度快迭代试错成本低。如果你有更大的数据量或者想要更高精度可以换成 yolov8s但先跑通流程更重要。cd ceramic_defect yolo detect train modelyolov8n.pt datadata.yaml imgsz640 epochs100 batch16 device0modelyolov8n.pt 会先下载预训练权重下载失败时可以手动把权重文件放到当前目录再指定。data 指向上一章写的 data.yaml。imgsz640 把输入图片统一缩放到 640 边长陶瓷盘原图经常是两千万像素直接原始分辨率训练显存完全不够640 是性价比最高的选择。device0 用第一块 GPU没有 GPU 就改成 devicecpu训练时间会翻几倍但数据量不大时也能接受。训练完成后runs/detect/train 目录下会生成 weights/best.pt 和 weights/last.pt 两个权重文件。best.pt 是验证集上表现最好的那一轮部署时用这个last.pt 是最后一轮的输出一般用来继续训练。同目录下的 results.csv 记录了每一轮的 loss 和指标直接拖进 Excel 就能画曲线比截图保存训练日志靠谱。4.2 必调的四个参数imgsz、batch、epochs、patience参数不是越多越好陶瓷盘缺陷检测场景下动这四个就够。imgsz 决定输入分辨率batch 受显存约束epochs 决定训练时长patience 控制早停。剩下的大部分参数用默认值等你发现具体问题再针对调整。参数建议值说明imgsz640原图超过 4000 像素时先做切图或缩放预处理batch168G 显存对应 1616G 显存对应 32爆显存就减半epochs100第一轮跑 100看 val loss 趋势再决定是否加到 200patience30连续 30 轮 val 指标不改善自动停止省时间workers4数据加载线程数CPU 核少就调成 2batch 的调整原则是越大越好但受显存限制。batch 从 16 降到 8训练时间接近翻倍精度变化不明显。epochs 不要一上来设 300陶瓷盘缺陷特征相对简单通常 60 到 80 轮就收敛了多跑的轮数只是在过拟合边缘摩擦。patience 是后悔药机制验证集指标连续不涨就提前收手不浪费算力。训练中途想改参数不用重来。CtrlC 中断后用 last.pt 作为预训练权重继续yolo detect train modelruns/detect/train/weights/last.pt datadata.yaml imgsz640 epochs200。这是 YOLO 系框架最方便的地方中断续训是标准操作不用自己手动保存 checkpoint。4.3 训练日志怎么读yolo 损失函数与指标列训练时屏幕输出的表格里有 box_loss、cls_loss、dfl_loss 三列对应 yolo 损失函数的三个组成部分。box_loss 衡量预测框和真实框的位置误差dfl_loss 是分布焦点损失负责边界框回归的精细度。cls_loss 在单类场景下几乎没有区分难度数值一直很低是正常的不用慌张。cd ceramic_defect yolo detect train modelyolov8n.pt datadata.yaml imgsz640 epochs100 batch16 device0 21 | tee train.log # 另一个终端 tail -f train.logtee 把训练日志同时输出到屏幕和文件跑完后再慢慢复盘。box_loss 应该在前 20 轮快速下降然后进入缓慢下降区间如果 box_loss 前几轮就跌到 0.01 以下多半是数据量太少模型在背训练集。val 列的指标要重点看 recall不是 mAP。单类检测里模型倾向于少输出precision 虚高mAP50 再好看也掩盖不了漏检。recall 才是这个场景的真实瓶颈。results.png 里画了所有曲线的汇总图val/recall 那条线如果在训练后期还在上升说明还可以继续训如果已经变成水平线再加轮数也是浪费。混淆矩阵图在单类场景下读法不同叶子结点只有两类背景和 defect看 defect 行里被分到背景的比例就是漏检率的直观呈现。训练结束后的 best.pt 在验证集上如果 recall 低于 0.8先别调模型结构回到数据层面补样本。5. 陶瓷盘缺陷检测避坑指南5 个真实翻车场景5.1 标签越界导致训练 loss 变成 NaN现象训练第一轮 loss 直接显示 nan 或 inf之后一直无法恢复。损失曲线像一条断崖logs 里没有任何权重更新。原因标签 txt 中存在中心点坐标大于 1 或宽高为负的框。转换脚本从 COCO 转 YOLO 时如果拿到的 image size 不是原图尺寸归一化结果就会越界。模型在算损失时对越界框做变换坐标落到图像外梯度变成无效值。解决不要手动翻标签文件跑第 3 章的合法性检查脚本输出所有越界框的文件名和行号直接删除或重新标注这几条。修复后重新划分数据再启动训练。预防措施是转换类脚本里加上坐标范围断言越界就抛异常别等训练时才发现。5.2 class id 与 data.yaml 的 names 对不上现象训练正常走完loss 曲线也很漂亮但调用模型预测时输出的类别名是乱码或空字符串可视化结果里标签位置显示 unknown。原因标签 txt 里的类别 ID 写了 1而 data.yaml 的 names 只有 0: defect 一项。YOLO 读取到 ID 为 1 的类别时去 names 里查不到对应名字静默失败不报错。这个问题隐蔽在“模型权重可用但部署结果异常”上检查时容易忽略。解决把所有标签文件里的类别 ID 统一替换成 0用 sed 或者写个小脚本都行。替换后重新跑可视化脚本打印出每个 txt 里出现的类别 ID 去重列表确认只有 0 再训练。单类模型没有类别混淆问题但 ID 对齐是基本卫生。5.3 反光区域被当成缺陷正常盘子误报现象验证集上 mAP 很高但部署到产线后正常釉面在特定光照角度下频繁输出缺陷框误报率高到无法上线。原因陶瓷盘釉面反光在图像上形成高亮斑块灰度纹理和裂纹、针孔相似。模型学到的是“高亮区域等于缺陷”这个伪特征在训练集里反光样本少模型看不出区别。这是视觉检测里最典型的域差异问题数据分布换了模型特征失效。解决采集阶段用偏振光源压制反光或者在数据集里专门加入反光但无缺陷的负样本。模型层面能做的有限靠调置信度阈值只能压低误报同时会把真缺陷也压掉。这个坑的教训是数据采集方案比模型调参更值得投入。5.4 正样本太少mAP 虚高没有说服力现象训练集只有 200 张图、150 个框验证集 mAP50 跑到 0.95你以为模型已经可用实际拿去测新拍的图漏检一片。原因验证集和训练集同源缺陷类型分布完全一致模型相当于在开卷考试。陶瓷盘缺陷种类多但每类样本少mAP 被少数几个好检测的缺陷类型拉高稀疏类型完全没学到。mAP 只反映平均精度不反映最差类别。解决训练完单独挑出包含细小裂纹的图片跑一批推理统计这类样本的召回率。如果召回率明显低于总体说明该缺陷类型欠拟合需要针对性补数据。另一个方法是把测试集单独存放训练过程中完全不碰它最后统一评估避免验证集参与反复调参导致的信息泄漏。5.5 快门模糊导致缺陷边缘漏检现象同一个陶瓷盘在静态图片上检测正常在传送带上运行时漏检率明显上升尤其是细小裂纹。原因传送带高速运动时照片产生运动模糊缺陷边缘被拉成渐变过渡带模型提取不到清晰的边界特征。裂纹这类线状缺陷对模糊最敏感几个像素的边缘模糊就足以让模型放弃输出。解决采集端缩短曝光时间或加频闪光源把运动模糊控制在 1 像素以内。数据集层面不要只放清晰样本把模糊样本单独放一批标注出来后加入训练集让模型见过模糊形态。如果产线速度改动过重新评估时把模糊样本单列一个集合做回归测试防止模型性能退化而不自知。6. 训练后的验证小工具把单张图检测结果一键批量导出训练完别只盯着终端指标写一个批量推理脚本把验证集所有图片的检测结果画出来逐张翻一遍比看十个指标都有用。这个脚本本质是把第 3 章的标注可视化脚本换成模型输出模型预测出框坐标画回原图输出到 out 目录。陶瓷盘缺陷检测关注的是漏检所以脚本里把置信度阈值调低到 0.25让模型多出框而不是少出框漏检问题才能暴露出来。from ultralytics import YOLO import cv2 import os from glob import glob os.makedirs(out, exist_okTrue) model YOLO(runs/detect/train/weights/best.pt) for img_path in glob(val_images/*.jpg): result model.predict( sourceimg_path, conf0.25, iou0.5, imgsz640, verboseFalse )[0] name os.path.basename(img_path) cv2.imwrite(fout/{name}, result.plot())conf0.25 是置信度阈值低于 0.25 的预测框会被过滤iou0.5 是 NMS 的 IoU 阈值重叠框的抑制力度。预测结果通过 result.plot() 画回原图自带框和置信度标签不需要自己重写绘制逻辑。翻看输出图片时把每张漏检的图单独复制到一个 miss 目录积累一批后分析漏检位置的分布规律。我习惯每训练一版就跑一次这个脚本把 miss 目录里的图片直接当标注候选用标注工具在原图上补框补完加入训练集重新训练。这样每一轮训练都在补上一轮的短板而不是盲目加数据。陶瓷盘缺陷检测做到最后模型结构基本不动真正起作用的是数据和标注口径的持续修正。希望这套流程能帮你少走点弯路。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑