ARTICLE DETAIL

建站实战干货

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

屏幕缺陷检测数据集构建与YOLOv8训练实战:从标注规范到模型调参

2026/9/7 13:05:39 拓冰建站 浏览量
屏幕缺陷检测数据集构建与YOLOv8训练实战:从标注规范到模型调参 616张图4个缺陷类别VOC和YOLO双格式标注这个规模在工业屏幕质检领域算是一个非常典型的实战数据集。最近刚好把手头一个屏幕缺陷检测项目的数据从采集、清洗、标注到训练完整跑了一遍踩了不少坑也积累了一些经验。这篇就围绕着“工业质检屏幕缺陷检测数据集”这个主题把整个数据集的构建思路、格式转换细节、训练配置和排错过程全部拆开讲既有可以直接抄走的脚本也有常规文档里不会写的工程经验。如果你正在做类似的任务比如LCD/OLED屏幕外观检测、手机面板缺陷识别或者你刚拿到一批工业现场图像还不知道怎么把数据整理成能喂给YOLO的样子那这篇文章应该能帮你少走很多弯路。1. 数据集的整体定位与设计逻辑1.1 屏幕缺陷检测为什么不能直接套用通用数据集做目标检测的人对COCO、VOC这些公开数据集都很熟但真到工业质检场景你会发现通用数据集基本帮不上忙。原因很直接屏幕缺陷不是日常物体它没有稳定的语义结构。日常物体比如人、车、猫有清晰的轮廓和颜色特征而屏幕缺陷往往是低对比度、小尺寸、形态不规则的灰度异常区域。拿COCO预训练权重训练出来的模型对自然图像的特征提取能力是有的但要它精确判断一块屏幕上的暗点是不是缺陷泛化效果通常很差。屏幕制造产线上的缺陷检测过去主要靠AOI光学检测设备加人工复判。AOI能拍出可疑区域但误报率不低最后还是靠人眼盯着屏幕看。人工复判的问题在于一致性差、疲劳度高而且节拍快的时候容易漏检。越来越多工厂开始尝试用深度学习模型来做二次分类或者直接检测但最大的门槛就是数据——产线上不良品率本身很低缺陷样本要靠长期积累而且缺陷种类多种多样不同型号屏幕的缺陷表现差异也很大。所以一个高质量的屏幕缺陷检测数据集价值不在于数量大而在于缺陷类型覆盖准、标注规范、格式通用能够让算法工程师快速验证模型方案也让产线工程团队能够基于它建立质检基线。这个616张4类别的数据集就是按照这个思路设计的。1.2 616张样本的规模逻辑与4类缺陷的取舍很多刚接触工业视觉的人会问616张是不是太少了如果你是做通用目标检测几百张确实不够看。但在工业质检场景这个规模是合理的起点原因有三层。第一层缺陷样本的获取成本很高。产线上的不良品率通常是千分之几甚至更低拍摄一张合格的缺陷图像需要等待缺陷出现、触发相机、保存图像整个流程受产线节拍限制。616张图意味着背后至少是几千片屏幕的筛查结果这个数据量在工业现场已经算是一次正经的数据积累。第二层屏幕缺陷的种类虽然多但从检测策略上可以归纳为几个大类。这个数据集选的四类非常有代表性亮点屏幕点亮后局部区域异常高亮通常直径只有几个像素到几十个像素属于典型的小目标。暗点局部区域亮度明显低于周围对比度低边缘模糊是最难检的一类。划伤屏幕表面或者内部膜层的线性机械损伤细长、方向随机容易与正常纹理混淆。脏污表面异物或油污形状不规则灰度分布不均匀边界不清晰。这四类基本覆盖了屏幕外观检测的核心痛点点缺陷、线缺陷、面缺陷。模型如果能把这几类检准产线上大部分复判工作就可以自动化。第三层616张图经过数据增强和迁移学习之后完全能够训练出一个可用的检测模型。YOLO家族本身就带Mosaic、MixUp等数据增强策略配合预训练权重微调几百张图跑出来的mAP可以到0.9以上。后面我会详细讲怎么配置。1.3 VOC和YOLO双格式的真实价值这个数据集同时提供VOC和YOLO两种格式不是厂家为了凑卖点而是工程实践的真实需求。VOC格式是Pascal VOC标准每张图对应一个XML文件标注信息包括目标类别、bounding box坐标、是否截断、是否难例等可读性强适合用LabelImg、X-AnyLabeling这类工具打开检查也方便做数据清洗和二次标注。YOLO格式是Ultralytics系列模型通用的TXT格式每行代表一个目标格式为“类别ID 中心点X 中心点Y 宽度 高度”坐标全部归一化到0到1训练时加载效率高IOU的计算也更直接。实际项目中标注人员通常使用VOC格式工作算法工程师训练时又需要YOLO格式所以双格式意味着数据可以直接在不同角色之间流转省去了每次交接都要重新转换格式的麻烦。数据集的目录结构设计我放在下一节讲。2. 数据采集、标注规范与双格式转换实操2.1 采集设备和打光方案的选择屏幕缺陷检测的数据采集核心不是相机的分辨率要多高而是打光方案要能凸显缺陷。以这个数据集的采集经验来看工业面阵相机配合环形光源或者同轴光源效果普遍比较好。相机分辨率建议不低于500万像素这样一小块屏幕上的细小缺陷才能被清晰记录。采集过程中要特别注意屏幕本身的点亮状态。屏幕缺陷检测必须在背光点亮的状态下进行而且最好使用固定灰阶画面比如128灰阶或者纯白、纯黑测试画面。原因很简单不同灰阶背景下缺陷的对比度表现完全不同。亮点在暗背景下更明显暗点和脏污在亮背景下更容易暴露。如果全部图像都用同一种灰阶训练出来的模型在切换测试画面时性能会掉得非常明显。采集台还要尽量固定相机与屏幕的相对位置。虽然目标检测模型对轻微平移有一定的鲁棒性但训练数据里如果出现大量位置漂移模型会浪费一部分能力去学习位置无关的特征对最终检测精度有负面影响。实际操作中用定位治具固定屏幕曝光时间设短一些避免生产线的震动造成图像模糊。2.2 四类缺陷的视觉特征与标注重难点标注环节是整个数据集质量的核心也是最容易被低估的一步。这四类缺陷的标注策略并不完全相同。亮点类缺陷目标非常小可能只有十几个像素标注框一定要紧贴缺陷亮区边缘不要留白边。留白过多会让模型误以为缺陷周围的高亮度区域也是目标的一部分扩大检测框的同时拉低IOU。暗点类缺陷是最难标注的因为它和正常屏幕背景的灰度差异很小。我的经验是先把图像灰度拉伸一下增强对比度再看或者直接用图像处理工具标记可疑区域后再人工确认。标注暗点时框的边界应该取在灰度变化最剧烈的过渡带上而不是暗点区域的视觉边缘因为暗点的灰度过渡带往往比实际缺陷区域宽很多。划伤类缺陷呈现细长形状标注框通常是高宽比很大的矩形。这里要特别注意如果划伤末端有延伸出来的浅色尾迹到底要不要包含进框里我的建议是只包含对比度明显的主体部分。尾迹部分的灰度变化太微弱强标进去会给模型增加噪声。脏污类缺陷形状不规则而且经常与屏幕边缘重叠。标注的时候尽量用旋转矩形或者精确框但VOC和YOLO的矩形框无法表达旋转所以只有一种选择用最小的轴对齐矩形框住脏污主体。如果脏污延伸到图像边缘就把图像边缘作为框的边界不要为了完整性把框扩大到包含大量背景区域。2.3 标注工具选择与VOC格式生成标注工具推荐用LabelImg或者X-AnyLabeling。LabelImg是老牌工具界面简单直接输出VOC格式XML而且支持自动保存适合大批量标注。X-AnyLabeling功能更强支持多种标注形状和AI辅助预标注适合标注效率要求高的场景。标注前先定义好类别列表并且固定在工具中避免不同标注人员手打类别名导致的大小写不一致问题。在实际项目中我见过太多因为类别名多了一个空格或者首字母大小写不同导致转换脚本报错的案例。用LabelImg标注完成后每张图像会生成一个同名XML文件。VOC格式的XML结构大致如下annotation folderJPEGImages/folder filenamescreen_defect_001.jpg/filename size width2048/width height1536/height depth3/depth /size object namescratch/name bndbox xmin356/xmin ymin421/ymin xmax587/xmax ymax438/ymax /bndbox /object /annotation这里要注意VOC格式中xmin,ymin,xmax,ymax是像素坐标直接来自标注框的左上角和右下角。这个信息后面在转换YOLO格式时非常重要因为两个坐标系之间需要换算。2.4 VOC转YOLO核心转换脚本与边界条件处理VOC转YOLO是整个数据准备流程里最容易出bug的环节我直接给出一份验证过的转换脚本并解释每一步为什么这么写。import xml.etree.ElementTree as ET import os def convert_voc_to_yolo(xml_file, class_names, target_dir): tree ET.parse(xml_file) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) yolo_lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: print(f警告未知类别 {name} 在 {xml_file}) continue class_id class_names.index(name) xmin float(obj.find(bndbox/xmin).text) ymin float(obj.find(bndbox/ymin).text) xmax float(obj.find(bndbox/xmax).text) ymax float(obj.find(bndbox/ymax).text) # 边界裁剪防止坐标越界 xmin max(0, min(xmin, img_w - 1)) xmax max(0, min(xmax, img_w - 1)) ymin max(0, min(ymin, img_h - 1)) ymax max(0, min(ymax, img_h - 1)) # 过滤掉宽或高为0的无效框 if xmax xmin or ymax ymin: print(f警告跳过无效框 {file_name}) continue # VOC坐标转YOLO归一化坐标 x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h box_w (xmax - xmin) / img_w box_h (ymax - ymin) / img_h yolo_lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) if yolo_lines: base_name os.path.splitext(os.path.basename(xml_file))[0] with open(os.path.join(target_dir, base_name .txt), w) as f: f.write(\n.join(yolo_lines)) class_names [bright_spot, dark_spot, scratch, stain] xml_dir Annotations target_dir labels os.makedirs(target_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if xml_file.endswith(.xml): convert_voc_to_yolo(os.path.join(xml_dir, xml_file), class_names, target_dir)这段脚本有几个细节值得多说两句。第一坐标越界问题。标注过程中偶尔会出现框的边界超出图像尺寸的情况虽然概率不高但一旦出现训练的时候就会报错所以要加一个裁剪逻辑。第二无效框过滤。如果某个标注框的宽或高为0说明标注失误直接跳过比强行保留更安全。第三归一化的精度。YOLO格式要求坐标归一化到0到1保留小数点后6位足够不必担心精度问题影响训练。2.5 数据集目录结构与train/val划分策略训练之前数据集目录结构要按照YOLO的约定组织好。无论你用的是YOLOv5、YOLOv8还是最新的YOLO版本目录结构基本是一致的dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml注意images和labels两个目录下必须有相同名字的子目录并且子目录内的文件名一一对应。训练脚本通过后缀自动匹配图片与标注文件比如screen_defect_001.jpg对应screen_defect_001.txt。划分比例我建议train:val:test 7:2:1。这个数据集616张也就是train约431张val约123张test约62张。val用于训练过程中的模型评估test用于最终验证模型的泛化能力。划分的时候还有一点很重要如果同一块屏幕上出现了多个缺陷并且被分成了多张图像那么这些图像必须全部放进同一个集合不能一部分在train、一部分在val否则会造成信息泄漏val指标虚高。实际操作中最好按照缺陷样本的来源批次划分而不仅仅是随机打散。data.yaml的写法也要注意。Ultralytics YOLO模型要求类别ID与训练时的class_id严格对应path: ./dataset train: images/train val: images/val test: images/test names: 0: bright_spot 1: dark_spot 2: scratch 3: stainnames的顺序必须和转换脚本里class_names的顺序一致这一点犯错的话模型训练出来的类别会全部错位而且很难发现。3. 基于YOLOv8的模型训练与调参实战3.1 环境准备AMD显卡跑YOLO的CUDA问题数据准备好了接下来就是训练环境。这里特别说一下AMD显卡的问题因为不少人的工作站用的是AMD显卡。YOLO系列模型本身是基于CUDA生态的官方预编译的PyTorch只支持NVIDIA CUDA。AMD显卡要跑YOLO需要安装ROCm版本的PyTorch并且ROCm对显卡型号和驱动版本有严格限制。以AMD RX 580这张卡为例ROCm对它的支持并不好RX 580属于GCN架构的老卡ROCm最近的版本已经放弃了这张卡的官方支持。实际测试下来用RX 580跑YOLOv8就算装上了ROCm训练速度也慢得让人难以接受。我的建议是训练用NVIDIA显卡实在没有就用云端GPU推理环节如果必须用AMD可以用ONNX Runtime的DMLDirectML后端但精度和性能都有一定损失。如果训练机是NVIDIA显卡环境配置就简单多了。以YOLOv8为例pip install ultralytics pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118CUDA版本和显卡驱动要匹配可以用nvidia-smi查看驱动支持的CUDA版本再选择对应的PyTorch版本。3.2 数据增强策略与关键训练参数训练配置是整个流程里最能体现工程经验的部分。直接贴一份经过验证的YOLOv8训练脚本from ultralytics import YOLO # 加载COCO预训练权重迁移学习的关键 model YOLO(yolov8s.pt) # 数据增强和训练参数 model.train( datadata.yaml, epochs100, imgsz640, batch16, lr00.01, lrf0.01, momentum0.937, weight_decay0.0005, warmup_epochs3, warmup_momentum0.8, warmup_bias_lr0.1, box7.5, cls0.5, dfl1.5, mosaic1.0, mixup0.2, copy_paste0.3, fliplr0.5, scale0.9, translate0.2, degrees0.1, hsv_h0.015, hsv_s0.7, hsv_v0.4, patience30, device0 )几个关键参数的解释imgsz640说明输入分辨率设为640x640。屏幕缺陷是典型的小目标比如几个像素大小的亮点缩放到640分辨率之后可能只剩一个点。如果图像原始分辨率是2048x1536我建议要么训练时把imgsz提高到1024要么先把图像按ROI裁剪成小块再做训练。这里有个权衡点提高分辨率会显著增加显存占用和训练时间但对小目标检测的收益非常直接。如果显卡显存充足12GB以上imgsz1024是更好的选择。mosaic1.0表示Mosaic增强的概率是1.0也就是每张训练图都通过4张图拼接生成。Mosaic对提升小目标的检测效果很有帮助因为拼接图里目标的尺度变化范围更大模型能学到更多尺度特征。mixup0.2和copy_paste0.3是另外两种增强策略分别对应图像混合和实例复制粘贴。对于616张小样本数据集这两个增强策略能有效缓解过拟合但强度不能太高否则模型训练不稳定。patience30是早停策略如果30个epoch内验证集指标没有提升就停止训练。这个参数在样本少的时候特别有用因为样本少意味着模型很快就能收敛再继续迭代只会过拟合。3.3 预训练权重策略冻结backbone还是全量微调小样本数据集训练的一个核心问题到底要不要冻结backbone层。我的经验是分两步看。第一步先用COCO预训练权重冻结backbone只训练检测头跑20到30个epoch让模型先学会把缺陷区域的语义特征映射到检测框上。第二步解冻所有层用小学习率全量微调让backbone的特征提取能力逐渐向屏幕缺陷的纹理特征偏移。在YOLOv8里冻结backbone可以通过传递freeze参数实现model.train( datadata.yaml, epochs20, freeze10, ... )freeze10表示冻结前10层对于YOLOv8s来说前10层基本覆盖了backbone的大部分卷积层。冻结阶段结束后再用完整训练跑30到50个epoch学习率可以设低一些比如lr00.005防止破坏已经收敛的特征结构。如果不冻结backbone直接全量微调在616张图的规模下模型很容易在训练初期就过拟合到训练集上backbone的特征提取能力被带偏val指标会一直上不去。3.4 训练过程的关键指标解读训练过程中要重点监控的指标不是训练集loss而是验证集的mAP0.5和mAP0.5:0.95。这两个指标的含义mAP0.5当预测框与真实框的IOU大于0.5时算正确预测统计所有类别平均精度。mAP0.5:0.95IOU从0.5到0.95每隔0.05计算一次取平均值。这个指标更严格对检测框的定位精度要求更高。对于屏幕缺陷检测mAP0.5更贴近实际产线需求。因为产线上的判定是“检出并抓取到缺陷区域”不要求检测框与缺陷精确重合IOU到0.5就已经足够让机械臂或者人工精准定位了。mAP0.5:0.95可以作为模型能力的一个参考但不必过于追求。训练日志里还要注意PPrecision和RRecall的变化趋势。采样数据不平衡的时候P和R往往呈现跷跷板效应。如果R特别低说明很多缺陷完全没被检出来应该考虑降低置信度阈值或者增加训练数据中困难样本的比例。如果P特别低说明误检严重常见原因是背景干扰或者标注边界模糊导致模型学到了错误的特征。4. 常见问题、调参与部署避坑实录4.1 小样本数据的过拟合信号与应对616张图训练过拟合是头号风险。过拟合最典型的信号是训练loss持续下降而验证集的mAP在上升一段时间后突然掉头向下同时训练集mAP继续上升到接近1.0。这说明模型已经开始“背答案”了对训练集的缺陷表现和背景噪声都形成了记忆。应对过拟合我试过不少方法最有效的是组合拳一控制训练轮数用patience早停机制mAP连续30个epoch不涨就停二把增强强度适度调高尤其是scale和translate让模型看到的缺陷位置和尺度更加多变三如果显存和内存允许可以按一定比例把多张测试图上相同的正常屏幕区域作为负样本导出让模型学习区分“正常”与“缺陷”。这个方法在工业视觉里叫背景负样本抑制实测能让误检率降低一大截。4.2 类别不平衡4类缺陷样本数差距太大的处理屏幕缺陷数据本身存在天然的不平衡。划伤和脏污出现的频率远高于亮点和暗点这会导致模型对样本量少的类别检测能力严重不足。我在这个数据集上统计过bbox数量划伤约占四成脏污约占三成亮点和暗点合计不到三成。处理不平衡有几种手段。第一直接在训练参数里配置类别权重。Ultralytics在训练时没有直接的class_weight参数但可以通过在数据加载阶段对图像做类别比例采样来缓解。第二对样本少的类别做离线增强把亮点和暗点的图像复制若干份配合随机旋转、裁剪、缩放生成新的训练样本人工增加这些类别的数量。第三调整损失函数中cls损失的权重。YOLOv8支持在自定义模型中修改cls损失系数把亮点和暗点的损失权重提高迫使模型更关注这些困难类别。从实测效果看离线增强和损失权重调整的联合使用效果最好。只调损失权重容易出现“检出来了但框不准”的问题而离线增强能同时改善分类和回归能力。4.3 标注格式转换和目录组织中的经典坑标注相关的坑值得单独整理一份清单第一个坑是类别名不一致。VOC的XML里写的类别名和YOLO转换脚本里的class_names列表顺序不符。比如XML里写了brightspot脚本里定义的是bright_spot这个样本会被跳过而且没有任何报错。训练时mAP偏低查找半天也找不到原因。建议转换完成后用脚本统计一下每个类别的样本数量和标注台账核对。第二个坑是训练集和验证集的信息泄漏。如果同一块屏幕被不同角度拍摄成多张图像随机拆分数据集时这些图像可能同时出现在train和val里。由于它们高度相似模型在val上会表现得比实际情况好很多。解决方法是按屏幕编号分组划分而不是按单张图像划分。第三个坑是路径问题。data.yaml里的path是相对路径如果在别的目录下执行训练命令路径就会失效。建议全部使用绝对路径或者把data.yaml放在数据集根目录下用path: .作为配置。第四个坑是中文路径或者特殊字符路径。Windows下如果数据集路径中包含中文很多图像处理库会出错。把所有路径改成纯英文小写最省心。第五个坑是图像通道问题。部分工业相机输出的是灰度图只有单通道而YOLO训练默认要求三通道输入。处理方式是把灰度图复制三个通道或者直接在上游把图像转成RGB再保存不要在训练配置里搞特殊处理。4.4 部署环境与推理速度的平衡模型训练完成后部署到产线才是最终目的。屏幕缺陷检测的推理环境通常不是GPU服务器而是一台工控机配备的可能只是普通的CPU或者低端显卡。导出模型时一般先把YOLOv8的PyTorch权重导出为ONNX格式再转换成TensorRT或者OpenVINO格式根据硬件选择推理后端。导出ONNX的命令很简单model.export(formatonnx, imgsz640, dynamicFalse)导出的ONNX模型可以用ONNX Runtime跑CPU推理速度在640分辨率的输入下大约每张50到100毫秒取决于工控机CPU的性能。如果工控机有NVIDIA显卡可以进一步转成TensorRT的fp16版本速度能提升3到5倍。部署时还需要考虑实际检测区域与训练图像的一致性。产线上的屏幕通常由定位治具固定在相机下方检测区域相对固定。可以设置ROI区域只对屏幕所在区域做推理跳过背景区域一方面降低误检率另一方面减少计算量。同时推理结果要结合产线的时序逻辑比如检测到连续两帧或者三帧都出现同一位置缺陷时才判定为真缺陷这个时域滤波策略能有效滤除偶发的信号干扰。经验小结最后分享一点个人体会。做工业质检数据集最耗费精力的环节往往不是模型调参而是数据的规范化。这份616张4类别的屏幕缺陷数据集之所以能快速落地训练核心在于类别定义清晰、标注边界统一、格式转换脚本健壮、目录结构规范。工业现场的缺陷形态永远比实验室复杂模型永远不会一次到位但数据整理的功夫到了后续所有迭代都会顺畅很多。如果你手头也有类似的工业图像数据我的建议是先花时间把标注规范定死再写一个可靠的格式转换脚本最后才考虑训练什么模型。顺序反过来后面一定踩坑。