ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

手机屏幕缺陷检测YOLO数据集训练指南:300张标注图踩坑与调优

手机屏幕缺陷检测YOLO数据集训练指南:300张标注图踩坑与调优 简介面向计算机视觉开发者与算法学习者的手机屏幕缺陷检测数据集适配YOLOv5、YOLOv7、YOLOv8、YOLOv9、YOLOv10、YOLO11等主流YOLO系列算法可直接用于目标检测模型的训练、验证与测试。数据集已预先划分完毕附带的data.yaml配置文件可无缝对接YOLO系列训练流程省去手动编写配置的步骤。包内共有901个文件包含300张jpg原图、300个YOLO格式txt标签、300个VOC格式xml标签以及1个yaml配置文件两种标签格式分别存放便于在不同工具链或标注规范间灵活切换整体压缩包约14.37MB轻量易用。目前已有220人学习下载适合刚接触目标检测的初学者快速搭建实验也适合有手机屏幕质检需求的开发者作为预训练与算法验证的数据基础。1. 手机屏幕缺陷检测数据集的正确打开方式三百张标注图够不够用手机屏幕缺陷检测是目标检测里特别实用但也特别容易翻车的场景产线上要识别划痕、暗点、污渍这类小目标缺陷往往只有几毫米宽普通目标检测思路直接套上去效果很差。这个“yolo算法-手机屏幕缺陷数据集-300张图像带标签.zip”就是为这类任务准备的带标注目标检测数据集。300张图像听起来不够但工业缺陷数据本来就是少量样本为主能拿到几百张干净标注的已经是幸运。这套数据真正的作用是让你在半小时内把yolo训练链路完整跑通并验证小样本下的数据增强、交叉验证和迁移学习策略。它适合三类人刚接触yolo想走通全流程的新人需要快速评估缺陷检测可行性的工程师以及做少样本检测实验的研究者。后面我会从解压和查标签开始讲清楚训练配置和最容易踩的坑。2. 数据集结构与标签格式解压后先做这三步检查压缩包到手后直接开训是新手最容易犯的错。zip里可能藏着目录嵌套、乱码、标签缺失一堆问题训练前花十分钟检查目录和标签能省出几天的返工时间。下面按三步走。2.1 解压目录与文件对应images 和 labels 不能错位先解压到干净目录。注意不要在Windows上双击直接解压文件名里的中文会带来一堆编码问题。我习惯在终端里操作mkdir -p ~/datasets/phone_screen_defect_raw cd ~/datasets/phone_screen_defect_raw unzip ~/downloads/yolo算法-手机屏幕缺陷数据集-300张图像带标签.zip解压后用tree列出目录结构tree -L 2 phone_screen_defect常见结构是images/和labels/两个目录图像后缀是jpg标签后缀是txt文件名前缀一一对应。比如IMG_0001.jpg对应IMG_0001.txt。如果发现某些标签文件名对应不上训练时dataloader会直接报错。先用find核对数量find phone_screen_defect -name *.jpg | wc -l find phone_screen_defect -name *.txt | wc -l300张图像就应该有300个标签文件某些负样本txt为空也占位。如果数字不一致优先怀疑解压时中文路径被截断把整个zip包移到纯英文路径重新解压。还要注意压缩包是否多套了一层目录。常见情况是解压后路径变成phone_screen_defect/phone_screen_defect/...这样后面写配置会很绕。发现多套一层就直接把内层目录提升上来mv phone_screen_defect/phone_screen_defect/* phone_screen_defect/执行完再跑一次find确认最外层目录里直接就是images和labels。目录这一关过了接下来就要看标签内容。标签格式不对训练阶段必然翻车。2.2 标签文件逐行解析类别ID与归一化坐标YOLO系列模型v5、v8、v11通用的标签格式是每行五个数字0 0.517578 0.428125 0.056641 0.031250 1 0.625000 0.415625 0.132812 0.056250第一个数字是类别ID从0开始。后四个是x_center y_center width height全部是相对原图宽高的归一化值。也就是说0.517578是框中心点的横坐标占图像宽度的比例不是像素值。这种方式与VOC的左上右下像素坐标完全不同。把VOC数据集转成YOLO格式时必须执行(x1 x2) / 2 / img_w这样的换算。如果搞错了训练出来的模型框的位置会整体偏移。为了直观判断标签是否贴图写一个脚本把框画回原图import cv2 image_path phone_screen_defect/images/IMG_0001.jpg label_path phone_screen_defect/labels/IMG_0001.txt img cv2.imread(image_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls, xc, yc, bw, bh map(float, parts) # 将归一化坐标还原为像素坐标并画框 x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(int(cls)), (x1, max(0, y1 - 6)), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) cv2.imwrite(debug_bbox.jpg, img)这段代码将归一化坐标还原成像素坐标然后画框。如果画出来的框没贴在屏幕缺陷上说明标签的参考系错了或者原始图像被resize过而标签没同步重算。代码里map(float, parts)会把类别和四个坐标都转成float后面再取int(cls)。要注意的是如果parts里出现了非数字字符这里会抛异常。这也是个信号标签文件可能混入了非法字符通常还会伴随类别ID越界。所以画框之前最好先做统计下面这步不能省。2.3 用脚本统计类别分布训练前必查手机屏幕缺陷往往不止一种。常见的有划痕、暗点、污渍、亮点、碎屏等。但这份数据集到底有几种缺陷、每种多少个框不能凭文件名猜直接统计标签文件import os from collections import Counter label_dir phone_screen_defect/labels class_counter Counter() box_count 0 for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname), r) as f: for line in f: line line.strip() if not line: continue cls int(line.split()[0]) class_counter[cls] 1 box_count 1 print(总目标框数:, box_count) for cls_id, cnt in sorted(class_counter.items()): print(f类别 {cls_id}: {cnt} 个框)运行结果可能类似类别0有236个类别1有188个类别2有47个。这时要立刻意识到类别2样本严重不足。用300张图训练时类别不平衡会导致少数类几乎学不到特征AP会很低。如果你接手的是工业场景缺陷少的类别恰恰是真正贵重的缺陷不能放着不管。还可以检查空标签文件find phone_screen_defect/labels -name *.txt -empty空txt说明对应图像没有标注框是负样本。YOLO训练时负样本对抑制误检有帮助但如果负样本占比例过大可能导致模型整体偏向不检测。另外要确认类别ID是否连续。YOLO要求nc等于类别总数id从0到nc-1。如果统计结果里类别1消失、类别2存在那说明数据集在预处理时丢了一类。这时要回到标注源文件查不能直接改nc假装没看到。3. 把数据集喂给YOLO目录改造与训练参数调优原始数据集的images和labels目录是平铺的YOLO训练期望train/val分目录。如果不重新组织训练脚本会找不到数据。这一步没有技术难度但目录结构写错会浪费大量时间。3.1 按YOLOv5/v8规范重排数据目录YOLO的数据目录要求是images/train、images/val、labels/train、labels/val。图像和标签目录平行且每个子目录中的文件一一对应。我在项目根目录下建datasets/phone_defect来放整理后的数据。mkdir -p datasets/phone_defect/images/train mkdir -p datasets/phone_defect/images/val mkdir -p datasets/phone_defect/labels/train mkdir -p datasets/phone_defect/labels/val目录建好后写一个python脚本完成数据划分。为了可复现固定随机种子import os import random import shutil src_images phone_screen_defect/images src_labels phone_screen_defect/labels dst_images datasets/phone_defect/images dst_labels datasets/phone_defect/labels random.seed(42) image_files [f for f in os.listdir(src_images) if f.endswith(.jpg)] random.shuffle(image_files) split int(len(image_files) * 0.8) for i, img_name in enumerate(image_files): label_name img_name.replace(.jpg, .txt) src_img os.path.join(src_images, img_name) src_lbl os.path.join(src_labels, label_name) # 标签缺失时打印并跳过避免训练时出错 if not os.path.exists(src_lbl): print(f缺失标签: {src_lbl}) continue if i split: dst_img os.path.join(dst_images, train, img_name) dst_lbl os.path.join(dst_labels, train, label_name) else: dst_img os.path.join(dst_images, val, img_name) dst_lbl os.path.join(dst_labels, val, label_name) shutil.copy(src_img, dst_img) shutil.copy(src_lbl, dst_lbl)这个脚本的逻辑并不复杂但有一个容易被忽略的点image_files过滤条件是.jpg如果数据集里混入.png或.jpeg就会被跳过。解压后先跑一下ls看看扩展名再改脚本里的后缀。关于划分比例300张图8:2划分训练240张、验证60张。60张验证数据对目标检测来说偏少但可以接受。如果想更稳把比例改成0.85训练255张、验证45张验证会更不稳。所以0.8是折中。复制完成后检查两边数量ls datasets/phone_defect/images/train | wc -l ls datasets/phone_defect/images/val | wc -l如果train和val数量加起来等于300说明没有丢数据。接下来写data.yaml。3.2 编写 data.yaml路径、类别数与名称data.yaml是YOLO训练时读取的数据描述文件。内容非常少但每个字段都有人写错过。path: datasets/phone_defect # 数据集根目录相对启动命令的路径 train: images/train val: images/val nc: 3 # 类别总数 names: 0: scratch 1: dark_spot 2: smudgepath是数据集根目录。这里的路径是相对你启动训练命令的目录而言。如果从YOLO项目根目录启动datasets/phone_defect没问题。如果从别的目录启动建议写绝对路径path: /home/user/datasets/phone_defecttrain和val的值相对于path。注意YOLOv8还支持在yaml里直接写train: /absolute/path/images/train但我建议保留path字段这样更直观。nc必须与上一步统计到的类别数一致。如果标签里出现了类别3这里就要写4。names列表的排序必须严格对应类别ID。类别0是scratch类别1是dark_spot类别2是smudge。顺序写反了训练不会报错但推理出来的语义结果全错。写完yaml后可以用一个小脚本验证路径是否正确解析import os import yaml with open(phone_defect.yaml, r) as f: cfg yaml.safe_load(f) for split_name in [train, val]: p cfg[path] / cfg[split_name] print(split_name, p, len(os.listdir(p)))如果打印出来的目录存在且数量正常就可以进入训练。3.3 训练命令与关键参数从 batch 到 epochs 的设置逻辑用YOLOv5训练最简单的命令如下python train.py \ --data phone_defect.yaml \ --weights yolov5s.pt \ --img 640 \ --epochs 100 \ --batch-size 16 \ --patience 20 \ --device 0先解释--weights。这里填的是预训练权重强烈不推荐从--weights 开始随机初始化。300张图随机初始化模型训练100轮基本不收敛。用coco上预训练的yolov5s.pt模型已经会提取通用特征只需要在屏幕缺陷上微调。数据量越少迁移学习的收益越明显。--img 640是输入分辨率。手机屏幕缺陷是小目标640是性价比最高的选择。如果屏幕在图像中占整个画面缺陷宽度只有十几个像素那就考虑把--img调到800或960。代价是显存和训练时间直线上升。--batch-size取决于显存。yolov5s模型在16GB显存上可以开到328GB建议16。batch过大容易把显存占满过小会引入噪声。对小样本batch16比较稳定。--epochs100轮是起点。小样本数据量少每一轮见的数据也少100轮跑起来很快。建议配合--patience早停验证集mAP连续20轮不上升就停。这样可以避免后段过拟合。--device 0指定GPU。没有GPU就写--device cpu但300张图epochs100也会非常慢不现实。训练过程中建议开tensorboard看曲线tensorboard --logdir runs/train重点看val/box_loss和val/cls_loss。训练loss持续下降验证loss在某个节点开始回升就是过拟合的典型信号。小样本训练时这个现象几乎一定会出现差别只是来得早晚。如果发现过拟合来得很早可以换yolov5n.pt。它参数更少模型容量小在数据量有限时更不容易死记硬背。如果yolov5n都过拟合那不是模型容量问题而是数据本身已有重复图像或者验证集划分泄漏了。数据泄漏的排查放到下一章。4. 避坑指南小样本数据集训练踩过的五个坑小样本训练最怕的不是代码报错而是模型训练完了指标虚高、实际不能用。下面五个坑是我实际踩过的每个都有现象、原因、解决。4.1 坑一标签与图像文件名不匹配现象训练报错assert len(imgs) len(labels)或者训练能跑但val mAP极低。细查发现一部分图像没有对应标签模型永远学不到那一类。原因有的是原始zip里标签文件名中带_label后缀比如IMG_0001_label.txt有的是解压时中文文件名被转码导致前后缀不一致。解决写脚本检查并重命名。import os img_dir datasets/phone_defect/images/train lbl_dir datasets/phone_defect/labels/train for img in os.listdir(img_dir): stem os.path.splitext(img)[0] expected os.path.join(lbl_dir, stem .txt) if not os.path.exists(expected): # 尝试带后缀的文件名 alt os.path.join(lbl_dir, stem _label.txt) if os.path.exists(alt): os.rename(alt, expected) print(f修复 {alt} - {expected}) else: print(f缺标签: {img})注意如果原始数据集里存在负样本没有缺陷的图也会缺txt。这种情况不需要重命名但要专门记下来在训练时保留空txt占位而不是删掉图像。4.2 坑二类别ID越界或标签值越界现象训练开始几轮正常突然loss变成nan或者进度条正常但mAP一直是0。原因标签txt中某一行写了一个不存在的类别ID比如数据集只有3类ID却写4。模型在计算类别损失时会越界。另一个常见原因是坐标没有归一化某个值大于1导致anchor匹配计算异常。解决训练前做一次全量标签校验import os nc 3 label_dir datasets/phone_defect/labels for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue path os.path.join(label_dir, fname) with open(path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f[格式错误] {fname}: {line.strip()}) continue cls int(parts[0]) xc, yc, w, h map(float, parts[1:]) if cls nc: print(f[类别越界] {fname}: cls{cls}) if not (0 xc 1 and 0 yc 1 and 0 w 1 and 0 h 1): print(f[坐标越界] {fname}: {line.strip()})如果找到越界样本直接删除该行或手动修正。不要抱着“就一个样本不影响”的心态一个nan就能毁掉整个训练过程。4.3 坑三过拟合验证集mAP和训练集mAP差距巨大现象训练结束后results.png里训练集mAP接近0.9验证集mAP只有0.3。训练曲线和验证曲线从中间就开始分叉。原因300张图对yolov5s来说太少。模型记住了训练图像的背景纹理而不是缺陷本身。尤其手机屏幕照片的背景是玻璃反光采样偏差会让模型学到的“特征”其实是反光角度。解决先换yolov5n再把数据增强里的mosaic概率开到最大。YOLOv5默认的增强配置在hyp.scratch-low.yaml把mosaic: 1.0、mixup: 0.2加上让模型每次看到的样本更“多变”。也可以加一个简单的失活操作不做Dropout而是把输入图像随机裁剪一小块再放回这相当于更激进的平移增强。另一个行之有效的方法是减少epochs并使用早停训练到验证mAP不再上升就停不要为了凑100轮继续往后跑。4.4 坑四zip压缩包解压失败或伪加密现象解压到一半报CRC error或者解压软件弹窗要求输入密码但资源描述没提密码。原因压缩包下载不完整或压缩时设置了伪加密标志。伪加密并没有真正的加密算法但不少解压工具检测到加密位就会要求输密码。解决先测试压缩包完整性unzip -t yolo算法-手机屏幕缺陷数据集-300张图像带标签.zip输出末尾如果出现No errors detected就正常。如果有error说明zip文件损坏重新下载。伪加密的情况用7z强制解压7z x yolo算法-手机屏幕缺陷数据集-300张图像带标签.zip7z对伪加密的识别更宽松通常能直接解开。如果7z也要密码那这个包真的加密了。另外中文文件名在Linux上解压可能乱码可以指定编码unzip -O gbk yolo算法-手机屏幕缺陷数据集-300张图像带标签.zip-O gbk是让unzip用GBK解码文件名避免变成?????.txt。这个坑在Windows下不明显在Linux/macOS下很常见。4.5 坑五验证集与训练集数据泄漏现象验证mAP出奇地高但把模型放到自己拍的照片上检测效果一塌糊涂。原因随机划分数据集时同一块屏幕模组的多张照片被拆到了训练和验证两个集合。模型在训练时已经见过相近的屏幕纹理验证时等于开卷考试。解决按图像前缀或批次分组划分。手机屏幕缺陷数据集的命名通常带批次信息比如panel_001_cam1.jpg和panel_001_cam2.jpg。用正则提取设备ID再按ID分组。import os import random from collections import defaultdict groups defaultdict(list) for fname in os.listdir(phone_screen_defect/images): if not fname.endswith(.jpg): continue prefix fname.split(_)[0] _ fname.split(_)[1] # 提取panel编号 groups[prefix].append(fname) random.seed(42) group_names list(groups.keys()) random.shuffle(group_names) split int(len(group_names) * 0.8) train_files [] val_files [] for i, g in enumerate(group_names): files groups[g] if i split: train_files.extend(files) else: val_files.extend(files) print(train:, len(train_files), val:, len(val_files))这样划分后同一个panel的图片不会跨集合。验证集指标虽然可能比随机划分低一些但更接近真实应用。5. 验证与进阶从mAP曲线到数据增强的小样本调优技巧训练完拿到best.pt先用验证集评估python val.py --data phone_defect.yaml --weights runs/train/exp/weights/best.pt --img 640输出会列出每个类别的precision、recall和mAP0.5。如果某类AP明显偏低打开混淆矩阵看它被误判成了哪一类。手机屏幕缺陷里划痕和污渍在低分辨率下很容易混淆这时候加大输入分辨率比加大模型更有效。数据增强是小样本最直接的作弊器。YOLOv5默认的hyp.scratch-low.yaml里mosaic已经开了但mixup默认是0。把它改成0.2两张图按权重混合成新样本能有效抑制过拟合。HSV扰动里hsv_h不要调太大屏幕颜色是重要特征色调翻得太厉害会让模型学到假的“色彩特征”。单次划分验证集有运气的成分。300张图随机划分可能恰好把最难样本全丢进验证集mAP波动很大。更稳的做法是五折交叉验证from sklearn.model_selection import KFold image_files [...] # 按分组划分后的文件列表 kf KFold(5, shuffleTrue, random_state42) for fold, (train_idx, val_idx) in enumerate(kf.split(image_files)): # 本轮用train_idx的文件复制train目录val_idx复制val目录 # 训练后记录best.pt的mAP print(ffold {fold} mAP: ...)五折交叉验证的训练成本是5倍但小样本数据量小用yolov5n每折100轮单卡完全能接受。最后取五折平均mAP比单次划分更有说服力。还有一个实用技巧中途用--resume续训。改完数据增强或学习率后不必从头开始用last.pt接着跑python train.py --data phone_defect.yaml --weights runs/train/exp/weights/last.pt --epochs 50 --resume--resume会从runs/train/exp读取之前的训练状态。小样本反复调参时这个命令能省下很多等待时间。回到数据集本身300张图不是限制而是让你把yolo训练、验证、交叉验证全流程跑通的最佳起点。数据量小反而逼着你把每一步都做扎实。从那以后我每次拿到小数据集都强制先跑一遍类别分布脚本再做分组划分和五折验证最后才进训练。这套流程虽然多花半小时但能省下后面几天返工时间。希望帮到你。本文还有配套的精品资源点击获取
返回列表