ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

热轧带钢表面缺陷检测:YOLOv8从训练到C++部署全流程实战

热轧带钢表面缺陷检测:YOLOv8从训练到C++部署全流程实战 简介基于YOLOv8的热轧带钢表面缺陷检测资源包面向工业质检场景的开发者与研究者提供从数据集构建、模型训练到推理验证的完整技术方案。资源覆盖横向裂缝、纵向裂缝、块状裂缝、龟裂、坑槽以及多种修补类缺陷的检测实现适合具备一定深度学习基础、希望将目标检测落地到实际产线的读者。包内文件共2000个以1808个txt标注/说明文件、161个md教程文档、14个py训练推理脚本为主另有少量yaml、xml、cpp等配置与辅助源码压缩包整体约74.49MB目录结构清晰便于按模块检索。目前已有841人学习下载配套教程详细讲解环境配置、参数调整、数据增强、模型评估及常见排错思路可帮助读者快速复现实验结果并迁移到自有带钢表面质检项目。1. 热轧带钢表面缺陷检测为什么YOLOv8成了工业质检的首选热轧带钢的表面缺陷检测一直是钢铁产线里最难啃的骨头。传统机器视觉靠灰度和阈值做分割遇到光照变化、氧化皮干扰和缺陷形态差异误检率居高不下后来换成了Faster R-CNN这类两阶段检测器精度上来了但单张推理动辄几十毫秒根本跟不上产线每秒几米的高速运动。YOLOv8把这个问题拉回到了平衡点——它在COCO上跑出53.9%的mAP推理速度在TensorRT加速下能达到毫秒级是工业场景里少有的精度和速度同时够用的模型。这份资源做的正是这件事把YOLOv8完整落地到热轧带钢的8类缺陷检测上包括横向裂缝、纵向裂缝、块状裂缝、龟裂、坑槽、修补网状裂缝、修补裂缝和修补坑槽配套了一份带标注的数据集、可用于实际推理的C工程源码以及一份中文版的使用教程。适合三类人刚入门目标检测、想拿工业数据集练手的同学做产线质检算法选型的工程师以及需要一套可复现基线来评估自研缺陷检测算法的研发人员。下载下来直接训练和推理不必从零攒数据集。2. 从YOLO到YOLOv8缺陷检测任务里的模型选型与数据基础2.1 为什么是YOLOv8而不是其他检测模型先理清一个容易被忽略的问题热轧带钢缺陷检测里缺陷长什么样。横向裂缝是垂直于轧制方向的细长条纵向裂缝沿着轧制方向走龟裂像龟壳上的碎纹坑槽是表面局部凹陷修补类的缺陷则带有明显的边界轮廓和纹理突变。这些缺陷有两个共同特点目标小、长宽比差异大。小目标意味着feature map上占的像素少长宽比差异大则要求anchor设计更灵活否则回归分支很难收敛。YOLOv8处理这两点的办法是anchor-free化通俗讲就是不再预先设定一组固定宽高的候选框而是让每个位置直接预测这个点离目标中心的距离和目标在这个点的宽高。相比YOLOv5的anchor-based机制它省去了聚类anchor的步骤也避免了小缺陷和长条形缺陷因为匹配不到合适anchor而被丢弃的问题。再加上C2f模块——就是把CSP的瓶颈结构换成更密集的分支连接——让高层语义和低层纹理信息更容易融合而这恰好是区分裂纹和划伤这类视觉特征极相似缺陷的关键。另一个选它的理由是生态成熟度。Ultralytics把训练、验证、导出做到了一个CLI命令搞定转ONNX、转TensorRT都有现成脚本。对比一下如果选mmdetection同样的配置需要写更多代码选YOLOv5虽然也成熟但anchor机制不够优雅。工业场景里精度差1个点、推理慢2倍产线就得重新算节拍所以选型不是看谁论文好看而是看谁能最快跑到可部署状态。2.2 缺陷数据集的构成与标注要求数据这部分是资源里最值钱的东西之一。热轧带钢表面缺陷不是随便拍拍就能收集的——绝大部分产线不会开放真实缺陷图像公开数据源也不统一。这套资源自带的数据集按8类缺陷组织图像来源是实际产线采集或与公开数据集如NEU-DET思路同源的带钢表面图像。你拿到手以后第一件事不是急着训练而是先把目录结构和标注文件过一遍确认每个类别的样本数量和标注格式。典型的目录组织是一个YOLO工程的data文件夹NEU-DET/ ├── images/ │ ├── train/ │ │ ├── 10.jpg │ │ └── ... │ └── val/ │ ├── 11.jpg │ └── ... └── labels/ ├── train/ │ ├── 10.txt │ └── ... └── val/ ├── 11.txt └── ...每张图的同名txt文件里每一行代表一个目标框格式是类别ID 中心x 中心y 宽 高四个坐标值统一归一化到0到1之间。举例来说0 0.512345 0.448765 0.120345 0.088712 2 0.765432 0.153210 0.045678 0.067890第一列是类别ID对应你在训练配置里定义的classes顺序后面四个数字分别是归一化中心坐标和宽高。拿到资源后先用Python脚本统计一下各类别的框数量如果发现某一类样本只有几十个训练时就要给这个类别加权重否则模型会偏向多数类。我一般会跑这段代码快速摸底import os from collections import Counter label_dir datasets/NEU-DET/labels/train cls_counter Counter() box_count 0 for f in os.listdir(label_dir): if not f.endswith(.txt): continue with open(os.path.join(label_dir, f), r) as fp: for line in fp: parts line.strip().split() if len(parts) 5: cls_counter[int(parts[0])] 1 box_count 1 print(total boxes:, box_count) for cls_id in sorted(cls_counter): print(fclass {cls_id}: {cls_counter[cls_id]} boxes)这段代码的逻辑很简单遍历labels/train目录下所有txt文件每读取一行就解析出类别ID并计数。输出的意义在于让你判断数据集的平衡性——如果某个类别的框数明显偏少训练时就要在损失函数里提高它的权重或者在数据增强阶段针对该类做oversample。这里有个常见的坏习惯只看图片数量不看框数量。一张图里可能同时存在10个坑槽和1个龟裂按图片统计完全无法反映真实分布。2.3 标注检查与缺陷类别的边界约定标注质量直接决定模型上限而热轧带钢缺陷的标注最容易犯的错是无边界统一。什么是龟裂和网状修补裂缝的边界产线上不同检验员会有不同判断。好在资源里已经给了每一个类别的样例图和标注文件建议你在训练前把每个类别的标注可视化一遍确认标准是统一的。这里给一段可视化代码用来检查标注框是否贴合缺陷轮廓import cv2 import numpy as np def draw_yolo_boxes(image_path, label_path, classes): img cv2.imread(image_path) h, w img.shape[:2] with open(label_path, r) as f: lines f.readlines() for line in lines: parts line.strip().split() cls_id int(parts[0]) cx, cy, bw, bh map(float, parts[1:]) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) color (0, 255, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, classes[cls_id], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) return img classes [crazing, inclusion, patches, pitted_surface, rolled_in_scale, scratches] img_path datasets/NEU-DET/images/train/10.jpg label_path datasets/NEU-DET/labels/train/10.txt result draw_yolo_boxes(img_path, label_path, classes) cv2.imwrite(check_annotation.jpg, result) print(标注可视化已保存请人工核对框的位置和大小)运行后生成的check_annotation.jpg会直接把框画在图上。重点检查三件事一是框是否完全包裹缺陷是否有过大或过小的情况二是类别ID和缺陷类型是否对应比如龟裂被标成网状修补裂缝这种混淆错误三是小缺陷是否漏标——YOLOv8对漏标极其敏感一个没框出来的缺陷会被当背景学习推理时就会变成漏检。标注检查这一步建议至少做两轮每张图过目一遍不要只抽检。这个诀窍能省掉你在训练环节排查badcase的一大半时间。3. 训练实战参数清单、数据增强与损失收敛3.1 把数据集和YOLOv8工程对接起来拿到资源后训练的第一步是配置好Ultralytics YOLOv8环境。项目里自带一份面向工业场景的代码包依赖集中在requirements.txt。如果你在Windows上做开发用conda创建环境是阻力最小的方案如果打算做TensorRT部署建议直接在Ubuntu 20.04上搭环境。项目里既有Python训练脚本也有C推理端常见的做法是训练用Python部署时切C所以环境上两头都要照顾。安装依赖的常用做法是conda create -n yolo python3.9 -y conda activate yolo pip install ultralytics -i https://pypi.tuna.tsinghua.edu.cn/simple pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install opencv-python labelme pyqt5参数说明ultralytics是YOLOv8官方训练与推理库torch的cu118后缀代表CUDA 11.8版本如果你本机是CUDA 12.x可以换成cu121或cu124opencv-python用于图像读写和可视化labelme是标注工具后面补样本时用得上。这里建议不要用最新版torch直接装而是先确认你的显卡驱动支持的CUDA版本再用对应版本的torch否则训练时直接报no kernel image is available也没地方哭。3.2 训练参数的设置逻辑训练配置在yolov8的yaml文件里核心是数据路径和类别定义。项目的datasets配置大概长这样path: ./datasets/NEU-DET train: images/train val: images/val nc: 6 names: [crazing, inclusion, patches, pitted_surface, rolled_in_scale, scratches]如果你把这套流程迁移到自己的缺陷类别上建议把6改成你的实际类别数names数组换成你的缺陷名。接下来是训练命令yolo detect train \ modelyolov8n.pt \ datadatasets/NEU-DET/neu_seg.yaml \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ optimizerAdamW \ patience20 \ nameneu_yolov8n逐个说明参数modelyolov8n.pt用的是YOLOv8纳米版预训练权重参数量3.2M适合在CPU或低端显卡上先跑通流程epochs150是训练的轮数工业缺陷检测建议不低于100轮因为缺陷样本量通常只有几千张太少轮数学不充分imgsz640是输入分辨率热轧带钢表面缺陷很多是小目标如果显卡显存允许可以调到1280会带来明显的精度提升但训练时间翻倍lr00.01是初始学习率YOLOv8官方默认就是0.01用AdamW优化器时建议不要超过这个值patience20是早停轮数连续20轮mAP没有提升就自动停避免过拟合同时也省算力。如果你把优化器换成SGD学习率习惯上要降到0.001到0.005之间因为SGD更敏感。我在这类数据集上的经验是缺陷图像背景相对干净、目标结构单一AdamW收敛更快SGD最终精度略高但需要更多轮数去磨。跑基线的时候用AdamW调精度的时候用SGD这个顺序最经济。3.3 数据增强策略的取舍数据增强是工业检测里一个关键的差异点。YOLOv8内置了丰富的增强策略包括mosaic、随机翻转、饱和度调整、缩放等。但热轧带钢缺陷检测有一个显著特点缺陷方向是有物理意义的。横向裂缝和纵向裂缝的区分本质上就是方向区分如果用了90度旋转增强横向裂缝旋转后变成了纵向裂缝等于告诉模型方向不重要这会导致模型在推理时对裂缝方向判断错误。正确的做法是关掉或者严格限制旋转类增强。YOLOv8配置中通过degrees参数控制旋转角度我一般把它设为0或很小值# augmentations hsv_h: 0.015 hsv_s: 0.4 hsv_v: 0.4 degrees: 0.0 translate: 0.1 scale: 0.5 fliplr: 0.5 mosaic: 1.0 mixup: 0.0解释一下这里几个关键参数。degrees0.0表示不做旋转避免方向语义被破坏fliplr0.5是水平翻转——水平翻转不会改变裂缝的物理方向语义横向的还是横向纵向的还是纵向所以可以开mosaic1.0表示mosaic增强的启用概率它能在一张图里拼4张图增加检测难度从而提升模型鲁棒性但mosaic在最后20个epoch建议手动关闭否则模型会一直在拼图模式下训练推理时遇到真实图像反而泛化不好这是典型的训练分布和推理分布不一致的问题。处理方式是把mosaic参数在训练后段调低Ultralytics在最后10轮会自动关闭mosaic但对于细粒度缺陷建议把这个机制提前到倒数20轮。3.4 损失曲线怎么读训练时最常看到的困惑是loss曲线一直在降但mAP不涨或者loss曲线下降但验证集recall极低。第一个现象通常是过拟合的前兆——训练loss降说明模型在训练集上拟合得不错但val集的mAP不涨说明泛化不行这时候要看数据增强是不是配置太激进或者早停参数是否太宽松。第二个现象往往是类别不平衡小样本类别的recall拖了后腿。YOLOv8训练时会在runs目录生成results.csv里面记录了每个epoch的train/box_loss、train/cls_loss、val/box_loss、val/cls_loss和mAP50、mAP50-95等指标。绘制曲线的常用做法是直接用pandas读取csv并画图import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/neu_yolov8n/results.csv) df.columns [c.strip() for c in df.columns] fig, axes plt.subplots(2, 2, figsize(12, 8)) axes[0, 0].plot(df[epoch], df[train/box_loss], labeltrain box loss) axes[0, 0].plot(df[epoch], df[val/box_loss], labelval box loss) axes[0, 0].set_title(Box Loss) axes[0, 0].legend() axes[0, 1].plot(df[epoch], df[train/cls_loss], labeltrain cls loss) axes[0, 1].plot(df[epoch], df[val/cls_loss], labelval cls loss) axes[0, 1].set_title(Cls Loss) axes[0, 1].legend() axes[1, 0].plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) axes[1, 0].plot(df[epoch], df[metrics/mAP50-95(B)], labelmAP50-95) axes[1, 0].set_title(mAP) axes[1, 0].legend() axes[1, 1].plot(df[epoch], df[metrics/precision(B)], labelprecision) axes[1, 1].plot(df[epoch], df[metrics/recall(B)], labelrecall) axes[1, 1].set_title(P/R) axes[1, 1].legend() plt.tight_layout() plt.savefig(training_curves.png, dpi150)这段脚本用matplotlib画四张子图左上角是框回归损失训练和验证曲线应该同步下降右上角是分类损失这里要看val/cls_loss有没有在训练后期反而抬头抬头就是过拟合信号左下角是mAP曲线mAP50关注的是IoU0.5时的平均精度mAP50-95则是对IoU从0.5到0.95逐级平均要求更苛刻右下角是precision和recall缺陷检测通常更看重recall因为漏掉一个缺陷比多报一个误检更致命。4. 从训练到推理C工程结构解析与部署细节4.1 工程文件的角色分工资源里的代码包包含inference.cpp、inference.h、main.cpp和一组前端文件这是一套典型的Python训练产出权重、C工程负责实际推理的工业落地结构。理解这套配合关系是部署的关键环节。inference.h是推理接口的头文件声明了模型加载函数、预测函数和结果输出结构体inference.cpp是具体实现包含ONNX Runtime加载模型、预处理图像、执行推理、解析输出框的完整流程main.cpp是入口文件读取测试图像路径、调用推理接口、把结果通过OpenCV画到图上输出。style.css、comments.html和source-file.html则是用于在本地生成可视化文档报告的前端文件方便你在浏览器里直观查看推理结果比如把每张缺陷图的检测框覆盖到原图上、把类别置信度标出来——这在产线质检场景里很实用现场操作员不需要懂深度学习看浏览器页面就知道哪一卷带钢有问题。当模型在Python侧训练完成后先导出成ONNX格式再交给C端加载。导出命令from ultralytics import YOLO model YOLO(runs/detect/neu_yolov8n/weights/best.pt) model.export(formatonnx, imgsz640, opset12, simplifyTrue) print(ONNX模型已导出)参数说明imgsz640要和训练时的输入尺寸一致否则输出张量的shape会错位opset12是ONNX算子集版本C部署端ONNX Runtime版本较旧时用高版本opset会因为算子不支持而加载失败建议先查部署端的runtime版本再决定opset12是兼容性较好的折中simplifyTrue会启用onnx-simplifier做计算图化简消除冗余节点推理时能快3%到8%。很多人在这一步翻车——训练时用1280导出时忘了改imgsz最后C端加载模型后推理结果全是小框位置偏移回头查半天才发现是尺寸不一致。4.2 C推理代码的骨架与关键参数C推理端的代码核心逻辑是读取图像转为RGB张量、按训练时的标准化参数做归一化、把数据送入ONNX Runtime、解析输出并过滤低置信度框。以下是项目中inference.cpp核心流程的简化示意#include inference.h #include opencv2/opencv.hpp #include onnxruntime_cxx_api.h #include algorithm std::vectorDetection InferenceEngine::runDetect(const cv::Mat img) { // 预处理resize到640x640归一化到[0,1] cv::Mat resized; cv::resize(img, resized, cv::Size(640, 640)); // HWC转CHWBGR转RGB cv::Mat rgb; cv::cvtColor(resized, rgb, cv::COLOR_BGR2RGB); std::vectorfloat input_data(640 * 640 * 3); for (int c 0; c 3; c) { for (int i 0; i 640 * 640; i) { input_data[c * 640 * 640 i] rgb.data[i * 3 c] / 255.0f; } } // 构造输入张量并执行推理 std::vectorint64_t input_shape{1, 3, 640, 640}; Ort::MemoryInfo memory_info Ort::MemoryInfo::CreateCpu( OrtArenaAllocator, OrtMemTypeDefault); Ort::Value input_tensor Ort::Value::CreateTensorfloat( memory_info, input_data.data(), input_data.size(), input_shape.data(), input_shape.size()); std::vectorOrt::Value output session-Run( Ort::RunOptions{nullptr}, input_names.data(), input_tensor, 1, output_names.data(), 1); // 解析输出形状为(1, 6, 8400)按置信度阈值过滤并实施NMS const float* data output[0].GetTensorDatafloat(); std::vectorDetection dets; for (int i 0; i 8400; i) { float conf data[4 * 8400 i]; if (conf conf_threshold) continue; Detection det; det.confidence conf; det.class_id std::max_element( data 5 * 8400 i, data 5 * 8400 i num_classes) - (data 5 * 8400 i); float cx data[i]; float cy data[8400 i]; float w data[2 * 8400 i]; float h data[3 * 8400 i]; det.bbox cv::Rect2f(cx - w / 2, cy - h / 2, w, h); dets.push_back(det); } return nmsFilter(dets, iou_threshold); }这段代码里的每个步骤都有它的工程意义。640x640的尺寸必须和导出模型时一致。归一化除以255.0对应PyTorch里ToTensor操作做的事情——如果你用了自定义的mean和std这里也要同步改否则输入分布和训练时不一致精度会掉。ONNX Runtime输出数据的布局需要特别留意YOLOv8输出的方式是objectness分支被合并到分类分支输出张量是(1, 4num_classes, 8400)也就是每个网格点直接预测4个边框参数中心x、中心y、宽、高和各类别得分不再有单独的置信度物体分支。遍历8400个候选框时conf取的是各类别中的最大得分class_id则用找最大值索引的方式确定。最后的nmsFilter做非极大值抑制用于消除同一个缺陷上重叠的多个框iou_threshold在0.4到0.5之间是常规选择工业检测中因为缺陷密集分布建议调到0.4让重合框删得更干净。需要注意这类模型输出排列里的索引技巧第i个候选框的中心x存在data[i]处中心y存在data[8400i]宽在data[28400i]高在data[38400i]类别得分从data[5*8400i]开始连续存放num_classes个。这种分块存储的布局是YOLO系列导出的惯例直接在代码里写死是完全可以的。4.3 推理端的环境依赖C端主要依赖OpenCV和ONNX Runtime。一个能编译运行的CMakeLists.txt大致形如cmake_minimum_required(VERSION 3.16) project(steel_defect_detector) find_package(OpenCV REQUIRED) find_package(onnxruntime REQUIRED) add_executable(run_inference main.cpp inference.cpp) target_link_libraries(run_inference ${OpenCV_LIBS} onnxruntime::onnxruntime )编译时容易出问题的点是ONNX Runtime的库路径。Windows下用vcpkg装了onnxruntime后CMake的find_package能找到对应cmake配置文件Ubuntu下从GitHub Release页下载的onnxruntime压缩包需要手动把libonnxruntime.so的路径加到CMAKE_PREFIX_PATH里。我一般习惯把onnxruntime放到工程的third_party目录里统一管理避免换机器编译时找不到库。5. 避坑指南热轧带钢缺陷检测的常见问题排查5.1 训练loss正常但val mAP为零现象训练过程中loss曲线正常下降但val集的mAP始终是0precision和recall输出为0或者极低。原因最常见的是数据集目录配置错了。你的data.yaml里写的val路径指向了一个没有图片的目录或者val目录下的图片和label没有对齐——图片是jpg、label是txt但文件名的前缀不匹配。Ultralytics在验证时如果找不到对应的label文件会把它当作背景计算导致所有目标都被判为误检。解决先检查val目录下图片和标签文件是否一一对应。写一段快速脚本对一下文件名ls images/val | sed s/\.jpg$// | sort img_names.txt ls labels/val | sed s/\.txt$// | sort lbl_names.txt diff img_names.txt lbl_names.txt mismatches.txt如果有输出说明有文件名不匹配的情况。不要小看这个labelme导出的文件可能有.jpg和.JPG后缀混用的情况Linux系统区分大小写Windows上没问题到Linux训练就全部失配。5.2 推理时所有框都偏到图像角落现象训练时mAP正常但用C推理或导出ONNX后检测框的位置明显偏小、偏移到图像边缘或者完全错位。原因训练时的letterbox预处理和推理时的预处理不一致。YOLOv8训练时会把图像等比例缩放到640x640多余部分填灰色114, 114, 114推理时也必须做完全相同的letterbox操作。如果你在推理端直接cv::resize拉伸到640x640图像内容变形模型输出的坐标完全不对。另一个原因就是导出ONNX时imgsz和推理端固定写死的640不一致。解决推理代码里加一个letterbox函数而不要直接resize。最常见的做法是计算缩放比例原始图resize后放在画布中央import cv2 import numpy as np def letterbox(img, new_shape(640, 640), color(114, 114, 114)): shape img.shape[:2] r min(new_shape[0] / shape[0], new_shape[1] / shape[1]) new_unpad int(round(shape[1] * r)), int(round(shape[0] * r)) dw (new_shape[1] - new_unpad[0]) / 2 dh (new_shape[0] - new_unpad[1]) / 2 top, bottom int(round(dh - 0.1)), int(round(dh 0.1)) left, right int(round(dw - 0.1)), int(round(dw 0.1)) img cv2.resize(img, new_unpad, interpolationcv2.INTER_LINEAR) img cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, valuecolor) return img, r, left, top如果推理端做推理时用了letterbox输出坐标也要逆变换回原图尺寸不然框的位置还是错的。这段代码之所以加上返回r和left/top就是为了在后面把检测框映射回原图坐标。5.3 小缺陷全部漏检大缺陷都有框现象模型对坑槽、裂缝这类小目标全部漏检对修补类的大缺陷检测正常recall曲线里小目标类别的值明显低。原因输入分辨率不够。你的图像如果整体是3000x1000的宽幅带钢表面图直接压缩到640x640小缺陷在图上可能只有不到10个像素模型根本学不到特征。这是工业场景里最常见的小目标丢检问题不是模型能力不够而是输入信息损失太严重。解决要么把imgsz调到1280甚至更高要么在预处理阶段做滑窗裁剪把原图切分成多个640x640的子图分别推理最后再把结果拼回去。滑窗推理的代价是推理时间成倍增加产线实时性要求高时不太现实。折中方案是训练时用mosaic增强把部分小目标放大同时推理时把imgsz设为接近原始图像的有效区域尺寸。调试时也可以直接用滑窗法看单张图像的检测上限借此判断模型本身有没有学到位——如果滑窗能检出块状漏检就说明是分辨率问题而不是模型能力问题。5.4 数据增强开太大把裂纹学成了纹理现象训练loss和验证loss都在下降但把模型跑在真实带钢图像上时裂纹区域被识别成背景或者背景纹理被误报成裂纹。原因增强参数里色调、饱和度、灰度变换设置过于激进。裂纹和龟裂在灰度图上的对比度本来就低如果hsv_h和hsv_s的值过大模型接收到的训练样本颜色分布已经偏离真实带钢表面的颜色特征它就学不到灰度突变这个关键判别信号。另一个因素是和mosaic/拼接搭配造成上下文的错乱背景和缺陷的边界在拼图里变得不真实。解决把hsv_h、hsv_s、hsv_v分别降到0.01、0.2、0.2以下关掉mixup。针对表面缺陷的本质是纹理异常可以增加高斯模糊和随机噪点的增强类型模拟实际产线中的光照波动和表面反光。Ultralytics的augment配置里没有直接开高斯模糊的flag常见的做法是在训练前自定义一个预处理函数或者在ultralytics的transforms里挂一个自定义增强模块。更务实的做法是先不加任何额外增强只用官方默认拿到一个基线再一点点加上去每加一个都跑一轮验证。5.5 部署端显存占用过高导致推理失败现象C程序启动后一秒内报错提示CUDA out of memory或者推理第一帧就崩。原因ONNX Runtime默认会在所有可用的GPU device上分配内存如果你的机器同时有集成显卡和独立显卡或者GPU显存本身只有4G而模型输入分辨率是1280一张图的前向计算就吃满了显存。还有一个容易被忽视的原因session在创建时没有开启内存优化选项导致CUDA缓存没有及时释放。解决初始化session时设置优化级别和GPU内存上限是有效的方案Ort::SessionOptions session_options; session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL); session_options.AppendExecutionProvider_CUDA(0); // 限制GPU显存上限 OrtCUDAProviderOptions cuda_options; cuda_options.device_id 0; cuda_options.arena_extend_strategy 0; cuda_options.gpu_mem_limit 2ULL * 1024ULL * 1024ULL * 1024ULL; session_options.AppendExecutionProvider_CUDA(cuda_options);注意OrtCUDAProviderOptions的gpu_mem_limit单位是字节这里设置2GB的目的不是限制模型性能而是防止显存分配策略过于激进导致系统卡死。产线工控机的显卡通常不主流很多是GTX 1660Ti或者MX系列显存就6G或4G不限制上限的话默认策略可能在显存充裕时申请一大块连续内存其他程序直接崩。另外确认一下你的ONNX Runtime是否带CUDA支持——有些包只含CPU算子了GPU模式下会报错“not implemented”。这个需要单独的onnxruntime-gpu包。6. 验证模型的上限从mAP到产线可见的置信度调试模型训练完不代表能直接上产线。mAP是一个全局平均指标它对每个类别的权重是一致的但产线更关心的是某些缺陷绝对不能漏。具体怎么做呢核心技巧是单独看每个类别的precision-recall曲线再针对性地调整置信度阈值。YOLOv8训练完成后在runs/detect目录里会生成confusion_matrix.png和PR_curve.png两张图。confusion_matrix告诉你哪两个类别最容易互相混淆——比如龟裂和网状修补裂缝如果这两类互相误判比例很高说明特征太接近你需要检查标注的边界划分。PR_curve则是每个类别的precision随recall变化的曲线曲线越靠近右上角说明模型越好。在推理阶段调整置信度阈值代码里一般会写一个简单的参数入口from ultralytics import YOLO model YOLO(runs/detect/neu_yolov8n/weights/best.pt) results model.predict( sourcedatasets/NEU-DET/images/val/20.jpg, conf0.35, iou0.5, imgsz640 ) result results[0] boxes result.boxes for i in range(len(boxes)): cls_id int(boxes.cls[i].item()) conf boxes.conf[i].item() xyxy boxes.xyxy[i].tolist() print(f缺陷类型: {model.names[cls_id]}, 置信度: {conf:.2f}, 位置: {xyxy})如果把所有图像都用conf0.1跑一遍你会发现某些类别的误检框大量出现。这时候不要盲目调高全局conf更好的做法是分两个档位高危缺陷类如坑槽、裂缝用低阈值保证不漏低危类如修补痕迹用高阈值控制误报。具体的调法从PR_curve上读出你想要的recall点对应的precision阈值既能让那个类别的recall达标又不会让误检数量爆炸。这是工业落地里最实用的一招——模型的mAP只是一个数字生产端真正关心的是设定什么样的阈值能让漏检率和误检率同时达标。再说一个部署时容易被忽略的细节ONNX模型在GPU和CPU上推理的精度表现会有差别因为GPU浮点运算的舍入机制和CPU不完全一致。我的习惯是模型导出后先在目标部署机器上跑一批有标准标注的验证图对比一下C端的mAP和Python端的mAP两者差值超过0.5%就要排查预处理是否完全一致。从那以后我每次做类似的工业检测项目都会强制走一遍这个流程先归一化检查、再letterbox检查、最后阈值微调。每个环节都用同一组验证图对比省下的排查时间足够训练两轮新模型。希望这份资料能让你在热轧带钢缺陷检测的路上少踩几个坑把精力放在真正有价值的调优上。本文还有配套的精品资源点击获取
返回列表