资讯动态

YOLOv5钢材缺陷检测:权重+双格式数据集,复现全流程避坑指南

发布时间:2026/10/14 9:41:04 来源:尧图企业网站定制
简介面向钢材表面缺陷检测场景的YOLOv5完整方案覆盖多种缺陷类型检测模型已经训练好并附带精确率-召回率曲线、损失曲线等训练过程可视化结果可直观评估模型表现。数据集图片为jpg格式标签同时提供XML与TXT两种格式分别存放在两个文件夹中便于根据不同框架灵活加载。包内共164个文件主要包含Python脚本、yaml配置、pt权重、xml标签和jpg图像还有训练日志、results.csv、Dockerfile、ipynb示例等辅助内容压缩包整体约106.77MB目录结构清晰可快速定位所需模块。资源包中同时提供检测结果参考便于对照理解模型输出。目前已有2276人学习下载。这套资源适合工业质检项目开发者和目标检测学习者拿到后既能加载权重快速推理也能利用已标注数据继续训练或调参配合可视化曲线做效果分析免去采集与标注的大量重复劳动。1. YOLOv5钢材缺陷检测一份权重加双格式数据集能省下两周踩坑时间拿到一份训练好的YOLOv5钢材缺陷检测权重很多人第一反应是直接跑推理结果一回头就在数据集格式上栽跟头。这份资源把整个闭环打包了训练好的检测权重、用labelImg标注好的钢材缺陷数据集jpq图片xml与txt两种格式标签分别存放在两个文件夹以及训练过程留下的PR曲线、loss曲线和results.csv记录。它解决的不只是“有没有模型可用”的问题更关键的是让你能直接复现一条从标注数据、转换格式、训练到评估的完整链路省掉自己攒数据、对类别顺序的折腾时间。适合两种人一是正在做钢材表面缺陷检测、需要一份可复现基线的人二是拿YOLOv5做课程设计或毕业设计的开发者用这份资源把数据、权重、曲线一次看明白。接下来按“数据怎么组织的、权重怎么跑起来、如何复训、坑在哪”的顺序拆给你看。2. 先理解数据再动手xml与txt双标签格式的对应关系与类别核对2.1 钢材表面缺陷的检测难点与单阶段选型理由钢材表面缺陷常见的有裂纹、夹杂、麻点、划痕、氧化皮压入等几类。这类缺陷的共同特点是目标偏小、形状细长或不规则、与背景纹理区分度低光照一变特征就飘。两阶段检测器精度上限确实高但产线质检要求每秒处理多帧速度扛不住。YOLOv5作为单阶段检测器anchor机制对小目标友好6.0版本生态成熟、复现成本低所以这份资源用它做基线是合理选择。YOLOv5 6.0的代码结构里train.py、detect.py、val.py职责分离数据集靠yaml驱动训练日志自动落到results.csvTensorBoard事件存成events.out.tfevents文件。这些文件在这次资源里都能看到它们不是摆设是判断训练是否正常的第一手材料。后面第4章会教你怎么从这些文件里读出有效信息。2.2 双标签数据的组织方式同名文件、两种坐标体系资源里的数据集图片是jpg标签用labelImg标注过同时保留了xml和txt两种格式分别存在两个文件夹。labelImg支持两种导出PascalVOC格式的xml记录的是绝对像素坐标YOLO格式的txt记录的是归一化中心坐标。两者以文件名为关联键比如同一张图0001.jpg对应labels_xml/0001.xml和labels_txt/0001.txt文件名完全一致只是后缀不同。两种格式的核心差异如下项目xml (PascalVOC)txt (YOLO)坐标体系xmin,ymin,xmax,ymax 绝对像素值class cx cy w h 归一化类别表达object/name 里的文本每行第一列的整数id是否依赖图片尺寸不依赖必须用图片宽高做归一化关键校验点txt里的类别id是整数它对应的是labelImg预设类别文件里的行号。如果预设类别文件里第0行是crazing第1行是inclusion那么txt里出现0就代表crazing出现1代表inclusion。这个“行号即类别id”的规则是后面所有坑的源头。下面这段代码读取同一个样本的xml和txt把txt的归一化坐标还原成像素坐标跟xml的bndbox对一对确认两个标签文件是不是指同一个框import xml.etree.ElementTree as ET with open(labels_txt/0001.txt, r) as f: line f.readline() cid, cx, cy, bw, bh map(float, line.split()) # 解析 xml拿到图片原始宽高和第一个目标框 tree ET.parse(labels_xml/0001.xml) root tree.getroot() w int(root.find(size/width).text) h int(root.find(size/height).text) b root.find(object).find(bndbox) print(txt 还原的中心点:, round(cx * w, 1), round(cy * h, 1)) print(txt 还原的宽高:, round(bw * w, 1), round(bh * h, 1)) print(xml 记录的 bndbox:, b.find(xmin).text, b.find(ymin).text, b.find(xmax).text, b.find(ymax).text)这段代码的逻辑是先读txt第一行把五个字段拆开再从xml的size节点取图片宽高最后把归一化坐标乘回去。如果还原出来的中心和宽高与xml的bndbox对不上说明其中一套标签做转换的时候除错了尺寸或者两个文件根本不是同一张图。我一般会把数据集里随机抽十几张都跑一遍全对才继续这步值得花十分钟。2.3 类别顺序核验统计txt中class id的分布另一个高频翻车点在类别顺序。很多人从网上找数据集时txt已经生成好了类别id是数字但你不知道这些数字对应什么缺陷名。如果直接按自己猜的顺序写dataset.yaml训练出来的模型mAP大概率是0还找不到原因。统计所有txt里类别id的出现次数是最快的核验方式import glob from collections import Counter counter Counter() for path in glob.glob(labels_txt/*.txt): with open(path, r) as f: for line in f: if line.strip(): counter[int(line.split()[0])] 1 for cid in sorted(counter): print(fclass {cid}: {counter[cid]} 个目标)这个脚本只做一件事遍历所有txt按第一列的类别id计数。跑完你就能看到数据集里实际存在哪几个类别id、分布是否均匀。如果出现id是3但你的yaml里只有3个类别说明names定义少了如果id分布和资源说明里的缺陷类型对不上说明类别文件顺序和你理解的不一致。先把这个搞清楚后面训练才不会白跑。3. 把权重跑起来的推理流程detect.py参数、输出目录与批量处理3.1 环境依赖6.0版本要求与显卡显存边界YOLOv5 6.0要求Python 3.8及以上PyTorch建议用1.8到1.12这个区间。CPU也能跑推理但一张640分辨率的图大概要两三秒批量跑会等到怀疑人生有NVIDIA显卡就装对应CUDA版本的PyTorch显存6G以上跑推理毫无压力。如果用的是新显卡、新驱动PyTorch版本装得太老可能识别不了显卡装得太新又可能和代码里的一些API不兼容这一步卡住的人不少。拿到压缩包后先看里面的requirements.txt按里面的版本安装依赖。装完用一条命令确认环境没问题python -c import torch; print(torch.cuda.is_available())返回True再继续。常见错误是torch装成了CPU版cuda.is_available()永远False推理速度直接拉胯。3.2 单张图片推理detect.py核心参数逐个拆解推理入口是detect.py最基础的用法python detect.py --weights best.pt --source images/0001.jpg \ --conf-thres 0.25 --iou-thres 0.45 --img-size 640 \ --save-txt --save-conf --project runs/detect --name steel_infer参数含义对照参数取值示例作用--weightsbest.pt指定训练好的权重文件--source图片/目录/视频路径推理输入单个文件或整个文件夹都行--conf-thres0.25置信度阈值低于此值的框被丢弃--iou-thres0.45NMS的IoU阈值越大越容易合并重叠框--img-size640推理分辨率越大对小目标越友好--save-txt无参数同时输出txt格式坐标文件--save-conf无参数在txt每行末尾附上置信度--project/--nameruns/detect/steel_infer自定义输出目录逻辑说明--save-txt和--save-conf是配套的只加--save-txt不加--save-conf输出txt里每行只有类别id和框坐标加上--save-conf后每行多一个置信度字段便于后续做阈值筛选。--img-size决定输入网络的缩放尺寸如果缺陷很小建议提到1280代价是推理变慢。第一次跑通看到标注框和置信度说明权重文件本身没问题可以进入下一步。3.3 批量推理与输出目录管理exp自动叠加的处理detect.py有个特点每次运行默认在runs/detect下生成exp目录第二次跑生成exp2第三次exp3以此类推。这不是bug是为了防止覆盖上一次的结果。但很多人第一次跑完第二次找结果时发现目录变了以为输出丢了。处理方式是用--project和--name把输出固定在同一个目录再配合--exist-ok覆盖旧结果python detect.py --weights best.pt --source test_images/ \ --conf-thres 0.3 --save-txt --save-conf \ --project runs/detect --name steel_batch --exist-ok批量推理时--source直接给文件夹路径detect.py会自动遍历里面的jpg。输出目录里能看到两类东西每张图对应的标注结果图以及labels目录下同名的txt文件。txt每行格式是“类别id cx cy w h 置信度”跟训练标签格式一致。这一步跑通后你就有了一个能直接对接业务侧的检测脚本雏形。4. 重新训练一份自己的权重数据划分、VOC转YOLO与曲线解读4.1 数据目录重组图片与txt按train/val划分如果想在现有数据集上重新训练或把这份资源当基线去扩充自己的数据第一步是把数据集整理成YOLOv5能直接读的结构。YOLOv5训练时要求图片和txt标签分开统一放在images和labels两个根目录下各自再分train和val子目录。mkdir -p dataset/images/train dataset/images/val mkdir -p dataset/labels/train dataset/labels/val划分代码用Python写带随机种子保证可复现import os, random imgs [f for f in os.listdir(images) if f.endswith(.jpg)] random.seed(42) random.shuffle(imgs) split int(len(imgs) * 0.8) for i, name in enumerate(imgs): sub train if i split else val base os.path.splitext(name)[0] os.rename(fimages/{name}, fdataset/images/{sub}/{name}) os.rename(flabels_txt/{base}.txt, fdataset/labels/{sub}/{base}.txt)逻辑说明先把所有jpg列出来并打乱按8:2切分然后把图片和同名txt搬进对应目录。这里用的是os.rename如果图片和标签本来就分散在多个目录改用shutil.copy更稳妥。seed固定在42这样每次跑出来的划分结果一致方便对比实验。搬完后检查一下dataset/labels/train里的txt数量和dataset/images/train里的jpg数量是否一致数量对不上说明有图片没标签或有标签没图片得先补齐再训练。4.2 VOC转YOLO格式xml到txt的转换脚本与校验资源里同时给了xml和txt直接用txt文件夹即可。但如果你拿到的是只有xml标签的其他数据集或者想把这份资源的xml用在自己项目里就得自己做转换。转换脚本关键点是归一化和类别id映射import xml.etree.ElementTree as ET import glob, os class_names [crazing, inclusion, patches, pitted_surface, scratches, rolled-in_scale] def convert(xml_path): tree ET.parse(xml_path) root tree.getroot() w int(root.find(size/width).text) h int(root.find(size/height).text) lines [] for obj in root.findall(object): # 类别文本必须能在 class_names 里找到找不到就跳过并打印 name obj.find(name).text if name not in class_names: print(未知类别:, name, 文件:, xml_path) continue cid class_names.index(name) b obj.find(bndbox) xmin int(b.find(xmin).text) ymin int(b.find(ymin).text) xmax int(b.find(xmax).text) ymax int(b.find(ymax).text) cx (xmin xmax) / 2 / w cy (ymin ymax) / 2 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cid} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) base os.path.splitext(os.path.basename(xml_path))[0] with open(flabels_txt_convert/{base}.txt, w) as f: f.write(\n.join(lines)) for xml in glob.glob(labels_xml/*.xml): convert(xml)这段脚本的要点中心点取(minmax)/2宽高取max-min全部除以图片原始宽高完成归一化class_names的顺序必须和将来yaml里names的顺序完全一致否则训练时类别会错位。类别匹配用界面文本判断遇到没见过的名字直接打印出来比静默跳过更利于排查。转换完回到2.3节的统计脚本核对转换后的txt类别分布是否和预期一致。4.3 训练启动dataset.yaml与关键超参数设置训练前要写一个数据配置文件指向刚划分好的数据集train: dataset/images/train val: dataset/images/val nc: 6 names: [crazing, inclusion, patches, pitted_surface, scratches, rolled-in_scale]nc是类别数names数组的顺序就是类别id的顺序必须和txt里的id一一对应。这里我只是举例实际以你统计脚本跑出来的类别结果为准。训练命令python train.py --data steel.yaml --weights yolov5s.pt \ --img 640 --batch 16 --epochs 100 --workers 8 \ --project runs/train --name steel_v1 --seed 42参数说明--weights指定预训练权重用官方yolov5s.pt做迁移学习能显著加快收敛--img 640是输入分辨率显存吃紧就降到512--batch受显存限制16G显存跑yolov5s基本能带16--epochs先跑100轮看趋势不用一上来就300轮。训练过程中终端会滚动打印每个epoch的box_loss、obj_loss、cls_loss以及precision、recall、mAP_0.5这些值同时追加写入results.csv。4.4 训练曲线解读从results.csv读取loss与PR指标训练完成后results.csv在runs/train/steel_v1/目录里每一行是一个epoch的指标汇总。很多人不看这个文件只看终端滚动日志其实这个csv才是排查训练问题的关键素材。import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(results.csv) # 列名首尾可能有空格先清理 df.columns [c.strip() for c in df.columns] plt.figure(figsize(10, 4)) plt.plot(df[epoch], df[train/box_loss], labeltrain box_loss) plt.plot(df[epoch], df[val/box_loss], labelval box_loss) plt.xlabel(epoch) plt.ylabel(loss) plt.legend() plt.grid(True) plt.savefig(loss_curve.png)这份资源里的results.csv和events.out.tfevents就是干这个用的。train/box_loss和val/box_loss两条曲线应该同步下降并趋于平缓如果train loss降而val loss不降说明过拟合如果val loss后期反弹说明学习率没降下来。PR曲线则在验证时生成好的PR曲线在召回率上升段下降平缓说明置信度排序靠谱。也可以用TensorBoard看交互式曲线tensorboard --logdir runs/train浏览器里打开生成的地址loss、mAP、学习率变化都在一个页面里比盯终端舒服得多。5. 复现避坑记录五条实测踩坑现象、原因与排查思路5.1 mAP全程为0类别id与names顺序错位现象训练跑完全程loss在降但val的mAP_0.5一直是0PR曲线整个贴在坐标轴上。原因txt里的类别数字和yaml里names数组的顺序对不上。比如txt里id2实际是划痕yaml里id2却是裂纹模型学到的类别语义是乱的评估时自然对不上。解决先停训练用2.3节的统计脚本把txt里的类别id全列出来再打开yaml按同一个顺序写names。别凭感觉猜顺序labelImg保存txt时的类别行号才是一切基准。5.2 检测框整体偏移归一化坐标换算错误现象训练能正常收敛loss也低但推理画出来的框位置明显偏框中心落在缺陷旁边甚至有的框跑到图像边缘外。原因xml转txt时没有除以图片宽高或者除反了把x坐标除了h、y坐标除了w。还有一种是转换时用了不配套的图片尺寸xml里写的是1024实际训练图被resize到640坐标就跟着错位。解决用2.2节那段回算代码把txt坐标还原成像素值跟xml bndbox对一遍。凡是转换脚本输出前强制加一行校验还原值超出图像范围就打印告警。图像长宽不一致的数据集必须逐张读取xml里的size不能所有图共用一个宽高。5.3 loss震荡不收敛学习率、batch与标注噪声现象前几十个epoch的loss曲线像锯齿一样剧烈上下跳val的mAP忽高忽低要么涨不上去要么涨上去又掉下来。原因最常见是学习率太高或batch太小导致梯度噪声大。此外如果数据集里存在明显误标的目标框模型会被带偏。解决把初始学习率降到0.01batch尽量提到显存能承受的最大值先只训50轮观察曲线是否平滑下降。同时抽样看几十张标注图重点看有没有框明显框错位置、类别标错。标注数据集的“脏”程度直接影响你能收敛到什么水平和模型结构关系不大。5.4 推理输出找不到runs/detect目录自动叠加现象第一次detect生成的结果在runs/detect/exp里第二次跑完发现exp里是新的结果旧结果找不到了或者生成了一堆exp2、exp3、exp4不知道哪个对应哪次实验。原因detect.py默认会在同名输出目录下自动递增序号这是防覆盖设计但批量操作时很容易看混。解决每次推理都用--project和--name固定输出路径加上--exist-ok控制覆盖行为。习惯做法是命名带上数据特征比如steel_defect_val_v1这样日志和结果对得上回看的时候不用猜。5.5 中文路径导致读图失败cv2静默返回None现象代码和数据集都在Windows下路径含中文或空格推理和训练时图片一张都读不出来程序却没有任何报错loss和mAP全乱。原因OpenCV的imread对非ASCII路径不友好读不到图时不抛异常直接返回None下游代码拿到None继续算数值就全错了。解决数据集路径全部改成纯英文用户目录下常见的“桌面”文件夹就经常是坑。项目目录放在盘符根目录下路径里的空格也一并处理掉。资源里文件夹名本身是英文直接解压到纯英文路径下最省事。6. 权重复验技巧先跑val.py再改阈值用confusion matrix定位bad case拿到任何一份别人训练好的权重我的习惯是先用val.py在测试集上跑一遍标准评估而不是直接拿demo图瞎试。demo图太主观一张图画得好不代表模型整体质量高只有指标和混淆矩阵才是客观的。python val.py --weights best.pt --data steel.yaml \ --img 640 --task val --conf-thres 0.001 --iou-thres 0.6注意这里--conf-thres要设成0.001不是推理时的0.25。评估逻辑是尽可能把所有候选框都算进去画出完整PR曲线阈值设高了PR曲线的左段会缺失算出来的mAP会虚高或失真。跑完runs/val目录下会生成PR_curve.png、confusion_matrix.png和一堆指标文件。先看mAP_0.5和mAP_0.5:0.95两个数前者反映粗粒度定位精度后者反映框和GT的重合质量再看confusion_matrix.png矩阵对角线的值越高越好。真正值钱的是矩阵里非对角线的高亮块比如“划痕被识别成裂纹”这类系统性混淆说明这两个类别特征太接近单靠网络结构解决不了得去检查那些样本的标注质量是不是类别标反了。验证完指标再回到推理环节调业务阈值。检测场景里缺陷被漏掉的代价往往大于误报所以部署时--conf-thres一般从0.25降到0.15或0.1同时用更高分辨率的--img-size把细小裂纹找出来但如果后续有人工复核环节阈值就可以抬高换精度。改完阈值后用第3章的批量推理脚本在真实采集图上跑一遍人工抽查几十张确认误检是否在可接受范围内。从那以后我每次拿到别人的权重都不会直接接进产线或演示脚本而是强制先跑一遍val.py再挑几张最难分辨的缺陷图看推理结果。这个习惯至少帮我堵掉了三次数据集错位事故也省掉了二次返工的麻烦。希望这份资源和这套验证流程能帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑