NEU-DET钢材表面缺陷检测实战:VOC转YOLO与YOLO训练全解析

发布时间:2026/10/9 17:14:56
NEU-DET钢材表面缺陷检测实战:VOC转YOLO与YOLO训练全解析 简介NEU-DET 钢材表面缺陷检测数据集是一份面向工业视觉检测与深度学习目标检测任务的标准资源适合入门至进阶的算法工程师、科研人员及竞赛选手用于模型训练与评估。资源包含 1799 张 JPEG 原图并提供 Pascal VOC 格式 XML 与 YOLO 格式 TXT 两套标注文件覆盖 crazing、inclusion、patches、pitted_surface、rolled-in_scale、scratches 共 6 个典型缺陷类别总标注框数达 4186 个各类别框数分布清晰可直接用于 YOLO、Faster R-CNN、SSD 等常见检测框架。数据均采用 labelImg 工具以矩形框精细标注便于用户快速开展缺陷识别、数据增强或预训练微调实验。压缩包共 2000 个文件主要包括 1799 个 xml 标注文件与 201 个 txt 标注及说明文件整体大小 63.1MB体积精简、解压即用目录组织简洁能大幅节省数据预处理时间。目前已有 1157 人学习使用是一份经过验证的高质量工业缺陷检测入门数据集。1. NEU-DET钢材表面缺陷检测数据集1799 张图到底够不够用做钢材表面缺陷检测的人大概率绕不开 NEU-DET钢材表面缺陷检测数据集。它用 1799 张带标注图像覆盖了六类最常见的钢板表面缺陷同时给出 VOC 和 YOLO 两种格式是验证检测算法、演练训练流程、练习格式转换时最顺手的起点。很多人第一眼嫌它小真正用起来才发现标注规范、类别清晰、解压后不用做太多清洗就能喂给训练脚本。这个数据集的定位不是生产级数据而是“让检测模型先跑起来”的种子数据。想验证 YOLO 系列在工业缺陷上的效果、想跑通一套从标注到训练的完整链路、想把自己的数据集整理成 VOC/YOLO 格式拿它做对照最合适。我最初在模拟项目X里接触目标检测靠的就是这条链路入门。下面从格式拆解开始讲到训练配置和踩坑尽量让你拿到压缩包后一个晚上就能跑通第一个模型。2. 拆开 NEU-DETVOC 与 YOLO 双格式的目录结构和六类缺陷网上流传的 NEU-DET 转存包很多解压后布局可能不太一样但核心就两块VOC 风格的图像加 XML 标注以及 YOLO 风格的 images/labels 目录。先把这两种格式各自长什么样搞清楚后面转换和训练时才不会看错文件。2.1 六类缺陷的形态与检测难点数据集里的六类缺陷分别是 crazing、inclusion、patches、pitted_surface、rolled_in_scale、scratches常见版本里图像尺寸基本在 200×200 左右内容以灰度纹理为主。先记下每类的视觉特征因为后面评估结果时你会发现模型在哪一类上翻车往往从图像形态上就能预判。XML 中的 name常见叫法视觉特征检测难点crazing裂纹/网状裂纹细密纹路灰度对比弱像背景纹理边界不清inclusion夹杂物块状或条状暗区与基材灰度接近patches斑块片状灰度异常边缘不规则面积大但特征模糊pitted_surface麻点/凹坑密集小点状凹陷目标小且密集rolled_in_scale氧化皮鳞片状亮/暗区域形状多变反光干扰scratches划痕细长三角形状条纹长宽比极大容易漏断这六类不是随便拍的基本覆盖了热轧、冷轧产线上最常引起客诉的缺陷类型。所以在做工业质检项目原型验证时它比很多通用目标检测数据集更贴近真实场景。另一个实际好处是类别少训练迭代周期短适合用来调参。2.2 VOC 格式XML 标注里哪些字段真正决定训练VOC 格式的核心是 JPEGImages 里的原始图像加 Annotations 里同名 XML。典型目录布局如下解压后根目录/ ├── VOCdevkit/ │ └── NEU-DET/ │ ├── Annotations/ # 每张图对应一个 .xml │ ├── JPEGImages/ # 原始图像jpg/png 常见 │ └── ... └── labels/ # 常见 YOLO 布局之一 ├── images/ └── labels/打开任意一个 XML结构基本是这种风格annotation folderNEU-DET/folder filenamexxx.jpg/filename size width200/width height200/height depth3/depth /size object namecrazing/name bndbox xmin10/xmin ymin20/ymin xmax180/xmax ymax190/ymax /bndbox difficult0/difficult /object /annotation对训练真正有用的字段只有 filename、size 和 object 里的 name、bndbox。folder、difficult 这些在大多数检测框架里都会被忽略。size 里的宽度和高度最容易被忽略但 VOC 转 YOLO 时坐标换算要靠它后面会专门讲。检查数据集时我会随手确认两件事每张图是否都有同名 XML每个 XML 里 bndbox 四个值是否为整数且没超出 0 到 200 的范围。超出范围的框基本是标注误操作训练时轻则警示重则让 loss 变成 NaN。很多转存包的问题不在数量而在个别标注文件被转存工具截断。2.3 YOLO 格式归一化 txt 的换算逻辑与类别 idYOLO 格式的标注不是 XML而是一个和图像同名的 txt 文件。每一行代表一个目标框格式是 class_id cx cy width height全部归一化到 0~1 之间。举个直观例子2 0.4750 0.5250 0.8500 0.4250这行表示该图像里有一个类别 id 为 2 的目标中心点在图像的 (0.475, 0.525) 处宽度约 0.85高度约 0.425。归一化的好处是训练时和图像实际尺寸无关无论输入是 320 还是 640标注都不会因为这个盒子被缩放而需要重算。类别 id 完全依赖你定义的顺序。NEU-DET 在 YOLO 格式包里最常用的映射是0 crazing 1 inclusion 2 patches 3 pitted_surface 4 rolled_in_scale 5 scratches这个顺序不是 XML 里的 name 排字典序而是打包者自己定的。拿到包后先打开两个 txt 和对应 XML 对照一遍确认 cats 顺序再动手否则模型训练到一半你会发现预测框的标签和图像内容对不上那时候改映射表等于重训一次。VOC 和 YOLO 双格式同时存在时我习惯以 XML 内容为基准去校验 txt而不是反过来。3. 动手前先做三件事完整性校验、VOC 转 YOLO 与分层划分拿到压缩包先别急着训练。NEU-DET 因为被多次转存常见问题是标签文件缺失、类别名大小写不一致、YOLO 标注和 VOC 标注对不上。我一般先做三件事总共不到十分钟能避免后面浪费几小时。3.1 用一段脚本查清 1799 张图与标注的缺口先写个脚本统计图像和标注数量并检查每张图是否有对应标注。路径按自己解压的实际目录改import xml.etree.ElementTree as ET from collections import Counter from pathlib import Path img_dir Path(JPEGImages) xml_dir Path(Annotations) imgs {p.stem for p in img_dir.glob(*.jpg)} xmls {p.stem for p in xml_dir.glob(*.xml)} print(图片数量:, len(imgs)) print(标注数量:, len(xmls)) print(缺失标注的图:, len(imgs - xmls)) print(缺失图片的标注:, len(xmls - imgs)) class_counter Counter() for xml_file in xml_dir.glob(*.xml): root ET.parse(xml_file).getroot() for obj in root.iter(object): name obj.findtext(name) if name: class_counter[name.strip().lower()] 1 print(class_counter.most_common())这段脚本用 stem 去掉扩展名后做集合差能同时找出“有图没标注”和“有标注没图”两种情况。类别统计里把 name 转成小写再计数是因为不同转存包里的类别名可能出现 Crazing 与 crazing 混用的情况。如果发现 1799 张图但有 1798 个 XML别觉得差一张无所谓直接找到那个没有 XML 的图名后面转换和训练时它一定是第一个报错的文件。如果包里带的是 YOLO 格式就把上面逻辑换成 images 目录和 labels 目录对比并用 txt 里第一列统计类别。没有对应 txt 的图像在 YOLO 训练时会被静默跳过可能整个类别都被跳过而你完全没察觉。3.2 VOC 转 YOLO坐标归一化脚本与几个边界坑即使压缩包里已经给了 YOLO 格式我仍建议自己做一遍转换。原因很现实转存包的 labels 可能和 images 不匹配也可能类别顺序和你准备用的训练框架不一致。自己转换能确保标注链路是可控的。import xml.etree.ElementTree as ET from pathlib import Path from PIL import Image ANNO_DIR Path(Annotations) IMG_DIR Path(JPEGImages) LABEL_DIR Path(labels) LABEL_DIR.mkdir(exist_okTrue) CLASS_NAMES [crazing, inclusion, patches, pitted_surface, rolled_in_scale, scratches] def convert_one(xml_file): root ET.parse(xml_file).getroot() lines [] for obj in root.iter(object): name obj.findtext(name).strip().lower() if name not in CLASS_NAMES: continue cls_id CLASS_NAMES.index(name) box obj.find(bndbox) xmin float(box.findtext(xmin)) ymin float(box.findtext(ymin)) xmax float(box.findtext(xmax)) ymax float(box.findtext(ymax)) cx ((xmin xmax) / 2) / img_w cy ((ymin ymax) / 2) / img_h bw (xmax - xmin) / img_w bh (ymax - ymin) / img_h cx, cy, bw, bh [max(0.0, min(v, 1.0)) for v in (cx, cy, bw, bh)] lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) return lines for xml_file in ANNO_DIR.glob(*.xml): with Image.open(IMG_DIR / (xml_file.stem .jpg)) as im: img_w, img_h im.size lines convert_one(xml_file) (LABEL_DIR / (xml_file.stem .txt)).write_text(\n.join(lines), encodingutf-8)这里最关键的是 img_w、img_h 用 PIL 从图像本身读取而不是直接信任 XML 里的 size。转存过程中图像经常被重新保存或缩放一旦实际尺寸和 XML 不一致按错误尺寸归一化出的坐标全是偏的但这种偏移在视觉上又不太容易发现。clip 到 0~1 是双刃剑。bndbox 越界时把它截断到边界能保住训练不报错但也掩盖了标注问题。我建议先不 clip 跑一遍如果 txt 里出现小于 0 或大于 1 的值把对应的 XML 文件名打出来逐个检查是标注确实越界还是图像被压缩过。3.3 按类别分层划分 train/val防止验证集缺类1799 张图听起来不少但如果随机划分六个类别里数量少的类可能被分到验证集只有几张导致那一类在训练集里完全没被充分学习。正确做法是按类别分层采样确保每个类别在训练集和验证集的比例基本一致。import random from pathlib import Path from collections import defaultdict random.seed(42) img_files list(Path(images).glob(*.jpg)) label_dir Path(labels) def classes_of(img_path): label_path label_dir / (img_path.stem .txt) if not label_path.exists(): return [] with open(label_path) as f: return sorted({line.split()[0] for line in f if line.strip()}) strata defaultdict(list) for img_path in img_files: cls_list classes_of(img_path) key cls_list[0] if cls_list else empty strata[key].append(img_path) train_ratio 0.8 train_files, val_files [], [] for key, paths in strata.items(): random.shuffle(paths) split int(len(paths) * train_ratio) train_files.extend(paths[:split]) val_files.extend(paths[split:]) print(train:, len(train_files), val:, len(val_files))这段脚本把同一图像里的多个类别去重后取第一个作为分层键。单张图只有一个缺陷的 NEU-DET 里足够用。如果发现 empty 分组里有文件说明标注缺失回到 3.1 去查原因。划分完成后把 train_files 和 val_files 分别按 YOLO 目录结构放好下一章直接用。4. 用 YOLO 在 NEU-DET 上完成训练配置文件、命令与评估要点数据准备好之后训练本身反而最顺利。用公开的 YOLO 系列框架只要目录结构和 yaml 没问题一条命令就能跑起来。重点在于配置文件怎么写、超参数怎么选、结果到底看什么指标。4.1 搭建标准目录结构和 data.yaml按 YOLO 约定数据集目录长这样dataset/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/训练前把 3.3 划分出的文件对应移动或软链进去。labels 下每个 txt 的文件名必须和 images 下同名图像一致框架是通过 stem 匹配的文件名对不上再好的标注也找不到。下面是 data.yaml# neu_det.yaml path: /path/to/dataset train: images/train val: images/val nc: 6 names: 0: crazing 1: inclusion 2: patches 3: pitted_surface 4: rolled_in_scale 5: scratchespath 建议写绝对路径因为相对路径在不同工作目录下行为不一致。names 的顺序必须和 3.2 里 CLASS_NAMES 完全一致这是 NEU-DET 容易出现标签错乱的最主要原因。下载后如果压缩包自带的 data.yaml 里顺序和标注不一致以标注文件实际的第一列为准。4.2 训练命令与关键超参数从预训练权重开始最省事的训练命令是这样yolo detect train dataneu_det.yaml modelyolov8n.pt \ epochs300 imgsz640 batch16 device0 cacheTrueNEU-DET 只有 1799 张图epochs 至少要给到 200 以上否则模型还没收敛就到头了。imgsz 我建议 448 或 640不建议降到 320。六个类别里 pitted_surface 和 scratches 属于典型的细小目标图像本身只有 200×200再降分辨率会让麻点和划痕的像素特征进一步丢失。预训练权重非常关键。从零训练一个小数据集收敛慢而且 mAP 通常不及预期。用在大规模自然图像上预训练过的权重做初始化等于让模型把“特征提取的基本功”带过来只学 NEU-DET 的领域差异这在实际实验中能明显缩短训练时间。batch 大小根据显存调整16 在常见显卡上都能跑batch 太大时 BN 层统计更容易波动。还可以顺手打开 cacheTrue把图像缓存到内存数据集小所以内存压力不大但能显著加快每个 epoch 的加载速度。4.3 评估到底看什么单类 AP 比总 mAP 更能发现问题训练完后用验证集评估yolo detect val dataneu_det.yaml \ modelruns/detect/train/weights/best.pt输出里除了 mAP50 和 mAP50-95一定要看每一类的 Precision、Recall 和 AP。NEU-DET 的 mAP50 普遍不低但这个总分很容易掩盖单类问题。我见过不少训练结果总分到了 0.8 以上但 crazing 的 AP 只有 0.3因为 crazing 在验证集里就几十张即使全错对总分影响也很小。更直接的做法是跑一轮批量预测yolo detect predict modelruns/detect/train/weights/best.pt \ sourcedataset/images/val预测结果会保存成带框的图片。快速翻一遍这些图重点看两类漏检的真实目标和把背景纹理误检成缺陷的假正例。这两个信息比任何指标都能说明模型下一步该怎么调。5. NEU-DET 使用中的五大坑与排查思路这一章都是实际跑数据时大概率会遇到的问题按现象到原因到解决来写方便你直接对照。5.1 1799 张图训练后 loss 很低但验证 mAP 上不去过拟合现象训练集上的 loss 降得很漂亮但验证集的 mAP 训练到一半就停滞甚至下降。原因很简单数据量太小模型把训练集的特征背下来了没有泛化能力。尤其是网络层数较深时这种现象更明显。解决优先加大数据增强crazing 和 scratches 这类目标对翻转、旋转不敏感可把 fliplr 开到 0.5mosaic 开满其次用预训练权重而不是从零训练再不行就换更小的模型比如直接上 n 系列而不是一上来就用 s 或 m。最后把 epochs 拉长配合早停让它自己停在泛化最好的位置。5.2 crazing 与 patches 的 Precision 长期为 0类别不均衡与标注粒度现象训练完看 per-class 指标这两类的 AP 明显低于其他四类有时 Precision 直接是 0。原因是这两个类别的样本量在数据集中偏少而且 crazing 的网状裂纹和背景纹理高度相似模型倾向把所有带纹理的区域都当成背景。解决思路分两步先看统计确认哪一类样本最少如果少到几十张级别考虑对这种缺陷做离线复制增强把框内区域随机贴到背景图合成新样本然后检查标注框是否太大crazing 的纹理区域往往是大框套着细小裂纹标注框过大会让模型学到大量背景。减小这类框的粒度往往比加数据更有效。5.3 转换后提示 no labels found坐标越界与目录层级现象训练命令一跑日志提示某个图像没有找到对应 labels或者直接报错找不到标签。原因通常是两种一是图像和 txt 不在预期的 labels/train 目录下框架默认在 images 同级找 labels目录层级错了就找不到二是转换出来的 txt 里坐标越界比如框的宽高算出来是负数部分框架会认为这是非法标注。解决先用 3.1 的校验逻辑检查每个 txt 是否有有效内容再确认 labels 下的文件和 images 下文件一一对应最后打开一个 txt 看数字是否都在 0~1 区间如果出现负值回到转换脚本检查 xmax 是否小于 xmin这种问题常见于标注框在被压缩后翻转的情况。5.4 预测类别张冠李戴类别 id 顺序不一致现象模型训练没有报错也收敛了但预测出来的框类别和图像内容对不上比如把氧化皮预测成夹杂物。终极原因只有一个训练时 CLASS_NAMES 的顺序和 data.yaml 里 names 的顺序不一致。VOC 转 YOLO 时你按一个顺序给类别编号训练时框架按照另一个顺序读取类别名同一个数字在两个文件里代表不同缺陷。解决把两张表并排打印出来人工核对。我习惯把类别顺序写进一个固定文件转换脚本、data.yaml、训练输出三处都从这个文件读取彻底消灭手写不一致的可能。5.5 训练效果换到产线现场就失效域差异现象NEU-DET 上 mAP 很高但拿到自己现场的钢板图像上一测掉点严重。原因是数据集本身来自特定产线、特定光照和特定拍摄条件灰度纹理风格比较单一而现场图像的分辨率、角度、氧化程度都不一样。这不算数据集的坑而是使用方式的问题。解决把 NEU-DET 当预训练和流程验证基准不要指望它替你覆盖现场域。常见做法是加载在 NEU-DET 上训练好的权重用现场采集的一两千张真实缺陷图做微调哪怕标注粗糙一些效果也远好于直接用 NEU-DET 权重做推理。6. 不换模型也能涨点的三个 NEU-DET 进阶技巧数据只有 1799 张但有些技巧能在不换网络结构的前提下把 mAP 再往上推一点。下面三个是我常用的。第一个是伪标注自训练。用训练好的 best.pt 对同一来源但未标注的图像做推理把置信度高的预测框直接当标注加入训练集再训一轮。要注意只挑选置信度大于 0.9 且面积合理的框人工扫一眼抽查几张。这个方法在小数据集上通常能带来稳定提升相当于用模型自己扩充了训练数据。第二个是灰度增强副本。NEU-DET 图像本质是灰度纹理但很多训练框架对灰度图会做三通道复制本身没有额外信息。我习惯把 CLAHE 或直方图均衡化后的图像作为第二个版本和原始图混合进训练强迫模型对光照和对比度变化更鲁棒。操作上不需要改代码提前跑个批处理生成增强副本放进 images/train 即可。第三个是推理时多尺度融合。因为原始图像是 200×200输入 640 本身已经是放大三倍多模型对尺度变化比较敏感。推理时可以用 TTA 或三尺度推理对三次预测结果做 NMS 合并。这一招对小目标类别特别有效代价是推理时间增加离线批量检测场景完全划得来。就这个数据集而言我最后悔的事情是早期只看总 mAP直到某次把 best.pt 放到现场图像上才发现 crazing 全线漏检。后来养成习惯每次训练完先翻每个类的坏例图再决定要不要调参。这个习惯帮我少走了很多弯路希望也能帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询