资讯详情

资讯详情

电缆损坏数据集YOLO训练全流程:标签解析、模型训练与避坑指南

简介一份面向YOLO系列算法的电缆损坏目标检测数据集适合需要训练破损检测模型的开发者与研究人员直接使用。资源包共两千个文件以可扩展标记语言和纯文本两种标签格式为主分别为约一千零八十一个和九百一十九个压缩包整体约一百零七兆字节。数据集已预先划分好训练集与验证集内置数据集配置文件可无缝适配多种主流版本的目标检测算法。标签内容包含类别索引、归一化的目标框中心坐标与宽高两种标签格式分别存储于不同文件夹便于在不同训练框架间切换省去格式转换时间。目前已有七十九人学习使用适合作为电缆损伤检测项目的起步数据也方便在此基础上进行二次标注或算法迭代。1. 电缆损坏数据集1318 张带标签图像到底能不能拿来直接训 YOLO这个数据集看起来就是一堆 txt 文件名实际上是一个标准的 YOLO 目标检测数据集1318 张电缆外观图像每张图同时配 YOLO 格式 txt 和 VOC 格式 xml 标签。相比自己组织拍摄、标注、清洗这个包把最耗时的工作省掉了。但拿到手千万别直接双击压缩包里的图片开始训练第一件事是打开标签文件确认类别索引、坐标是否归一化、train/val/test 划分是否干净再决定用哪个版本的 YOLO。适合刚开始做目标检测的开发者也适合要做电缆损坏识别但手里没有现成数据的从业人员。下面按我实际拆包的顺序把数据集结构、训练流程和常见坑逐个过一遍。2. 数据集结构与标签格式txt/xml 双标注的对应关系与坐标换算解压之后看到一堆img_0957_477.txt这样的文件先不要慌这只说明标签文件命名用的是图片名加后缀。整个数据集不是把图片和标签混在一个文件夹而是按规范把 images、YOLO 标签、VOC 标签和 data.yaml 分开放置。为什么要分开放因为训练框架加载标签时会按扩展名自动过滤混在一起容易导致读取错误。2.1 文件清单images、labels_yolo、labels_voc、data.yaml 的存放逻辑常见做法是解压后得到这样几个部分images 目录1318 张电缆外观图像包含 JPG 或 PNG 格式文件名类似 img_0957_477.jpg。labels_yolo 目录与图像同名的 txt 文件每行一个目标内容是 class x_center y_center width height。labels_voc 目录同名 xml 文件按 VOC 格式保存 object 节点里面有 name 和 bndbox。data.yaml训练配置文件声明 train/val/test 路径、类别数量 nc、类别名称 names。表格形式如下目录格式内容用途imagesjpg/png原始图像输入图片labels_yolotxt归一化框坐标YOLO 系列直接训练labels_vocxml绝对像素坐标框VOC 转其他格式data.yamlyaml路径/类别配置训练入口注意这个数据集已经进行了划分train/val/test 分别对应不同的子目录data.yaml 里的 train/val 路径应该填写划分后的目录。如果没有子目录只有一个 images 目录需要自己划分。我拿到包后会先跑一个文件数量核对脚本确认每个图片都有对应标签且数量完全一致。用 bash 循环可以做这个检查for img in images/*.jpg; do base$(basename $img .jpg) if [ ! -f labels_yolo/$base.txt ]; then echo missing yolo label: $img fi if [ ! -f labels_voc/$base.xml ]; then echo missing voc label: $img fi done这个脚本的逻辑是遍历每张 jpg 图片取出不带后缀的文件名然后去两个标签目录找同名文件。如果输出 missing 信息说明该图片缺少对应标签需要补标或者剔除如果没有输出就可以放心进入下一步。这里有个参数容易忽略如果扩展名不是 jpg需要把第一行.jpg改成实际后缀否则basename提取出的名字不对后面的判断就会误报。另一个常见情况是压缩包里既有 .jpg 也有 .png循环只处理了一种容易漏检我一般用find images -name *.jpg -o -name *.png扩展。2.2 YOLO 归一化坐标解析从 txt 一行数值反推像素框YOLO 标签格式描述里写得清楚 x_center y_center 四个坐标值都是相对图片宽高的比例范围 0~1。这意味着同样一行数值在不同分辨率的图片上代表不同的像素区域。你可能看到 0.5 0.5 0.2 0.2以为是一个居中的大方框但如果图片是 1920×1080实际框就是 384×216 像素如果是 640×480实际框只有 128×96 像素。所以解析标签前必须先拿到图片的真实宽高。我处理任何 YOLO 数据集都会写脚本抽查几张图把标签框画出来确认没有跑偏。下面这段 Python 代码可以把归一化坐标转换成像素坐标并打印import cv2 img cv2.imread(images/img_0957_477.jpg) h, w img.shape[:2] with open(labels_yolo/img_0957_477.txt) as f: for line in f: cls, xc, yc, bw, bh map(float, line.split()) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) print(fclass{cls}, box({x1}, {y1}, {x2}, {y2})) cv2.imwrite(check_img.jpg, img)代码里的关键点先用img.shape[:2]取高度和宽度然后用公式x_center ± width/2算出左右边界y_center ± height/2算出上下边界。注意 OpenCV 的 shape 返回的是 (height, width, channels)不要把顺序搞反。打印出的坐标可以画在图上如果框和实际损坏区域重叠说明标签格式正常如果框偏移很大多半是标注时使用了绝对像素坐标但没有归一化这时候训练出来的模型会很不稳定。另外要留意 class 的值。txt 文件第一列是类别索引从 0 开始不是类别名称。数据集的 data.yaml 中的 names 列表才定义了索引对应的文字含义。如果训练时发现 loss 很高也可以先看看是不是类别索引跨越了 nc 范围。2.3 VOC 格式 xml 的读取与转 YOLO 时最常踩的边界错误数据包里还提供了 VOC 格式的 xml 标签文件内容大致是这样的结构annotation object name0/name bndbox xmin120/xmin ymin45/ymin xmax160/xmax ymax80/ymax /bndbox /object /annotation注意这里的 bndbox 是绝对像素坐标不是归一化比例。如果你想用 XML 标签训练 YOLO就要转换成 txt。这个转换脚本看起来不难但有不少细节import xml.etree.ElementTree as ET def xml_to_yolo(xml_path, img_w, img_h): root ET.parse(xml_path).getroot() lines [] for obj in root.iter(object): name obj.find(name).text # 这里需要你的类别映射表例如 names [broken, crack] cls 0 # 从 names.index(name) 得到真实索引 xml_box obj.find(bndbox) xmin float(xml_box.find(xmin).text) ymin float(xml_box.find(ymin).text) xmax float(xml_box.find(xmax).text) ymax float(xml_box.find(ymax).text) x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h lines.append(f{cls} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) return lines参数说明img_w 和 img_h 必须来自实际读图得到的宽高不能想当然用数据集默认分辨率。cls 需要通过names.index(name)动态算出如果 name 本身就是索引字符串也要先转 int 再写如果不用 index 而直接给 0所有类别都会变成同一类。另外输出时保留 6 位小数是为了让宽度高度在小目标场景下不因为四舍五入产生明显偏移。转换完成后如何验证我会挑一张图把 yolo 标签对应回去画框再和 xml 里原始坐标画的框对比如果两个框边缘完全重合转换正确。最容易犯的错是中心点写成(xmin width / 2)宽度写成xmax - xmin却没有除以图像宽高导致数值全部大于 1训练时目标框超出图片边界。这也是我为什么一直强调「归一化」这个动作。3. 直接训练 YOLOv5/v8/v11data.yaml 配置与训练命令数据集格式再标准也要把 data.yaml 写对训练命令才能跑起来。很多失败不是算法问题而是配置文件里的路径和类别数不一致导致的。下面把这部分拆开说。3.1 data.yaml 字段详解path、train、val、nc、names 一个都不能少data.yaml 是整个 YOLO 训练流程的入口内容大致如下path: /absolute/path/to/cable_dataset train: images/train val: images/val test: images/test nc: 2 names: 0: broken 1: crackpath 必须写成绝对路径因为在不同机器上训练相对路径很容易因当前工作目录不同而找不到图片。train/val/test 可以写相对 path 的路径框架会自动拼接成/absolute/path/to/cable_dataset/images/train。nc 是类别数量必须等于 names 的长度。如果你看到的标签类别索引最大是 1那 nc2 是对的如果索引最大是 2那 nc 至少是 3。names 的顺序也要和 txt 中的索引一致例如索引 0 对应 broken1 对应 crack不能调换。很多人会把 train/val 路径写成绝对路径但在不同环境间迁移就要改不如统一用 path 相对路径。另外如果你的数据集目录里已经划分好了 train/val不要把train写成整个 images 目录否则验证集也会被当训练集读进去。这种配置错误不会立刻报错只会让你的 mAP 虚高。3.2 训练命令与参数设置从 YOLOv5 到 v8/v11 的差异YOLOv5 使用独立的 train.pyYOLOv8/v11 使用 ultralytics 包但两者都能读同一份 data.yaml。以 640 分辨率为例# YOLOv5 python train.py --data data.yaml --weights yolov5s.pt --img 640 --epochs 100 --batch 16 # YOLOv8 / v11 yolo train datadata.yaml modelyolov8n.pt epochs100 imgsz640 batch16参数说明--img/imgsz 是输入训练分辨率针对电缆损坏这种小目标640 算基础如果显存允许可以提到 960 或 1280。--batch/batch 是批量大小一般 16 起步显存不足就降到 8。epochs 建议在 100 左右再搭配 early stopping。超参数里比较关键的是mosaic和hsv增强电缆表面纹理复杂过度色彩增强可能让模型关注颜色而不是结构所以如果训练发现 mAP 不稳可以尝试关闭部分增强。在 ultralytics 的 Python 接口里还可以这样写from ultralytics import YOLO model YOLO(yolov8n.pt) model.train(datadata.yaml, epochs100, imgsz640, batch16, workers4, patience20)最后一个参数 patience20 表示如果连续 20 个 epoch 验证 mAP 没有提升训练提前结束。这个参数在长训练中很实用可以节省大量时间。workers4 是读图线程数Windows 上如果报 DataLoader worker 错误把 workers 设为 0 最稳。3.3 数据划分验证train/val/test 互斥与标签完整性数据包描述说已经划分好了但是不是每个子目录都完整仍然需要验证。最粗暴的办法是统计各目录图片数是否与描述数量级一致再检查是否有重叠。用 bash 的 comm 命令可以快速找出两个目录的公共文件名ls images/train /tmp/train.list ls images/val /tmp/val.list comm -12 /tmp/train.list /tmp/val.list | head -20comm -12 的作用是输出两个文件都存在的行。如果这里显示了图片名就说明 train 和 val 有重叠需要重新划分。严格的数据集应当保证图片不重复否则验证集失去意义。另一个检查是确认标签目录和图片目录的名称一一对应可以沿用第 2 章的循环脚本。对比完成后还要用head data.yaml确认路径指向的目录真实存在。常见的坑是复制数据集时只复制了 images忘记复制 labels训练时框架找不到同名 txt就会跳过该图片导致实际训练数量变少但日志里不会明显提示。3.4 从零训练还是迁移学习增量训练时如何保留原有类别如果你希望模型在电缆损坏之外还保持原有类的检测能力需要在 data.yaml 里把原有类和新类一起列出来而不是把 data.yaml 覆盖成只有电缆损坏。常见做法是新建一个 data_all.yaml把 names 设置为原类别加新类别然后在训练时加载官方预训练权重model YOLO(yolov8n.pt)这样模型会从 COCO 预训练的知识开始而不是从随机权重开始。对于 1318 张图的数据量从随机权重训练 100 epoch 也能收敛但迁移学习会让收敛更快小目标检测的稳定性也更好。注意迁移学习时不要把 nc 改小否则最后一层输出的维度会和预训练不一致加载权重时报错。改完 names 后要检查原有标签中是否出现了不在 names 里的旧类别索引。如果原标签有 class 5 而新 names 只有 0~1加载时会直接报 index out of range这时需要做标签映射把所有旧的类别索引重映射到新列表位置。4. 验证与测试mAP、混淆矩阵与标签质量检查模型训练完成后验证环节不能只看一张 loss 曲线图。电缆损坏目标尺度小、背景干扰强需要针对性地看几个指标。4.1 用验证集评估mAP50 与 mAP50-95 的差异在 ultralytics 里执行验证yolo val modelruns/train/exp/weights/best.pt datadata.yaml输出结果里最重要的两个数是 mAP50 和 mAP50-95。mAP50 是 IoU 阈值取 0.5 时的精度mAP50-95 是在 0.5 到 0.95 每 0.05 取一个阈值再平均。如果 mAP50 很高但 mAP50-95 很低说明你的模型虽然能找到目标但框的边界不够准。对于电缆损坏这类需要定位到具体位置的场景mAP50-95 更值得关注。看到 mAP50-95 低我一般先检查训练分辨率。640 分辨率下很多细纹目标只有 10 像素左右自动下采样后特征已经丢失此时把输入放大到 1280或者用更大的模型通常会有改善。另外可以看验证集图片上的预测框如果框普遍偏大或偏小可能是标签里宽度高度计算有问题。4.2 推理时的 letterbox 与置信度调整验证通过后对测试图做推理可以使用 predict 接口from ultralytics import YOLO model YOLO(runs/train/exp/weights/best.pt) results model.predict(sourceimages/test, conf0.25, imgsz640, save_txtTrue, save_confTrue)conf0.25 是置信度阈值低于这个值的预测会被丢弃imgsz 必须和训练时一致否则输入分辨率变化会导致精度漂移。save_txtTrue 会输出预测结果 txtsave_confTrue 会额外把置信度写进 txt。如果你要统计所有可能的误检可以把 conf 降到 0.1再人工筛选。这里有个容易忽略的点ultralytics 在推理时会自动做 letterbox 填充让图片长边缩放到 imgsz短边填充灰色。如果你的数据集中电缆是细长形状letterbox 后会产生大量无效填充区域模型可能把填充边缘误检为目标。遇到这种情况可以试试rectTrue让推理时按图片原始宽高比缩放不强制填充到正方形。4.3 标签质量检查空标签、过小框、越界框输入标签的质量决定模型上限所以训练前和训练后都要检查。下面脚本扫一下所有 txtimport glob import os bad_files [] for txt in glob.glob(labels_yolo/*.txt): if os.path.getsize(txt) 0: bad_files.append((txt, empty label)) continue with open(txt) as f: for line in f: parts list(map(float, line.split())) if len(parts) ! 5: bad_files.append((txt, field count error)) break cls, xc, yc, bw, bh parts if cls 0 or cls 10: bad_files.append((txt, fclass index {cls})) break if bw 0 or bh 0 or bw 1 or bh 1: bad_files.append((txt, box out of range)) break print(fbad files: {len(bad_files)})这个脚本检查三类问题空标签、字段数量不为 5、框宽高超出 0~1。更严格的越界检查需要结合原图宽高如果xc bw/2 1说明框超出图片右边界可能是标注时画到边缘外。对于 1318 张图的数据集这类问题通常不多但混入压缩包时偶尔会发生。检查到坏标签后直接删除该图片和标签或手动重标不要带着问题训练。4.4 混淆矩阵与类别间误检排查验证集评估有时会隐藏一个关键问题类别 A 的目标被误检成类别 B。只看 mAP 很难发现需要看混淆矩阵。ultralytics 在验证结束后会生成confusion_matrix.png横轴是真实类别纵轴是预测类别。如果某个非背景类误检很多矩阵里相应格子颜色就会偏深。电缆损坏类别少时尽量人工检查矩阵。类别 0 预测成类别 1原因通常是两类目标外观接近或者标注时类别顺序混乱。解决方向有两个第一确认训练标签里的类别索引没有整体平移一位比如原本是 0结果标成了 1第二如果确实是类间相似可以用 focal loss 调整难例权重但我更推荐合并相似类别减少模型区分压力。类别越少训练数据越集中精度反而更容易做上去。5. 避坑电缆损坏数据集训练中的五个常见问题与排查在实际操作中新手经常会卡在奇怪的地方。这里挑五个我反复遇到的坑每条都按现象、原因、解决三步写清楚。5.1 现象训练刚开始就报错Label class 2 exceeds nc2 in data.yaml原因txt 标签里存在类别索引为 2 的标注但 data.yaml 里 nc 只有 2YOLO 类别索引从 0 开始最大只允许 1。造成这种情况通常是多份数据合并时类别编号冲突或者标注工具导出的类别没有从 0 重新编号。解决先用命令统计标签实际类别awk { print $1 } labels_yolo/*.txt | sort -nuawk 利用空格把每行第一个字段打印出来sort -nu 得到唯一且排序后的类别列表。如果输出是 0 1说明类别索引正常报错是 data.yaml 写错如果输出 0 1 2就把 nc 改为 3 并补上 names。如果出现 0 1 5那很可能是某些标签文件本身就标错了需要打开对应图片确认而不是盲目扩大 nc 掩盖问题。5.2 现象验证集画框显示框偏移训练 loss 长时间不降原因xml 转 txt 时没有按原图实际宽高做归一化或者图像带 EXIF 旋转信息读取后宽高互换。电缆照片经常来自不同设备手机或相机拍摄时存储了方向信息OpenCV 默认不处理 EXIF导致最后标签和图像错位。解决在读取图像时用cv2.imread(path, cv2.IMREAD_IGNORE_ORIENTATION)禁止自动按 EXIF 旋转保证和标注工具看到的一致。如果是转换问题把转换前后的框画在同一张图上对比确认中心点和宽高完全一致。我一般会在标注阶段就统一去旋转并在转换脚本里打印每张图的宽高防止某张异常图拖垮整个训练。5.3 现象模型 Precision 高但 Recall 低小目标全漏掉原因YOLO 模型在多次下采样后小尺寸目标可能缩小到几个像素甚至消失。电缆裂痕、断口等损坏区域在原图中占比小训练时如果输入分辨率才 640模型几乎没有足够特征去学习这些细节。解决先把训练分辨率提高到 960 或 1280同时增大 batch 保持显存利用率。如果显存有限可以改用模型输入尺寸自动调整或者在预处理中把图片切块让每个 patch 里的损坏目标占比变大。另一种做法是开启 mosaic 增强在训练初期把四张图拼成一张让每一层的特征图都能看到小目标但最后一个 epoch 要关闭 mosaic防止目标被切碎。5.4 现象训练到中途报错CUDA out of memory调小 batch 仍然复现原因数据集图片尺寸差异大训练时自动按 batch 内最大尺寸做 padding导致显存占用不稳定。你把 batch 从 16 调到 8 之后如果还有特别大的图仍然会显存溢出。另一个因素是开启了 cacheTrue图片被提前加载进显存。解决在 ultralytics 里设置rectTrue按宽高比把图片分组同一个 batch 的尺寸接近减少 padding 浪费同时设置batch-1让框架根据显存自动估算最大 batch。如果还不稳定把cacheFalse关闭显存缓存换用内存缓存。对于 1318 张图的规模内存缓存足够速度也不会慢太多。5.5 现象混淆矩阵显示大量背景被误检成损坏类别原因部分图片的标签画得太松把背景纹理也框进来或者类别定义边界模糊。比如电缆本身有磨损痕迹标注者把磨损也算成损坏模型就会学习到「所有纹理复杂区域都输出框」。解决把训练集中所有标签画到图片上逐个检查那些与背景相似的目标。如果确定是误标删除坏标签并重新标注。如果类别本身模糊可以增加一个「背景」负样本类别或者在后处理里把 conf 阈值调高到 0.4牺牲一定召回换取更干净的输出。更根本的做法是检查测试集是否包含足够多的负样本图片如果测试集全是正样本mAP 再高也不能反映实际落地效果。6. 进阶导出模型并批量分析预测框与真实框的重合度训练做完了别急着把模型塞进业务。我会再做一步批量验证将测试集预测结果导出成 YOLO 格式计算每个真实框匹配到的最大 IoU按目标尺寸把失败样本分类这样能看出模型在小目标上到底差多少。6.1 批量预测并保存归一化坐标用 Python 脚本遍历一张张图片保存预测框import cv2 from pathlib import Path from ultralytics import YOLO model YOLO(runs/train/exp/weights/best.pt) test_dir Path(images/test) for img_path in test_dir.glob(*.jpg): img cv2.imread(str(img_path)) h, w img.shape[:2] result model.predict(str(img_path), conf0.25, imgsz640)[0] out_path img_path.with_suffix(.txt).name with open(fpred/{out_path}, w) as f: for box in result.boxes: x1, y1, x2, y2 box.xyxy[0].tolist() xc ((x1 x2) / 2) / w yc ((y1 y2) / 2) / h bw (x2 - x1) / w bh (y2 - y1) / h cls int(box.cls[0]) f.write(f{cls} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}\n)这段代码输出的是归一化坐标方便和 ground truth 在同一空间比较。注意预测结果需要除以当前图片的宽高如果不除后续 IoU 计算会因为图片尺寸差异而全错。conf0.25 是一个起始阈值使用时可以根据验证集情况调整。6.2 计算 IoU 并定位失败样本IoU 函数大家都会写关键是匹配策略。我一般遍历每个真实框在预测框里找最大 IoU如果最大 IoU 低于 0.5就把这张图和 label 转存到一个 hard_examples 目录后期针对性增强。def iou_boxes(box_a, box_b): xa max(box_a[0], box_b[0]) ya max(box_a[1], box_b[1]) xb min(box_a[2], box_b[2]) yb min(box_a[3], box_b[3]) inter max(0, xb - xa) * max(0, yb - ya) area_a (box_a[2] - box_a[0]) * (box_a[3] - box_a[1]) area_b (box_b[2] - box_b[0]) * (box_b[3] - box_b[1]) return inter / (area_a area_b - inter 1e-6)参数说明bndbox 是绝对像素坐标的 [x1, y1, x2, y2]。最后加 1e-6 是防止分母为 0。如果框坐标是归一化的分母同样使用归一化面积结果不变因为同图缩放不影响 IoU。把每张图的平均最大 IoU 排序排名靠后的那张大概率是模糊、遮挡或者目标太小这些样本需要单独处理而不是无脑调轮数。从那以后我每次拿到标注好的数据集都会强制走一遍「标签审计 → 数据划分检查 → 训练 → 批量 IoU 分析」的流程。这套流程走下来模型翻车的概率低了很多。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →