简介:这份钢铁表面缺陷检测图像数据集面向从事机器视觉、工业质检与深度学习缺陷识别的研究人员和工程师,提供可直接用于模型训练与验证的真实样本。数据集共1800张钢铁表面图像,每张均配有对应标签,覆盖裂纹、锈蚀、凹坑、划痕等多种典型缺陷,适合开展图像分类、目标检测等监督学习任务。压缩包为zip格式,内含2000个文件,以1800张jpg图像为主体,并附有txt标注说明、xml标注文件及data、names等配置数据,整体约26.81MB,目录结构便于按训练集与验证集划分使用。目前已有2450人学习下载,说明其在工业缺陷检测领域具有较高的参考价值。读者可借助该数据集完成从图像预处理、特征提取到CNN等模型训练与调优的完整流程,并利用标签评估精度、召回率与F1分数,为钢铁制造场景下的自动化质检方案提供扎实的数据基础。1. 钢铁表面缺陷检测图像数据集:1800张图能撑起一个可用的质检模型吗产线质检的朋友跟我吐槽,说他们那条冷轧线一天过几千卷钢,靠人眼在出口段盯表面缺陷,漏检率随班次往后走一路飙升,夜班后半段基本靠玄学。想上视觉检测,第一步就卡在数据上——公开的钢铁缺陷数据集要么只有几百张、类别残缺,要么标注格式跟自家训练框架对不上。钢铁表面缺陷检测图像数据集(1800张图像,含标签)这类资源,解决的正是“从零起步没有标注数据”这个最痛的点:它把划痕、夹杂、结疤、氧化铁皮压入、裂纹这几类常见表面缺陷的图连标签一起给你,让你能先把训练管线跑通、把基线模型立起来,再谈往产线上迁。它适合两类人:一类是想验证某个检测框架在工业缺陷场景下到底行不行的算法工程师,另一类是手里有产线但还没攒够标注数据的质检团队。1800张不算多,但足够回答“这条路走不走得通”这个问题。2. 先搞清楚这1800张图里到底有什么:类别分布与标注格式拿到一个数据集,最忌讳的就是直接train.py一把梭。我一般会先花半小时把数据摸清楚:类别是不是均衡、标注是整图分类还是目标框、有没有脏样本。这一步做扎实,后面调参才有方向,不然模型训崩了你都不知道是数据问题还是超参问题。2.1 缺陷类别与典型成像特征钢铁表面缺陷的类别定义各家产线不完全一致,但常见的几类在成像上有比较稳定的视觉特征,理解这些特征直接决定你后面怎么做增强、怎么设 anchor。缺陷类型典型视觉特征检测难点划痕细长条状,方向性强,灰度略低于背景与轧制纹理方向混淆,细划痕易漏夹杂不规则块状,灰度对比明显尺寸跨度大,小目标多结疤局部凸起,边缘有阴影与光照不均产生的伪影难区分氧化铁皮压入片状、颜色偏暗红或黑与背景灰度接近,对比度低裂纹分叉状细线,走向随机极细,分辨率不足时直接消失这张表不是让你背,而是提醒你:如果数据集里裂纹样本只有几十张,你训出来的模型对裂纹的召回一定难看,这时候要么补数据,要么在损失函数上给稀有类加权。很多人忽略这一步,训完发现 mAP 还行但产线上裂纹全漏,回头查才发现是类别长尾。2.2 标注格式识别与转换工业数据集常见的标注格式有三种:整图分类(一个图一个类别标签)、VOC 风格的 XML(目标框)、YOLO 风格的 txt(归一化中心点加宽高)。你得先确认手里这份是哪种。下面这段脚本用来快速统计类别分布并判断标注格式,我基本每个新数据集都会先跑一遍。import os import glob from collections import Counter import xml.etree.ElementTree as ET data_root = "./steel_defect" # 数据集根目录 img_dir = os.path.join(data_root, "images") ann_dir = os.path.join(data_root, "annotations") # 1. 统计图像数量与扩展名 imgs = glob.glob(os.path.join(img_dir, "*")) ext_counter = Counter([os.path.splitext(p)[1].lower() for p in imgs]) print("图像总数:", len(imgs), "扩展名分布:", ext_counter) # 2. 判断标注格式:优先找 xml,其次找 txt xmls = glob.glob(os.path.join(ann_dir, "*.xml")) txts = glob.glob(os.path.join(ann_dir, "*.txt")) print("XML 标注数:", len(xmls), "TXT 标注数:", len(txts)) # 3. 如果是 VOC XML,统计每个类别的框数量 if xmls: cls_counter = Counter() for x in xmls: tree = ET.parse(x) root = tree.getroot() for obj in root.findall("object"): name = obj.find("name").text.strip() cls_counter[name] += 1 print("VOC 类别分布:", cls_counter.most_common()) # 4. 如果是 YOLO txt,统计每类出现次数(第一列是类别索引) if txts: cls_counter = Counter() for t in txts: with open(t, "r") as f: for line in f: line = line.strip() if not line: continue cls_id = int(line.split()[0]) cls_counter[cls_id] += 1 print("YOLO 类别索引分布:", cls_counter.most_common())这段代码的逻辑很直白:先数图,再数标注文件,最后按格式分别统计类别。参数上唯一要注意的是data_root要指向你解压后的真实目录,很多数据集解压后会多套一层文件夹,路径写错就会得到“图像总数 0”这种结果。跑完你手里应该有一份类别分布,如果发现某一类框数量不到总数的 5%,就要在训练时考虑类别加权或者过采样,否则模型会偏向多数类。2.3 划分训练集、验证集、测试集1800 张图,我一般按 7:2:1 切,也就是训练 1260、验证 360、测试 180。但工业缺陷数据有个坑:同一卷钢上截下来的多张图高度相似,如果随机切分,训练集和验证集里会出现近乎重复的样本,验证指标虚高,上线就翻车。稳妥做法是按“卷号”或“采集批次”分组切分,同一批次的图只进一个集合。如果数据集没提供批次信息,退而求其次,用图像感知哈希做一次去重,把相似度极高的图归到同一组再切。import os import shutil import random from collections import defaultdict random.seed(42) data_root = "./steel_defect" img_dir = os.path.join(data_root, "images") out_root = "./steel_split" # 假设文件名前缀代表批次,如 batch01_0001.jpg groups = defaultdict(list) for name in os.listdir(img_dir): batch = name.split("_")[0] if "_" in name else "default" groups[batch].append(name) batches = list(groups.keys()) random.shuffle(batches) n = len(batches) train_b = batches[:int(n * 0.7)] val_b = batches[int(n * 0.7):int(n * 0.9)] test_b = batches[int(n * 0.9):] for split, blist in [("train", train_b), ("val", val_b), ("test", test_b)]: os.makedirs(os.path.join(out_root, split), exist_ok=True) for b in blist: for img in groups[b]: shutil.copy(os.path.join(img_dir, img), os.path.join(out_root, split, img)) print(split, "图像数:", sum(len(groups[b]) for b in blist))这里用批次前缀分组是关键,random.seed(42)保证你每次切分结果一致,方便复现。如果你的文件名没有批次信息,就得换成感知哈希聚类,思路一样:相似的归一组,组间切分。切分完记得把对应的标注文件也按同样规则复制过去,别只复制图。3. 用这份数据跑通第一个检测基线:从环境到训练数据摸清楚了,接下来就是把它喂给一个检测框架,拿到第一组指标。这一步的目标不是刷高精度,而是确认整条链路通畅:数据能读、模型能训、指标能算、结果能可视化。我一般选 YOLO 系列做基线,因为改起来快、社区坑少。3.1 环境与依赖的版本约束工业视觉项目最怕环境乱。CUDA 版本、PyTorch 版本、检测框架版本三者不匹配,报错能让你查一晚上。我的习惯是先锁一个大版本区间,再装依赖。# 创建独立环境,避免污染系统 Python conda create -n steel_defect python=3.10 -y conda activate steel_defect # 安装 PyTorch,CUDA 11.8 是当前兼容性较好的选择 pip install torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cu118 # 安装检测框架与可视化依赖 pip install ultralytics opencv-python matplotlib pandas参数说明:python=3.10是因为部分检测框架对 3.11 以上支持还不稳;CUDA 11.8 对应的 PyTorch 2.1.0 是经过大量项目验证的组合。如果你机器只有 CPU,把--index-url那行去掉装 CPU 版即可,但训练会慢到让你怀疑人生,1800 张图 CPU 训一轮可能几十分钟,建议至少一张 8G 显存的卡。3.2 把标注转成训练框架要的格式YOLO 训练需要每张图对应一个 txt,每行是类别 中心x 中心y 宽 高,全部归一化到 0 到 1。如果你手里是 VOC XML,就得转。下面这个转换脚本我用了很多次,处理了边界框越界和空标注两个常见问题。import os import glob import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, img_w, img_h, class_map): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall("object"): name = obj.find("name").text.strip() if name not in class_map: continue bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 边界裁剪,防止标注越界导致归一化后超出 0-1 xmin = max(0, min(xmin, img_w)) xmax = max(0, min(xmax, img_w)) ymin = max(0, min(ymin, img_h)) ymax = max(0, min(ymax, img_h)) if xmax <= xmin or ymax <= ymin: continue # 跳过无效框 cx = (xmin + xmax) / 2.0 / img_w cy = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{class_map[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") return lines # 类别映射,按你的数据集实际类别改 class_map = {"scratch": 0, "inclusion": 1, "scar": 2, "scale": 3, "crack": 4} img_w, img_h = 640, 640 # 改成你图像的真实尺寸 xml_dir = "./steel_defect/annotations" out_dir = "./steel_defect/labels" os.makedirs(out_dir, exist_ok=True) for xml_path in glob.glob(os.path.join(xml_dir, "*.xml")): stem = os.path.splitext(os.path.basename(xml_path))[0] lines = voc_to_yolo(xml_path, img_w, img_h, class_map) with open(os.path.join(out_dir, stem + ".txt"), "w") as f: f.write("\n".join(lines)) print("转换完成,共处理", len(glob.glob(os.path.join(xml_dir, '*.xml'))), "个标注文件")逻辑说明:class_map把类别名映射成从 0 开始的整数,顺序必须和后面训练配置里的names完全一致,错一个位置模型就学错类。边界裁剪那几行是血泪经验,工业数据集标注时经常有框超出图像边缘,不裁剪归一化后会出现大于 1 的值,训练直接报错。img_w和img_h要填图像真实尺寸,不是模型输入尺寸,这里搞混是新手最常见的翻车点。3.3 训练配置与首轮指标解读转换完写一个数据配置文件,然后启动训练。配置文件里path、train、val指向你切分好的目录,names和转换时的class_map对齐。# steel.yaml path: ./steel_split train: train val: val nc: 5 names: 0: scratch 1: inclusion 2: scar 3: scale 4: crack# 启动训练,输入尺寸 640,批次 16,训 100 轮 yolo detect train data=steel.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16 device=0参数说明:model=yolov8n.pt用 nano 版先跑通,显存占用小、速度快,确认链路没问题再换 s 或 m 版。imgsz=640是工业缺陷检测的常用输入尺寸,再小细划痕就糊了,再大显存吃不消。batch=16在 8G 显存上比较稳,爆显存就降到 8。训完看结果,重点不是看 mAP 绝对值,而是看每一类的召回:如果裂纹类召回低于 0.3,说明样本太少或特征太弱,得回去补数据或调损失权重,而不是继续加轮数。4. 小样本工业缺陷检测的避坑清单:5个真实踩坑记录1800 张图说多不多,训起来坑一点不少。下面这几条是我和身边同行反复踩过的,按“现象→原因→解决”写清楚,你对照排查能省不少时间。4.1 验证集 mAP 很高,上线全漏检现象:本地验证 mAP 到 0.85,拿到产线测试漏检严重。原因:训练集和验证集来自同一批次,图像高度相似,模型记住了背景而不是缺陷。解决:按批次分组切分,或者用感知哈希去重后再切,确保验证集里的背景和训练集有差异。判断方法很简单,看验证集图像的采集时间和训练集是否重叠。4.2 训练 loss 不降,一直震荡现象:loss 在前 20 轮上下横跳,不收敛。原因:学习率设太大,或者标注里有大量越界框导致梯度异常。解决:先把学习率降到 0.001 试,同时跑一遍标注校验脚本,把宽高小于 0.001 的框和超出 0-1 范围的框清掉。工业数据集标注质量参差,这一步不能省。4.3 小目标缺陷几乎检不出现象:大块夹杂能检,细划痕和裂纹召回极低。原因:输入尺寸下采样后细目标只剩几个像素,特征被吞掉。解决:把imgsz提到 1024,或者在模型里加 P2 小目标检测层。代价是显存和推理时间上升,产线节拍紧的话要权衡。另一个办法是切图推理,把大图裁成小块分别检测再合并。4.4 类别不均衡导致模型只认多数类现象:划痕和夹杂检得挺好,结疤和裂纹基本不报。原因:这两类样本占比太低,损失被多数类主导。解决:在损失里给稀有类加权,或者对稀有类做过采样和针对性增强(旋转、对比度扰动)。别指望靠加轮数解决,轮数越多模型越偏向多数类。4.5 换一批产线图像指标断崖下跌现象:在数据集上训得好好的,换一条产线的图直接崩。原因:不同产线的光照、相机参数、钢种表面反射率差异大,模型过拟合了原数据集的成像风格。解决:做域增强,训练时随机调亮度、对比度、加噪声,模拟不同成像条件;有条件的话在新产线上标几百张做微调。这一步是工业视觉落地的必经之路,没有哪个模型能零样本跨产线。5. 把1800张图用出更大价值:增强策略与迁移验证数据集规模固定,但你能通过增强和迁移策略把它的价值放大。这一章讲两个具体技巧,一个是怎么做工业场景下真正有效的增强,一个是怎么验证模型换产线后还稳不稳。5.1 工业缺陷场景下真正有效的增强组合通用增强里,翻转、旋转、缩放对钢铁缺陷基本安全,因为缺陷方向本身随机。但有两类增强要慎用:一是大角度透视变换,会让细划痕几何形变失真,模型学到错误的形状先验;二是强色彩抖动,钢铁表面颜色信息对区分氧化铁皮压入和背景很关键,抖过头反而丢特征。我一般用这组配置:增强类型参数范围说明水平翻转概率 0.5缺陷方向无强先验,安全随机旋转±15 度角度太大细目标会失真亮度扰动±20%模拟产线光照波动对比度扰动±15%增强低对比度缺陷的鲁棒性高斯噪声标准差 0.01模拟相机噪声马赛克增强概率 0.3提升小目标,但别开太高马赛克增强对小目标有帮助,但它会把四张图拼一起,工业缺陷的上下文被破坏,开太高模型会学到不真实的场景。0.3 左右是个平衡点。5.2 用留出批次做跨域验证想提前知道模型换产线会不会崩,最土但最有效的办法是留出一个完整批次不参与训练,专门当“模拟新产线”测试集。具体做法:切分时不要随机,而是按批次留出最后 10% 的批次,训练时完全不碰,训完直接在这个留出集上跑指标。如果留出集指标比验证集低超过 15 个百分点,说明模型对成像风格过拟合,得加强域增强或准备微调数据。# 在留出批次上评估,观察跨域性能差距 from ultralytics import YOLO model = YOLO("./runs/detect/train/weights/best.pt") metrics = model.val(data="steel_holdout.yaml", imgsz=640, batch=8) print("留出批次 mAP50:", metrics.box.map50) print("留出批次 mAP50-95:", metrics.box.map)steel_holdout.yaml里val指向留出批次目录,其余配置和训练时一致。跑完对比训练时验证集的 mAP,差距就是你的跨域风险敞口。这个数字比任何离线指标都更能说明模型能不能上产线。5.3 一个我常犯的错我早期做这类项目,总想着把 mAP 刷到最高再交付,结果在留出批次上一测就露馅。后来养成习惯:每训完一版,先看留出批次指标,再看验证集指标,两者差距大的版本直接不进入下一轮。数据集的 1800 张图是起点不是终点,真正决定成败的是你有没有用留出批次给自己留一颗后悔药。希望帮到你。本文还有配套的精品资源,点击获取
简介:这份钢铁表面缺陷检测图像数据集面向从事机器视觉、工业质检与深度学习缺陷识别的研究人员和工程师,提供可直接用于模型训练与验证的真实样本。数据集共1800张钢铁表面图像,每张均配有对应标签,覆盖裂纹、锈蚀、凹坑、划痕等多种典型缺陷,适合开展图像分类、目标检测等监督学习任务。压缩包为zip格式,内含2000个文件,以1800张jpg图像为主体,并附有txt标注说明、xml标注文件及data、names等配置数据,整体约26.81MB,目录结构便于按训练集与验证集划分使用。目前已有2450人学习下载,说明其在工业缺陷检测领域具有较高的参考价值。读者可借助该数据集完成从图像预处理、特征提取到CNN等模型训练与调优的完整流程,并利用标签评估精度、召回率与F1分数,为钢铁制造场景下的自动化质检方案提供扎实的数据基础。1. 钢铁表面缺陷检测图像数据集:1800张图能撑起一个可用的质检模型吗产线质检的朋友跟我吐槽,说他们那条冷轧线一天过几千卷钢,靠人眼在出口段盯表面缺陷,漏检率随班次往后走一路飙升,夜班后半段基本靠玄学。想上视觉检测,第一步就卡在数据上——公开的钢铁缺陷数据集要么只有几百张、类别残缺,要么标注格式跟自家训练框架对不上。钢铁表面缺陷检测图像数据集(1800张图像,含标签)这类资源,解决的正是“从零起步没有标注数据”这个最痛的点:它把划痕、夹杂、结疤、氧化铁皮压入、裂纹这几类常见表面缺陷的图连标签一起给你,让你能先把训练管线跑通、把基线模型立起来,再谈往产线上迁。它适合两类人:一类是想验证某个检测框架在工业缺陷场景下到底行不行的算法工程师,另一类是手里有产线但还没攒够标注数据的质检团队。1800张不算多,但足够回答“这条路走不走得通”这个问题。2. 先搞清楚这1800张图里到底有什么:类别分布与标注格式拿到一个数据集,最忌讳的就是直接train.py一把梭。我一般会先花半小时把数据摸清楚:类别是不是均衡、标注是整图分类还是目标框、有没有脏样本。这一步做扎实,后面调参才有方向,不然模型训崩了你都不知道是数据问题还是超参问题。2.1 缺陷类别与典型成像特征钢铁表面缺陷的类别定义各家产线不完全一致,但常见的几类在成像上有比较稳定的视觉特征,理解这些特征直接决定你后面怎么做增强、怎么设 anchor。缺陷类型典型视觉特征检测难点划痕细长条状,方向性强,灰度略低于背景与轧制纹理方向混淆,细划痕易漏夹杂不规则块状,灰度对比明显尺寸跨度大,小目标多结疤局部凸起,边缘有阴影与光照不均产生的伪影难区分氧化铁皮压入片状、颜色偏暗红或黑与背景灰度接近,对比度低裂纹分叉状细线,走向随机极细,分辨率不足时直接消失这张表不是让你背,而是提醒你:如果数据集里裂纹样本只有几十张,你训出来的模型对裂纹的召回一定难看,这时候要么补数据,要么在损失函数上给稀有类加权。很多人忽略这一步,训完发现 mAP 还行但产线上裂纹全漏,回头查才发现是类别长尾。2.2 标注格式识别与转换工业数据集常见的标注格式有三种:整图分类(一个图一个类别标签)、VOC 风格的 XML(目标框)、YOLO 风格的 txt(归一化中心点加宽高)。你得先确认手里这份是哪种。下面这段脚本用来快速统计类别分布并判断标注格式,我基本每个新数据集都会先跑一遍。import os import glob from collections import Counter import xml.etree.ElementTree as ET data_root = "./steel_defect" # 数据集根目录 img_dir = os.path.join(data_root, "images") ann_dir = os.path.join(data_root, "annotations") # 1. 统计图像数量与扩展名 imgs = glob.glob(os.path.join(img_dir, "*")) ext_counter = Counter([os.path.splitext(p)[1].lower() for p in imgs]) print("图像总数:", len(imgs), "扩展名分布:", ext_counter) # 2. 判断标注格式:优先找 xml,其次找 txt xmls = glob.glob(os.path.join(ann_dir, "*.xml")) txts = glob.glob(os.path.join(ann_dir, "*.txt")) print("XML 标注数:", len(xmls), "TXT 标注数:", len(txts)) # 3. 如果是 VOC XML,统计每个类别的框数量 if xmls: cls_counter = Counter() for x in xmls: tree = ET.parse(x) root = tree.getroot() for obj in root.findall("object"): name = obj.find("name").text.strip() cls_counter[name] += 1 print("VOC 类别分布:", cls_counter.most_common()) # 4. 如果是 YOLO txt,统计每类出现次数(第一列是类别索引) if txts: cls_counter = Counter() for t in txts: with open(t, "r") as f: for line in f: line = line.strip() if not line: continue cls_id = int(line.split()[0]) cls_counter[cls_id] += 1 print("YOLO 类别索引分布:", cls_counter.most_common())这段代码的逻辑很直白:先数图,再数标注文件,最后按格式分别统计类别。参数上唯一要注意的是data_root要指向你解压后的真实目录,很多数据集解压后会多套一层文件夹,路径写错就会得到“图像总数 0”这种结果。跑完你手里应该有一份类别分布,如果发现某一类框数量不到总数的 5%,就要在训练时考虑类别加权或者过采样,否则模型会偏向多数类。2.3 划分训练集、验证集、测试集1800 张图,我一般按 7:2:1 切,也就是训练 1260、验证 360、测试 180。但工业缺陷数据有个坑:同一卷钢上截下来的多张图高度相似,如果随机切分,训练集和验证集里会出现近乎重复的样本,验证指标虚高,上线就翻车。稳妥做法是按“卷号”或“采集批次”分组切分,同一批次的图只进一个集合。如果数据集没提供批次信息,退而求其次,用图像感知哈希做一次去重,把相似度极高的图归到同一组再切。import os import shutil import random from collections import defaultdict random.seed(42) data_root = "./steel_defect" img_dir = os.path.join(data_root, "images") out_root = "./steel_split" # 假设文件名前缀代表批次,如 batch01_0001.jpg groups = defaultdict(list) for name in os.listdir(img_dir): batch = name.split("_")[0] if "_" in name else "default" groups[batch].append(name) batches = list(groups.keys()) random.shuffle(batches) n = len(batches) train_b = batches[:int(n * 0.7)] val_b = batches[int(n * 0.7):int(n * 0.9)] test_b = batches[int(n * 0.9):] for split, blist in [("train", train_b), ("val", val_b), ("test", test_b)]: os.makedirs(os.path.join(out_root, split), exist_ok=True) for b in blist: for img in groups[b]: shutil.copy(os.path.join(img_dir, img), os.path.join(out_root, split, img)) print(split, "图像数:", sum(len(groups[b]) for b in blist))这里用批次前缀分组是关键,random.seed(42)保证你每次切分结果一致,方便复现。如果你的文件名没有批次信息,就得换成感知哈希聚类,思路一样:相似的归一组,组间切分。切分完记得把对应的标注文件也按同样规则复制过去,别只复制图。3. 用这份数据跑通第一个检测基线:从环境到训练数据摸清楚了,接下来就是把它喂给一个检测框架,拿到第一组指标。这一步的目标不是刷高精度,而是确认整条链路通畅:数据能读、模型能训、指标能算、结果能可视化。我一般选 YOLO 系列做基线,因为改起来快、社区坑少。3.1 环境与依赖的版本约束工业视觉项目最怕环境乱。CUDA 版本、PyTorch 版本、检测框架版本三者不匹配,报错能让你查一晚上。我的习惯是先锁一个大版本区间,再装依赖。# 创建独立环境,避免污染系统 Python conda create -n steel_defect python=3.10 -y conda activate steel_defect # 安装 PyTorch,CUDA 11.8 是当前兼容性较好的选择 pip install torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cu118 # 安装检测框架与可视化依赖 pip install ultralytics opencv-python matplotlib pandas参数说明:python=3.10是因为部分检测框架对 3.11 以上支持还不稳;CUDA 11.8 对应的 PyTorch 2.1.0 是经过大量项目验证的组合。如果你机器只有 CPU,把--index-url那行去掉装 CPU 版即可,但训练会慢到让你怀疑人生,1800 张图 CPU 训一轮可能几十分钟,建议至少一张 8G 显存的卡。3.2 把标注转成训练框架要的格式YOLO 训练需要每张图对应一个 txt,每行是类别 中心x 中心y 宽 高,全部归一化到 0 到 1。如果你手里是 VOC XML,就得转。下面这个转换脚本我用了很多次,处理了边界框越界和空标注两个常见问题。import os import glob import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, img_w, img_h, class_map): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall("object"): name = obj.find("name").text.strip() if name not in class_map: continue bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 边界裁剪,防止标注越界导致归一化后超出 0-1 xmin = max(0, min(xmin, img_w)) xmax = max(0, min(xmax, img_w)) ymin = max(0, min(ymin, img_h)) ymax = max(0, min(ymax, img_h)) if xmax <= xmin or ymax <= ymin: continue # 跳过无效框 cx = (xmin + xmax) / 2.0 / img_w cy = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{class_map[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") return lines # 类别映射,按你的数据集实际类别改 class_map = {"scratch": 0, "inclusion": 1, "scar": 2, "scale": 3, "crack": 4} img_w, img_h = 640, 640 # 改成你图像的真实尺寸 xml_dir = "./steel_defect/annotations" out_dir = "./steel_defect/labels" os.makedirs(out_dir, exist_ok=True) for xml_path in glob.glob(os.path.join(xml_dir, "*.xml")): stem = os.path.splitext(os.path.basename(xml_path))[0] lines = voc_to_yolo(xml_path, img_w, img_h, class_map) with open(os.path.join(out_dir, stem + ".txt"), "w") as f: f.write("\n".join(lines)) print("转换完成,共处理", len(glob.glob(os.path.join(xml_dir, '*.xml'))), "个标注文件")逻辑说明:class_map把类别名映射成从 0 开始的整数,顺序必须和后面训练配置里的names完全一致,错一个位置模型就学错类。边界裁剪那几行是血泪经验,工业数据集标注时经常有框超出图像边缘,不裁剪归一化后会出现大于 1 的值,训练直接报错。img_w和img_h要填图像真实尺寸,不是模型输入尺寸,这里搞混是新手最常见的翻车点。3.3 训练配置与首轮指标解读转换完写一个数据配置文件,然后启动训练。配置文件里path、train、val指向你切分好的目录,names和转换时的class_map对齐。# steel.yaml path: ./steel_split train: train val: val nc: 5 names: 0: scratch 1: inclusion 2: scar 3: scale 4: crack# 启动训练,输入尺寸 640,批次 16,训 100 轮 yolo detect train data=steel.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16 device=0参数说明:model=yolov8n.pt用 nano 版先跑通,显存占用小、速度快,确认链路没问题再换 s 或 m 版。imgsz=640是工业缺陷检测的常用输入尺寸,再小细划痕就糊了,再大显存吃不消。batch=16在 8G 显存上比较稳,爆显存就降到 8。训完看结果,重点不是看 mAP 绝对值,而是看每一类的召回:如果裂纹类召回低于 0.3,说明样本太少或特征太弱,得回去补数据或调损失权重,而不是继续加轮数。4. 小样本工业缺陷检测的避坑清单:5个真实踩坑记录1800 张图说多不多,训起来坑一点不少。下面这几条是我和身边同行反复踩过的,按“现象→原因→解决”写清楚,你对照排查能省不少时间。4.1 验证集 mAP 很高,上线全漏检现象:本地验证 mAP 到 0.85,拿到产线测试漏检严重。原因:训练集和验证集来自同一批次,图像高度相似,模型记住了背景而不是缺陷。解决:按批次分组切分,或者用感知哈希去重后再切,确保验证集里的背景和训练集有差异。判断方法很简单,看验证集图像的采集时间和训练集是否重叠。4.2 训练 loss 不降,一直震荡现象:loss 在前 20 轮上下横跳,不收敛。原因:学习率设太大,或者标注里有大量越界框导致梯度异常。解决:先把学习率降到 0.001 试,同时跑一遍标注校验脚本,把宽高小于 0.001 的框和超出 0-1 范围的框清掉。工业数据集标注质量参差,这一步不能省。4.3 小目标缺陷几乎检不出现象:大块夹杂能检,细划痕和裂纹召回极低。原因:输入尺寸下采样后细目标只剩几个像素,特征被吞掉。解决:把imgsz提到 1024,或者在模型里加 P2 小目标检测层。代价是显存和推理时间上升,产线节拍紧的话要权衡。另一个办法是切图推理,把大图裁成小块分别检测再合并。4.4 类别不均衡导致模型只认多数类现象:划痕和夹杂检得挺好,结疤和裂纹基本不报。原因:这两类样本占比太低,损失被多数类主导。解决:在损失里给稀有类加权,或者对稀有类做过采样和针对性增强(旋转、对比度扰动)。别指望靠加轮数解决,轮数越多模型越偏向多数类。4.5 换一批产线图像指标断崖下跌现象:在数据集上训得好好的,换一条产线的图直接崩。原因:不同产线的光照、相机参数、钢种表面反射率差异大,模型过拟合了原数据集的成像风格。解决:做域增强,训练时随机调亮度、对比度、加噪声,模拟不同成像条件;有条件的话在新产线上标几百张做微调。这一步是工业视觉落地的必经之路,没有哪个模型能零样本跨产线。5. 把1800张图用出更大价值:增强策略与迁移验证数据集规模固定,但你能通过增强和迁移策略把它的价值放大。这一章讲两个具体技巧,一个是怎么做工业场景下真正有效的增强,一个是怎么验证模型换产线后还稳不稳。5.1 工业缺陷场景下真正有效的增强组合通用增强里,翻转、旋转、缩放对钢铁缺陷基本安全,因为缺陷方向本身随机。但有两类增强要慎用:一是大角度透视变换,会让细划痕几何形变失真,模型学到错误的形状先验;二是强色彩抖动,钢铁表面颜色信息对区分氧化铁皮压入和背景很关键,抖过头反而丢特征。我一般用这组配置:增强类型参数范围说明水平翻转概率 0.5缺陷方向无强先验,安全随机旋转±15 度角度太大细目标会失真亮度扰动±20%模拟产线光照波动对比度扰动±15%增强低对比度缺陷的鲁棒性高斯噪声标准差 0.01模拟相机噪声马赛克增强概率 0.3提升小目标,但别开太高马赛克增强对小目标有帮助,但它会把四张图拼一起,工业缺陷的上下文被破坏,开太高模型会学到不真实的场景。0.3 左右是个平衡点。5.2 用留出批次做跨域验证想提前知道模型换产线会不会崩,最土但最有效的办法是留出一个完整批次不参与训练,专门当“模拟新产线”测试集。具体做法:切分时不要随机,而是按批次留出最后 10% 的批次,训练时完全不碰,训完直接在这个留出集上跑指标。如果留出集指标比验证集低超过 15 个百分点,说明模型对成像风格过拟合,得加强域增强或准备微调数据。# 在留出批次上评估,观察跨域性能差距 from ultralytics import YOLO model = YOLO("./runs/detect/train/weights/best.pt") metrics = model.val(data="steel_holdout.yaml", imgsz=640, batch=8) print("留出批次 mAP50:", metrics.box.map50) print("留出批次 mAP50-95:", metrics.box.map)steel_holdout.yaml里val指向留出批次目录,其余配置和训练时一致。跑完对比训练时验证集的 mAP,差距就是你的跨域风险敞口。这个数字比任何离线指标都更能说明模型能不能上产线。5.3 一个我常犯的错我早期做这类项目,总想着把 mAP 刷到最高再交付,结果在留出批次上一测就露馅。后来养成习惯:每训完一版,先看留出批次指标,再看验证集指标,两者差距大的版本直接不进入下一轮。数据集的 1800 张图是起点不是终点,真正决定成败的是你有没有用留出批次给自己留一颗后悔药。希望帮到你。本文还有配套的精品资源,点击获取