尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

小样本工业缺陷分类实战:三星油污数据集上的头发丝与小黑点识别

发布时间:2026/9/29 18:49:29

资讯中心
01
ARTICLE

小样本工业缺陷分类实战:三星油污数据集上的头发丝与小黑点识别

小样本工业缺陷分类实战:三星油污数据集上的头发丝与小黑点识别
简介面向工业视觉缺陷检测场景的三星油污数据集聚焦头发丝TFS与小黑点XZW两类典型瑕疵共660张已标注图像可直接用于YOLO、SSD等目标检测模型的训练与验证适合算法工程师、科研人员及竞赛选手进行瑕疵识别与精度调优。压缩包共1324个文件内含660张jpg原图与660个xml标注文件标注信息完整另有4个说明性txt文件整体大小约314.54MB图片命名清晰区分正常与异常样本便于数据划分与快速加载XML标注格式兼容VOC规范可借助常用脚本转为COCO或YOLO格式降低数据预处理门槛。已有1400余人学习下载结合缺陷检测常见优化策略可帮助读者理解油污缺陷数据特点、标注格式转换、数据增强及模型调优方法是开展工业视觉检测实验和落地应用的实用数据基础。1. 三星油污缺陷里的头发丝和小黑点660张图为什么值得认真做第一眼看到“三星油污缺陷类别头发丝和小黑点数据集大小660张”这个数据集多数人会觉得只有两类缺陷、几百张图分类有什么难的但真正下过产线的工程师都清楚这恰恰是典型的“看着简单、做起来翻车”的小样本工业图像数据集。油污没有固定边界厚度、反光、附着形态都会让样本差异放大头发丝是细线、小黑点是圆斑两者同框时甚至像随机噪声。660张图每类平均只分到三百张左右随便从头训练一个ResNet训练集准确率能轻松跑到99%一到现场换打光、换批次就原形毕露。这篇笔记按缺陷检测项目里最常用的一条路线把任务边界、模型选型、数据增强、训练配置、数据清洗和上线前验证完整讲一遍适合手里握着几百张缺陷图、想判断这个方向值不值得投入的算法和产线工程师。2. 先想清楚任务再选模型两类缺陷的信号差别决定了技术路线2.1 头发丝和小黑点在图像特征上的本质差异先别急着写训练脚本把两类缺陷当成纯图像信号来看差别比想象中大。头发丝是典型的线状缺陷长宽比极端可能达到30:1以上在图中只占极少像素它的主要特征是连续边缘和高频信息。小黑点则是近圆形斑点面积往往比头发丝粗不少但边缘经常是模糊的——油污在表面慢慢扩散从暗到亮的过渡带会让模型很难界定边界。这两类缺陷对卷积网络的考验完全不同。处理头发丝需要网络保留足够的分辨率常规ResNet下采样到7×7的特征图时一根头发丝可能只剩一个像素的响应高层语义特征几乎丢失而小黑点更依赖局部对比度对分辨率没那么敏感但对光照和表面底色敏感。如果你直接把图片缩到224×224就开训等于默认放弃头发丝的高频信息。这个结论直接影响后边的输入尺寸选择我一般会把训练分辨率拉到256甚至288而不是套用ImageNet默认的224。另一个容易忽略的点是这个标题只给了“头发丝”和“小黑点”两个正类并没有给OK类。实际应用里不可能每张图都恰好有缺陷模型在OK样本上也会强行输出概率最高的类别。所以做方案时我通常建议把数据集扩展成三类头发丝、小黑点、无缺陷/其他。哪怕当前训练集里没有OK图也要尽量去产线上补一批否则分类器上线后会把所有正常表面都判定成某一类缺陷。2.2 为什么迁移学习是660张样本唯一靠谱的起点从零训练一个ResNet用660张图的规模而不是660万张基本必过拟合。卷积神经网络学到的低级特征比如边缘、角点、条纹基元天然适合描述头发丝这类线性纹理但它的统计需要大量样本来支撑上百层的参数。660张图经过增强翻10倍也只有六千张对深层网络来说是杯水车薪。ImageNet预训练权重的价值在于网络的浅层已经把边缘、颜色、纹理基元这些通用模式编码好了。用预训练权重初始化再针对油污缺陷微调高层和分类头相当于把“会看基本形状”的模型改造成“会认头发丝和黑点”的模型。我在工业缺陷项目里比较常用两种迁移策略。第一种是整体微调backbone用1e-4量级学习率分类头用1e-3适合训练集画面风格和预训练数据差异较大的情况。第二种是只训练分类头、冻结backbone也就是常说的linear probe适合快速验证当前特征能不能区分两类缺陷这个方案在MVTec AD这类工业基准上也验证过即使只做线性分类ImageNet特征通常也能提供可用的基线。视觉Transformer在小数据集上的表现要谨慎看待。ViT的全局自注意力需要大数据量和大增强才能发挥效果几百张图直接微调注意力矩阵很容易退化成本地模板匹配效果反而比不上同样规模的CNN。660张图的现实决定了CNN加迁移学习仍然是性价比最高的起点。2.3 模型选型对比ResNet、EfficientNet 与 ViT 的取舍模型参数量适合分辨率小样本表现预期部署成本ResNet18/3411M/21M256~288强配合预训练收敛平稳低CPU或边缘盒子可跑EfficientNet-B05M256~288中需要EMA、强增强等辅助技巧低但要调的细节多MobileNetV32.5M256中分类头容量偏小很低适合嵌入式ViT-Small22M224~384弱数据量不够时难收敛中推理库要求更高综合经验我一般直接选ResNet34不选ResNet50。原因是50层对几百张图的分类任务来说容量过大更容易把增强后的细节当作重要特征记住ResNet34的容量与头发丝、黑点这类浅层视觉特征更匹配训练也更快。输入层之后的第一个conv不需要改分类头改成两层全连接中间加Dropout。如果后续发现过拟合还没压住可以把最后三个残差块里的BatchNorm冻结只让分类头更新这个技巧在后面避坑章节会展开。EfficientNet在这个数据集上不是首选。它的训练技巧相当敏感比如EMA参数、随机深度、AutoAugment强度任何一个没调好效果就比ResNet差不少。660张图也撑不起这些增强带来的正则化收益。另外经常有人问是不是可以直接把图片切成patch后做检测比如套用YOLOv8训练自己的数据集那套流程这里我再替大家踩一脚刹车这是图像级分类任务不是目标检测。660张图去画目标框标注成本直接翻几倍而且头发丝这种细长目标对检测框回归很不友好。先用分类思路拿到可用基线如果确实需要定位缺陷再考虑后续扩展。3. 用PyTorch把基线跑通目录加载、数据增强与训练配置3.1 数据集目录规范与ImageFolder加载先从文件命名开始拿到这套三星油污缺陷数据集第一步是确认目录结构。PyTorch的ImageFolder要求数据按“根目录/类别/图片文件”组织我一般会整理成下面这样dataset/ hair/ # 头发丝样本 spot/ # 小黑点样本 ok/ # 无缺陷样本如果补齐了如果你的原始目录不是这个结构先写一个Python脚本重命名和归类。注意ImageFolder按字母序分配类别索引hair是0ok是1spot是2。如果后续要导出ONNX部署label顺序千万不要在中间改动否则模型输出的每一个数字都错了。from torchvision.datasets import ImageFolder train_ds ImageFolder(rootdataset/train, transformtrain_transform) val_ds ImageFolder(rootdataset/val, transformval_transform) # 打印类别映射确认与标注一致 print(train_ds.class_to_idx) # 返回 {hair: 0, ok: 1, spot: 2}这段代码的关键是验证class_to_idx。实际项目里常见的低级错误是有人手工把标签拼进文件名里比如hair_001.jpg、spot_002.jpg然后直接按文件名写自定义Dataset这种做法也能跑但边界情况特别多一旦某个文件名不规则就整个报错。ImageFolder的好处是天然按文件夹分配标签且每个类别的样本数可以通过遍历很容易统计。加载之后建议顺手检查每个类别的数量660张图里两类数量很可能不平衡这会直接影响后边损失函数的设计。3.2 增强策略针对线状与点状缺陷的transform组合缺陷分类的增强策略和通用的ImageNet分类不一样。通用的RandomResizedCrop默认scale取值范围是(0.08, 1.0)意思是裁剪区域可以缩小到原图的8%这对缺陷图是灾难。如果把一个只有2%图像面积的头发丝裁掉了留下的全是背景表面模型学到的就不是缺陷本身。所以增强要克制所有操作以“保留缺陷的原始形态”为前提。from torchvision import transforms as T train_transform T.Compose([ T.Resize((288, 288)), # 先放大一点给裁剪留空间 T.RandomResizedCrop(size(256, 256), scale(0.7, 1.0), # 最小裁原图70%防止缺陷被裁掉 ratio(0.9, 1.1)), # 长宽比扰动小一些 T.RandomHorizontalFlip(p0.5), T.RandomVerticalFlip(p0.3), T.ColorJitter(brightness0.2, contrast0.2, saturation0.1, hue0.0), # 不动色相油污颜色有物理含义 T.RandomRotation(degrees15), # 头发丝方向如果随机小角度旋转够用 T.ToTensor(), T.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) val_transform T.Compose([ T.Resize((256, 256)), T.ToTensor(), T.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ])这里逐项说明参数的选择逻辑。Resize到288再随机裁剪到256等价于做了一点尺度扰动同时避免直接使用默认的RandomResizedCrop把细节丢失。ColorJitter里我把hue设成0因为油污颜色本身是分类的重要线索如果允许色相大幅偏移模型会被迫忽略颜色信息对暗色黑点和透明油污都更不敏感。RandomRotation只给了15度考虑到产线可能固定摄像头角度超过30度的旋转会引入现实里不存在的形态最后模型对真实旋转样本反而不稳。验证集不要用任何随机增强只用Resize和Normalize这样才能保证评估结果可复现。3.3 训练配置与评估指标小样本下的损失函数选择训练配置这块我先说结论用预训练ResNet34优化器用AdamWbackbone学习率1e-4分类头学习率1e-3训练30到40个epoch损失函数用Focal Loss或带类别权重的交叉熵。下面是一段可以直接跑的简化训练逻辑。import torch import torch.nn as nn from torch.optim import AdamW from torch.optim.lr_scheduler import CosineAnnealingLR class FocalLoss(nn.Module): def __init__(self, gamma1.0, alphaNone): super().__init__() self.gamma gamma self.alpha alpha def forward(self, logits, targets): ce nn.functional.cross_entropy(logits, targets, reductionnone) pt torch.exp(-ce) loss (1 - pt) ** self.gamma * ce if self.alpha is not None: alpha_t self.alpha[targets] loss alpha_t * loss return loss.mean() model torchvision.models.resnet34(weightsIMAGENET1K_V1) model.fc nn.Sequential( nn.Dropout(0.3), nn.Linear(512, 3) ) param_groups [ {params: model.conv1.parameters(), lr: 1e-4}, {params: model.layer1.parameters(), lr: 1e-4}, {params: model.layer2.parameters(), lr: 1e-4}, {params: model.layer3.parameters(), lr: 1e-4}, {params: model.layer4.parameters(), lr: 1e-4}, {params: model.fc.parameters(), lr: 1e-3}, ] optimizer AdamW(param_groups, weight_decay1e-4) scheduler CosineAnnealingLR(optimizer, T_max35)Focal Loss在这里的gamma我设成1.0而不是目标检测里常用的2.0。因为缺陷分类的类别数量少太难样本的比例不高gamma过大会让模型过度关注那些标注本身就有争议的模糊样本产生反作用。如果类别不平衡比较严重比如头发丝有400张、黑点只有200张在alpha里按类别频率反比设置权重如果大致均衡alpha可以不设。评估指标不要只看准确率。缺陷场景里漏检代价远大于误检所以要看每类的召回率和精确率尤其要单独观察hair这一类的召回率。我会把confusion matrix打印出来重点检查hair和spot之间是否互相错认以及两类缺陷是否都被分到了ok类。错误类型不同后续要调的参数完全不同hair被误判成spot说明模型学到的形状信息不足需要增加分辨率或调整输入预处理hair被误判成ok说明缺陷信号太弱需要从增强强度和损失函数权重上找原因。4. 数据比模型更值得花时间清洗、均衡与验证集划分4.1 给660张图做一次“肉眼体检”过曝、欠曝与重复样本很多工程师拿到数据集后直接进训练脚本其实先花半天把所有图快速翻一遍收益比调十次学习率都大。油污缺陷在采集过程中经常伴随光源反射、表面划痕、脏点等干扰这些干扰可能让标签本身就不干净。我用下面这段代码统计每张图的基本质量指标然后人工复核排名靠前的异常样本。import cv2 import numpy as np import glob paths glob.glob(dataset/train/*/*.jpg) rows [] for p in paths: img cv2.imread(p, cv2.IMREAD_GRAYSCALE) if img is None: rows.append((p, 0, 0, 0, 0)) continue h, w img.shape mean_intensity img.mean() contrast img.std() laplacian_var cv2.Laplacian(img, cv2.CV_64F).var() rows.append((p, h * w, mean_intensity, contrast, laplacian_var)) # 按对比度升序看最容易找到欠曝、失焦或重复样本 for item in sorted(rows, keylambda x: x[3])[:30]: print(item)代码的思路是同时计算亮度均值、对比度标准差和拉普拉斯方差。亮度均值接近0或255大概率过曝或欠曝对比度标准差很低说明画面几乎是一个灰面缺陷可能肉眼不可见拉普拉斯方差低则提示图像失焦或压缩过度。这三类图混进训练集会干扰BatchNorm统计让模型误以为“暗黑背景”也是缺陷的一部分。我的原则是保留边缘情况但单独拎出来放到验证集不参与训练如果某类只有一两张极端样本直接删除更省事。4.2 类别不平衡与分组拆分不能按图随机切分660张图做训练/验证/测试拆分时最容易犯的一个错误是直接对文件列表随机打乱。工业缺陷数据集的特殊性在于同一个缺陷可能连续拍了好几张图甚至同一片油污上的不同视角也都被放进数据集。如果这些同源样本同时出现在训练集和验证集模型相当于偷看了答案验证分数会虚高到没有任何参考价值。常见做法是给每个样本标注一个group_id然后按组拆分。比如文件名是hair_20241011_03_001.jpg可以通过前缀提取拍摄批次。from sklearn.model_selection import GroupShuffleSplit df[group_id] df[image_path].str.extract(r(hair_\d|spot_\d)) gss GroupShuffleSplit(n_splits1, test_size0.2, random_state42) train_idx, val_idx next(gss.split(df, groupsdf[group_id]))如果原始数据集没有提供批次信息就根据文件名前缀、采集尼康时间或者目录层级推断。实在无法分组时宁可把验证集比例降到15%也不要让同源样本穿透。我这里习惯是训练、验证、测试按6:2:2切但测试集只在最终确认方案时用日常调参只看验证集。660张的规模下更好的做法是5折交叉验证每折80%训练、20%验证最后报告五折的平均F1。交叉验证能显著降低小数据集上单次切分运气的影响代价是训练时间乘以五但这点投入完全值得。4.3 复制粘贴增强会翻车合成样本的边界与泊松融合数据不够时最容易想到的办法就是从真实缺陷图上抠出缺陷粘贴到无缺陷表面上去合成新样本。这个方向可行但直接硬切一定翻车。原因是缺陷与背景的边界会形成一条明显的拼接缝模型很快学到“只要边缘有颜色突变就是缺陷”等上线之后真实油污边缘是渐变的反而识别不出来。我常用的做法是对缺陷mask做高斯模糊软化边界或者直接使用泊松融合让粘贴区域的梯度自然过渡。下面是一个简化版本def paste_defect(background, defect_img, mask, center): mask_soft cv2.GaussianBlur(mask, (15, 15), 5) mask_3 cv2.merge([mask_soft] * 3) roi background[center[0]-h//2:center[0]h//2, center[1]-w//2:center[1]w//2] blended (defect_img * mask_3 roi * (1 - mask_3)) background[center[0]-h//2:center[0]h//2, center[1]-w//2:center[1]w//2] blended.astype(np.uint8) return background这里mask_soft就是关键参数。高斯核越大过渡越自然但缺陷边缘也越模糊15×15、sigma5对油污这种本身边缘不锐利的缺陷比较合适。合成样本还要注意不要只贴到图像中央位置分布要接近真实采集时的出现概率另外粘贴的缺陷数量也不要超过两个否则模型可能学成“图像里必须有缺陷区域”。说到底合成数据只是补充先保证660张真实样本的标签质量再谈生成更多数据。5. 避坑与常见问题排查油污分类最容易踩的五个坑5.1 反光让油污“隐身”或“变形”的坑现象训练集里模型F1达到0.95拿到现场灯光下一测误检率突然飙升很多正常表面被判成油污部分真实缺陷反而漏掉。原因油污本身有厚度和光泽在不同角度打光下会形成高亮反光带训练集里的反光样本太少模型没有见过“亮斑形态的缺陷”。解决在增强里加入亮度和对比度扰动尤其是局部Gamma扰动同时建议收集多个光照角度下拍摄的同一缺陷图像把光照变化做成一个专门的验证子集用来衡量模型的真实鲁棒性。如果现场确实只有单光源环境最省事的办法是在分类前提做一次简单的光照归一化。5.2 同源样本泄露导致的虚高指标现象验证集F1达到0.99模型下载到边缘设备后表现断崖式下跌。原因训练集和验证集存在同源样本可能是同一片油污的两张连拍图也可能是同一缺陷不同裁切随机拆分时它们被分到了两侧。解决按缺陷实例分组拆分而不是按文件名随机拆分。如果发现当前数据集已经拆完了无法回溯就用图像哈希做近似去重把相似度高于阈值的图全部放进同一组我给这个坑写的教训是小数据集上验证分数越高越要警惕先检查泄露再做任何模型改进。5.3 BatchNorm在训练和验证时的统计漂移现象训练loss稳定下降但验证准确率每几个epoch剧烈波动甚至训练集本身的准确率也来回跳。原因batch_size被显存限制在8甚至4BatchNorm在小batch上估计均值和方差的噪声很大running_stats没有代表性的统计值。解决优先把batch_size提到32以上实在提不上去把骨干网络部分的BatchNorm层替换成GroupNorm或者冻结前几个residual block的BN参数。工业数据集由于图像分辨率高显存紧张时很容易犯这个错属于典型的硬件限制导致的参数玄学。5.4 标注噪声集中在边界样本上现象训练到后期模型总在个别样本上反复波动loss降不下来。人工复查发现这些样本有的其实看不出明显缺陷有的是严重划痕而被标成头发丝。原因人工标注按“像不像”来判断油污和划痕在颜色、形态上天然重合。解决先用置信学习或直接用训练好的模型找出预测概率接近0.5的样本拉出来人工复审训练时开启label smoothing 0.05让模型不至于对错误标签过度自信。这一步在660张规模下收益非常明显因为几个噪声样本就能影响一整个类的决策边界。5.5 增强强度过大反而把缺陷“洗”没了现象加了RandAugment、Cutout之后验证F1不但没涨反而从0.90降到0.82。原因头发丝只占图像极小面积Cutout随便挖掉一个区域就可能把整根头发丝清除RandAugment里的旋转、缩放组合也可能让细线缺陷变形到无法辨认。解决对这类小目标缺陷增强强度要降级使用把线性缺陷的形态约束当成先验。我的建议是先用没有任何随机增强的baseline跑一版再逐步增加增强项每次只加一项观察验证集F1和误报率变化不要一次性堆满全套增强。6. 从“看起来准”到“敢上线”难样本回溯、模型导出与现场验证6.1 建一个专门的难样本回归集模型训练完成不等于项目结束。我通常会在训练集之外留下一个“难样本集”里面的图全部来自训练过程中验证集分类错误的样本、现场反馈的误报样本、以及人工觉得普通工程师都很难判断的模糊图。每次调整模型后都先跑这组回归集再跑标准验证集。原因很简单小样本模型很容易在修复一个问题的同时破坏掉之前已经学会的能力回归集就是给模型准备的后悔药。难样本集不需要大20张到30张足够但每张图必须有明确的正确标签。6.2 ONNX导出与推理时保持预处理一致模型要部署到产线环境最常见的方式是导出ONNX用ONNX Runtime或OpenVINO推理。这里最大的坑不是导出本身而是推理时的预处理必须与训练时完全一致包括Resize的插值方式、是否归一化、通道顺序。下面是一段导出参考。import torch model.eval() dummy_input torch.randn(1, 3, 256, 256) torch.onnx.export( model, dummy_input, hair_spot.onnx, opset_version12, input_names[input], output_names[output], dynamic_axes{input: {0: batch}, output: {0: batch}}, )导出后建议先用ONNX Runtime加载对同一张图分别走PyTorch和ONNX推理对比输出结果是否一致差异超过1e-3就要查找是哪一步预处理没对齐。部署时我还习惯把阈值设置在0.7而不是0.5预测概率低于阈值时把结果标记为“不确定”让产线人工复核而不是直接归类到某一缺陷。660张图训练的模型再怎么调优也必然存在没见过的表面形态给模型一个拒绝回答的选项比强行输出分类结果要可靠得多。这也是我做工业质检这几年最深的教训线上稳定性靠的不是更聪明的网络结构而是数据边界和推理时的保守策略希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。