简介:面向瓷砖表面缺陷检测的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 stopping;test集训练期间不要碰,只在全部训练结束后评估一次。如果手痒提前把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(encoding="utf-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时容易带BOM,ultralytics读取时第一个类名开头会多一个不可见字符,导致names匹配不上。
3. 用PyCharm跑通YOLOv8训练:数据yaml、损失函数与最省心的启动参数
3.1 使用PyCharm安装YOLO:虚拟环境与PyTorch分开装
使用PyCharm安装并使用YOLO是新手高频搜索,实际操作就三步:建conda虚拟环境、装对应显卡的PyTorch、最后装ultralytics。关键点在于别在base环境里直接pip install ultralytics,项目装多了依赖必然互相打架,这个亏我吃过不止一次。
conda create -n yolo python=3.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环境。
参数说明:python=3.10是通用选择,ultralytics对3.8到3.11都支持;cu121对应CUDA 12.1的预编译轮子,显卡驱动较老就换成cu118。没有NVIDIA显卡就跳过index-url安装CPU版torch,后面训练命令加device=cpu。
我一般会顺手装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 stopping,test只用于最终评估,平时训练流程不会读它。
参数说明:nc必须等于class文件的行数;names里的0、1顺序必须和class文件逐行对应,不要自己重新起名或调整顺序。path用绝对路径,可以避免“明明数据集就在工程目录里,一换工作目录就报错”的诡异问题。
很多人会问:数据太少,train和val指向同一份行不行。答案是能跑,但early stopping等于失效,因为验证用的样本模型已经见过,loss开始涨的那一轮根本不是真正过拟合的点。这套资源既然把train和val划分好了,直接按上面填,没必要自废武功。
3.3 启动训练:yolo命令的必调参数与三条损失曲线怎么看
ultralytics 8.x的训练命令是子命令风格,跑通这套2类裂缝数据的最小命令就是这个:
yolo detect train \ data=tile_crack.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ patience=20 \ seed=42 \ project=runs \ name=tile_crack参数看着多,真正每次必调的其实就几个,列成表:
| 参数 | 建议值 | 说明 |
|---|---|---|
| data | tile_crack.yaml | 上一步写的yaml文件路径 |
| model | yolov8n.pt | 预训练权重,不是随机初始化 |
| epochs | 100 | 2类小数据集50到200之间足够 |
| imgsz | 640 | 细裂缝可以提到960或1280 |
| batch | 16 | 受显存限制,OOM就减半 |
| patience | 20 | 验证集连续20轮不提升就早停 |
| seed | 42 | 固定随机种子,实验可复现 |
逻辑说明:model写yolov8n.pt时,框架会自动下载预训练权重并做迁移学习;如果本地已有权重,改成绝对路径即可。patience和epochs配合使用,epochs设大一点没关系,早停会在真正收敛处掐住训练。
参数说明:batch不只看显存,它影响梯度估计的噪声。batch太小收敛容易来回震荡,batch太大又会降低梯度多样性,取16配合imgsz=640是大多数8到12G显存显卡的甜点位。如果batch=16直接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", encoding="utf-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(encoding="utf-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之外的类别ID,Counter的key会自动扩展,不需要改脚本。想画柱状图,就在循环结束后加两行matplotlib,把counter转成x和y画bar即可。
遇到分布差异大的情况,要换分层划分:在每个类别内部做随机抽样,按比例切进train和val,再合并目录。手工操作容易出错,写30行脚本也用不了多久。这套资源如果是脚本自动划分出来的,大概率走的是全局随机,值得用上面的脚本验证一下。
4.3 推理图验证:可视化脚本的最后一公里
训练结束后还要跑一条预测命令,把best.pt在val图上过一遍:
yolo detect predict \ model=runs/tile_crack/weights/best.pt \ source=dataset/images/val \ save=True \ conf=0.25逻辑说明:预测结果图会保存在runs/detect/predict目录,每张图画出模型预测的框、类别和置信度。这里看的重点有两个:框有没有把整片阴影或纹理起伏当成裂缝;高置信度的预测是否畸形地集中在某一类。
参数说明:conf是置信度阈值,默认0.25。裂缝检测想多看点漏检就降到0.1,想看更干净的结果就抬到0.5。save=True把渲染图落盘,不写的话只输出统计数字,看不到图。
这一步本质上也是可视化脚本的延续:训练前用脚本检查标注质量,训练后把预测结果渲染出来检查模型行为。两道检查都做完,整个数据闭环才算真正关上。
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。
原因:batch=16、imgsz=640这个默认组合对8G以下显存不友好。裂缝数据如果原图分辨率高,数据增强还会放大内存占用。
解决:把batch降到8或4重跑。batch减半收敛会慢一点,但2类裂缝数据集规模通常不大,多等几十分钟换来能跑完,比硬撑OOM一次次重启实在。如果batch降到4仍然OOM,先nvidia-smi看显存是不是被其它进程占满。
6. 把2类裂缝数据的精度再往上提:迁移学习与小目标增强参数
6.1 从nano到s/m:迁移学习不是玄学,是精度换速度
nano模型在流程验证阶段最省心,出结果快,适合先快速判断数据有没有问题。流程全通之后想提精度,就换更大的预训练权重:
yolo detect train data=tile_crack.yaml model=yolov8s.pt epochs=100 imgsz=960 batch=8换成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调高一档的效果更明显。数据标注质量在裂缝检测这种任务上,比超参数值钱得多,这也是资源包里那套可视化脚本真正的长期价值——每次新增数据都先跑一遍,低成本确认数据没有悄悄变质。
我现在收到任何一份数据集,都先跑可视化脚本,再开训练,这已经成了习惯。先把说服不了自己的数据拦在训练之前,后面所有实验才站得住。希望帮到你。
本文还有配套的精品资源,点击获取