YOLO瓷砖裂缝数据集验货指南:标注检查与训练避坑全流程
简介面向瓷砖表面缺陷检测的YOLO格式数据集适合计算机视觉学习者与工业质检场景开发者使用。数据覆盖正常与裂缝两个类别使用LabelImg标注为txt文本并对部分样本进行了翻转、加噪等数据增强已按目录划分好可直接用于YOLO系列模型训练。资源共2000个文件主要为1765个txt标签文件、234张jpg图像与1个Python可视化脚本打包为7z格式整体大小91.75MB。其中可视化脚本支持随机输入一张图片即自动绘制并保存边界框无需修改即可运行方便快速检查标注效果。目前已有194人学习适合需要标准化缺陷检测数据、快速搭建瓷砖裂缝识别方案的读者。1. 拿到这份瓷砖裂缝YOLO数据集先花10分钟验货再谈训练刚拿到写着“YOLO数据集瓷砖裂缝识别检测2类”的资源包时我建议的第一件事不是解压后立刻开训而是先验货。带新人时常碰到这类翻车图片和txt都齐了损失也正常下降mAP50却一直是0最后查出来是class文件里的类别顺序和标注文件第一列数字错位。这套数据集自带划分好的train/val/test目录、class类别文件和数据可视化脚本省掉了采集和标注的脏活适合做瓷砖外观缺陷检测落地的工程师也适合用YOLOv8做毕设、想尽快跑通完整检测流程的在校生。但“划分好”不等于“没毛病”训练前花十来分钟按下面顺序验一遍能省下后面几天的排查时间。2. 读懂class文件与目录结构两份文件决定你训练路上要绕多远2.1 目录结构images与labels必须严格一一对应“划分好的数据集”最常见的长这样dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/图片和标签分images与labels两棵子树train/val/test三对目录一一配套。划分比例常见8:1:1也有7:2:1具体数字不重要重要的是三个集合在训练流程里各司其职val集每个epoch结束都要做一次验证用loss和mAP决定early stoppingtest集训练期间不要碰只在全部训练结束后评估一次。如果手痒提前把test集混进训练集或val集最后的评估数字就是脏的。检查这套数据是否真的“划分好”最值得做的一个动作是对比文件名。给一段按文件名求差集的代码from pathlib import Path img_dir Path(dataset/images/train) lbl_dir Path(dataset/labels/train) imgs {p.stem for p in img_dir.glob(*.jpg)} labels {p.stem for p in lbl_dir.glob(*.txt)} print(有图无标注, len(set(imgs) - set(labels))) print(有标注无图, len(set(labels) - set(imgs))) print(总数, len(imgs), 张图, len(labels), 份标注)逻辑说明glob按后缀取出图片名和标注名再对集合求差。有图无标注的目标会在训练中直接被忽略有标注无图会导致datasets加载时报错或静默跳过。无论哪种实际训练样本数都会变少。跑完看到两个差集都是0文件夹这关就过了。参数说明图片后缀如果是png或jpeg把glob里的“*.jpg”改成对应后缀如果images目录下还有子目录用rglob代替glob才会递归扫描。2.2 YOLO标注格式归一化坐标不是玄学是硬规则目标检测数据集的标注格式有VOC的XML、COCO的JSON以及YOLO系最常用的txt。这份资源对应的是YOLO原生文本格式每行一个目标class x_center y_center width height。后面四个数都是归一化坐标也就是除以图片宽高之后的比例值。举个例子一行标注0 0.5134 0.4289 0.1832 0.0671。意思是类别0目标中心点位于图片横向51.34%、纵向42.89%的位置宽度占图片宽度的18.32%高度占6.71%。因为坐标是比例而不是像素图片做resize、letterbox时标注才不需要跟着改这是YOLO系训练能做各种尺寸增强的基础。归一化坐标必须落在0到1之间类别号必须小于类别总数。数据量一大人工抽查根本看不过来这段小脚本可以把整个labels目录扫一遍from pathlib import Path labels_dir Path(dataset/labels/train) bad_count 0 for txt in labels_dir.glob(*.txt): for line in txt.read_text(encodingutf-8).splitlines(): parts line.split() if len(parts) ! 5: print(f{txt.name}: 字段数不是5 - {line}) bad_count 1 continue cls, xc, yc, bw, bh map(float, parts) if int(cls) not in (0, 1): # 2类数据集类别ID只能是0或1 print(f{txt.name}: 类别越界 - {cls}) bad_count 1 if not (0 xc 1 and 0 yc 1 and 0 bw 1 and 0 bh 1): print(f{txt.name}: 坐标越界 - {line}) bad_count 1 print(f检查完成异常 {bad_count} 条)逻辑说明遍历labels目录下每个txt的每一行先看字段数再看类别ID最后检查坐标范围。字段数不等于5说明可能混入了VOC XML泄出的格式或CSV导出残留坐标越界会让模型学出“往画面外飘”的框类别ID超过0-1范围则说明标注时和class文件用的不是同一套类别表。参数说明类别越界判断写成了not in (0, 1)对应本数据集的2类你的class文件类别名不同就把这个元组改成实际类别ID集合。空txt文件这段脚本不会报错想连空文件一起查在循环外加一个if txt.stat().st_size 0的判断。2.3 class文件是“翻译表”类别顺序错一位训练白忙class文件本身没有太多技术含量就是一行一个类别名crack_h crack_v我这里用“横向裂纹/纵向裂纹”做例子实际以你手里那份class文件为准。训练时yaml配置里的names列表决定检测器输出结果的标签含义推理时模型输出的类别数字回填成可读名字也靠同一份names。顺序必须严格一致。这种错位最坑的地方在于mAP和loss都很正常你以为模型学得不错实际它把所有类都认成了错位的名字。瓷砖裂缝这2类外观本来就接近错位后在验证数字上几乎看不出来。怎么自查用可视化脚本把标注框渲染回原图看类别0的框对应的裂纹形态和class文件第0行的名字是否吻合。这个动作应该发生在训练前而不是训练结束之后。提示class文件里不要留空行也不要有多余的BOM头。Windows下用记事本另存为UTF-8时容易带BOMultralytics读取时第一个类名开头会多一个不可见字符导致names匹配不上。3. 用PyCharm跑通YOLOv8训练数据yaml、损失函数与最省心的启动参数3.1 使用PyCharm安装YOLO虚拟环境与PyTorch分开装使用PyCharm安装并使用YOLO是新手高频搜索实际操作就三步建conda虚拟环境、装对应显卡的PyTorch、最后装ultralytics。关键点在于别在base环境里直接pip install ultralytics项目装多了依赖必然互相打架这个亏我吃过不止一次。conda create -n yolo python3.10 -y conda activate yolo # 先跑 nvidia-smi 看驱动支持的最高CUDA版本再选对应的pytorch轮子 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install ultralytics逻辑说明先装PyTorch再装ultralytics。ultralytics是上层封装安装时会检测环境中是否有torch顺序反了大概率报ImportError重装torch就能解决。装完后在PyCharm的Settings Project Python Interpreter里把解释器切到yolo环境否则在PyCharm里跑可视化脚本时仍然走旧的base环境。参数说明python3.10是通用选择ultralytics对3.8到3.11都支持cu121对应CUDA 12.1的预编译轮子显卡驱动较老就换成cu118。没有NVIDIA显卡就跳过index-url安装CPU版torch后面训练命令加devicecpu。我一般会顺手装opencv-python和matplotlib数据可视化脚本和推理结果图都用得上避免后面缺依赖再返工。3.2 数据yaml怎么填路径与names必须和class文件对齐每个YOLO训练任务都要一份数据描述yaml。以这份2类裂缝数据为例最简配置如下path: /home/user/tile_crack_dataset # 数据集绝对路径最省事 train: images/train # 相对path的目录 val: images/val test: images/test nc: 2 # 类别总数与class文件行数一致 names: # 顺序与class文件逐行一致 0: crack_h 1: crack_v逻辑说明path字段是根路径train/val/test都是相对它写的训练时框架会拼出path/train的实际地址。三个集合可以都填但val和test的区别要拎清val影响训练中的early stoppingtest只用于最终评估平时训练流程不会读它。参数说明nc必须等于class文件的行数names里的0、1顺序必须和class文件逐行对应不要自己重新起名或调整顺序。path用绝对路径可以避免“明明数据集就在工程目录里一换工作目录就报错”的诡异问题。很多人会问数据太少train和val指向同一份行不行。答案是能跑但early stopping等于失效因为验证用的样本模型已经见过loss开始涨的那一轮根本不是真正过拟合的点。这套资源既然把train和val划分好了直接按上面填没必要自废武功。3.3 启动训练yolo命令的必调参数与三条损失曲线怎么看ultralytics 8.x的训练命令是子命令风格跑通这套2类裂缝数据的最小命令就是这个yolo detect train \ datatile_crack.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ patience20 \ seed42 \ projectruns \ nametile_crack参数看着多真正每次必调的其实就几个列成表参数建议值说明datatile_crack.yaml上一步写的yaml文件路径modelyolov8n.pt预训练权重不是随机初始化epochs1002类小数据集50到200之间足够imgsz640细裂缝可以提到960或1280batch16受显存限制OOM就减半patience20验证集连续20轮不提升就早停seed42固定随机种子实验可复现逻辑说明model写yolov8n.pt时框架会自动下载预训练权重并做迁移学习如果本地已有权重改成绝对路径即可。patience和epochs配合使用epochs设大一点没关系早停会在真正收敛处掐住训练。参数说明batch不只看显存它影响梯度估计的噪声。batch太小收敛容易来回震荡batch太大又会降低梯度多样性取16配合imgsz640是大多数8到12G显存显卡的甜点位。如果batch16直接OOM优先降到8而不是去改更小的imgsz。训练开始后终端每一行会输出box_loss、cls_loss、dfl_loss三个损失值这就是yolo损失函数的三个分量。正常的训练画面是三条线整体向下val侧的指标随epoch慢慢上升。对瓷砖裂缝这类长条形小目标mAP50-95通常比mAP50低一大截这是IoU对细长框天然不友好的结果不用因此怀疑数据。真正该警惕的是训练损失下降但验证损失掉头向上那是过拟合信号patience会自动帮你停。训练结束后runs/tile_crack/weights下会有best.pt和last.pt两个权重后续推理统一用best.pt。到这里yolov8训练自己的数据集的最小路径就闭环了这也是yolo入门阶段最值得做的一遍完整流程。4. 数据可视化脚本的正确用法训练前看标注、训练后看预测4.1 渲染回原图可视化脚本的第一层用途资源包里那份数据可视化脚本第一用途不是发朋友圈而是数据质检。把标注框渲染回原图上一眼就能看出框是否贴住裂缝、类别ID是否对得上、有没有把瓷砖纹理误标成裂缝。YOLO训练不会因为一张标注图难看而报错但模型会实打实地被脏数据带偏。import cv2 from pathlib import Path img_path Path(dataset/images/val/img_001.jpg) label_path Path(dataset/labels/val/img_001.txt) img cv2.imread(str(img_path)) h, w img.shape[:2] class_names [crack_h, crack_v] # 以你手上的class文件为准 colors [(0, 0, 255), (0, 255, 0)] with open(label_path, r, encodingutf-8) as f: for line in f: cls, xc, yc, bw, bh map(float, line.strip().split()) 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), colors[int(cls)], 2) cv2.putText(img, class_names[int(cls)], (x1, y1 - 6), cv2.FONT_HERSHEY_SIMPLEX, 0.6, colors[int(cls)], 2) cv2.imwrite(visual_check.jpg, img)逻辑说明核心是归一化坐标乘回图片宽高把txt还原成像素坐标系里的矩形。cls不仅决定框颜色也决定左上角标注的类别名字如果框里画的是正常瓷砖纹理而不是裂缝多半是图片和txt配套错了。参数说明class_names和colors两个列表都要与class文件顺序一致类别超过2个就继续往后加。cv2.imread读进来的是BGR格式写出的图片在普通查看器里颜色正常不需要额外转RGB。跑脚本时建议一次抽20张不同目录的图而不是只抽一张。抽查范围要覆盖train和val各一半两个集合一旦出现分布差异训练时很难被发现渲染图里却很明显。4.2 类别分布统计二分类不均衡时比想象中更影响结果瓷砖裂缝2类数据不均衡是常态比如类0占70%、类1占30%。模型对多数类会学得更好这是数据本质决定的不算bug。真正的坑出现在划分时全局随机切分可能让验证集里某一类只剩几个目标。from pathlib import Path from collections import Counter counter Counter() for txt in Path(dataset/labels/train).glob(*.txt): for line in txt.read_text(encodingutf-8).splitlines(): cls int(line.split()[0]) counter[cls] 1 print(train 类别计数, dict(counter)) total sum(counter.values()) for cls, cnt in counter.items(): print(f类别 {cls}: {cnt} 个目标占比 {cnt / total:.1%})逻辑说明统计每个txt里每一行的第一个数字也就是类别ID汇总成Counter。跑一遍train目录再跑一遍val目录把两份占比对比。如果train里类0占70%val里类0只占10%说明划分时没有按类别分层。参数说明如果class文件是0到1之外的类别IDCounter的key会自动扩展不需要改脚本。想画柱状图就在循环结束后加两行matplotlib把counter转成x和y画bar即可。遇到分布差异大的情况要换分层划分在每个类别内部做随机抽样按比例切进train和val再合并目录。手工操作容易出错写30行脚本也用不了多久。这套资源如果是脚本自动划分出来的大概率走的是全局随机值得用上面的脚本验证一下。4.3 推理图验证可视化脚本的最后一公里训练结束后还要跑一条预测命令把best.pt在val图上过一遍yolo detect predict \ modelruns/tile_crack/weights/best.pt \ sourcedataset/images/val \ saveTrue \ conf0.25逻辑说明预测结果图会保存在runs/detect/predict目录每张图画出模型预测的框、类别和置信度。这里看的重点有两个框有没有把整片阴影或纹理起伏当成裂缝高置信度的预测是否畸形地集中在某一类。参数说明conf是置信度阈值默认0.25。裂缝检测想多看点漏检就降到0.1想看更干净的结果就抬到0.5。saveTrue把渲染图落盘不写的话只输出统计数字看不到图。这一步本质上也是可视化脚本的延续训练前用脚本检查标注质量训练后把预测结果渲染出来检查模型行为。两道检查都做完整个数据闭环才算真正关上。5. 瓷砖裂缝检测避坑笔记Loss正常却白训的5个原因下面五条按常见程度排前两条最容易让训练白跑几乎每份数据集都会中一个。5.1 val集少了某一类指标虚高推理全废现象训练日志里验证loss一路走低mAP50也很好看但部署推理时其中一类裂缝几乎一个都检测不出。原因划分数据集时用了全局随机切分占比低的那类在val集中只剩很少目标。训练本身也会因为梯度里该类占比小把该类“藏”起来。val指标看似不错只是评估集合本身缺失了这一类。解决按类别分层重新划分。按类别ID把图片分组每个组内随机抽相应比例进val其余进train合并后同时更新images和labels目录再用可视化脚本把“有图无标注”和“类别缺失”各查一遍。5.2 mAP50为0但box_loss正常下降class文件顺序错位现象box_loss、cls_loss都在正常下降验证集mAP50或mAP50-95一直是0训练结束都抬不起头。原因yaml里names顺序和class文件顺序不一致或者某个txt里混入了本来不该有的类别ID。模型输出的数字和验证时解析的数字对不上指标自然归零。解决先跑一遍2.2节里的越界检查脚本看有没有类别ID越界再用渲染脚本抽查10张验证集图确认每个框的类别名字和内容吻合。最后打开yaml把names逐行和class文件对照。5.3 ModuleNotFoundError可视化脚本和训练脚本用了两个解释器现象训练命令能正常启动一跑数据可视化脚本就报ModuleNotFoundError: No module named cv2或matplotlib。原因最常见是PyCharm里项目解释器还指在base环境而训练是在conda的yolo环境里用命令行跑的可视化脚本里cv2和matplotlib没装在当前解释器里。解决在PyCharm的Settings Project Python Interpreter里把解释器切到yolo环境。也可以在终端里对当前环境补装依赖pip install opencv-python matplotlib逻辑说明这条命令只把可视化脚本缺的可视化依赖补进当前conda环境不影响已有训练依赖解决问题最直接。5.4 细裂缝大量漏检imgsz与增强参数的取舍现象粗裂缝检得很好细且短的裂缝大量漏检可视化脚本里很多框只盖住裂缝一半。原因裂缝宽度经常只有几个像素resize到640后整条裂缝的特征被压缩没了。模型在特征图里能看到的裂缝只剩一条若有若无的亮线自然学不到可用特征。解决首选把imgsz从640提到960或1280这会直接增加裂缝在特征图上的像素数显存允许就上。显存不够就用切片推理把原图切成四块分别检测再把坐标拼回原图坐标系。同时把mosaic等大尺度数据增强关掉这类增强会把目标缩小到看不清。5.5 CUDA out of memory显存不足的常规处置现象训练刚启动或到第五六个epoch就退出日志末尾写着CUDA out of memory。原因batch16、imgsz640这个默认组合对8G以下显存不友好。裂缝数据如果原图分辨率高数据增强还会放大内存占用。解决把batch降到8或4重跑。batch减半收敛会慢一点但2类裂缝数据集规模通常不大多等几十分钟换来能跑完比硬撑OOM一次次重启实在。如果batch降到4仍然OOM先nvidia-smi看显存是不是被其它进程占满。6. 把2类裂缝数据的精度再往上提迁移学习与小目标增强参数6.1 从nano到s/m迁移学习不是玄学是精度换速度nano模型在流程验证阶段最省心出结果快适合先快速判断数据有没有问题。流程全通之后想提精度就换更大的预训练权重yolo detect train datatile_crack.yaml modelyolov8s.pt epochs100 imgsz960 batch8换成yolov8s后细裂缝的表示能力比nano明显好代价是训练和推理变慢。对2类瓷砖裂缝s通常是性价比甜点m再往上边际收益开始变小适合对精度要求苛刻、显存也够的产线场景。6.2 收紧数据增强细长裂缝经不起马赛克折腾裂缝和普通目标最大的区别是细长、方向性强、对比度低。YOLO默认开启的增强参数是为通用目标设计的直接套在瓷砖裂缝上经常适得其反hsv_h: 0.0 hsv_s: 0.0 hsv_v: 0.0 fliplr: 0.0 flipud: 0.0 mosaic: 0.0逻辑说明产线瓷砖图片光源和颜色相对固定HSV扰动会让模型去学实际不存在的颜色变化。左右翻转和上下翻转对裂缝检测也不友好裂缝本身可能对称但标注框的位置比例、长宽统计在翻转后会偏离原始分布。参数说明这组参数可以直接填进数据yaml同级的训练配置里。关掉之后如果过拟合比之前来得早就手动把hsv_h加回0.01试试。增强不是越少越好只是裂缝场景下默认值偏激进。6.3 用混淆矩阵挑难例数据比超参数更值钱训练结束后runs目录下会生成confusion_matrix.png这是判断哪些类别没学好的最直观工具。从混淆矩阵里经常能看到某个类别有一大块被分到background说明数据里该类目标太小、太少或者外观和背景纹理太接近。处理方式不是去调conf阈值而是把误检为背景的图片挑出来人工看一遍针对性地补充这类样本。补标几十张难例往往比把imgsz调高一档的效果更明显。数据标注质量在裂缝检测这种任务上比超参数值钱得多这也是资源包里那套可视化脚本真正的长期价值——每次新增数据都先跑一遍低成本确认数据没有悄悄变质。我现在收到任何一份数据集都先跑可视化脚本再开训练这已经成了习惯。先把说服不了自己的数据拦在训练之前后面所有实验才站得住。希望帮到你。本文还有配套的精品资源点击获取