简介:工业视觉缺陷检测是智能制造中的关键环节,而图像分类技术作为核心算法之一,通过深度学习模型对产品表面缺陷进行自动识别与分类,能够大幅提升质检效率。在构建工业级分类数据集时,需关注数据采集一致性、标注规范、类别定义与样本均衡性,并配合数据增强、迁移学习等手段优化模型性能。本文以瓷砖质检场景为例,系统梳理5种常见缺陷(裂纹、崩边、针孔、麻面、色差)的分类数据集构建流程,涵盖数据划分、模型选型、训练调参、常见问题排查及ONNX/TensorRT部署实践,并结合YOLOv8分类模式提供快速落地方案,帮助开发者将深度学习分类模型高效应用于实际产线。 瓷砖质检这个场景,在工业视觉里算是非常经典的落地项目了。我刚入行那会儿,生产线上瓷砖缺陷检测还主要靠老师傅肉眼盯,后来才开始慢慢转向基于深度学习的图像识别方案。这个项目标题——5种常见瓷砖缺陷分类数据集——看起来只是一个数据准备的工作,但实际上它背后牵扯到的数据采集、标注规范、类别定义、模型选型、训练调参,每一个环节都有不少坑。这篇文章我就围绕这套分类数据集的构建和使用,把我实际做过的方案、踩过的坑、总结出来的经验,完完整整分享出来。
1. 项目整体设计与思路拆解
1.1 为什么选这5类缺陷作为分类目标
做瓷砖缺陷检测,第一步不是急着找模型,而是先定义清楚到底要分哪些类别。你翻几家瓷砖厂的质量检验标准,会发现缺陷类型五花八门,什么落脏、熔洞、坯裂、釉裂、缺角、波纹、色差,加起来能有几十种。但实际生产线上,真正高频出现、对产品等级判定影响最大的,其实就那么几类。
我这次定的是5类:裂纹、崩边、针孔、麻面、色差。选择标准很简单——覆盖高频、形态差异足够明显、便于标注一致性控制。
- 裂纹:细线状,方向不固定,长度和深度变化大,是导致瓷砖降级甚至报废最主要的原因。
- 崩边:边缘区域的块状缺损,通常在切割或搬运环节产生,形状相对规整。
- 针孔:釉面微小孔洞,直径多在1毫米以内,分布密度不均匀,属于烧成阶段气泡破裂遗留。
- 麻面:表面粗糙,局部区域光泽度下降,通常由釉料配方或烧成温度异常引起,呈现片状分布。
- 色差:同一批次或同一片砖内颜色不均匀,涉及灰度或色相偏移,对视觉一致性影响明显。
这5类放在一起,既涵盖了点状、线状、块状、面状四种几何形态,也涵盖了浅色底、深色底、纹理底等不同背景条件,作为训练集来说多样性足够。
1.2 分类思路还是检测思路的选择
很多人拿到这个需求第一反应是:既然要识别缺陷,为什么不直接上目标检测,把每个缺陷框出来?我的回答是:看场景。
这套数据集定位是“分类数据集”,那核心逻辑就是——输入一张瓷砖图像,输出5个类别中的一个(或者加一个正常类,共6类)。这里有个关键点:是每张图像只包含一种缺陷,还是允许一张图包含多个缺陷同时出现?如果允许混合缺陷,分类任务的标签就变成多标签,训练难度和评估复杂度都会提升。
我这次采用单标签分类方案。理由很直接:
在生产节拍固定的流水线上,检测系统通常会对每片瓷砖拍摄多张图像,然后按区域或按单张图像做缺陷判定。如果一张图像里只有单一缺陷形态,用分类模型做初筛,速度快、精度高、易解释,后续再配合检测模型做精确定位,整个流程是清晰的两级架构。
如果非要一步到位全用目标检测,那就不是这个数据集要解决的问题了。
1.3 数据集规模的确定逻辑
图像识别模型的性能,很大程度上取决于训练数据量和类内多样性。不是图多就一定好,而是要看每个类别的变化是否覆盖充分。比如裂纹,有直线型、弧线型、网状型,有深色裂纹、浅色裂纹,有在纯色砖上的,有在仿古砖纹理上的——这些都要在数据里体现,否则模型学到的只是“训练集裂纹的样子”。
我这次的数据规模是每类1200张左右,总共6000余张,训练集、验证集、测试集按8:1:1划分。划分的时候有讲究:同一片瓷砖拍摄的多张图像要放进同一个集合,不能这边一张训练那张验证,否则会造成数据泄漏,评估结果虚高。
表1:数据规模规划
| 缺陷类别 | 训练集 | 验证集 | 测试集 | 合计 |
|---|---|---|---|---|
| 裂纹 | 960 | 120 | 120 | 1200 |
| 崩边 | 960 | 120 | 120 | 1200 |
| 针孔 | 960 | 120 | 120 | 1200 |
| 麻面 | 960 | 120 | 120 | 1200 |
| 色差 | 960 | 120 | 120 | 1200 |
| 合计 | 4800 | 600 | 600 | 6000 |
2. 核心细节解析与实操要点
2.1 图像采集环境的一致性控制
图像识别模型对采集环境极其敏感。同一个缺陷,在光源角度、亮度、相机曝光时间不同的情况下拍出来,特征分布会差异很大,模型训练出来就可能“认生”。所以采集环节要模拟产线真实环境,光源位置固定、色温恒定、拍摄角度垂直、曝光参数锁定。
我遇到的典型问题是:早期采集的一部分样本是在实验室环境拍的,背景干净、光照均匀,但产线现场拍的图片有粉尘、有震动模糊、光照有波动。两个来源的数据混在一起训练,模型在验证集上表现还行,一到产线实测准确率掉了十来个点。后来把产线数据单独抽出来做测试集,问题立刻暴露了。
实操建议:
- 采集时固定相机型号和参数,不要多台相机混采后在标注时不记录来源
- 建议至少采集3个不同批次的产品,覆盖不同釉色和纹理
- 光照明暗变化通过数据增强模拟,不要依赖现场补拍
2.2 标注规范与质量管控
数据集的灵魂在标注质量。分类任务虽然只标注一个类别标签,看起来简单,但缺陷类别的边界判断非常容易产生分歧。比如一条很短的裂纹,它算裂纹还是算瑕疵品?一个直径不到0.5毫米的凹陷,算针孔还是麻面?
我制标注规范的时候,几条硬性规定如下:
- 裂纹必须长度大于5毫米或贯穿多个纹理周期,短于该阈值的线性缺陷不计入
- 崩边必须是边缘区域(距离边界3毫米内)的缺损,内部缺损不算
- 针孔限定为直径1毫米以内的单个凹陷,超过1毫米按麻面处理
- 麻面要求成片区域,面积占比超过瓷砖表面的5%
- 色差以人眼可辨识为标准,并通过像素统计确认灰度偏差大于设定阈值
这些规则不是拍脑袋定的,而是和工厂质检负责人反复对齐后的结果。标注人员也要经过培训和考试,保证不同人对同一种情况的判断一致。
2.3 标注工具与格式转换
分类数据集标注相对简单,不需要画框、画多边形,只需要把图像按类别放入不同文件夹,或者维护一个CSV映射表。我推荐的做法是目录即标签:train/crack/xxx.jpg、train/chip/xxx.jpg,这样后续切换PyTorch的ImageFolder、Keras的flow_from_directory、YOLO的分类模式,都非常方便。
不过考虑到很多实际项目后面会升级到目标检测,我建议即使是分类项目,也顺手把关键缺陷的边界框标注一起做了,用LabelImg或X-AnyLabeling都可以。多花一点标注时间,后续切换到目标检测的时候就不用重新标注了。这个经验是从一个真实项目里学到的——前期只做了分类,年底客户说要缺陷定位,整个数据集重新标了一遍,痛苦至极。
标注格式如果后续需要兼容YOLO,文件夹结构可以这样组织:
dataset/ ├── images/ │ ├── train/ │ │ ├── crack/ │ │ ├── chip/ │ │ ├── pinhole/ │ │ ├── blister/ │ │ └── color_diff/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ └── val/ └── classes.txt2.4 数据增强策略选择
6000张图像对深度学习来说不算多,尤其CNN模型参数量大,容易过拟合。数据增强是最好的“免费数据扩充”手段。但不是所有增强方式都适合瓷砖缺陷,这是很多人容易踩坑的地方。
比如随机旋转90度,对大多数场景没问题,但如果瓷砖有方向性纹理,旋转后语义就变了;再比如水平翻转,对于裂纹这种方向敏感特征,确实能增加方向多样性,但过度翻转可能导致模型对真实缺陷方向产生误判。
我在这个项目中采用的增强组合如下:
- 随机水平翻转 + 垂直翻转:概率0.5
- 随机旋转:范围0到30度,配合旋转后裁剪避免黑边
- 随机亮度调整:系数0.8到1.2
- 随机对比度调整:系数0.8到1.2
- 随机高斯噪声:标准差0.005
- CutMix/MixUp:概率0.3,用于提升泛化能力
还要注意一点:验证集和测试集绝对不能做增强。增强只用于训练过程,否则评估结果失真。
3. 实操过程与核心环节实现
3.1 环境准备与依赖安装
训练这套分类模型,硬件配置不需要特别夸张,一张8GB显存的GPU就能跑起来。我用的环境是Ubuntu 22.04 + PyTorch 2.1 + CUDA 11.8,显卡是RTX 3060 12GB。如果只有CPU,也能跑,一个Epoch可能要几分钟,总训练时间会长很多,但逻辑完全一样。
安装关键依赖:
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install numpy pandas matplotlib opencv-python scikit-learn tqdm这里有个小提示:torchvision的版本和torch必须匹配。如果后续要转换模型到TensorRT加速,CUDA版本最好和部署环境一致,不要图省事装了最新版,后面部署时才发现不兼容,又得重新配环境。
3.2 数据加载与预处理
用PyTorch实现分类任务的数据加载,通常用torchvision.datasets.ImageFolder最简单。但在工业场景里,还要在预处理阶段多做几步:统一图像尺寸、归一化、通道顺序确认。
瓷砖图像的原图分辨率通常很高,4000x3000很常见。直接送到网络里不现实,我先把图像统一缩放到224x224,对模型识别微小缺陷如针孔会有影响,所以实际项目中用了512x512。Timm库中很多预训练模型支持任意输入尺寸,在创建模型时设置好即可。
数据加载代码:
import torch from torchvision import datasets, transforms from torch.utils.data import DataLoader train_transform = transforms.Compose([ transforms.Resize((512, 512)), transforms.RandomHorizontalFlip(p=0.5), transforms.RandomVerticalFlip(p=0.5), transforms.RandomRotation(degrees=15), transforms.ColorJitter(brightness=0.2, contrast=0.2), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) val_transform = transforms.Compose([ transforms.Resize((512, 512)), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) train_dataset = datasets.ImageFolder(root='dataset/images/train', transform=train_transform) val_dataset = datasets.ImageFolder(root='dataset/images/val', transform=val_transform) train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=8) val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False, num_workers=8)注意mean和std用的是ImageNet统计值。如果你的数据集和ImageNet分布差异大,建议自己在训练集上计算均值和标准差,效果会更好,尤其是色差这类颜色敏感的缺陷。
3.3 模型选型:ResNet、EfficientNet还是VGG
分类任务可选的模型很多,我实测对比过几款:
| 模型 | 参数量 | 单张推理耗时(ms) | 验证集准确率 | 备注 |
|---|---|---|---|---|
| ResNet50 | 25.6M | 12 | 96.8% | 经典稳定,部署友好 |
| ResNet18 | 11.7M | 8 | 95.2% | 轻量,适合边缘设备 |
| EfficientNet-B3 | 12.3M | 15 | 97.4% | 精度高,但部署稍麻烦 |
| MobileNetV3 | 4.2M | 6 | 93.6% | 适合移动端,精度略低 |
| VGG16 | 138M | 22 | 95.8% | 参数量大,不推荐 |
选择模型时除了精度,还要看推理速度和部署难度。如果产线用GPU服务器,ResNet50是不错的选择;如果要上边缘盒子,选择MobileNetV3或EfficientNet-Lite更合适;如果只有CPU环境,那MobileNetV3是首选。
实际项目里我最后选了ResNet50搭配迁移学习。理由:精度足够、对GPU推理引擎支持完善、踩坑资料最多、后续做剪枝量化的技术路线成熟。追求极致的可以用EfficientNet-B3,但量化部署时会遇到一些自定义算子兼容性问题,调试成本高一些。
3.4 训练过程与关键参数解析
迁移学习是最合理的起点。使用ImageNet预训练权重能大幅缩短收敛时间,防止过拟合。关键是冻结部分层的策略和超参数调整。
训练代码核心部分:
import torch.nn as nn import torch.optim as optim from torchvision import models from timm import create_model # 使用timm创建模型,支持更多预训练权重 model = create_model('resnet50', pretrained=True, num_classes=6) # 修改分类头 model.reset_classifier(6) # 冻结特征层参数 for param in model.parameters(): param.requires_grad = False # 只训练最后的分类头 for param in model.get_classifier().parameters(): param.requires_grad = True # 或者用渐进解冻策略 # 先训练分类头几个epoch,再解冻最后两层 criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.get_classifier().parameters(), lr=1e-3) scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=30)训练50个epoch,前10个epoch冻结特征层只训练分类头,后面40个epoch解冻全部参数,使用更小的学习率微调。初始学习率设为1e-4,并使用余弦退火调度。
这里我给一个完整的训练脚本模板:
import torch import torch.nn as nn import torch.optim as optim from tqdm import tqdm from sklearn.metrics import confusion_matrix, classification_report def train_one_epoch(model, train_loader, criterion, optimizer, device): model.train() running_loss = 0.0 correct = 0 total = 0 for images, labels in tqdm(train_loader, desc='Training'): images, labels = images.to(device), labels.to(device) optimizer.zero_grad() outputs = model(images) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() * images.size(0) _, predicted = torch.max(outputs, 1) total += labels.size(0) correct += (predicted == labels).sum().item() epoch_loss = running_loss / total epoch_acc = correct / total return epoch_loss, epoch_acc def validate(model, val_loader, criterion, device): model.eval() running_loss = 0.0 correct = 0 total = 0 all_preds = [] all_labels = [] with torch.no_grad(): for images, labels in tqdm(val_loader, desc='Validating'): images, labels = images.to(device), labels.to(device) outputs = model(images) loss = criterion(outputs, labels) running_loss += loss.item() * images.size(0) _, predicted = torch.max(outputs, 1) total += labels.size(0) correct += (predicted == labels).sum().item() all_preds.extend(predicted.cpu().numpy()) all_labels.extend(labels.cpu().numpy()) epoch_loss = running_loss / total epoch_acc = correct / total return epoch_loss, epoch_acc, all_preds, all_labels device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = create_model('resnet50', pretrained=True, num_classes=6).to(device) criterion = nn.CrossEntropyLoss() optimizer = optim.AdamW(model.parameters(), lr=1e-4, weight_decay=5e-4) scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=50) best_acc = 0.0 for epoch in range(50): train_loss, train_acc = train_one_epoch(model, train_loader, criterion, optimizer, device) val_loss, val_acc, preds, labels = validate(model, val_loader, criterion, device) scheduler.step() print(f'Epoch {epoch+1:3d}/{50} | Train Loss: {train_loss:.4f} | Train Acc: {train_acc:.4f} | Val Loss: {val_loss:.4f} | Val Acc: {val_acc:.4f}') if val_acc > best_acc: best_acc = val_acc torch.save(model.state_dict(), 'best_model.pth')3.5 类权重处理
如果数据集中各类别样本数量不均衡,或者某种缺陷的漏检代价大于误检,就需要调整损失函数的权重。瓷砖缺陷场景下,裂纹漏检是最严重的,所以我会适当提高裂纹类别的损失权重。
class_weights = torch.tensor([1.5, 1.0, 1.0, 1.2, 1.0]).to(device) criterion = nn.CrossEntropyLoss(weight=class_weights)权重的设置需要结合业务逻辑和生产数据中各类缺陷的实际频率。如果某一类缺陷在生产中极少出现,但在质检中一旦漏掉会造成重大客诉,那这个类别需要更高的权重。这里要在实验基础之上定量分析,不能全凭感觉。
4. 常见问题与排查技巧实录
4.1 模型严重过拟合怎么办
症状:训练集准确率接近100%,验证集准确率只有85%左右,而且每个epoch验证集的指标波动很大。
排查步骤:
- 检查数据划分是否泄漏——同一片瓷砖的图像是否被同时分到训练集和验证集
- 检查增强策略是否足够强——试试更激进的CutMix、RandAugment
- 检查模型的Dropout和权重衰减——ResNet50默认没有Dropout,可以在分类头前加一个Dropout层
- 检查学习率是否过大——过大的学习率会导致模型记住训练集中的噪声模式
实际项目中,我曾经调试一个模型过拟合严重,怎么调都降不下来,最后发现问题是数据划分时用了随机划分,导致同一片瓷砖的多张图像同时出现在训练集和验证集。重新按瓷砖ID划分之后,问题立刻解决。
4.2 针孔和麻面分类混淆严重
这个问题非常典型。针孔和麻面在形态上确实有重叠,尤其是密集针孔区域,视觉上跟麻面很相似。我在项目中的处理方式是:
- 回到数据层面,重新审视标注规范,把两类缺陷的判定规则更加明确化
- 增加高分辨率的局部特征提取分支,在训练时对图像的关键区域进行裁剪放大
- 引入注意力机制,让模型更关注微小的局部纹理差异
最终帮助最大的是用EfficientNet替换ResNet。EfficientNet使用更细粒度的缩放策略,在微小纹理差异的捕捉上更敏感,混淆率从12%降到了7%。
4.3 色差类缺陷在灰度图像上看不出来
如果是灰度相机拍摄的图像,色差问题确实很难通过RGB信息来识别。这种情况下,要么换成彩色相机重新采集数据,要么在预处理阶段做颜色特征提取。
我的建议是:色差缺陷检测尽量使用彩色相机。如果条件不允许,可以使用HSV颜色空间的色相和饱和度通道作为辅助输入。具体的做法是把RGB图像转为HSV,取H和S通道作为额外通道,和灰度图拼接成三通道输入模型。
4.4 推理速度不达标
产线对检测速度有硬性要求,每片瓷砖的检测时间通常不能超过几百毫秒。如果模型推理耗时超过限制,有以下几个优化手段:
- 模型轻量化:用MobileNetV3、EfficientNet-Lite或者对ResNet50做通道剪枝
- 输入尺寸降级:从512x512降到384x384,精度损失通常在1到2个百分点以内
- TensorRT加速:FP16精度推理能将速度提升2到3倍,INT8量化能提升3到5倍
- ONNX Runtime支撑多线程和批处理推理
我实际做过的一个项目里,ResNet50用TensorRT FP16推理,在Jetson Xavier上单张图像推理时间从28毫秒降到9毫秒,完全满足产线节拍要求。
4.5 标注规范不一致导致模型学习混乱
多人参与标注时,容易出现同一类缺陷有的人标裂纹,有的人标麻面的情况。这种情况对模型的影响极其隐蔽——训练集标注噪声高,模型学到的决策边界会被拉偏,验证集准确率虚高但实际效果差。
解决方案:
- 标注完成后做二次审核,随机抽20%人工复核
- 训练后用模型预测结果和标注结果做交叉比对,找出不一致的样本
- 如果模型在某个类别的置信度高,但标注标签不同,大概率是标注问题
4.6 类别定义完整性问题
项目初期定义5类缺陷,模型训练完成后,上线不久就遇到一种从未见过的缺陷类型。模型会把所有没见过的东西强行分到已知类别里的某一类,造成误判。这是封闭集分类的固有问题。
解决办法是加一个“正常”类别,让模型至少能区分“有没有问题”,然后再考虑“是什么问题”。更进一步的做法是引入异常检测机制——先用自编码器学习正常样本的表征,当输入图像的重构误差超过阈值时直接判定为“未知缺陷”,再交给分类模型处理。
5. 部署与工程化实践
5.1 模型导出与格式转换
训练好的PyTorch模型要部署到生产环境,通常需要先导出为通用格式。我推荐ONNX作为中间格式,再根据目标推理引擎转换成TensorRT或其他格式。
导出代码:
import torch import onnx import onnxruntime as ort model.load_state_dict(torch.load('best_model.pth')) model.eval() dummy_input = torch.randn(1, 3, 512, 512) torch.onnx.export( model, dummy_input, 'tile_defect.onnx', export_params=True, opset_version=13, do_constant_folding=True, input_names=['input'], output_names=['output'], dynamic_axes={'input': {0: 'batch_size'}, 'output': {0: 'batch_size'}} ) # 验证ONNX模型 ort_session = ort.InferenceSession('tile_defect.onnx') outputs = ort_session.run(None, {'input': dummy_input.numpy()})动态轴配置允许推理时改变batch size,但如果部署时只用单batch推理,可以去掉dynamic_axes,部分推理引擎对静态shape优化更好。
5.2 推理服务与接口设计
生产环境部署推荐使用FastAPI封装推理服务。核心代码:
from fastapi import FastAPI, UploadFile, File import numpy as np import cv2 import onnxruntime as ort app = FastAPI() ort_session = ort.InferenceSession('tile_defect.onnx') def preprocess(image_bytes: bytes): nparr = np.frombuffer(image_bytes, np.uint8) img = cv2.imdecode(nparr, cv2.IMREAD_COLOR) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = cv2.resize(img, (512, 512)) img = img.astype(np.float32) / 255.0 mean = np.array([0.485, 0.456, 0.406], dtype=np.float32) std = np.array([0.229, 0.224, 0.225], dtype=np.float32) img = (img - mean) / std img = np.transpose(img, (2, 0, 1)) img = np.expand_dims(img, axis=0) return img @app.post("/predict") async def predict(file: UploadFile = File(...)): image_bytes = await file.read() input_tensor = preprocess(image_bytes) outputs = ort_session.run(None, {'input': input_tensor}) probabilities = softmax(outputs[0][0]) predicted_class = np.argmax(probabilities) confidence = probabilities[predicted_class] return { "class": class_names[predicted_class], "confidence": float(confidence), "probabilities": probabilities.tolist() }接口设计要考虑生产环境的需求:需要记录日志,每个请求的耗时、返回结果和置信度要存储,方便后续做模型监控和迭代。需要加一层缓存机制,相同图像重复请求直接返回结果,不重复推理。
5.3 边缘设备部署
如果产线没有GPU服务器,需要部署到边缘设备上。目前工业上主流的边缘计算设备包括Jetson系列设备、各类RK3588盒子等。整体部署逻辑类似,但要注意设备内存和算力限制。
我踩过的坑:在Jetson上部署ONNX模型,直接用CPU模式推理,速度只有几个FPS,后来启用TensorRT加速才把性能提上来。如果边缘设备是英伟达平台,建议直接导出TensorRT引擎,不要走ONNX Runtime再转一层。
TensorRT导出流程简述:
trtexec --onnx=tile_defect.onnx \ --saveEngine=tile_defect_fp16.engine \ --fp16 \ --workspace=4096转换完成后,在Jetson上加载.engine文件推理,速度提升明显。
6. 基于YOLOv8的分类路径补充
如果只是做分类任务,上面的PyTorch方案已经完全够用。不过很多读者在搜“yolov8训练自己的数据集”,我就在这里再补充一种方案:用YOLOv8的分类模式做瓷砖缺陷分类。
YOLOv8的detect模块广为人知,但它的classify模式同样好用,训练流程比自写方案简单得多。
# 训练分类器 yolo classify train data='dataset/' model=yolov8s-cls.pt epochs=50 imgsz=512 batch=32 # 验证 yolo classify val model=runs/classify/train/weights/best.pt data='dataset/' # 推理 yolo classify predict model=runs/classify/train/weights/best.pt source='test.jpg'目录结构要求如下:
dataset/ ├── train/ │ ├── crack/ │ ├── chip/ │ ├── pinhole/ │ ├── blister/ │ └── color_diff/ └── val/ ├── crack/ ├── chip/ ├── pinhole/ ├── blister/ └── color_diff/YOLOv8s精度在类似数据集上已经不错,实测能达到96%左右。如果精度还不够,换yolov8m或yolov8l。训练的日志和指标曲线自动保存,非常方便。
我个人的对比结论是:YOLOv8分类模式适合快速验证和工程落地,代码量小、流程自动化。但如果你想做更深度的定制——比如自定义损失函数、多分支网络、注意力机制嵌入——还是用PyTorch方案更灵活。两者并不冲突,先跑YOLOv8拿到Baseline,再上PyTorch微调,效率最高。
7. 数据集迭代与模型持续优化
7.1 困难样本挖掘
模型上线之后,不代表工作就结束了。持续收集误判样本、标注、补充训练,是保证模型在生产环境中长期稳定的关键。
我通常是这么做的:
- 每月从产线收集所有预测置信度低于0.85的样本
- 质检人员人工复核这些低置信度样本
- 将确认的新样本按类别加入训练集,定期增量训练
- 每次增量训练后,用固定测试集回归测试,确保没有类别退化
7.2 测试集管理
测试集要作为“压箱底”的数据,不能频繁变动。每次模型迭代,都用同一个测试集做最终效果评估,才能公平对比不同版本模型的优劣。
如果测试集长期不更新,可能和实际生产数据的分布产生漂移,建议每季度从新增数据中抽取一批样本,更新测试集,同时保留上一版测试集做对比,确保评估连续性。
7.3 数据版本管理
深度学习项目很大一个痛点就是数据和模型版本混乱。训练了一个新模型,想复现上次的效果,结果发现数据集已经被更新过,训练结果对不上。推荐用DVC做数据版本管理,或者至少把数据集上传到NAS后,按日期和版本号命名目录。
我习惯用下面的命名规范:
dataset/tile_defect_v1.0_20250115/ dataset/tile_defect_v1.1_20250301/ dataset/tile_defect_v2.0_20250601/模型权重同样规范管理,训练脚本、配置参数、数据集版本、训练日志打成一套,存到固定目录。这样任何一个版本出了问题,都能追溯回当时的环境。
8. 实操总结与经验沉淀
做这套瓷砖缺陷分类数据集的完整流程走下来,我最深的体会是:深度学习模型训练只是整个项目最后一步,前面数据采集、标注规范、验证集设计才是决定成败的关键。
实际项目里,模型选型那一步通常半天就能定,训练调参一周内也能完成,但数据采集和标注花了将近一个月。而且前期的数据和标注质量,直接决定了后期模型精度的上限。用一句话总结:标注规范和验证集划分这两个环节前期打磨得越好,后面省下的时间越多。
再分享一个具体的小技巧:训练前先跑一个包含10个epoch的“冒烟测试”,只取少量数据验证整条训练流程是否跑通,检查数据加载器是否有问题、类别映射是否正确、代码有没有隐藏的Bug。这个冒烟测试不会花太多时间,但能避免你启动一个完整训练后才发现代码问题,白等一两个小时。
按照这套流程下来,分类模型在5类缺陷上的准确率能做到96%以上。如果后续有定位需求,分类数据和检测标注可以复用,迁移成本不高,这是我在项目开始就规划好的,也建议你考虑进去。
本文还有配套的精品资源,点击获取