NEU-DET钢板缺陷数据集详解:YOLO全系列实战与标签转换

发布时间:2026/8/27 5:34:42
NEU-DET钢板缺陷数据集详解:YOLO全系列实战与标签转换 简介在计算机视觉领域目标检测是工业质检自动化落地的核心技术之一而高质量的数据集往往决定了算法效果的上限。NEU-DET作为热轧带钢表面缺陷检测的经典基准数据集涵盖裂纹、麻点、划伤等六类典型缺陷是验证YOLO等主流检测算法的理想素材。本文从目标检测的基本概念出发剖析该数据集的构成与两种主流标注格式——VOC格式XML与YOLO格式TXT的转换原理并给出完整脚本与目录组织方案。结合YOLOv5、YOLOv8及YOLO11的实际训练配置对比其精度与效率同时梳理标签越界、指标全零等高频踩坑场景。无论是入门深度学习工程应用还是横向评估检测模型性能这份实践指南都能帮你快速上手工业表面缺陷检测任务。 做工业视觉的同行应该都明白数据集这东西有时候比算法本身还难搞。我前几天整理硬盘翻出一个压箱底的数据集——NEU-DET钢板表面缺陷检测数据集当年做冶金行业质检项目时就是靠它把yolo全系列算法从头到尾跑通了一遍。这个数据集虽然只有1800张图但却是目标检测入门、算法效果横向对比的绝佳素材而且我手上这个版本已经配好了yolo格式的txt标签和voc格式的xml标签拿到手就能直接开训不用再自己折腾格式转换。这篇就把数据集的底细、两种标签格式的细节、以及我用yolo各版本实战踩过的坑一次说清楚想追工业缺陷检测方向的朋友可以参考。1. 钢板缺陷检测任务与NEU-DET数据集的底细1.1 为什么钢表面缺陷检测是个正经难题钢铁表面缺陷检测在工业质检里属于典型的高频刚需场景。热轧带钢在轧制过程中因为辊面状态、冷却不均、氧化皮脱落、夹杂物上浮等原因表面会形成裂纹、麻点、划伤、氧化铁皮压入等各种缺陷。这些缺陷直接影响成品的强度、耐腐蚀性和外观等级严重时整卷钢都要降级处理损失是几十万起步的。但在产线环境下做检测又非常难搞。钢带以每秒好几米的速度运动表面还有高温、水雾、氧化皮反光传统人工目检根本盯不住漏检率很高而且质检员三班倒下来眼睛基本废掉。所以钢铁厂很早就开始上机器视觉方案用工业相机配合光源拍表面图像再用算法自动框出缺陷。这个场景就是典型的目标检测落地需求也是NEU-DET数据集存在的大背景。NEU-DET之所以成为圈子里人尽皆知的基准数据集是因为它把工业现场最常见、最难分的6类表面缺陷都收进来了而且图像尺寸统一、标注干净、数量适中。不管你是刚入门目标检测还是想验证新网络的泛化能力拿它做实验都很顺手。1.2 NEU-DET里到底有什么NEU-DET的全称是Northeastern University - Defect Dataset源自东北大学宋克臣团队发布的NEU表面缺陷数据库。采集对象是热轧带钢表面数据一共包含6类典型缺陷每类300张总计1800张灰度图像原始尺寸统一为200x200像素。这6类缺陷分别是crazing裂纹、inclusion夹杂、patches斑块、pitted_surface麻点、rolled-in_scale氧化铁皮压入、scratches划伤。原始标注格式为VOC风格的xml文件使用外接矩形框框出每个缺陷区域整份数据大概有5000多个标注框平均每张图2到3个目标。类别编号类别名称中文含义典型形态0crazing裂纹表面网状或线状开裂1inclusion夹杂嵌入表面的颗粒状异物2patches斑块大面积灰暗色块状区域3pitted_surface麻点密集凹陷小坑4rolled-in_scale氧化铁皮压入鳞片状压入痕迹5scratches划伤连续沟槽状伤痕这6类在视觉上很有迷惑性。比如crazing和scratches都是线状patches和rolled-in_scale都是片状只靠颜色阈值或传统图像处理很难稳定区分必须用深度学习模型学习纹理和上下文特征。这正好是yolo这类目标检测算法的用武之地。1.3 这个数据集包为什么值得留一份网上流通的NEU-DET版本非常多早期的官方发布其实只有图像和分类标签后来才有人整理出带bbox的检测版本。问题在于很多版本的标注格式不统一有的只有xml有的txt文件里类别顺序和通用约定不一致还有的train/val划分不明新手一上来就踩坑。我这份zip包是整理过的双格式版本images目录下是原始灰度图annotations目录下是voc格式xml标签labels目录下是yolo格式txt标签。两份标签内容一一对应且txt标签的类别顺序按0到5排列crazing、inclusion、patches、pitted_surface、rolled-in_scale、scratches。这样就能直接适配yolov5、yolov8、yolo11等全系列算法也可以反哺到基于voc格式训练的SSD、Faster R-CNN等框架。1800张图对单卡训练非常友好几分钟就能跑一个epoch特别适合做算法选型对比。2. 两种标签格式的完整拆解txt与xml到底怎么互相转化2.1 VOC格式xml标签长什么样VOC格式是目标检测领域最经典的标注格式之一LabelImg标注工具默认输出的就是这种格式。NEU-DET的xml文件里每个文件对应一张图像记录了文件路径、图像尺寸、标注框坐标等信息。典型的xml内容长这样annotation folderNEU-DET/folder filenamecrazing_22.jpg/filename size width200/width height200/height depth3/depth /size object namecrazing/name bndbox xmin55/xmin ymin43/ymin xmax174/xmax ymax157/ymax /bndbox /object /annotation这里最关键的三个信息是width和height决定了图像的宽高name是类别名称bndbox里的四个值是缺陷框的左上角和右下角像素坐标。只要抓住这三点就能把VOC转换成任意格式的标签。很多人在这一步会忽略size字段其实它特别重要。转换yolo格式时必须用图像的真实宽高做归一化如果图像实际尺寸和xml里写的不一致生成的归一化坐标就会整体偏移训练时模型看到的框和标签对不上轻则loss异常重则指标全崩。2.2 YOLO格式txt标签长什么样YOLO格式和VOC格式完全不同它不保存像素坐标而是保存归一化后的中心点坐标和宽高。每一行对应一个目标格式是class_id x_center y_center width height比如crazing_22这张图xml里那个标注框转成yolo格式就是0 0.5725 0.5000 0.5950 0.5700注意所有值都是0到1之间的浮点数由像素坐标除以图像的宽高得到。class_id从0开始计数0对应crazing1对应inclusion以此类推。这个顺序必须和训练配置里的类别列表完全一致否则模型训练出来预测框名字全是乱的。YOLO格式的标注文件一般放在labels目录下文件名和图片名保持一致但扩展名是.txt。比如图像叫crazing_22.jpg标签就叫crazing_22.txt。Ultralytics系列的训练代码会自动根据images目录里的图片找同名txt文件所以文件命名绝对不能出错。2.3 转换逻辑与手写脚本理解了两边格式转换就是纯粹的数学计算了。核心公式如下x_center (xmin xmax) / 2 / image_widthy_center (ymin ymax) / 2 / image_heightwidth (xmax - xmin) / image_widthheight (ymax - ymin) / image_height反过来从yolo转voc就是先把归一化坐标乘回图像宽高再算左上角和右下角xmin (x_center - width / 2) * image_widthxmax (x_center width / 2) * image_widthymin (y_center - height / 2) * image_heightymax (y_center height / 2) * image_height下面是我当年用的转换脚本把xml目录全部转成txt同时生成类别文件import os import xml.etree.ElementTree as ET # 类别顺序必须和训练时保持一致 classes [crazing, inclusion, patches, pitted_surface, rolled-in_scale, scratches] def convert_xml_to_yolo(xml_path, txt_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) width int(size.find(width).text) height int(size.find(height).text) with open(txt_path, w) as f: for obj in root.iter(object): name obj.find(name).text if name not in classes: continue class_id classes.index(name) bndbox obj.find(bndbox) xmin int(bndbox.find(xmin).text) ymin int(bndbox.find(ymin).text) xmax int(bndbox.find(xmax).text) ymax int(bndbox.find(ymax).text) x_center (xmin xmax) / 2.0 / width y_center (ymin ymax) / 2.0 / height w (xmax - xmin) / width h (ymax - ymin) / height f.write(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n) xml_dir annotations label_dir labels os.makedirs(label_dir, exist_okTrue) for xml_name in os.listdir(xml_dir): if xml_name.endswith(.xml): txt_name xml_name.replace(.xml, .txt) convert_xml_to_yolo(os.path.join(xml_dir, xml_name), os.path.join(label_dir, txt_name))写完脚本之后强烈建议用可视化脚本抽几张图把标签框画在原图上肉眼核对一遍。这一步只要偷懒后面训练出现的所有问题都会变得很玄学。我以前就因为宽高字段读错了生成的框整体偏移白白浪费了一晚上排查。3. 把数据集组织成YOLO标准目录一个细节都不能错3.1 标准目录结构示例YOLO系算法对数据目录有固定要求核心原则是images和labels一一对应且同名的图片和标签必须在各自的train/val子目录里。以Ultralytics框架为例推荐目录结构如下neu-det/ ├── images/ │ ├── train/ │ │ ├── crazing_1.jpg │ │ └── ... │ └── val/ │ └── ... ├── labels/ │ ├── train/ │ │ ├── crazing_1.txt │ │ └── ... │ └── val/ │ └── ... └── neu-det.yaml注意labels目录里的txt文件名必须和images里对应的jpg文件名完全一致只是扩展名不同。文件夹名字必须是images和labels不能改成imgs或label否则框架会找不到配对文件。3.2 数据划分脚本与随机种子NEU-DET一共1800张图建议按8:1:1划分为train/val/test或者至少train和val两段测试集单独留出来做最终评估。划分的时候一定要固定随机种子否则每次跑出来数据分布都不一样实验结果没法对比。下面是我常用的划分脚本import os import random import shutil random.seed(42) data_dir neu-det images [f for f in os.listdir(os.path.join(data_dir, images)) if f.endswith(.jpg)] random.shuffle(images) train_ratio 0.8 val_ratio 0.1 train_split int(len(images) * train_ratio) val_split int(len(images) * (train_ratio val_ratio)) splits { train: images[:train_split], val: images[train_split:val_split], test: images[val_split:] } for split, file_list in splits.items(): os.makedirs(os.path.join(data_dir, images, split), exist_okTrue) os.makedirs(os.path.join(data_dir, labels, split), exist_okTrue) for img in file_list: base_name img.replace(.jpg, ) shutil.move(os.path.join(data_dir, images, img), os.path.join(data_dir, images, split, img)) shutil.move(os.path.join(data_dir, labels, base_name .txt), os.path.join(data_dir, labels, split, base_name .txt))划分完最好检查一下每个类别在train和val里的数量占比。NEU-DET本身每类300张非常均衡按比例切分后基本不会有类别倾斜问题但如果将来替换成别的数据集这一步一定要做不然模型很可能在样本少的类别上直接失灵。3.3 data.yaml配置与类别顺序的致命细节Ultralytics框架训练前需要一个yaml配置文件里面写清楚数据路径、类别数量、类别名称。NEU-DET对应的配置如下path: neu-det train: images/train val: images/val test: images/test nc: 6 names: 0: crazing 1: inclusion 2: patches 3: pitted_surface 4: rolled-in_scale 5: scratches这里有一个全宇宙通用的大坑names顺序必须和txt标签里的class_id完全一致。比如txt里第一行是0 0.5725 0.5000 ...0对应crazing那么yaml里0也必须写crazing。如果哪份资料里把crazing排到了类别1而你把yaml里的0写成scratches训练出来的模型预测结果就会张冠李戴看起来loss正常、精度正常但实际全错。我拿到任何一份数据集的第一件事就是翻一个txt标签确认类别id含义再回头写yaml。这一步能省下后面80%的debug时间。4. YOLO全系实战从v5到v8再到v11的配置要点4.1 环境准备与代码库选择YOLO系列经过多年发展已经分成了好几条技术线。yolov5是ultralytics早期作品目前仍在维护用的是train.py加命令行参数的老流程yolov8和yolo11都是ultralytics统一框架用yolo train命令一条龙搞定。yolov6是美团开源的yolov7是AB大神发布的各自有独立的代码库。NEU-DET这个数据集因为格式标准几乎能在所有yolo变体上直接跑。考虑到现在主流是yolov8和yolo11我下面以这两个为主v5也会提一下。环境方面建议Python 3.9以上PyTorch 2.0以上一张8G显存的显卡如RTX 3060/4060就够跑了。4.2 v5/v8/v11训练命令对比以yolov5为例训练命令长这样git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt python train.py --data neu-det/neu-det.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 200yolov8和yolo11就简洁得多直接一把梭pip install ultralytics yolo train dataneu-det/neu-det.yaml modelyolov8s.pt epochs200 imgsz640 batch16yolo train dataneu-det/neu-det.yaml modelyolo11s.pt epochs200 imgsz640 batch16三个版本在数据组织层面几乎是通用的核心区别体现在网络结构上。yolov5的anchor是预设的训练时需要根据数据集特性做自适应anchor计算yolov8是anchor-free设计对anchor不敏感直接训练即可yolo11在v8基础上优化了C3k2模块和检测头精度和速度都有小幅提升。NEU-DET这种中小目标较多的数据集v8和v11的体验明显比v5省心。4.3 训练参数怎么选才合理NEU-DET的原始图像只有200x200直接拿640作为训练输入会有一定程度的放大但效果通常更好因为模型在高分辨率下能学到更多纹理细节。如果显存不够用320输入也完全能跑。batch size取16到32之间显存小的就把batch调低配合梯度累积效果差不多。epochs方面1800张图的数据量不算大200个epoch以内基本能收敛。我实测下来yolov8s在200个epoch时mAP50已经能达到77%左右继续训练到300个epoch提升有限反而有过拟合风险。建议训练时开启早停机制patience30loss连续几十个epoch不降就自动停省时间。优化器和增强策略按Ultralytics默认配置就好。唯一要注意的是NEU-DET这种钢板表面图像纹理丰富、目标区域和背景对比度有时不高过强的Mosaic增强比如mosaic1.0反而可能让模型学偏可以适当降低到0.5试试。4.4 验证评估与性能参考训练完成后用以下命令在验证集上评估yolo val modelruns/detect/train/weights/best.pt dataneu-det/neu-det.yamlNEU-DET在不同yolo版本上的mAP水平我根据自己跑过的实验和公开repo记录整理了一个大致参考范围都是640输入、200epoch、默认增强模型参数量mAP50参考值mAP50-95参考值备注yolov5s7.2M72%-78%52%-58%稳定省资源yolov8s11.2M75%-80%55%-60%综合体验好yolov8x68.2M80%-85%62%-68%精度上限高显存要求大yolo11s9.1M76%-81%57%-61%小模型里值得优先试这些数据会随随机种子和数据划分浮动不要当作绝对指标。重点看相对关系模型越大精度越高但训练时间翻倍yolo11这种新结构在同量级下确实比v5略强。5. 常见问题与排查技巧实录5.1 训练指标全是0先查这三件事我见过最多的问题就是训练跑了好几个epochloss也降了但precision、recall、mAP全是0。这种症状99%是数据没对齐优先检查三点。第一txt标签文件是否为空。NEU-DET部分图像可能因为原始标注遗漏对应txt是0字节训练时模型读到空标签就直接跳过。用一行命令排查find labels -name *.txt -size 0把空文件找出来删掉或补充标签。第二类别id是否越界。如果txt里的class_id是6或更大而配置里nc是6代码会直接忽略这个目标导致模型学不到东西。用脚本检查txt里每个文件的类别最大值确保不超过5。第三data.yaml里的路径是否是绝对路径或正确相对路径。在Jupyter里训练时path字段如果写错框架会静默使用空数据集表现就是loss正常但所有指标为0。这也是我最开始踩的坑。5.2 损失不下降问题可能出在增强策略训练初期loss降不下来先别急着换模型把batch size调大一点学习率降到默认的一半试试。如果还是不行关掉Mosaic等强增强只用基础翻转和色彩抖动看看loss是否下降。NEU-DET这类纹理密集的图像过强的拼接增强会制造大量不自然的样本模型很容易被带偏。还有一种情况是标注框太小导致回归分支学不动。NEU-DET里麻点和夹杂类目标经常只有几个像素大小200x200下的缺陷框归一化后特别小在640输入下放大到几十个像素虽然能看但相对整图还是很小。这时用yolov8的anchor-free机制反而比老版yolov5的固定anchor更友好。5.3 标签越界与归一化坐标奇怪的排查思路如果训练时报错说标签坐标越界或者可视化时发现框跑到图像外面去了优先检查转换脚本里的图像宽高是否和解码后的实际尺寸一致。有些网上流传的xml里写的是BGR通道数3但图像实际是单通道灰度图depth字段和实际通道数不一致虽然不影响训练但如果转换脚本里错误地读取了别的字段坐标就会乱。另外注意txt标签里的坐标必须全部在0到1之间且width、height必须为正数。有个冷门情况是标注工具在框的左右两边缘时xmax可能等于图像宽度这时候归一化得到1.0属于正常边界值不要误判成越界。5.4 提升mAP的实战技巧总结数据量就这么大想榨出更高精度主要靠三个方向。第一多尺度训练。Ultralytics默认开启多尺度imgsz640时会随机缩放0.5到1.5倍这个对NEU-DET效果明显因为板材缺陷的尺度变化本来就大。第二模型外接SE注意力或者改用yolo11的C3k2模块通常能小幅提升mAP。第三换用更科学的损失函数和训练策略比如使用Wise-IoU替代默认CIoU对微小缺陷框的回归更友好。这些改动可以参考社区开源的yolo改进项目按需叠加。最后还有一个容易被忽视的点训练完成后可以用测试集做一个交叉验证看看模型在没见过的图像上的泛化效果。NEU-DET因为类内形态差异大同一个类别在不同样本上可能长得完全不一样泛化能力比训练集精度更值得关注。我个人在实际操作中最大的体会是NEU-DET看似简单但它把工业场景里的核心难点都浓缩进去了——小目标、类间相似、表面纹理干扰、光照不均。如果你能把这1800张图在yolo全系算法上的指标稳定做上去再去碰其他工业缺陷数据集很多问题都能一眼看穿。最后再分享一个小技巧每次训练前花两分钟把txt标签可视化合到原图上扫一遍。这个习惯我保持到现在它帮我挡下了至少十次因为标签错乱导致的无效训练。本文还有配套的精品资源点击获取