资讯动态

YOLOv3焊缝质量检测复现:权重、标注训练与部署避坑全指南

发布时间:2026/10/4 4:55:56 来源:尧图企业网站定制
简介面向焊缝质量自动检测场景的YOLOv3完整资源包适合工业视觉质检与目标检测方向的开发者和研究者解决焊缝好坏自动识别及数据准备问题。包内既包含已训练好的检测模型权重也附带PR曲线、loss曲线等训练评估结果可直接用于实测推理、效果对比或在此基础上继续微调训练。资源共3403个文件以1134张jpg焊缝/钢材图像为核心配套1134个xml与1135个txt标签文件分别存放于独立文件夹兼顾VOC与YOLO两种标注格式的训练需求压缩包约456.53MBrar格式便于存储与传输。数据由LabelImg工具标注类别覆盖焊缝质量状态适合作为目标检测模型训练、验证与算法对比的标准数据。已有874人学习适合需要快速获得焊缝质检可行方案、完整标注数据集及预训练权重的学习者。1. YOLOv3焊缝质量检测一份模型、权重、标注数据都齐的复现资源我拿到这套YOLOv3焊缝质量检测资源时最直观的感受是“终于不用东拼西凑了”。做工业质检的算法工程师都懂最耗时间的不是模型训练本身而是数据清理和标签格式转换。这个资源把训练好的权重、LabelImg标注的两套标签xml和txt、以及训练过程产出的PR曲线和loss曲线一次性打包好你可以直接拿权重过来推理也可以复现完整训练流程。它的适用人群很明确刚接触焊缝表面缺陷检测、想快速跑通一个YOLOv3基线的从业者或者项目组需要先验证“现有数据下检测精度能到多少”再决定是否上更重方案。它能帮你回答一个很实际的问题这批焊缝图片用YOLOv3训练权重文件到底能不能用、推理边界在哪、在产线落地会遇到哪些坑。下面我按从选型到推理的顺序把这份资源拆开讲。2. 焊缝质量检测任务与YOLOv3选型为什么老模型在工业场景反而好落地2.1 焊缝缺陷检测的任务边界不是所有缺陷都要检出来焊缝质量检测在工业现场的任务划分很清晰第一阶段是缺陷检出也就是在焊接后的表面照片里找到气孔、咬边、裂纹、未熔合这类区域第二阶段才是缺陷分类和严重程度评估通常依赖检测框内的纹理特征、尺寸和形态学参数来做。这套资源里的YOLOv3权重解决的是第一阶段把“哪里有缺陷”以边界框形式输出之后再接人工复核或尺寸测量。很多同行一上来就选YOLOv8甚至更重的模型理由是精度高、部署生态好。但焊缝缺陷和通用目标检测有个本质区别缺陷区域和背景之间的灰度对比度低类别边界模糊气孔和夹渣在低分辨率下几乎就是一个框。YOLOv3那个年代的输出设计反而对这种“小目标但纹理可辨”的场景友好它的三个尺度特征图天然覆盖了从几十像素到几百像素的缺陷尺寸范围不需要额外引入分割分支或注意力模块。从工程角度讲YOLOv3的推理实现到处都是现成版本Darknet源码、OpenCV的dnn模块、各种第三方封装都能直接加载权重。工业现场的工控机往往没有配套的深度学习环境甚至还有不少是Windows 7系统YOLOv3这种纯C生态能跑得很稳。我见过不止一个项目因为新框架依赖的CUDA版本和显卡驱动冲突最后又退回YOLOv3的。2.2 YOLOv3的anchor设计与钢结构表面缺陷的适配逻辑YOLOv3在训练时使用九个anchor分别分配给三个尺度的特征图。焊缝表面缺陷的尺寸分布有自身特点气孔通常直径在20到80像素咬边是长条形宽度窄但长度可以到200像素以上。直接沿用coco预训练权重里的默认anchor损失函数前期很难收敛因为模型需要花时间重新学习先验框的尺寸分布。从这份资源里的数据集命名来看图片是分批收集的缺陷形态各不一样说明标注框的长宽比跨度很大。我在类似项目上做过anchor聚类中心点集中在图片中部偏下长宽比大致有1:1、1:3、1:6三种形态。如果你要重新训练而不是直接用现成权重建议先跑一次K-means聚类把结果写进cfg文件替换默认anchor。import numpy as np from sklearn.cluster import KMeans # 读取所有txt标签提取归一化宽高 def load_wh(txt_dir): wh_pairs [] for txt in txt_dir.glob(*.txt): with open(txt) as f: for line in f: parts line.strip().split() w float(parts[3]) h float(parts[4]) wh_pairs.append([w, h]) return np.array(wh_pairs) wh load_wh(txt_dir) # 聚成9类对应yolov3三个尺度的 anchor kmeans KMeans(n_clusters9, random_state42).fit(wh) # 还原成像素尺寸416输入下乘以416即可 anchors kmeans.cluster_centers_ * 416 anchors anchors[anchors[:, 0].argsort()] print(anchors.astype(int))聚类得到的anchor按宽度排序后从小到大依次对应小尺度特征图、中尺度特征图和大尺度特征图。要注意的是anchor的宽高是归一化到输入尺寸上的这里输入用的是YOLOv3训练常见的416x416如果改用608x608需要重新乘对应倍数。2.3 训练好的权重与数据集目录结构一件资源拆开看这份资源的组成可以分为三块训练好的YOLOv3权重文件、标注好的钢材表面缺陷图片集、训练过程的指标记录。图片是jpg格式命名是dataset_000.jpg、dataset_00143.jpg、dataset_00888.jpg这种不连续编号说明数据来源并非单条产线一次性拍摄而是多批次汇总。标签文件按格式分成两个独立文件夹xml格式是LabelImg导出的Pascal VOC标准结构包含object节点、name和bndbox坐标txt格式是YOLO训练所需的归一化标签每行内容为“类别序号 中心点x 中心点y 宽度 高度”所有坐标值都在0到1之间通过除以图片宽高完成归一化。两类标签并存的意义在于你既可以用txt直接跑YOLOv3系列的训练代码也可以随时切回xml做二次标注比如在现有基础上补充新的缺陷类别再转换一次即可。在实际项目中我会保留xml作为原始标注存档txt作为训练输入一旦发现训练数据有问题回查xml比直接看txt直观得多。文件类型格式典型内容主要用途jpg图片图像文件焊缝表面照片训练与推理输入xml标签VOC标注object节点加bndboxLabelImg二次编辑与存档txt标签YOLO归一化类别id与中心点宽高Darknet及派生框架训练weights权重Darknet二进制模型参数推理与迁移微调曲线图png图片loss曲线、PR曲线验收模型训练效果dataset_000.jpg和dataset_001082.jpg这种编号跨度在划分训练集时是第一个要留意的地方。后文避坑部分我会专门讲如何避免同源图片同时进入训练集和验证集导致PR曲线虚高的问题。3. 从标注到训练把LabelImg产物喂给YOLOv3并跑通训练3.1 数据集格式转换xml转txt的一次性脚本LabelImg导出xml后YOLOv3训练需要txt格式的标签而且txt文件要和jpg图片同名方便训练时按图片路径找到对应标签。数据集里同时有xml和txt两个文件夹说明作者已经做过一次转换但如果你要往数据集里补充新图片这一步就得自己来。下面这个脚本是批量转换的常见做法我把关键容错也加了进去避免标注不规范时直接崩溃。称之为“一次性脚本”是因为跑完一遍后通常就归档不再用但它的正确性直接决定后续训练数据是否干净。import os import glob import xml.etree.ElementTree as ET # 类别列表顺序必须和cfg中classes顺序一致 classes [weld_defect] def convert_xml(xml_path, out_dir): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name is None or name not in classes: continue cls_id classes.index(name) bndbox obj.find(bndbox) # 左上角坐标和右下角坐标 xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 转成YOLO格式中心点坐标和宽高除以图片宽高归一化 if xmax xmin or ymax ymin: continue x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) out_name os.path.basename(xml_path).replace(.xml, .txt) with open(os.path.join(out_dir, out_name), w) as f: f.write(\n.join(lines)) xml_files glob.glob(labels_xml/*.xml) os.makedirs(labels_txt, exist_okTrue) for xml_file in xml_files: convert_xml(xml_file, labels_txt) print(fconverted {len(xml_files)} xml files)这段脚本的几个参数要按数据集实际情况改。第一classes列表的类别顺序一旦确定就不要再动后续cfg里模型输出类别数和这里必须一一对应。第二尺寸读取路径是size/width和size/height这是原图的实际像素值不是标注框的坐标别读错节点。第三坐标值检查是必要的标注时偶尔会出现xmax小于xmin的反向框直接跳过比硬转好。我拿这套逻辑转过几百个xml没出过问题唯一的坑是某些标注软件会残留没有name子节点的object空框所以加了name is None判断。转换完成后建议随机抽查三个txt文件手动对照xml里的bndbox确认归一化后没有出现大于1的异常值。3.2 训练配置文件与超参数一组可复现的数值YOLOv3训练依赖两个配置文件网络结构cfg和训练脚本中的超参数。cfg文件里最关键的是三个位置每个yolo层前面的卷积层filters数量、yolo层的classes数量、以及random参数。filters的计算公式是(classes 5) * 3焊缝单类检测时就是18很多教程里默认写了255对应coco的80类忘记改会导致输出层维度不匹配训练直接报错或收敛异常。训练超参数的设置我一般用下面这组数值打底python train.py \ --data data/weld/weld.data \ --cfg cfg/yolov3-weld.cfg \ --weights weights/yolov3.weights \ --epochs 200 \ --batch-size 16 \ --learning-rate 0.001 \ --weight-decay 0.0005--weight-decay也就是权重衰减它在焊缝数据集上很容易被忽略。默认0.0005是通用目标检测的安全值但工业数据集往往只有几百张图片权重衰减设得太大比如0.01模型会欠拟合训练结束后置信度普遍偏低设得太小又容易把背景纹理当作缺陷特征。我在一个600张左右的焊缝数据集上做过对比0.0005配合随机翻转和光线扰动收敛比较稳。batch-size这里写16是因为工业现场常见的工控机显卡显存只有4到6GB。如果显存不够优先降到8而不是调小输入尺寸因为输入尺寸对缺陷检测的影响比batch-size大得多。学习率0.001是YOLOv3迁移训练的标准起点配合coco预训练权重做微调前20个epoch是主收敛阶段后面是缓慢的震荡下降。3.3 训练日志里看什么loss曲线与PR曲线的判读这份资源里带了训练过程产出的loss曲线和PR曲线说明作者记录了完整的训练指标。对焊缝检测来说loss曲线重点看三个阶段的表现。前期快速下降阶段YOLOv3的loss包含坐标误差、置信度误差和类别误差三部分。焊缝是单类检测类别误差占比小主要看坐标和置信度是否同步下降。如果坐标loss下降但置信度loss横盘大概率是背景区域样本太多模型在学“哪里不是缺陷”而不是“哪里是缺陷”此时应该检查正负样本比例。中期平台期loss在0.1到0.2之间小幅震荡属于正常工业数据噪声大不要追求像coco数据集那样一路降到0.05以下。后期发散阶段如果验证集loss反弹而训练集还在降说明过拟合优先加权重衰减或增强数据扰动而不是提前停止训练。PR曲线针对的是焊缝这类“有缺陷/无缺陷”的检测任务比mAP更直观。它是在不同置信度阈值下以召回率为横轴、精确率为纵轴画出的曲线。焊缝质检更看重召回率因为漏检一个缺陷的代价可能是整批次工件返工所以判断权重好坏的标准不是曲线面积越大越好而是看召回率在0.8附近时精确率还能维持多少。如果这个值在0.7以上权重基本可以进入试部署环节。4. 推理部署与工程质量权重跑起来之后的验收标准4.1 用训练好的权重做批量检测拿到权重后最直接的验证方式是先跑通单张图片推理。Darknet本身带有detector命令但工程化的做法是写Python脚本批量检测把每张图的结果落盘方便后续抽检和缺陷率统计。import cv2 import os import time def load_yolo(cfg, weights): net cv2.dnn.readNetFromDarknet(cfg, weights) # 工控机无GPU时用CPU后端 net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV) net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU) return net def detect_weld(net, img_path, conf_threshold0.5, nms_threshold0.4): img cv2.imread(img_path) h, w img.shape[:2] blob cv2.dnn.blobFromImage(img, 1/255.0, (416, 416), swapRBTrue, cropFalse) net.setInput(blob) layer_names net.getLayerNames() out_layers [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()] outs net.forward(out_layers) boxes, confs [], [] for out in outs: for detection in out: scores detection[5:] max_conf scores[0] if max_conf conf_threshold: cx, cy, bw, bh detection[:4] * [w, h, w, h] x int(cx - bw / 2) y int(cy - bh / 2) boxes.append([x, y, int(bw), int(bh)]) confs.append(float(max_conf)) indices cv2.dnn.NMSBoxes(boxes, confs, conf_threshold, nms_threshold) return img, boxes, confs, indices这段代码有两个关键点。第一cfg和weights必须配对使用权重文件是哪次训练产出的推理就必须用同一个cfg拿coco原版cfg去加载微调后的权重前向输出层结构不匹配可能不报错但检测结果全乱。第二DNN_BACKEND_OPENCV是纯CPU推理一张416x416的焊缝图大约需要80到150毫秒满足离线抽检的需要如果要上实时检测产线需要编译带CUDA的OpenCV或直接用Darknet源码。批量检测时我还会加一个落盘逻辑每张图检测完成后把坐标信息追加到CSV里包括图片名、置信度、框坐标这样后续可以用脚本统计当天检测的缺陷总数和分布。4.2 置信度阈值与NMS参数的调整逻辑焊缝检测的置信度阈值不能套用通用目标检测默认的0.5。工业场景里光照、飞溅、打磨痕迹都会让缺陷特征发生偏移同一个缺陷在亮区和暗区的置信度能差出0.2以上。所以推理前我会做一次阈值扫描for conf in [0.3, 0.4, 0.5, 0.6]: img, boxes, confs, indices detect_weld(net, test_img, conf_thresholdconf) detected len(indices) if indices is not None else 0 print(fconf{conf} detected boxes{detected})观察阈值从0.5降到0.4时检出框数量的变化幅度如果框数量翻倍说明模型对一部分缺陷的置信度本来就压在0.4到0.5之间产线场景就应该把阈值定在0.4或更低。焊缝是漏检成本远高于误报成本的场景宁可多报几个框交给人工复核也不要追求肉眼看起来干净的检测结果。反过来说如果阈值降到0.3框的数量也没有显著增加说明模型本身对缺陷的响应不强问题出在训练环节调阈值只能缓解。NMS的iou阈值我默认用0.4但在处理长条裂纹时会把两个相邻小框合并成一个长框导致框长度明显不足。这种场景可以降到0.3让NMS更激进地合并重叠框。NMS阈值的调整要用肉眼验证看的是框是否贴合缺陷边界而不是看框的数量。4.3 检测结果可视化与焊缝区域裁剪部署阶段有个容易被低估的环节结果输出。直接把框画在图上当然可以但如果模型要交给现场质检员使用最好是同时输出裁剪后的缺陷子图方便二次确认和归档。def save_crops(img, boxes, indices, out_dir): if indices is None: return for i in indices.flatten(): x, y, bw, bh boxes[i] # 四周扩20像素保留缺陷周围的熔池纹理 x0 max(0, x - 20) y0 max(0, y - 20) x1 min(img.shape[1], x bw 20) y1 min(img.shape[0], y bh 20) crop img[y0:y1, x0:x1] cv2.imwrite(f{out_dir}/crop_{i}.jpg, crop)扩边20像素不是随意定的。焊缝缺陷的判定往往要看缺陷周围的熔池纹路和热影响区只裁标注框内的区域会把最有判断价值的上下文丢光。裁剪输出后按日期建子文件夹管理后期统计缺陷率、按班次追溯质量问题时这套目录结构能省大量时间。部署参数推荐值调整依据conf_threshold0.4漏检代价高时降到0.35nms_threshold0.4长条缺陷合并时降到0.3输入尺寸416显存充裕可试608提升小目标召回后端CPU OpenCV有CUDA时用GPU推理5. 避坑清单焊缝数据集复现中最容易翻车的五件事5.1 现象模型训练loss能降但推理几乎不检出缺陷这个现象我遇到过不止一次。问题根因是图片尺寸不一致存在多种宽高比从接近正方形的相机原始图到2:1以上的截图都有。YOLOv3推理会把输入统一resize到416x416细长图片里的缺陷被横向压缩后纹理丢失置信度全部压在0.2以下正常阈值当然什么都检不出来。解决方法是训练和推理都做letterbox处理先把图片按比例缩放到短边416再在另一边用灰色填充而不是直接拉伸。这一步在Darknet cfg里对应letter_box1参数或者在预处理代码里用cv2.resize配合填充实现。处理后的图片保留了原始宽高比缺陷形态不会被破坏。5.2 现象训练时报错“All bounding boxes may have only 0 or 1 class”这是数据集格式转换阶段的典型问题。原因有两类一类是xml转txt时类别名和classes列表不一致比如标注员写的是“ 未熔合”带了个空格或者“未熔合”和“未融合”混用类别id匹配不到就跳过另一类是数据集里存在没有object节点的空xml文件转换脚本直接写了一个空txt训练时读不到有效框。解决方法是转换脚本里加严格的容错逻辑并在转换完成后统一校验。我自己的习惯是统计所有txt文件每行的第一个数字检查它们是否都在类别范围内一旦出现越界值就定位到对应的xml人工修复。这一步虽然繁琐但比训练到一半才发现数据错误省时间得多。5.3 现象OpenCV加载权重时Shape不匹配或输出全为0加载权重时Shape不匹配几乎都是cfg的filters和权重文件训练时的结构不一致。常见场景是修改了类别数但只改了yolo层的classes没有改前一层的filters。拿单类焊缝检测举例filters必须是18如果你下到的权重是在别的cfg基础上微调过的推理端就必须用生成它时的那个cfg换一个cfg再干净也可能崩。解决方法是把cfg文件当作权重的配套档案看待同一次训练的cfg和weights放在同一个目录里不要手动改名。我在部署时还会加一个检查输出层的shape维度是否等于3乘以类别数加5不匹配就直接终止程序而不是继续跑出乱结果。5.4 现象PR曲线很漂亮但现场测试效果很差PR曲线漂亮而现场效果差最常见的原因是数据集泄漏。这套资源里的图片编号既有dataset_000.jpg这种短编号也有dataset_00888.jpg这种长编号说明收集自不同批次和不同机位。如果划分训练集和验证集时直接按文件列表顺序切分同一个焊缝的角度变化或连续帧可能同时出现在两边验证指标自然虚高但模型实际没见过真实的拍摄多样性。解决方法是按编号前缀或者收集批次分组划分数据集保证一个物理实体的图片不跨集合。具体做法是先把所有图片按编号前缀聚类然后对聚类结果做随机划分这样验证集能真实反映模型面对新焊缝时的表现。5.5 现象程序跑着跑着被kill或者显存直接溢出推理程序被kill的问题经常被忽略它不一定是内存不足也可能是批量检测时图片一次性全部读进内存。如果数据集有几万张焊缝图每张原图都是几千像素全部读进来内存很容易被吃满。训练时显存溢出则多半是batch-size设太大或者开启多尺度训练后输入尺寸动态变化显存峰值不可控。解决方法是推理端改为流式处理一次只读当前批次图片检测完成后立刻释放内存训练端把batch-size降到8起步如果还是溢出进一步开启subset按比例抽样训练。工控机显存只有4G的话不要同时开mosaic增强和多尺度训练两者叠加的显存峰值会显著高于单开。6. 不看曲线也能验证权重的四个现场技巧6.1 用极端图像测试鲁棒性PR曲线反映的是整体统计性能但产线现场的图片光照和收集时差距很大。我拿到权重后会专门挑三类图测强反光表面的焊缝、夜间低照度图、以及有人手或工具遮挡的半成品图。正常权重在这三类图上应该出现置信度轻微下降但坐标框仍然稳定的现象如果框在连续两帧之间跳来跳去说明特征提取不够稳需要回炉再训。6.2 用标注框和预测框的IoU分布判断过拟合取三张模型没见过的图片逐框计算标注框与预测框的IoU。焊缝检测的IoU普遍比通用目标低因为缺陷边界模糊人工标注本身就有偏差IoU大于0.5就算可用。如果数据显示所有预测框的IoU都集中在0.9以上先怀疑数据泄漏而不是模型强大。我在资源复现时就用这个办法发现验证集里混进了训练集同源图片原因就是IoU高得不正常。6.3 打印每个类别的召回率漏洞即使整个数据集的平均召回率达标也要按类别单独看。焊缝缺陷单类模型没有这个问题但如果你把数据集重新标注成多类别比如气孔、裂纹、夹渣分开训练务必输出每个类别的召回率。气孔和裂纹在低分辨率下形态接近模型常常倾向于检出其中一类而忽略另一类平均指标掩盖了这个矛盾。单独看每个类的召回率才能发现模型对哪类缺陷不敏感。6.4 固定随机种子复跑一次训练本身的随机性会导致权重差异固定随机种子能让结果可复现也便于不同配置之间做公平比较。Darknet支持在cfg或者启动命令里设置seedPyTorch版本可以在训练脚本里用torch.manual_seed(42)。从那以后我每次拿到新的焊缝数据集都会强制走一遍极端图像测试和IoU分布检查跳过这两步的模型我从来不敢直接上产线因为曲线反正是可以“刷”出来的真正决定权重生死的是它在陌生图片上的反应。希望这套YOLOv3焊缝检测资源能帮你少走我当年走过的这些弯路。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑