
简介针对钢材表面缺陷检测需求这份工程包基于YOLOv8实现划痕、凹坑、氧化皮、腐蚀等多类缺陷的自动识别与定位适合深度学习初学者、毕业设计及课程设计开发者参考。包内共2000个文件压缩包大小60.81MB其中1800余个txt为标签标注文件187张jpg为钢材表面样本图像5个py脚本承担数据预处理、模型训练与评估3个pt文件提供不同训练阶段的YOLO权重另有yaml配置和环境依赖文件可支撑从数据准备到模型部署的完整流程。系统内置数据增强、优化损失函数与迭代训练策略配合多个训练脚本及灵活参数设置便于针对不同硬件调整方案同时提供详细配置文件和可视化检测结果降低复现门槛。内置多套权重便于对比不同模型的精度与速度目录结构按功能模块划分训练、评估、推理脚本清晰易查。目前已有75人学习下载适合作为工业视觉方向课程设计、毕设项目或YOLOv8实战练手资源。1. 评测集mAP再高产线上照样会被漏检卡住这套钢材缺陷检测系统到底在解决什么把训练脚本跑通、mAP刷到0.9以上只完成了这套基于YOLOv8的钢材表面缺陷检测系统的三分之一。真正让它在现场站住脚的是数据怎么组织、标注怎么转、训练参数怎么设、推理阈值怎么调以及导出之后部署在哪。钢材表面缺陷检测这个任务难的不是模型结构而是缺陷形态差异极大——细长的划伤、成片的麻点、模糊的辊印有的对比度极低有的跟氧化铁皮背景长得几乎一样。你要是只把它当成一个「YOLOv8训练自己的数据集」的demo来做换个数据集就翻车。这篇笔记会从数据准备讲到板端部署把每一步的参数和坑位都摆出来。适合正在做毕业设计、刚接手钢板质检项目的工程师以及准备评估这个方向值不值得投入的技术负责人。2. 数据处理是这套系统最容易被低估的环节把labelme标注转成YOLO训练集YOLOv8的训练代码非常成熟官方仓库拉下来就能跑但钢材缺陷数据集的整理没有任何官方模板能替你省事。缺陷检测这类小样本、强纹理背景的任务数据侧的功夫决定了最终精度的上限模型结构反而不是瓶颈。我见过太多人拿着别人整理好的zip包直接开训最后在推理阶段被误检搞到崩溃问题几乎都出在标注格式不统一、类别分布悬殊、增强策略太激进这三个地方。2.1 先把目录结构和data.yaml定死后面所有训练都依赖它拿到原始图像和标注之后第一步不是写训练脚本而是把数据集目录按YOLO的约定组织好。YOLOv8训练时通过data.yaml里的路径去找图片和对应的txt标签文件它不会智能地帮你扫描整个磁盘。后面所有训练命令都依赖这个文件所以路径写错一个字母整个训练过程都会白跑。我习惯的数据集根目录结构是这样的images和labels两个最外层目录严格区分train和val各自独立。注意不要把val的图片全部塞进train后面选早停和调阈值全靠val集。train: /data/steel/images/train val: /data/steel/images/val nc: 5 names: 0: rolled_in_scale 1: crazing 2: pitted_surface 3: inclusion 4: scratches这里nc是类别数names的索引顺序必须和标签txt里的第一个数字完全对应否则模型学到的东西和你心里想的东西不是一回事。train和val这两行地址我建议直接用绝对路径相对路径在某些环境里解析出来的根目录会不一样排查起来非常费劲。钢材缺陷类别名不要用中文yaml和命令行工具对中文路径的兼容性在不同操作系统上表现不一致统一用英文小写加下划线最稳。2.2 用labelme标注转YOLO格式转换脚本与四个边界点labelme输出的json文件和YOLO要求的txt格式差别很大。json里存的是多边形的顶点坐标而YOLO的标签格式是「类别id 中心点x 中心点y 宽 高」全部归一化到0到1。很多做yolov8处理数据集的教程里会用labelme自带脚本转但那个脚本输出的是COCO格式还得再转一次。我一般直接写一个转换脚本把外接矩形一次性算出来。import json import os from PIL import Image def labelme_to_yolo(json_path, img_dir, out_dir, class_names): with open(json_path, r, encodingutf-8) as f: data json.load(f) img_path os.path.join(img_dir, data[imagePath]) img Image.open(img_path) w, h img.size img_name os.path.splitext(os.path.basename(img_path))[0] out_txt os.path.join(out_dir, img_name .txt) lines [] for shape in data[shapes]: label shape[label] if label not in class_names: # 标注里出现未登记类别时跳过而不是直接报错 continue cls_id class_names.index(label) points shape[points] xs [p[0] for p in points] ys [p[1] for p in points] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) # 归一化到 [0,1]YOLO 训练时不会再帮你做一次 x_center (x_min x_max) / 2.0 / w y_center (y_min y_max) / 2.0 / h box_w (x_max - x_min) / w box_h (y_max - y_min) / h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) with open(out_txt, w, encodingutf-8) as f: f.write(\n.join(lines))这段代码把每个shape的外接矩形转成一行txt核心操作是读json、取points、归一化。四个边界点你一定要注意第一labelme的坐标没有归一化必须除以当前图像的宽和高如果图像在标注后又被resize过坐标就全废了。第二yolo格式里的宽高是中心点加宽高不是左上角加右下角别从其他检测框架转过来时空格顺序写错。第三json里同一个类别名可能拼写不一致比如scratch和scratches同时存在你需要在转换前做一次标签名对齐。第四多边形的外接矩形必然引入部分背景像素这在钢材缺陷检测里是可接受的——细长裂纹如果截得太紧训练时下采样容易把缺陷特征弄丢稍微带点背景反而对召回率友好。2.3 类不平衡与纹理干扰钢材缺陷的增强策略要克制钢材缺陷数据集的类别分布差异通常很夸张。麻点类缺陷一张图上能出现几十个而边裂或夹杂类缺陷可能几十张图才出现一次。直接用原始分布训练模型会把少数类全部预测成背景mAP看着还行一查每一类的召回率就露馅。这种场景下复制粘贴增强copy-paste比单纯过采样有效得多把少数类的目标区域抠出来粘贴到同一批次里的其他钢材图像上能人工制造出更自然的样本分布。增强策略的选择要克制。表里这几项是我在钢材缺陷任务上的经验值。mosaic虽然能提升泛化能力但在细长缺陷上副作用很大建议初值设到0.5以下训练到后期直接关掉。增强类型推荐设置典型风险mosaic0.3~0.5最后15个epoch关闭细长划伤被缩放后像素太稀疏hsv_h/hsv_s0.01 / 0.5钢材灰度图像过度变色会让模型依赖颜色translate/rotate0.1 / 0.0旋转会破坏划伤的连续性copy-paste对少数类单独开贴图边缘痕迹过重会被模型学成特征有两点要特别强调一是钢材表面是强纹理背景氧化铁皮、水渍、辊印都可能被误检成缺陷增强策略里不要加过多的色彩扰动否则模型会开始依赖颜色和光照现场换一条产线光照一变就废掉。二是训练集和验证集的划分固定随机种子同一份数据每次切出来的子集不一样你前两天跑出来能复现的曲线过两天就变了这个坑极其隐蔽。3. YOLOv8训练前的选型与参数从环境搭建到损失曲线数据准备好之后进入训练阶段。这个阶段三个问题最多环境装不上、模型选型凭感觉、训练参数看不懂。我先给一套我在Ubuntu 20.04上反复验证过的搭建流程再讲模型结构上的取舍最后把训练命令里的参数逐个拆开。3.1 搭建最小可运行环境Ubuntu 20.04上CPU版本先验证、GPU再提速环境搭建不必追求最新版本稳定能跑就行。Ubuntu 20.04上我常用的Python版本是3.10用conda建独立环境避免系统Python被装乱。下面这一段照抄基本就能把yolov8环境搭建步骤走通。conda create -n steel python3.10 -y conda activate steel pip install ultralytics # 有NVIDIA显卡时安装GPU版torch注意cuda版本要和驱动匹配 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 验证环境 python -c from ultralytics import YOLO; m YOLO(yolov8n.pt); print(ok)上面最后一行第一次执行会从官方仓库下载yolov8n.pt预训练权重几十MB网络正常都能下。如果GPU版torch装不上CPU版本也能跑通推理和训练只是速度慢好几倍验证数据加载逻辑没问题真正训练还是得想办法找GPU。我在GTX 1660 Ti这类6GB显存的卡上训练过这个任务batch和imgsz必须压下来后面避坑章节会细说。装完之后别急着开训先用一张带缺陷的样例图跑一次predict确认整个链路没有报错再进入参数设计阶段。3.2 结构选型为什么YOLOv8默认头在钢材缺陷上够用以及在哪里值得改进YOLOv8的结构并不复杂主干用CSPDarknet提取多尺度特征颈部用C2f模块做跨尺度融合检测头是Anchor-Free的Decoupled Head在三个尺度上输出预测框。钢材表面缺陷检测这个任务缺陷尺寸跨度大、长宽比极端、部分目标对比度低这些特点YOLOv8的默认设计都能覆盖不需要上来就改网络结构。很多人一上来就把yolov8改进挂在嘴边想在C2f里插注意力模块或者换检测头但改结构之前得先有一条稳定的baseline否则你根本分不清涨点是结构带来的还是数据抖动带来的。模型的体量选择我直接用一张表说明白模型推理速度体积适用场景yolov8n最快最小边缘板卡、实时性要求极高的场合yolov8s快小多数产线检测性价比最高yolov8m中中缺陷极小或背景极度相似时优先考虑yolov8l/x慢大离线检测或GPU服务器钢材缺陷检测项目我一般从yolov8s起步。n模型在细小裂纹上的召回率会明显偏低m模型精度提升有限但速度掉了三分之一。如果你标注数据量不大从s往上调是对的。想看网络结构图不需要翻论文训练前把模型导出成onnx用netron打开每一层的输入输出一目了然这比看官方结构图更贴近你手里这个模型。3.3 训练参数不是玄学从预训练权重微调出一个可用模型参数设计是整个训练阶段最值得花时间的地方。钢材缺陷数据通常只有几百到几千张不能从头训练必须用预训练权重做迁移学习。YOLOv8官方权重下载很简单第一次运行YOLO类时传入yolov8s.pt框架自动拉取到权重缓存目录。COCO预训练模型学到的通用特征对钢材纹理是有帮助的直接热启动能省大量训练时间。yolo train \ modelyolov8s.pt \ data/data/steel/data.yaml \ imgsz640 \ batch16 \ epochs80 \ patience15 \ optimizerAdamW \ lr00.001 \ mosaic0.5 \ cos_lrTrue \ projectruns/steel \ nameexp1 \ cacheTrue这条命令里每个参数都有自己的作用直接改数字会影响训练行为。我把最关键的几个参数含义列出来参数含义我的经验值imgsz输入分辨率640是速度和精度的平衡点640小目标多时试1280但要降batchbatch每轮迭代的图片数6GB显存用816GB显存用16epochs最大训练轮数80~120预训练权重微调够用patience早停轮数val指标不再提升就停15lr0初始学习率0.001AdamW配这个值很稳mosaic马赛克增强概率0.5细长缺陷场景不能开到1.0cos_lr余弦学习率衰减True后期收敛更平滑训练结束后很多人不知道怎么看损失函数曲线。在runs/steel/exp1目录下会生成results.csv里面每一轮的box_loss、cls_loss、dfl_loss以及precision、recall、mAP都记录在案。自己画一条损失曲线来判断训练是否健康比只看终端输出的日志靠谱得多。import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/steel/exp1/results.csv) 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.savefig(steel_loss.png, dpi150)train和val的box_loss曲线会指示过拟合的起点。钢材缺陷数据量少通常训练到30轮附近val loss就开始走平再往后train loss继续下降而val loss反弹那就是过拟合了。早停机制会帮你在合适的位置停住但你还是得时不时打开这张图看看确认模型是在学缺陷特征而不是在背训练集。4. 避坑排查钢材缺陷检测训练中的5个高频拦路问题训练和调参这一路踩坑是常态。我总结五个在钢材缺陷检测里翻车频率最高的问题每一条都按「症状—原因—解决」来写。这些问题单看都不复杂但叠在一起足以让你怀疑人生。4.1 显存溢出GTX 1660 Ti这类6GB卡怎么压下去症状是训练跑了几个step之后就报CUDA out of memory尤其是在imgsz调大或者开了cache之后。根子是6GB显存对于YOLOv8训练本来就紧张默认配置是按12GB以上显存设计的。我的处理办法是把batch降到8imgsz保持640cache关掉或者只用ram模式。还有一个等效手段是开梯度累积batch4加上accumulate4效果等同于batch16但显存占用小得多。如果这样还爆就把模型从s换成n这是最省事的解法。4.2 钢材表面纹理被当成缺陷背景干扰的典型误检症状是训练时loss正常验证集mAP也还行但一到没有标注纹理的干净钢板上模型疯狂出框。根子在于训练集里的正样本框把周围的氧化皮背景也包进来了模型把背景纹理的特征学进了缺陷特征里。解决分两步先检查标注框如果框特别松把外接矩形往里收一点然后在训练集里加入一批没有缺陷的钢板图labels目录放空YOLOv8训练时会把这些图当成纯背景参与损失计算模型就能学会区分纹理和缺陷。这一步的效果非常明显比调任何检测阈值都管用。4.3 细长裂纹被mosaic增强抹掉了召回率不升反降症状是开mosaic训练后划伤类的召回率明显低于不开的情况。根子是mosaic把四张图裁切缩放成一张640x640的图细长裂纹本来可能占几十个像素缩放置后只剩下几个像素特征直接被下采样抹掉了。处理办法是我在训练参数表里写的mosaic设到0.5并且在最后15个epoch关闭。mosaic的随机性在前中期能提升泛化但后期模型需要精调边界时过于激进的增强反而干扰收敛。如果裂纹类缺陷特别多直接把mosaic降到0.3用copy-paste增强来补偿。4.4 标签风格不一致导致验证集指标失真症状是训练曲线很漂亮val mAP稳定在0.85以上但你随机抽几张val图像目检发现预测框和真实缺陷的位置总是差半截。常见原因是标注的人不同有的把整个缺陷连通域全框住有的只框最亮的核心区域验证集里的GT框标准不一致模型学到的边界和评估时用的标准是两回事。这就是标签风格漂移很难从代码层面解决只能回去复查标注。我现在的习惯是训练前把标注框统一渲染到图像上过一遍专门筛出那些框得特别松或特别紧的图该重标就重标这个时间省不得。4.5 训练loss下降但precision和recall纹丝不动症状是train/cls_loss一路走低但val的precision和recall在早期震荡之后再也不动。最常见的根子是类别极度不平衡多数类把损失主导了模型把多数类学得很好少数类直接当背景忽略掉了整体mAP被多数类撑住但每类指标拉开差距特别大。处理办法是去复制粘贴增强里把少数类单独提出来增多它的样本数同时牺牲一些batch里的多数类样本。另一个辅助手段是在损失函数层面给少数类更高的权重YOLOv8超参文件里cls_pw可以对不同类别设置权重但先做数据层面的平衡效果更可控。5. 推理与后处理调优压漏检也压误报训练完成只是拿到了一个best.pt权重推理这一侧还有大量工作要做。钢材缺陷任务在现场面对的真实诉求不是mAP高而是该检出来的缺陷一个不能漏同时误报框不能多到让工人烦躁。这两个诉求在阈值上是互相拉扯的推理代码必须能快速调节。5.1 跑通一个最小推理脚本先看输出字段再谈优化趁热打铁先写一个能直接用的推理脚本把检测结果的坐标、置信度、类别都打出来调阈值时心里有数。from ultralytics import YOLO model YOLO(runs/steel/exp1/weights/best.pt) results model.predict( sourcedemo.jpg, conf0.15, iou0.45, imgsz640, saveTrue, projectout, namesteel_infer, ) r results[0] for box in r.boxes: cls_id int(box.cls) conf float(box.conf) xyxy [round(float(v), 2) for v in box.xyxy[0]] name model.names[cls_id] print(f{name} {conf:.3f} {xyxy})conf参数是置信度阈值低于该值的框直接被丢弃iou是NMS的IoU阈值控制重叠框的保留策略钢材缺陷场景下0.45是个安全的中间值imgsz推理尺寸建议和训练一致不一致会让框的位置产生偏移。saveTrue会保存可视化图片调试期开着能直观看到漏检和误检分布。注意box.cls给出的是类别索引打印时通过model.names映射成真实名称很多新手直接打印数字对着类别表数半天才发现差一位。5.2 conf阈值和NMS怎么调才匹配现场诉求调阈值不是靠感觉而是要在验证集上跑一遍把所有检测框的置信度分布拉出来看。我常用一个很土但有效的办法把置信度阈值从0.05到0.5每隔0.05跑一遍统计每档阈值下检测框的数量再目检几个边界样本。你可以把模型预测出的所有框先保存下来再离线分析。由于conf阈值超过0.2之后检测框数量会出现明显的下降通常意味着这个区间里挤着大量低置信度误检而这个拐点就是现场阈值的选择依据。钢材缺陷场景里误检可以靠后处理去过滤漏检却是不可接受的所以最终阈值我一般落在0.15到0.25之间而不是训练脚本默认的0.25。注意这只是入口阈值NMS阶段的iou阈值也值得单独调缺陷密集区域框重叠特别多时iou调到0.5能保留更多相邻缺陷缺点是可能出现一个缺陷被两个框覆盖的情况。5.3 同一道缺陷被切成多个框后处理合并与二次标注长条形的划伤和裂纹经过NMS之后经常出现一个问题一条连续缺陷被切成两三个框每个框都只有一段。这种情况不能靠调阈值解决NMS的IoU条件对这种前后相接的框不敏感得在推理后处理里自己加一个合并逻辑。常见的做法是判断两个同类框的中心距离如果距离小于较小那个框的短边乘一个系数就把它们合并成一个外接矩形。核心是我在项目里用的这个函数它不复杂但能救回大量被切碎的裂纹框。def merge_close_boxes(boxes, cls_ids, confs, dist_iou0.3): merged [] for i, b in enumerate(boxes): matched False for m in merged: if cls_ids[i] ! m[cls]: continue c1 ((b[0] b[2]) / 2, (b[1] b[3]) / 2) c2 ((m[box][0] m[box][2]) / 2, (m[box][1] m[box][3]) / 2) dist ((c1[0] - c2[0]) ** 2 (c1[1] - c2[1]) ** 2) ** 0.5 h1 min(b[3] - b[1], m[box][3] - m[box][1]) if dist h1 * dist_iou: x1 min(b[0], m[box][0]) y1 min(b[1], m[box][1]) x2 max(b[2], m[box][2]) y2 max(b[3], m[box][3]) m[box] [x1, y1, x2, y2] m[conf] max(m[conf], confs[i]) matched True break if not matched: merged.append({box: list(b), cls: cls_ids[i], conf: confs[i]}) return mergeddist_iou参数控制合并的敏感度0.3意味着两个框中心距离小于短边30%就合并这个值适合细长目标。如果你处理的缺陷大多是大块麻点而不是细长划伤把系数降到0.15更安全避免把相邻的两个独立缺陷错误合并。合并之后再检查一遍可视化结果把那些被合并得离谱的案例收集起来重新去标注和训练这个闭环迭代比改任何参数都有效。二次标注不是浪费时间它是数据驱动型项目精度往上走的唯一途径。6. 导出与部署验证ONNX、板端路线与产线指标best.pt只是训练阶段的产物现场要跑的是导出后的推理模型。部署环节的翻车点不太在模型本身而在导出参数、预处理对齐和目标板卡的适配。记住一句话训练代码里的预处理是PyTorch在管导出后这部分逻辑就消失了得你自己补回来。6.1 导出ONNX并检查输入输出部署最常见翻车点在这YOLOv8导出onnx是命令行一步的事但导出之后的验证不能省。yolo export modelruns/steel/exp1/weights/best.pt formatonnx opset12 imgsz640 simplifyTrueopset版本不要追新12或13在大多数推理框架里兼容性最好simplifyTrue会做常量折叠等图优化去掉一些冗余算子。导出后先开netron看一眼输入输出的维度输入一般是1x3x640x640输出是1x84x8400这种形状如果发现输出里没有框的坐标检查一下是不是把NMS也导出进去了。用onnxruntime跑一张测试图和PyTorch的predict结果对比。这个环节最容易踩的坑是预处理对齐训练时YOLO会自动做letterbox把图片等比例缩放到640x640剩余区域用灰色填充而不是简单的resize拉伸。很多部署项目在这里直接cv2.resize导致长宽比变了框的位置全部漂移。自己写预处理时letterbox的逻辑必须复刻训练时的逻辑。6.2 NPU板端部署的常见路线rknn与TensorRT的取舍如果现场用的是RK3588这类带NPU的板子常见路线是把onnx用rknn-toolkit2再转一次转换时的预处理配置必须和之前letterbox逻辑一致。这里有一个容易忽略的点模型导出时把NMS留在模型内部还是卸掉。多数NPU工具链对带NMS的模型支持很差常见做法是导出不带NMS的onnx检测头输出的原始张量在板端CPU上做后处理。另一条路线是Jetson Orin上用TensorRT流程短很多TensorRT可以直接吃onnx精度校准用训练集的一部分图片做int8校准集。无论哪个平台板端推理的bug十有八九出在图像预处理和后处理代码上模型本身反而没什么问题。6.3 用漏检率、误检率和单帧耗时建立产线基线再谈优化部署完成之后评估指标要立刻从mAP切换到产线指标。mAP是一个综合排序指标它不告诉你产线上每1000块钢板会漏几个缺陷、误报几次。我建议现场测试阶段记录三件事漏检率、误检率和单帧耗时。指标现场怎么算对产线的意义漏检率漏掉缺陷数除以人工复检确认的缺陷总数这是质检系统的生命线误检率误报框数除以总检测框数直接影响工人对系统的信任单帧耗时相机采图到输出结果的总延迟不达标就得降模型体量或换硬件基于这三项指标回头调阈值思路会清晰很多。如果漏检的缺陷置信度分布都在0.1以下说明模型没有学到这类缺陷的特征你调阈值也救不回来应该去补数据重训如果漏检的缺陷置信度在0.3到0.5之间那阈值压低一点就能显著改善。我现在的习惯是拿到新产线数据后先把置信度阈值压到0.05跑一遍全量图把所有低置信度输出可视化快速判断漏检是阈值问题还是训练问题然后再逐步把阈值收紧到业务可接受的范围。这个流程帮我少走了很多弯路希望帮到你。本文还有配套的精品资源点击获取