
简介一份面向毕业设计、课程设计及深度学习初学者的YOLOv8热轧带钢表面缺陷检测完整实践工程覆盖从数据集整理、模型训练到推理部署的全流程可用于工业质检场景的目标检测学习。工程包含源码、标注数据与详细使用教程源码已在本地编译可运行评审分达98分难度适中适合系统掌握缺陷检测项目的落地方法。包体共2000个文件核心包括1808个txt标注文件、161个md说明文档、14个Python脚本以及yaml配置、C推理代码和HTML查看页面等Python脚本对应训练与评估流程md文档提供环境配置与操作指引txt为缺陷标签信息整体约74.49MB目录结构清晰便于按模块检索。目前已有251人学习使用能够支撑完整复现热轧带钢表面缺陷检测流程。通过该资源可掌握数据预处理、模型微调与结果可视化等关键环节配套教程还包含常见问题排错与目录结构说明非常适合作为高分毕业设计或期末大作业的参考方案。1. yolov8 热轧带钢表面缺陷检测源码、数据集与训练全流程热轧带钢产线每秒跑几十米表面缺陷全靠人工盯屏幕漏检一个裂纹就是整卷带钢判废这是钢铁行业最典型的视觉质检场景。我拆过的这个项目是一套基于 yolov8 的完整落地资源源码、NEU-DET 热轧带钢数据集、从环境搭建到训练推理的使用教程都齐了训练出来的模型能对裂纹、夹杂、斑块、麻点、氧化皮、划痕六类缺陷做实时检测。对做毕设的同学来说它的价值在于不用自己攒数据、不用从零写网络直接站在一个可运行的项目上做改进对想了解工业质检的从业者它也完整演示了从 VOC 标注到 YOLO 训练再到部署的链路。下面的内容我会按我自己复现这个项目的顺序来写环境怎么搭、数据怎么转、训练怎么调以及那些不跑一遍绝对发现不了的坑。2. 环境搭建与工程选型为什么是 yolov8而不是 v5 或 v72.1 yolov8 相比前代的三个核心变化先回答一个很多人纠结的问题检测热轧带钢缺陷yolov8 比 v5、v7 好在哪我自己的判断是三点。第一yolov8 把 Anchor-Based 换成了 Anchor-Free模型的输出头直接预测中心点与宽高后端不再需要复杂的 anchor 匹配逻辑训练和推理的代码路径都更干净第二C2f 模块替换了 v5 的 C3梯度分流更充分在同样参数量的前提下小目标特征提取能力有实打实的提升——热轧带钢的麻点和细小划痕就是典型的小目标这一点对缺陷检测很关键第三ultralytics 把训练、验证、预测、导出封装成了统一命令行做毕设和快速验证时不用在炼丹框架里来回折腾。环境搭建我分成 CPU 和 GPU 两条路线说。如果你只有 CPU比如 ubuntu20.04 虚机完全能跑通流程只是训练时间要认账如果你有 GTX 1660Ti 或更高型号的卡训练效率会快一个数量级。CPU 版的搭建命令如下# 创建 Python 3.8 虚拟环境避免污染系统环境 conda create -n yolo python3.8 -y conda activate yolo # 安装 ultralytics 全家桶依赖的 torch 会被一并拉起来CPU 版 pip install ultralytics # 验证安装能输出版本号说明环境 OK yolo checks这里的逻辑是ultralytics这个包自带yolo命令行入口yolo checks会检查 torch、torchvision、CUDA 是否可用。如果你的机器是 GPU 环境不要直接pip install torch而是先去 PyTorch 官网选对应 CUDA 版本的安装命令再装 ultralytics否则 torch 会默认装成 CPU 版白瞎显卡。装完之后yolo命令会自动下载 yolov8n.pt / yolov8s.pt 等预训练权重这是整个流程里唯一需要联网的步骤。2.2 工程目录与数据集放置规范yolov8 对数据集的目录组织有固定约定我一般按下面的结构放能让训练器零配置直接跑datasets/ ├── steel_defect/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ ├── labels/ │ │ ├── train/ │ │ └── val/ │ └── data.yamlimages 和 labels 的 train/val 目录必须同名一一对应否则训练时找不到对应标注会直接报错。data.yaml 是这个项目的配置核心它告诉 yolov8 类别名和路径# 数据集配置文件类别顺序必须与标注文件中的类别 ID 严格一致 path: datasets/steel_defect # 数据集根目录 train: images/train # 训练集相对路径 val: images/val # 验证集相对路径 names: 0: crazing # 裂纹 1: inclusion # 夹杂 2: patches # 斑块 3: pitted_surface # 麻点 4: rolled-in_scale # 氧化皮 5: scratches # 划痕这里有个最容易被忽略的细节names 的索引顺序就是标签 txt 里每行开头那个数字的含义。后面从 XML 转 YOLO 格式时如果类别名映射写错训练出来的模型会把裂纹认成划痕而且损失曲线看起来还是正常的——这属于最坑的隐藏错误我建议你拿到任何数据集第一步先用脚本把训练集里所有标签文件的类别 ID 打印出来跟 data.yaml 对一遍再开工。3. 数据集处理从 NEU-DET 原始标注到 YOLO 训练格式3.1 认识数据集结构这个项目用的是 NEU-DET 热轧带钢表面缺陷数据集一共 1800 张灰度图像每张尺寸是 200x200六类缺陷每类 300 张。它是从东北大学NEU热轧产线实地采集的缺陷种类覆盖了带钢表面最常见的那几类。每张图对应一个 XML 文件格式是 VOC 式的用 PASCAL VOC 的annotation标签组织里面有bndbox的 xmin/ymin/xmax/ymax 坐标和name类别名。但 yolov8 需要的标签不是 XML而是每个图像同名的一个 txt 文件每行格式是class_id center_x center_y width height其中坐标全部归一化到 0~1。这一步不做训练跑不起来所以把这个转换脚本单独拎出来讲它也是整个项目里复用性最高的部分。3.2 XML 转 YOLO 格式脚本我直接给可运行的转换脚本用 Python 标准库加 lxml 实现不需要额外装大型依赖import os import random import xml.etree.ElementTree as ET # 类别映射表VOC 名称转成 YOLO 类别 ID # 顺序必须保持稳定训练时 data.yaml 里的索引也要同步一致 CLASS_MAPPING { crazing: 0, inclusion: 1, patches: 2, pitted_surface: 3, rolled-in_scale: 4, scratches: 5, } def convert_xml_to_txt(xml_path, output_dir, img_width200, img_height200): # XML 里没有原始图像宽高时需要手工传入 tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.iter(object): name obj.find(name).text if name not in CLASS_MAPPING: # 遇到没见过的类别名直接跳过避免把脏数据带进训练集 print(f[warn] unknown class: {name}, file: {xml_path}) continue class_id CLASS_MAPPING[name] 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) # 归一化坐标中心点与宽高都除以图像尺寸 x_center (xmin xmax) / 2.0 / img_width y_center (ymin ymax) / 2.0 / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_height # 边界保护防止标注越界产生 1 的坐标 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) width min(max(width, 0.0), 1.0) height min(max(height, 0.0), 1.0) lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) if not lines: return False txt_name os.path.basename(xml_path).replace(.xml, .txt) with open(os.path.join(output_dir, txt_name), w, encodingutf-8) as f: f.write(\n.join(lines)) return True # 批量处理把 raw/xmls 里的全部标注转出 xml_dir raw/xmls label_dir datasets/steel_defect/labels_train_tmp os.makedirs(label_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if xml_file.endswith(.xml): convert_xml_to_txt(os.path.join(xml_dir, xml_file), label_dir)这段代码的逻辑和边界都写在注释里了。需要特别说明的参数有三个img_width/img_heightNEU-DET 是固定 200x200所以可以硬编码但如果是自己标的数据最好从 XML 里的size节点读取否则归一化算出来全是错的CLASS_MAPPING这是整个转换里最容易翻车的地方比如把pitted_surface写成pittedsurface模型能训练但类别对不上号最后一行的批量处理跑完后到 label_dir 里随便抽查三五个 txt肉眼确认每行只有五个数字、第一列在 0~5 之间再进训练阶段。3.3 划分训练集与验证集转换完后需要把 1800 张图按比例拆成训练集和验证集。yolov8 官方推荐的做法是直接在 images 目录下建 train/val 子目录再把图片和对应同名标签分别拷进去不需要生成 txt 索引文件。我一般用下面的方式# 进入数据集根目录 cd datasets/steel_defect # 先建目标目录 mkdir -p images/train images/val labels/train labels/val # 洗牌并按 8:2 划分1800 张里 1440 张训练、360 张验证 ls images/ | shuf -n 360 | xargs -I {} mv images/{} images/val/ ls images/ | xargs -I {} mv images/{} images/train/ # 同步移动同名标签文件 for img in images/val/*; do base$(basename $img .jpg) mv labels_convert/$base.txt labels/val/ done for img in images/train/*; do base$(basename $img .jpg) mv labels_convert/$base.txt labels/train/ done这段命令的核心思路是先随机抽 360 张进验证集剩下全进训练集再按文件名把标签挪到对应位置。注意最后一个 for 循环里源目录要换成你实际转出来的标签目录名。划分完成后检查一件事images/train和labels/train下的文件数量必须一模一样yolov8 在训练开始前会对这个做校验缺一个标签就报错退出。如果你后续要采集自己的带钢数据用 labelme 标注完转成 yolov8 格式也是同样的思路labelme 导出的是 JSON写个解析脚本把points转成归一化矩形框即可核心逻辑跟上面 XML 转 txt 完全一致。4. 训练与调参把 mAP 拉上去的关键参数4.1 训练命令与超参数语义环境就绪、数据就位后训练命令如下# 用 yolov8n 骨架 预训练权重开始训练 yolo detect train \ datadatasets/steel_defect/data.yaml \ modelyolov8n.pt \ imgsz640 \ epochs100 \ batch16 \ projectruns/steel_defect \ nameexp1 # 如果要指定 GPU 或强制 CPU加下面参数 # device0 # 0 表示第一张显卡 # devicecpu # 纯 CPU 训练速度会非常慢先逐个拆解参数含义。modelyolov8n.pt是预训练权重文件它会自动下载 COCO 上训好的模型再用它对带钢数据做迁移学习这是小数据集能收敛的关键如果换成yolov8n.yaml就是不加载预训练权重、从零开始训新手别这么干1800 张图从零训练效果会差很多。imgsz640是训练输入分辨率NEU-DET 原图只有 200x200yolov8 会自动 resize 到 640这相当于做了超分辨率对提高小缺陷检测能力有帮助但代价是显存和训练时间上涨。batch16取决于显存大小1660Ti 6GB 跑这个参数基本是临界值跑不动就降到 8。epochs100对 1440 张训练图来说是合理区间早停机制会在连续 50 个 epoch 验证集指标不上升时自动掐断训练保存最佳权重。这里还有两个隐藏参数值得说。cacheTrue可以把训练集全部缓存进显存或内存省去每个 epoch 重新读盘的时间数据集小的时候体感很明显但如果显存紧张它会跟 batch 抢空间我一般建议cacheram而不是cacheTrue用内存换磁盘 IO。patience50是早停的等待轮数默认就是 50对小数据集来说够用不需要手调。4.2 训练过程的监控与损失曲线读取训练开始后终端会刷每一轮的 loss 和精度信息初次看的人很容易懵。yolov8 的训练日志核心只有四个量box_loss是框回归损失cls_loss是分类损失dfl_loss是分布焦点损失负责框的边界精度val前缀的是验证集上的指标。看训练是否正常我有个土办法前 20 个 epoch 里训练 loss 必须呈单调下降趋势验证集 mAP50 可以先不动但如果 30 个 epoch 后验证集精度还纹丝不动基本是标签或数据配置有问题不是训练不够的问题。训练结束后在runs/steel_defect/exp1下会生成results.png和results.csv。results.png是八张曲线拼出的格子图包括训练/验证的 box_loss、cls_loss、mAP50、mAP50-95 等。如果你想自己画更细致的损失函数曲线比如对比两次实验的收敛速度从 results.csv 取数是最快的import pandas as pd import matplotlib.pyplot as plt # 读取训练日志 CSV行是 epoch列是各项指标 df pd.read_csv(runs/steel_defect/exp1/results.csv) # 去掉列名里多余的空格col 名称类似 train/box_loss df.columns [c.strip() for c in df.columns] plt.figure(figsize(10, 6)) # 对比验证集 mAP50 和 mAP50-95 两条曲线判断模型真实水平 plt.plot(df[metrics/mAP50(B)], labelmAP50) plt.plot(df[metrics/mAP50-95(B)], labelmAP50-95) plt.xlabel(epoch) plt.ylabel(score) plt.legend() plt.title(yolov8 steel defect mAP curves) plt.savefig(mAP_curves.png, dpi150)这段代码的逻辑是从训练产物里直接读取指标数据绘出 mAP 曲线用来做多组实验的横向对比。mAP50是 IoU 阈值取 0.5 时的平均精度均值mAP50-95是 IoU 从 0.5 到 0.95 连续取值后的平均后者更严格也更能反映框的定位精度。带钢缺陷里像氧化皮这种纹理型缺陷mAP50 可能不低但 mAP50-95 很难看这就是边界框不够贴合的典型信号。4.3 模型评估与权重选择训练完成后眉毛鼻子一把抓地去翻runs/steel_defect/exp1/weights会踩坑。这个目录里有两个权重文件best.pt是验证集整体指标最高时的权重last.pt是最后一个 epoch 的权重。我见过有同学无脑用last.pt结果模型过拟合到训练集验证集上漏检严重——默认必须用best.pt。用下面命令对验证集做评估拿到最终的精度指标yolo detect val \ datadatasets/steel_defect/data.yaml \ modelruns/steel_defect/exp1/weights/best.pt \ batch16验证结束后会打印每类的 Precision、Recall、mAP50、mAP50-95。我做过的测试里crazing裂纹和 pitted_surface麻点的 mAP50 通常会比 patches 低几个点因为这两类的纹理特征跟背景相近边界模糊。如果某类 mAP50 掉到 0.5 以下不用急着调网络结构先回第 3.3 节检查该类在训练集里的样本数这个场景下样本不平衡是最大嫌疑。5. 避坑与排查五条真实踩坑记录5.1 类别名不一致导致训练报错或错乱现象XML 转 txt 后启动训练yolov8 报错提示标签数与样本数不匹配或者不报错但验证时某类 AP 为 0loss 还一直在降。原因XML 里的类别名带空格、大小写差异或者像rolled-in_scale被写成了rolled_in_scale导致CLASS_MAPPING匹配不到该样本被静默跳过检查时又没核对数量。解决转换脚本里我留了 warn 打印和继续逻辑但真正治本是转完后跑一遍数量校验ls labels/train | wc -l对比ls images/train | wc -l不一致就是有文件被丢掉了再回到 XML 目录看是哪张图的标注有异常。5.2 mAP50 高但 mAP50-95 奇低现象验证集 mAP50 达到 0.85 以上但 mAP50-95 只有不到 0.5。原因框定位不够精准IoU 阈值提高到 0.75 后检测框与标注框的重合度达不到。这类情况多出现在缺陷边缘模糊的类别上比如氧化皮的边界本身在标注时就有主观性。解决先排除数据问题再看超参。数据层面检查标注框是否有明显偏差参数层面imgsz从 640 提到 960 通常能明显改善定位精度代价是推理变慢我的权衡是缺陷检测场景保精度优先所以imgsz提到 960。5.3 CPU 训练慢到怀疑人生现象ubuntu20.04 CPU 环境上训练一个 epoch 要跑十几分钟甚至更久100 个 epoch 根本等不起。原因CPU 训练本身慢再加上 imgsz 大、batch 大问题被放大。解决preprocess 阶段imgsz416、batch8先跑 30 个 epoch 验证数据和模型链路是通的如果想在 CPU 上训练大规模数据结局都不会太好建议直接上云 GPU 或本地显卡。GTX 1660Ti 跑这个数据集640 分辨率、batch16一个 epoch 大约 1~2 分钟是体验上的分水岭。5.4 推理时小缺陷大面积漏检现象训练时 mAP 看着不错但用yolo predict跑单张图小尺寸的麻点和划痕不响应。原因推理时用的imgsz默认是训练值 640原图经过缩放后小缺陷在特征图上占比太小另一个原因是置信度阈值设置太高。解决推理命令显式给两个参数imgsz960放大输入分辨率conf0.1放宽置信度阈值iou 保持 0.5。如果放大后检测速度不可接受再退回到 800 试中间值。5.5 CUDA 显存溢出现象训练跑了几个 epoch 后直接报RuntimeError: CUDA out of memory。原因batch 与 imgsz 乘积超出显存还有一类是workers开太多导致数据加载进程占用额外显存。解决第一步batch8、workers2重试如果还爆把 cache 关掉用磁盘 IO 换显存空间。显存溢出没有玄学就是 batch、imgsz、cache、workers 四者的组合问题逐个降参数一定能跑起来。6. 推理验证与部署进阶把模型用起来6.1 编写推理脚本与结果解析训练完成后推理阶段最关键的已不是跑通而是把结果里的框坐标和类别名拿出来做二次处理。我习惯写一个独立脚本而不是依赖yolo predict的行命令原因是行命令只能保存图像不方便在代码里接下游逻辑from ultralytics import YOLO # 加载训练产出的最佳权重 model YOLO(runs/steel_defect/exp1/weights/best.pt) # 推理单张图像imgsz 用 960 而不是训练时的 640 results model.predict( sourcedatasets/steel_defect/images/val/xxx.jpg, imgsz960, conf0.1, iou0.5, verboseTrue ) # 逐框输出检测结果names 是 data.yaml 里的类别映射 for r in results: boxes r.boxes for box in boxes: cls_id int(box.cls[0].item()) conf float(box.conf[0].item()) x1, y1, x2, y2 box.xyxy[0].tolist() print(f缺陷类别: {model.names[cls_id]}, 置信度: {conf:.2f}, 坐标: ({x1:.0f}, {y1:.0f}, {x2:.0f}, {y2:.0f}))这里的imgsz960与置信度conf0.1是带钢场景的参数组合放大分辨率有助于召回小缺陷放低阈值给下游 NMS 更多候选如果误检增多优先把conf从 0.1 提到 0.2比调iou直接。6.2 边缘端部署路线落地如果项目下一步要接到产线模型导出这一步反而比训练更考验工程习惯。yolov8 自带导出命令# 导出 ONNX 格式后续可转 RKNN 或 TensorRT yolo export modelruns/steel_defect/exp1/weights/best.pt formatonnx imgsz960 opset12导出 ONNX 后常见的落地路线有两条一是用 TensorRT 在 NVIDIA 设备上做 FP16 量化部署二是走 rk3588 这类边缘盒子先转 RKNN 格式再做 INT8 量化。rk3588 部署 yolov8 的坑主要在量化校准数据集上要选 100 张覆盖所有缺陷类别的验证图不然量化后 AP 掉得厉害。从那次我把 project 路径写错覆盖掉上一版权重之后我每次接新的检测项目都强制先做两件事打印全部标签类别集合目标环境推理分辨率与训练分辨率保持一致。项目里的源码和数据集按前面章节的顺序跑环境、数据、训练、推理是能直接串联起来的希望你也能一口气跑通。本文还有配套的精品资源点击获取