铝片表面缺陷检测:YOLOV5数据集格式与训练实战

📅 发布时间:2026/9/23 12:35:21
铝片表面缺陷检测:YOLOV5数据集格式与训练实战
简介一套面向工业质检场景的铝片表面缺陷目标检测数据集按 YOLOv5 标准目录结构组织适合计算机视觉初学者或工程师直接用于模型训练与验证。数据包含针孔、擦伤、脏污、褶皱四类常见缺陷图像分辨率为640×480共 1120 张训练图像与 280 张验证图像配套 txt 格式标注及类别说明文件。资源总文件数约 2000 个以 txt 标签、jpg 图像和可视化 py 脚本为主压缩包约 82MB便于快速下载解压。自带的可视化脚本可随机读取图片并绘制边界框省去手工标注检查的麻烦。目前已有 195 人学习下载适合需要标准格式缺陷检测数据来调试 YOLOv5 流程、验证数据增强或迁移学习效果的用户。1. 铝片表面缺陷检测一个 YOLOV5 目录格式数据集能解决的问题做铝片缺陷检测这件事最常见的落地场景是电池铝壳、易拉罐、手机中框和散热片产线。人工目检在高速产线上漏检率不稳定而换成目标检测模型后一条皮带线配上工控机和工业相机就能把划痕、凹坑这类缺陷实时框出来。本文围绕的就是这样一个东西一份按 YOLOV5 目录格式组织好的铝片表面缺陷目标检测数据集4 个缺陷类别带训练集和验证集拿到手就能直接开训。适合正在做工业视觉项目选型、想把 YOLOV5 训练自己的数据集跑通的工程师也适合需要一份干净工业数据集做算法验证的研究者。看完你会明白目录格式为什么重要、标注该怎么做、训练参数怎么调以及哪些坑不值得再踩一遍。2. YOLOV5 的目录格式images、labels 和 4 类缺陷的定义2.1 为什么 YOLOV5 对目录结构这么敏感先用一句话说透YOLOV5 训练时不是靠数据库管理图片和标注而是靠“同一个文件名前缀”在images和labels两个目录里互相查找。图片叫alum_0001.jpg对应的标签文件就必须叫alum_0001.txt放在labels目录的对应子目录下。这个约定一旦破坏训练过程不会直接报错而是静默跳过缺失样本最后表现为 mAP 异常低、训练集样本数对不上。工业数据集和公开数据集最大的差别在于公开数据集已经按规范整理好工业数据往往散落在产线测试目录、微信传输记录、现场电脑桌面上。图片命名可能是IMG_20240315_093211.jpg也可能是相机自动生成的frame_00042.bmp标注人员把标注文件导出后文件名大小写不一致、后缀不统一都是常态。所以数据集准备的第一步不是训练而是把文件名规则统一掉。我给 YOLOV5 系列数据集整理目录时长期使用的结构是官方推荐的那种扁平式布局datasets/ └── aluminum_defect/ ├── images/ │ ├── train/ │ │ ├── alum_0001.jpg │ │ └── ... │ └── val/ │ ├── alum_0501.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── alum_0001.txt │ │ └── ... │ └── val/ │ ├── alum_0501.txt │ └── ... └── data.yaml注意images/train和labels/train一一对应images/val和labels/val一一对应。常见做法是先建目录再批量拷文件用一条命令就能搭出来mkdir -p datasets/aluminum_defect/{images/{train,val},labels/{train,val}}2.2 4 类缺陷怎么定类别顺序就是标签编号这个数据集包含 4 个类别常见定义是划痕scratch、凹坑dent、污渍contamination、氧化斑oxide。类别顺序在 YOLOV5 里就是标签文件的第一个数字0代表names[0]1代表names[1]以此类推。所以类别定义一旦确定整个数据集都不该再改顺序否则所有标签文件都要重写。我见过不止一个项目在标注中途加了类别导致前 1000 张图片的标签和后面 800 张的编号错位训练出的模型把划痕认成凹坑。所以建目录的同时就要把data.yaml写好固定类别清单# data.yaml train: datasets/aluminum_defect/images/train val: datasets/aluminum_defect/images/val nc: 4 names: [scratch, dent, contamination, oxide]train和val在这里指向图片目录YOLOV5 会根据这个路径自动推导标签目录把images替换为labels。所以不要在 yaml 里写labels路径除非你确定自己在做自定义解析。2.3 检查目录对应关系的小脚本整理完数据后我会习惯跑一遍对应关系检查避免出现“图片在但标签缺”“标签在但图片缺”的情况。YOLOV5 对这种不匹配容忍度很高它不会因此中断训练只会悄悄减少样本量for f in labels/train/*.txt; do basename ${f%.txt}; done | sort /tmp/label_ids.txt for f in images/train/*.jpg; do basename ${f%.jpg}; done | sort /tmp/img_ids.txt diff /tmp/label_ids.txt /tmp/img_ids.txt逻辑说明分别提取labels/train和images/train下的文件名前缀排序后做差集。diff没有任何输出就说明两边完全对齐有输出就是有孤儿文件。basename和sort是这里的关键手法前者去掉路径和后缀后者保证两个列表的对比顺序一致。这个检查我通常在每次训练前跑一遍比训练完再回头查数据靠谱得多。图片后缀可能是.bmp或.png把上面命令里的*.jpg换成对应后缀即可。3. 从标注到 YOLO 标签标注工具选型与 xml 转 txt 脚本3.1 标注工具怎么选铝片缺陷的标注核心诉求有两个一是支持矩形框二是能导出 VOC 格式或 YOLO 格式的标签。市面上主流的目标检测常用标注工具里LabelImg 是经典选择支持矩形框能直接输出 YOLO 格式X-anylabeling 是更现代的替代品支持自动标注辅助Labelme 适合多边形标注但对矩形框场景来说有点重。我更推荐的做法是标注时统一输出 VOC 格式的 XML 文件因为有 XML 在手后续转成 YOLO、COCO 都很方便。同理如果标注工具直接支持导出 YOLO txt那可以直接生成但注意不同工具导出的 txt 中类别索引可能从 0 或 1 开始要把classes.txt的排序和 YOLOV5 的names对齐。3.2 xml 格式的标签文件细节一张带标注的图片对应一个同名.xml文件核心结构如下annotation filenamealum_0001.jpg/filename size width1280/width height1024/height depth3/depth /size object namescratch/name bndbox xmin156/xmin ymin208/ymin xmax624/xmax ymax315/ymax /bndbox /object /annotationsize下的宽高是转换脚本的关键参数因为 YOLO 格式需要归一化坐标必须知道图片原始尺寸。如果图片被标注工具压缩过size记录的是原图尺寸转换脚本就不该拿实际图片尺寸去算必须读 XML 里的值否则坐标全部偏移。3.3 转换脚本从 xml 到 txt下面是把 VOC 格式 XML 转成 YOLOV5 标签 txt 的脚本单个文件即用型import os import xml.etree.ElementTree as ET def convert_annotation(xml_path, output_dir, class_names): tree ET.parse(xml_path) root tree.getroot() size root.find(size) width int(size.find(width).text) height int(size.find(height).text) filename root.find(filename).text out_name os.path.splitext(os.path.basename(filename))[0] .txt lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue class_id class_names.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 防御性检查跳过宽高为 0 的异常框 if xmax xmin or ymax ymin: print(fskip invalid box: {xml_path} - {name}) continue x_center (xmin xmax) / 2.0 / width y_center (ymin ymax) / 2.0 / height box_w (xmax - xmin) / width box_h (ymax - ymin) / height lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) with open(os.path.join(output_dir, out_name), w) as f: f.write(\n.join(lines)) class_names [scratch, dent, contamination, oxide] xml_dir annotations/ out_dir labels/train/ os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if xml_file.endswith(.xml): convert_annotation(os.path.join(xml_dir, xml_file), out_dir, class_names)逻辑说明脚本先解析 XML读图片宽高然后遍历所有object节点。坐标从左上右下转为中心点加宽高的归一化形式这是 YOLO 格式的标准。class_names.index(name)这句把类别字符串映射成数字编号顺序和data.yaml中的names必须完全一致。参数说明class_names是唯一的“全局配置”改动它等于改全数据集的语义xml_dir和out_dir分别指向标注目录和输出目录。输出精度保留 6 位小数足够工业相机的像素误差远大于这个量级。脚本里的防御性检查很重要标注时手滑把框拖成反方向的在 VOC 里合法在 YOLO 里会产生负数宽高不查的话后面训练会出现难以追踪的 loss 异常。3.4 标注质量的两个基础检查转换完成后立刻做一次抽样可视化把 YOLO 标签画回图片上。这一步能发现坐标偏移、类别错位、框过大过小等问题。YOLOV5 仓库里自带detect.py有可视化能力但更快捷的是用 OpenCV 画一个简单检查脚本import cv2 def draw_yolo_label(image_path, label_path, class_names): img cv2.imread(image_path) h, w img.shape[:2] with open(label_path) as f: for line in f: parts line.strip().split() if len(parts) 5: continue cls, x_c, y_c, bw, bh parts x_c, y_c, bw, bh map(float, (x_c, y_c, bw, bh)) x1 int((x_c - bw / 2) * w) y1 int((y_c - bh / 2) * h) x2 int((x_c bw / 2) * w) y2 int((y_c bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names[int(cls)], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) return img img draw_yolo_label(images/train/alum_0001.jpg, labels/train/alum_0001.txt, [scratch, dent, contamination, oxide]) cv2.imwrite(check_alum_0001.jpg, img)这段脚本把归一化中心点坐标换算回像素坐标再画矩形框和类别名。随机挑选 20 到 30 张图人工过一遍能把大多数标注问题挡在训练之前。4. 用 YOLOV5 训练铝片缺陷数据集data.yaml、超参数与最小命令4.1 训练前的环境与硬件判断训练 YOLOV5 前先想清楚你的硬件上限。铝片缺陷检测的照片分辨率通常在 1280 以上但训练时--img参数一般设为 640因为工业场景更关注推理速度缩到 640 训练也能让小缺陷保持基本形状。如果显存只有 6Gbatch size 设为 8 到 16 之间图片尺寸降到 480 也能跑如果用的是 3090 或 A5000 这类 24G 显存卡batch size 上到 32 问题不大。环境配置按 YOLOV5 官方 README 的方式即可核心是 Python 3.8 以上、PyTorch 1.8 以上、CUDA 对应版本。用pip install -r requirements.txt装依赖其中opencv-python、numpy、matplotlib是必须项。4.2 data.yaml 和基础训练命令数据集的 4 个类别固定后训练命令可以保持统一。以下是我在一个铝片缺陷项目上跑通的基础命令python train.py \ --data datasets/aluminum_defect/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --cache ram \ --patience 20参数说明--weights yolov5s.pt加载 COCO 预训练权重做迁移学习这是缺陷检测任务里性价比最高的做法几百张工业图片从头训练基本收敛不到可用精度--cache ram把图片一次性加载到内存减少磁盘 IO--patience 20表示 20 个 epoch 内验证集指标没有提升则提前停止训练。yolov5s.pt的下载是自动的train.py 会在第一次运行时从官方 Release 拉取。如果服务器无法访问外网需要手动下载后放到 YOLOV5 项目根目录train.py 检测到本地文件就不会重复下载。4.3 小数据集下的超参数调整策略铝片缺陷数据集如果总量在 1000 张以内微调超参数的优先级应该从高到低排列。第一是--batch工业缺陷图往往有明显的光照差异batch 太小会导致 BN 层统计量抖动第二是学习率YOLOV5 默认lr00.01小数据集上常见训练初期 loss 震荡甚至炸掉把--lr0调到 0.005 或 0.003 能明显稳下来第三是--hyp参数文件里的mosaic默认开启但当缺陷本身是细长划痕时马赛克增强会把缺陷裁切得面目全非这时需要调低增强强度。强化缺陷对比度是另一个常见做法。铝片表面反光强缺陷与背景的对比度不稳定可以在超参数文件里把hsv_h、hsv_s、hsv_v的扰动范围适当放开让模型更适应光照变化。以下是一个适用于工业缺陷的 hyp 片段# hyp.industrial.yaml lr0: 0.003 lrf: 0.12 momentum: 0.937 weight_decay: 0.0005 warmup_epochs: 3 mosaic: 0.8 mixup: 0.1 hsv_h: 0.02 hsv_s: 0.6 hsv_v: 0.5使用该配置时训练命令末尾加上--hyp hyp.industrial.yaml。mosaic从默认 1.0 降到 0.8保留大部分增强效果但减少细长缺陷被切碎的概率hsv_s和hsv_v的扰动幅度加大模拟产线不同时段的光照波动。4.4 验证集数量与划分原则标题明确包含训练集、验证集但验证集怎么分很有讲究。缺陷检测数据集的划分原则是“按缺陷类型分层抽样”而不是纯随机划分。如果 4 个类别里scratch占了 60%纯随机划分会让验证集里dent的样本只有个位数产生一个 mAP 虚高的假象。我一般会写一个分层抽样脚本保证验证集里每个类别至少出现一二十次import os import random import shutil random.seed(42) def split_by_class(source_txt_dir, img_src_dir, train_img, train_lbl, val_img, val_lbl, val_ratio0.2): for d in [train_img, train_lbl, val_img, val_lbl]: os.makedirs(d, exist_okTrue) class_files {} for f in os.listdir(source_txt_dir): if not f.endswith(.txt): continue with open(os.path.join(source_txt_dir, f)) as fp: first_line fp.readline().strip() if first_line : continue cls first_line.split()[0] class_files.setdefault(cls, []).append(f) for cls, files in class_files.items(): random.shuffle(files) val_count max(1, int(len(files) * val_ratio)) val_set set(files[:val_count]) for f in files: stem os.path.splitext(f)[0] src_img os.path.join(img_src_dir, stem .jpg) if f in val_set: shutil.copy(os.path.join(source_txt_dir, f), val_lbl) shutil.copy(src_img, val_img) else: shutil.copy(os.path.join(source_txt_dir, f), train_lbl) shutil.copy(src_img, train_img) split_by_class(annotations_txt/, images/, images/train, labels/train, images/val, labels/val)逻辑说明先按标签首行的类别编号把文件分组再在每个类别内部做随机划分。这样每个类别在验证集中都有独立比例的存在不会因为样本总量失衡而缺席验证。val_ratio0.2表示每个类别取 20% 放入验证集max(1, ...)保证样本极少的类别也能留出至少 1 个验证样本。参数说明random.seed(42)固定随机种子保证每次划分结果一致。如果标注数据里有空标签文件first_line 的判断会跳过它避免把无缺陷图片的错误信息带入统计。划分后用上一章的 diff 命令再验一遍确认两边文件数一致。5. 训练铝片缺陷模型的避坑清单5 个高频翻车点与排查5.1 标签偏移但训练无报错现象loss 正常下降验证集 mAP 也有数值但检测框整体比真实缺陷位置偏左或偏上且偏移量固定。原因标注工具导出 XML 后图片被批处理压缩或裁剪过XML 里的size还是原图尺寸而转换脚本按 XML 大小归一化训练时读到的图片尺寸与标注基准不一致。解决用标注工具重新导出或确保 XML 中的size与实际图片一致。排查时随机抽取 10 张“标签画回图”人工核对偏移是系统性的看几张就能发现。5.2 细长划痕被裁成碎片导致小目标漏检现象验证集上scratch类别的 recall 明显低于其他类别分析发现漏检的都是长条划痕。原因YOLOV5 默认的 mosaic 增强把图片缩放到目标尺寸后随机裁剪拼接一条跨越大半张图的划痕可能被切成几段每段在增强图中只剩几个像素宽成了不折不扣的小目标检测难题。解决调低mosaic权重甚至可以设为 0 测试一个纯基础训练的基线再逐步加回同时给scratch单独检查标注框有没有被切断。划痕这类缺陷如果标注框是紧贴缺陷的细长矩形建议适当外扩几个像素给模型更充分的上下文。5.3 验证集 mAP 高但现场误检多现象离线验证 mAP0.5 到 0.95 都很好部署到产线后把铝片边缘的倒影、反光误判成缺陷。原因训练集和验证集拍摄环境单一光源角度固定模型学到了“光照变化 缺陷”的虚假相关性。这是工业视觉部署最经典的翻车方式。解决在数据集中加入不同光照角度、不同批次铝片表面状态的照片如果现场只有固定光源则调整打光方式减少反光。模型层面的补救是调高--conf-thres推理阈值但根本上解决还是要扩充数据多样性。5.4 loss 正常但 mAP 一直为 0现象训练结束后验证集 mAP 始终是 0但 loss 曲线正常下降。原因最常见的不是训练问题是类别索引错位。data.yaml里的names顺序与标签文件里的第一个数字不一致。例如标注脚本生成的class_names [dent, scratch, ...]而 data.yaml 里dent是 1 号导致同一张图标签语义错位。解决写一个快速检查脚本遍历验证集标签统计每个类别编号出现次数然后和data.yaml的nc、names比对awk {print $1} labels/val/*.txt | sort | uniq -c输出应该是 0 到 3 四个数字数量级合理如果出现大于等于 4 的编号或者最前面的编号不是从 0 开始优先检查转换脚本的class_names顺序。5.5 训练时内存溢出或显存不足现象开启--cache ram后内存被吃满系统卡死或者小显存显卡训练到一半报CUDA out of memory。原因--cache ram会把所有训练图片解码后存入内存工业相机拍的是 500 万像素原图时尤其明显batch size 设置过大则直接撑爆显存。解决大图先离线缩放到 1280 再入数据集不要指望训练时缩放省显存显存不足时把--batch降到 4或开启--rect按长宽比分批内存紧张时把--cache从ram改为disk用磁盘缓存换内存空间。6. 验证一个好模型用混淆矩阵和难例集收敛你的检测效果模型训练完第一步不该是拿去现场跑而是打开runs/train/exp/confusion_matrix.png和results.png。混淆矩阵能直观告诉你哪两个类别在相互误判。铝片缺陷里最常见的混淆是scratch和oxide因为氧化斑在某个生长阶段也会呈现细长条形态特征空间高度重叠。这时靠调参解决不了要么增加这两个类别的样本量要么考虑合并类别把任务从 4 分类改成 3 分类。难例挖掘是我每轮迭代都做的最后一道工序。把验证集里预测置信度在 0.3 到 0.6 之间的检测结果全部保存成图人工过一遍你会看到两类东西一类是标错的标签另一类是真正难分的样本。标错的直接修标注难分样本收集起来单独放进训练集让模型专门吃几轮“易错题”。这个过程比盲目加数据有效得多因为产线数据往往是同一时段集中采集的多样性并不高。最后说一个我踩过多次的教训每轮训练完固定用同一组验证集图片、同一个置信度阈值把检测结果画到图上肉眼过一遍。不要只看 mAP 数字涨了没有有时候数字涨了现场翻车的地方反而更多。标注质量、目录对应关系、类别顺序这些基础工作每次都要检查一遍没有一劳永逸。希望帮到你。本文还有配套的精品资源点击获取