ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

YOLO实战:输电线路数据集VOC转YOLO格式与训练全流程

YOLO实战:输电线路数据集VOC转YOLO格式与训练全流程 简介面向电力巡检、计算机视觉目标检测的研究者与算法工程师这份YOLO算法输电线路数据集提供了3334张带标签图像可直接用于训练和验证YOLO系列目标检测模型。压缩包内共2000个文件全部为XML标注文件包体大小约366.78MB标签中记录了边界框坐标、目标类别等关键信息覆盖电线、绝缘子、铁塔等输电线路组件。配合YOLO算法实时检测的特点该数据集适合开展设备缺陷识别、老化监测、无人机自主巡检等方向的实验也能为模型迁移学习、轻量化部署和异常检测研究提供数据基础。其中包含真实场景图像与精细标注能显著减少从零采集和标注数据的成本已有508人学习下载适合需要高质量电力场景数据来验证算法性能、推进科研或工程项目的开发者。对于电力智能化改造团队还可将XML标注直接对接YOLO训练流程缩短算法迭代周期。1. yolo算法实训入门这份输电线路数据集为什么值得先下载yolo算法做目标检测的人拿到输电线路数据集的第一反应通常是先看标签格式——这份3334张图像带标签的zip解压后全是jpg与xml同名配对的VOC风格标注正好是YOLOv5、YOLOv8这类主流框架在训练前最常遇到的输入形态。它解决的问题很具体电力巡检里绝缘子、导线、铁塔这些组件的检测既缺标注质量过关的公开数据也缺能直接进训练流程的样本集。适合两类人一是刚想用YOLO跑通一个真实场景项目的研究生和工程师二是要验证算法改进小目标、细长目标、遮挡但不想从头标数据的从业者。这篇笔记就按我平时接手一套新数据集的习惯从解压、摸底、转格式、训练到踩坑完整走一遍。2. 先搞清数据集的“长相”3334张图像与XML标签的真实结构2.1 文件组织方式与命名规律拿到压缩包先别急着解压训练第一步是看文件是怎么组织的。这份数据集解压后是图像和标签成对出现的形态每张jpg对应一个同名xml像img_0793_3236.jpg和img_0793_3236.xml这种配对方式。文件名前缀统一是img_0793后面的数字是递增序号说明这批图像来自同一个采集任务大概率是同一条线路的连续巡检拍摄。这种命名习惯在实际项目里很常见但也藏着一个隐患如果采集设备在拍摄中途重启或者丢帧序号会出现跳号导致图片和xml对不上。我拿到这类数据集的习惯是先用脚本核对一遍配对关系而不是直接信文件名。另外压缩包内部如果还有多级目录训练脚本处理起来会麻烦最好先整理成扁平结构。在Linux下用解压命令时注意文件名里的下划线不会被转义但中文路径或者特殊字符就可能出问题所以一解压完就要检查有没有乱码文件。把目录结构理清之后下一步不是打开图片一张张看而是直接解析XML标签因为标签才决定这个数据集能不能训出好东西。2.2 XML标注里藏着哪些关键字段VOC格式的XML标注是整个数据集的信息核心。一份典型的标注文件长这样annotation foldertransmission/folder filenameimg_0793_3236.jpg/filename size width1920/width height1080/height depth3/depth /size object nameinsulator/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin812/xmin ymin345/ymin xmax1024/xmax ymax512/ymax /bndbox /object /annotation解析这份文件时有几个字段必须理解到位。size下的宽高是原始图像尺寸后面做坐标归一化时依赖它如果这个值跟实际图片尺寸不一致转换出来的YOLO标签会全部偏移。object里的name是类别名一份XML里可以有多个object也就是一张图有多个目标。bndbox里的xmin/ymin/xmax/ymax是目标的绝对像素坐标左上角和右下角各一组。两个容易忽略的字段是truncated和difficult。truncated1表示目标被图像边界截断difficult1表示目标难以辨认常出现在目标极小、极度遮挡或模糊的场景。后面转YOLO格式时会说到这两个字段如果处理不当轻则训练样本污染重则训练时报错中断。这里我先提个醒大部分标注工具生成的XML都会有这两个字段但值不一定是0得看这批数据的标注规范。2.3 用脚本盘点类别分布与目标数量在写转换脚本之前我建议先对数据集做一次全面摸底。这一步能帮你发现三个问题有多少个类别、每个类别多少个目标、是否存在类别严重不平衡。输电线路场景里最常见的类别是绝缘子insulator、铁塔tower、导线conductor这几类但你没有读数据前不能想当然得靠脚本统计。import xml.etree.ElementTree as ET import glob from collections import Counter xml_files glob.glob(path/to/xmls/*.xml) class_counter Counter() total_boxes 0 image_size_set set() for xml_file in xml_files: tree ET.parse(xml_file) root tree.getroot() size root.find(size) image_size_set.add((size.find(width).text, size.find(height).text)) for obj in root.iter(object): name obj.find(name).text class_counter[name] 1 total_boxes 1 print(fXML文件总数: {len(xml_files)}) print(f标注框总数: {total_boxes}) print(f图像尺寸种类: {image_size_set}) for cls, count in class_counter.most_common(): print(f{cls}: {count})这段脚本做了三件事用glob拿到所有XML路径用ElementTree解析每个文件再遍历所有object节点统计类别名出现次数。Counter是统计频率的利器image_size_set用来检查这批图像的尺寸是否统一——如果存在多种尺寸后面转换脚本要按各自的size字段归一化不能写死一个分辨率。跑完这个脚本你手里就有了一份数据资产的清晰账目。如果发现某个类别的目标数量只有个位数那这个类别基本训不出来要么补数据要么直接把它从标签里去掉否则模型会把所有框都预测成大类。这一步做完接下来才是真正的格式转换。3. 把VOC标签转成YOLO格式坐标归一化与txt生成脚本3.1 为什么必须转格式两套坐标体系的差异VOC的XML里存的是xmin/ymin/xmax/ymax绝对像素坐标而YOLO系列训练时读的是纯文本txt每行格式是class_id cx cy w h其中cx/cy是归一化后的中心点坐标w/h是归一化后的宽高。四个值全是0到1之间的小数与图像实际宽高无关。这套设计的合理性在于模型对输入图片做缩放时归一化坐标天然适配不同分辨率。但代价是标签文件无法脱离原图独立存在一旦图片尺寸变了标签就全错。这也是为什么转换脚本必须从每份XML的size里动态读取宽高而不是统一用1920或1080去算。我自己踩过这个坑有批数据混入了720P的样本转换时写死了宽高结果那批图的框全都偏了半个身位。常见做法是先读完XML的size字段再做如下换算cx (xmin xmax) / 2 / width cy (ymin ymax) / 2 / height w (xmax - xmin) / width h (ymax - ymin) / height四个值都限制在[0,1]区间如果某个目标被截断坐标值可能越界转换时要做裁切或标记这就是前面提到truncated字段要处理的地方。3.2 转换脚本VOC转YOLO的完整实现下面这份脚本是我每次接到VOC数据都会改改就用的版本支持自动收集类别、过滤difficult目标、输出YOLO格式txt。import xml.etree.ElementTree as ET import os def convert_voc_to_yolo(xml_path, class_names, output_dir, skip_difficultFalse): tree ET.parse(xml_path) root tree.getroot() size root.find(size) width int(size.find(width).text) height int(size.find(height).text) lines [] for obj in root.iter(object): difficult int(obj.find(difficult).text) if skip_difficult and difficult 1: continue name obj.find(name).text if name not in class_names: continue class_id class_names.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) xmin max(0.0, min(xmin, width)) xmax max(0.0, min(xmax, width)) ymin max(0.0, min(ymin, height)) ymax max(0.0, min(ymax, height)) cx (xmin xmax) / 2.0 / width cy (ymin ymax) / 2.0 / height w (xmax - xmin) / width h (ymax - ymin) / height lines.append(f{class_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) base_name os.path.splitext(os.path.basename(xml_path))[0] out_path os.path.join(output_dir, base_name .txt) with open(out_path, w) as f: f.write(\n.join(lines)) return len(lines)这段代码的核心逻辑分四步解析XML并读取图像尺寸遍历所有object节点做过滤决策对坐标做边界裁切后按公式归一化最后拼成YOLO要求的单行格式并写入txt。参数里skip_difficult我建议训练初期设成True把难样本先排除等基础模型训出来再用全量数据微调。class_names是一个列表比如[insulator, tower, conductor]脚本用index()把类别名转成从0开始的整数ID这也是YOLO类别编码的唯一依据。一个容易被忽略的细节是坐标裁切。如果truncated目标的边界框超出了图像范围不裁切的话归一化后会出现大于1或小于0的值训练时YOLO内部解析标签会报AssertionError: labels should be in [0, 1]。所以我在归一化前强制把所有坐标压到[0, width]和[0, height]区间内宁可框变小一点也不让训练中断。3.3 划分train/val随机种子与比例怎么定标签转换完成后还差最后一步把数据划分成训练集和验证集。直接随机分会有两个坑一是同一张图的目标可能既在训练集又在验证集二是用小样本类别如果划分不当验证集里可能一个该类目标都没有。常见做法是固定随机种子按8:2或9:1划分保证可复现。import os import random from glob import glob random.seed(42) image_files glob(path/to/images/*.jpg) random.shuffle(image_files) val_ratio 0.2 val_count int(len(image_files) * val_ratio) val_files image_files[:val_count] train_files image_files[val_count:] with open(train.txt, w) as f: for img in train_files: f.write(img \n) with open(val.txt, w) as f: for img in val_files: f.write(img \n) print(ftrain: {len(train_files)}, val: {len(val_files)})random.seed(42)固定随机源保证每次运行划分结果一致杜绝“上次跑和这次跑数据集不同”导致的对比无效。8:2是最常规的比例如果目标类别多且有重复标注可以考虑9:1给训练留更多样本。按图片路径划分而不是按XML划分是因为YOLO训练时是通过图片路径找对应txt的。划分完把train.txt和val.txt留下来后面训练时--data参数不一定用这两个文件但这份清单在你排查“哪张图在哪个集里”时特别有用。到这一步VOC原始数据已经变成了YOLO直接能吃的格式下面可以进入训练配置了。4. 用YOLOv5/v8训练自己的数据集yaml配置与训练命令实战4.1 数据集目录结构images和labels怎么摆放YOLOv5和YOLOv8对数据集的目录结构有约定虽然不是强制的但按官方约定摆放能省掉很多路径错误。我常用的结构是datasets/transmission/ ├── images/ │ ├── train/ │ │ ├── img_0793_3236.jpg │ │ └── ... │ └── val/ │ ├── img_0793_125.jpg │ └── ... └── labels/ ├── train/ │ ├── img_0793_3236.txt │ └── ... └── val/ ├── img_0793_125.txt └── ...注意图片和标签的目录名必须严格对应images/train里放训练图片labels/train里放对应的txt标签文件名必须完全一致扩展名不同。很多新手翻车就是把标签全放在一个目录里训练时YOLO找不到对应标签日志里全是WARNING: 找不到标签文件模型训完和没训一样。文件组织这块有一个常见做法是从原来的扁平目录里按train.txt清单把文件复制到新结构。这里我用的是shutil.copy而不是move因为在整理阶段原始文件被误删了没有后悔药。整理完检查一下两个目录的文件数是否一致防止源目录里图片多而标签少的情况被带进训练集。4.2 数据集yaml配置path、train、val、nc、names目录结构就绪后在datasets/transmission/下新建一个transmission.yaml内容如下path: /absolute/path/to/datasets/transmission train: images/train val: images/val nc: 3 names: [insulator, tower, conductor]这里四个关键字段逐一说明。path建议写绝对路径写相对路径时YOLO会基于当前工作目录去拼接一旦你换了个目录启动训练就找不到数据。train和val是相对于path的路径指到images下面的子目录即可YOLO会按同名文件自动去labels里找标签。nc是类别数量必须和names列表长度一致少写一个类别训练就报错多写一个类别会训练出大量空预测。names的排列顺序必须和转换脚本里class_names列表顺序完全一致。如果转换脚本里insulator是第0类YOLO这边names第一个也必须是insulator顺序错了整个模型预测结果就全乱了。这里没有ESR级别的报错模型照常训练、loss照常下降但推理时框和标签名全是错位的属于最难排查的一类问题。4.3 训练命令与关键超参数img、batch、epochs、patience配置好yaml后在YOLOv5工程目录下执行训练命令。这里以YOLOv5为例YOLOv8的CLI写法有差异但参数含义相通。python train.py \ --data transmission.yaml \ --weights yolov5s.pt \ --img 1280 \ --batch 16 \ --epochs 150 \ --patience 20 \ --name transmission_exp几个参数的实际含义和调参方向得说透。--img是训练时输入网络的图像尺寸输电线路数据集的目标大多是细长形绝缘子640的输入会把目标压到几十个像素特征几乎丢失所以我通常会直接拉到1280。代价是显存占用翻倍如果你的卡只有8Gbatch就得降到8甚至4。--batch受显存限制训练时监控nvidia-smi不要让显存占用接近100%否则会OOM中断。--patience是早停轮数验证集指标连续20轮不提升就停止训练可以省时间。但我建议第一次训别设太小因为数据质量参差不齐指标可能会有平台期20到30是比较稳妥的范围。--weights yolov5s.pt表示从COCO预训练权重开始微调比从零训练收敛快得多输电线路组件跟COCO里的物体形态差异大但低层特征边缘、纹理是通用的这个策略基本是必选的。训练过程中的输出要重点盯三个值box_loss、obj_loss和mAP0.5。如果box_loss在前30轮基本不降大概率是标签转换出了问题而不是网络结构问题。train结束后在runs/train/transmission_exp/目录下能看到weights/best.pt和last.ptbest是验证集上指标最好的权重后续推理和部署都用它。5. 避坑输电线路数据集训练最常见的五个翻车点5.1 现象解压后图片路径含中文或特殊字符训练报Dataset not found原因zip在Windows下解压时如果原压缩包内含中文目录名或以空格命名的文件路径会被转义或变成乱码YOLO读取数据集路径时直接找不到目录。我在Linux下用unzip命令解压时遇到过文件名编码错乱显示成img_0793_?.jpg这种情况训练脚本一启动就报路径错误。解决解压后第一件事就是跑ls -la检查文件名是否有乱码有中文路径就统一重命名为纯英文数字。我常用的方式是一条rename命令把所有带空格的替换成下划线再检查一遍确保所有路径字符都在ASCII范围内。这是拿钱买不来的习惯数据集到手先在路径上花两分钟后面至少省两小时。5.2 现象训练时标签数量比预期少一大截loss值偏高原因XML里的difficult1目标被直接忽略了。前面转换脚本里skip_difficult设成True就会跳过这些目标如果一份xml里三个目标全是difficult整个txt就是空的。空标签文件在YOLO训练时不报错但等于这张图没目标模型会倾向把它预测成背景。解决我一般在转换阶段生成两份标签一份严格过滤的版本用于第一轮训练一份包含所有目标的版本用于后续迭代。对比两份数据统计可以判断difficult目标一共占多少比例。如果超过10%说明标注质量存疑需要人工抽检而不是单纯调参。处理完再配合训练日志里的labels图表确认每个类别都有足够的正样本。5.3 现象训练正常loss正常mAP也还行但推理时标签和框对应错乱原因类别映射表顺序对不上。转换脚本里class_names列表和yaml里的names列表顺序不一致比如转换脚本是[insulator, tower]yaml写成了[tower, insulator]第0类在两个文件里含义不同。这种情况训练不报错因为数字和loss都是自洽的但模型学到的是错位的映射关系。解决转换脚本里加一行类别映射导出把class_names存成json训练前用脚本把json内容打印出来和yaml手动比对一次。我现在基本是这个流程每次新建数据集都把json和yaml比对当成固定步骤彻底杜绝手写names列表时把顺序搞错。另外推理脚本里加载类别名时也直接读yaml不走硬编码。5.4 现象模型输出一堆小置信度的框或者召回了所有目标但位置全偏原因标签空间分布不均匀。如果标注时目标边界框过大或者过小比如绝缘子的框被标成了包含整串绝缘子的大框YOLO学到的中心点和宽高分布会非常集中导致泛化性差。还有一种情况是图片尺寸不统一转换脚本写死了宽高比例导致归一化坐标失真。解决在跑训练前把标签文件里的宽高分布画成直方图看看是长条形居多还是正方形居多。输电线路上绝缘子通常是细长条长宽比可以到5:1以上如果直方图显示长宽比集中在1:1附近说明标注质量有问题建议重新检查这批数据。分布正常再进入训练这个习惯能帮你消除掉至少一半的模型表现异常。5.5 现象mAP0.5能到0.9但mAP0.5:0.95只有0.3小目标完全漏检原因YOLO的默认anchor尺寸是针对COCO的常规目标设计的输电线路的绝缘子和导线属于小目标加细长目标默认anchor的尺度覆盖不到。再加上训练时如果用了过小的img尺寸小目标特征被压缩得几乎不可见。解决要么把--img拉到1280甚至1536让目标在输入图像里占更多像素要么在数据预处理阶段做滑动窗口切图把大图切成1024×1024的小图块分别训练。切图有一个额外的坑——目标被切在边界时会被切断需要至少保留一个完整的检测框或者对边界框做抑制。这个问题是输电线路数据集里的典型难点没有统一的银弹我一般先试大尺寸输入效果不够再加切图。6. 从“能训”到“好用”验证指标解读与推理侧的小技巧6.1 验证命令与指标解读训练完的模型先用验证脚本确认真实表现这一步做的不是看loss而是看val.py输出的mAP指标。python val.py \ --data transmission.yaml \ --weights runs/train/transmission_exp/weights/best.pt \ --img 1280 \ --task val \ --save-txt \ --save-conf--task val指定用验证集评估--save-txt会把推理结果保存为txt格式的标签--save-conf会把每个框的置信度一并写入。跑完后重点看三列mAP0.5、mAP0.5:0.95和Precision/Recall。mAP0.5是IoU阈值为0.5时的平均精度反映粗粒度检测能力一般能到0.85以上就算合格。mAP0.5:0.95是IoU从0.5到0.95按步长0.05取平均对目标的定位精度要求更高。输电线路这种细长目标mAP0.5和mAP0.5:0.95的差距往往很大这是正常的。如果差距超过0.4说明框的定位精度不足需要从标签质量和输入分辨率两个方向排查。Precision和Recall的取舍也要注意——巡检场景宁可多报假阳性也不能漏检因为漏检一个绝缘子炸串后果比多框一个背景严重得多。6.2 推理侧最实用的一个技巧保持训练和推理的输入尺寸一致很多人训练时用1280推理时为了省时间把输入压到640这会导致小目标检测能力断崖式下降。特征是训练时网络学会的尺度推理时强制改变输入尺寸等于让模型在不熟悉的尺度上做判断。detect.py里面--img参数必须和训练时的--img保持一致。如果确实需要提速优先考虑用TensorRT或者ONNX导出做FP16推理而不是降低输入分辨率。另外对视频流做逐帧推理时如果画面抖动会造成预测框跳动常见做法是做简单的帧间平滑——对连续帧的检测框做IoU匹配置信度取相邻帧的平均值能明显提升视觉体验。这些都属于推理侧的工程优化和训练无关但对落地很重要。6.3 一条提升小目标召回率的务实建议坚持用1280以上的输入尺寸训练整图然后配合滑窗推理作为补充。我见过太多人在数据集上直接套默认配置效果差还以为是算法问题。如果你是做绝缘子这种密集小目标检测可以试一下切图推理的思路同时把注意力放在边界盒的处理上保证被切到边缘的目标不丢失。这类项目的通病是“做了训练没做工程化”导致模型表现和预期差距大实际上很多时候问题不在模型而在推理策略。我自己的教训就挺典型第一次拿类似数据集训练时图省事用640输入跑了100轮mAP0.5有0.78看起来不差后来把输入换到1280同样配置重训一次mAP0.5直接到0.91召回率从0.68升到0.84。从那以后我每次拿到新数据集第一件事就是统计标注框的尺寸分布再决定输入分辨率再进训练。这套流程走顺了数据集的真实价值才能被释放出来希望帮到你。本文还有配套的精品资源点击获取
返回列表