尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

电力输电线路目标检测数据集处理与YOLOv8训练避坑指南

电力输电线路目标检测数据集处理与YOLOv8训练避坑指南 简介面向电力行业智能巡检与设备状态监测场景这份电力输电线路目标检测数据集包含969张工业现场实拍图片覆盖绝缘子、电缆接头、连接金具等35类输电线路核心部件并划分训练集674张、验证集189张、测试集106张。所有标注均采用YOLO格式边界框由领域专家校验可直接用于YOLOv5/YOLOv8等主流目标检测模型的训练与评估。资源包共1940个文件内含969个jpg图像、969个txt标签文件另附yaml配置文件与docx说明文档整体仅88.54MB便于下载和快速迭代实验。数据集源自真实输电场景包含正常、老化、安装位置差异等多种工况适合用于无人机巡线缺陷识别、设备老化预警、变电站监控等方向的研究与产品开发。已有496人学习下载适合算法工程师、电网运维人员及高校研究者作为模型训练和算法验证的基础数据。1. 电力输电线路目标检测数据集为什么难搞的从来不是模型而是数据做输电线路巡检的人大多数都被同一个问题卡住目标检测模型不难搭难的是手里没有一份能用的数据。电力输电线路目标检测数据集这种资源恰恰把最费时间的采集和标注环节替你预压缩好了解开压缩包就是图片加标签直接进训练流程。真正适合它的是正在做无人机巡检、绝缘子破损检测、电线杆异物识别的人。新手缺的是标注规范和类别定义熟手缺的是小目标样本和难例这类数据集往往能把两件事一起解决。但别急着跑训练先把包里的标签格式、类别顺序和图像尺寸看清这一步能省掉后面至少一晚上的调参时间。2. 拆开zip先看数据结构标注格式、类别顺序与小目标分布2.1 先看压缩包内部结构再决定要不要写转换脚本拿到一个电力输电线路目标检测数据集我先做的不是解压。因为解压一旦发现格式不对又得重新整理目录浪费一个晚上。更好的做法是直接用 zipfile 模块把压缩包当黑匣子先探一圈内部的组织方式。import zipfile from collections import Counter zip_path 电力输电线路目标检测数据集.zip with zipfile.ZipFile(zip_path) as zf: names zf.namelist() print(总文件数:, len(names)) suffix_count Counter(n.split(.)[-1] for n in names if . in n) print(suffix_count) for n in names[:30]: print(n)逻辑说明这条脚本会先打印压缩包内部所有文件名再按后缀统计出现次数。在不解压的情况下就能看出有没有 Annotation 或 xml 目录、有没有 jpg 或 png 图像以及标签到底是 json、xml 还是 txt。参数说明zip_path 换成你实际的压缩包路径suffix_count 统计常用于判断是否存在大量 json 标注文件和 jpg 图像。如果看到 xml说明标签可能是 VOC 风格如果看到 txt 且数量和图片一一对应大概率已经是 YOLO 风格。这里有一个很多新手容易跳过的细节不要只看后缀还要对比标签文件和图片文件的数量。输电线路场景经常出现无人机拍摄的连续帧里有空镜头上一版标注工具可能漏标了某几帧没提前发现的话训练时模型会被空图片干扰验证指标也跟着飘忽。所以一般我会在看完文件清单后再做一次数量对照检查。images [n for n in names if n.endswith(.jpg)] labels [n for n in names if n.endswith(.txt)] print(图片数量:, len(images)) print(标签数量:, len(labels))2.2 解析标注内容类别统计、目标尺寸和归一化坐标确认标签是 YOLO 的 txt 格式后下一步是读内容。电力输电线路里的目标不是常规的行人车辆像绝缘子、防震锤这类部件往往只占整张图像的千分之几如果不提前统计目标尺寸后面训练时会一直卡在漏检上还误以为是模型能力不行。import glob from collections import Counter label_files glob.glob(labels/*.txt) category_counter Counter() box_sizes [] for label_file in label_files: with open(label_file, r, encodingutf-8) as fp: for line in fp: parts line.strip().split() if len(parts) 5: continue cls_id int(parts[0]) w float(parts[3]) h float(parts[4]) category_counter[cls_id] 1 box_sizes.append((w, h)) print(类别分布:, category_counter) small_target [1 for w, h in box_sizes if w 0.1 and h 0.1] print(小目标占比:, len(small_target) / len(box_sizes))逻辑说明这段代码会遍历 labels 目录下所有 txt读取每一行的 class id 和归一化后的 bbox 宽高。YOLO 格式的统一约定是 class x_center y_center width height四个坐标都除以了原图宽高结果在 0 到 1 之间。参数说明len(parts) 5 的 continue 是在跳过空行或损坏行w 0.1 and h 0.1 是我判断小目标的经验阈值如果占比超过 40%基本可以认定这套数据必须上小目标优化方案。一般来说电力输电线路数据集里的类别会围绕巡检任务展开常见的有绝缘子、防震锤、间隔棒、均压环、杆塔、导线等。不同数据集的 classes.txt 顺序差别很大有的把杆塔放在第 0 类有的把绝缘子放在第 0 类。拿到数据后一定先读 classes.txt再决定训练配置里的 names 怎么写顺序错了后面全是无效训练。2.3 类别顺序与 classes.txt 映射最容易翻车的地方我见过不止一次训练损失正常下降但预测时发现模型把绝缘子全部标成防震锤。最后查了半天才发现classes.txt 的顺序和标注文件里的 class id 对不上。比如标注里用 0 代表 insulator训练 yaml 里 0 却写成了 vibration_damper。为避免这种问题我建议把类别映射做成一个独立文件不靠肉眼记。with open(labels/classes.txt, r, encodingutf-8-sig) as fp: classes [line.strip() for line in fp if line.strip()] for i, name in enumerate(classes): print(i, name)逻辑说明直接从 classes.txt 里读类别名按行号生成 id。之后无论是生成 data.yaml 还是写转换脚本都让脚本读这份文件而不是手敲映射表。参数说明classes.txt 路径按实际位置改如果文件带 BOM用 encodingutf-8-sig 读能避免第一行出现 \ufeff 这种隐藏字符。顺手做图像尺寸检查也很值得。航拍大图常见的是 4000x3000 这种尺寸如果压缩包里图片尺寸差距过大说明可能存在不同相机来源后面训练增强策略要相应调整。from PIL import Image import glob image_files glob.glob(images/*.jpg)[:20] for image_path in image_files: with Image.open(image_path) as img: print(img.size)这里抽前 20 张做抽样检查就够了。如果发现尺寸分布跨度大训练时统一用 imgsz 参数并打开 letterbox 填充而不是直接 resize否则标注框的坐标会被拉伸得对不上。3. 把数据集对齐到YOLOv8VOC转换、分组划分与训练命令实战3.1 VOC 标注转 YOLO坐标归一化与边界清理如果压缩包里是 VOC 格式的 xml就需要先转成 YOLO。VOC 里保存的是 xmin ymin xmax ymax 的绝对像素坐标YOLO 需要的是归一化后的中心点坐标和宽高二者不能混用。常见做法是写一个转换脚本逐段读取 bndbox 节点。import xml.etree.ElementTree as ET class_mapping {insulator: 0, vibration_damper: 1, spacer: 2, tower: 3, conductor: 4} def convert_voc_to_yolo(xml_path, out_path): 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): name obj.find(name).text if name not in class_mapping: continue box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center (xmin xmax) / 2 / width y_center (ymin ymax) / 2 / height w (xmax - xmin) / width h (ymax - ymin) / height x_center max(0.0, min(x_center, 1.0)) y_center max(0.0, min(y_center, 1.0)) w max(0.001, min(w, 1.0)) h max(0.001, min(h, 1.0)) lines.append(f{class_mapping[name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_path, w, encodingutf-8) as fp: fp.write(\n.join(lines))逻辑说明函数接受 xml 路径和输出 txt 路径先读图像宽高再遍历所有 object 节点把四个角点换算成中心点与宽高。clip 和 min 这两步不是多余的很多标注工具在图片边缘会把框拉出边界导致 w 或 h 大于 1进入训练后会让 loss 数值异常。参数说明class_mapping 必须根据数据集自带 classes.txt 修改如果里面没有 conductor 类就把对应映射删掉保留 0.001 的下限是为了避免出现宽高为 0 的退化框。跑完转换脚本后要抽查几个文件。典型做法是随机挑三张图用 OpenCV 画框看位置是否贴合。import cv2 image_path images/IMG_001.jpg txt_path labels/IMG_001.txt img cv2.imread(image_path) height, width img.shape[:2] with open(txt_path, r) as fp: for line in fp: parts line.strip().split() if len(parts) 5: continue x_center float(parts[1]) * width y_center float(parts[2]) * height w float(parts[3]) * width h float(parts[4]) * height x1 int(x_center - w / 2) y1 int(y_center - h / 2) x2 int(x_center w / 2) y2 int(y_center h / 2) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.imwrite(check.png, img)逻辑说明把 YOLO 的归一化坐标反算回像素坐标再画到原图上。写成 check.png 比对 imshow 更省事尤其在服务器上跑训练时直接把图拖到本地看就行。3.2 数据划分按杆塔分组而不是随机打散数据划分往往被当成小事在输电线路场景里却是影响验证集有效性的关键。无人机拍摄的巡检视频往往绕着一个杆塔连续拍几十帧画面高度重复。如果只按随机比例划分训练集里出现过的机位和角度验证集里大概率也有mAP 虚高得厉害。我一般按文件名前缀代表一次巡线任务来做分组划分。import os import random from collections import defaultdict image_dir images val_ratio 0.2 random.seed(42) groups defaultdict(list) for f in os.listdir(image_dir): if f.endswith(.jpg): group_key f.split(_)[0] groups[group_key].append(f) train_list, val_list [], [] for group_key, files in groups.items(): random.shuffle(files) split_idx max(1, int(len(files) * (1 - val_ratio))) train_list.extend(files[:split_idx]) val_list.extend(files[split_idx:]) with open(train.txt, w) as fp: for f in train_list: fp.write(os.path.join(image_dir, f) \n) with open(val.txt, w) as fp: for f in val_list: fp.write(os.path.join(image_dir, f) \n) print(train:, len(train_list), val:, len(val_list))逻辑说明按文件名下划线前的字段把所有样本分到若干组再按组划分。这样同一段巡线视频的连续帧不会一半在训练集一半在验证集。参数说明image_dir 改成实际图片目录如果数据集文件名没有明显前缀规律就换成按目录划分比如每个杆塔一个文件夹按文件夹名分组。这种划分方式会让验证集比随机划分更难mAP 略低但这个低才是真实水平。遇到 val 指标比训练时高出很多的情况先想想是不是划分方式偷了懒。3.3 data.yaml 与模型选择小目标优先选大输入尺寸数据准备好之后需要写一个 data.yaml。YOLOv8 会用它来找训练集、验证集和类别名。names 的顺序必须和 labels 里 class id 完全一致否则模型等于在用错误标签监督。path: D:/datasets/power_line train: train.txt val: val.txt names: 0: insulator 1: vibration_damper 2: spacer 3: tower 4: conductor逻辑说明train 和 val 指向文件列表 txt里面每一行是图片的完整路径。YOLOv8 支持这种写法比直接写目录路径更可控。参数说明path 最好写绝对路径避免在本地和服务器之间来回切如果用了 classes.txt建议让脚本直接生成 names 字段不要手敲。模型选择上我倾向于先用 yolov8s 起步而不是一上来就用最大的模型。电力输电线路数据通常有几千到几万张类别集中在五六个类yolov8s 的容量足够承载而且迭代速度快适合快速验证数据质量。yolo detect train datapower_line.yaml modelyolov8s.pt epochs120 imgsz896 batch8 patience15 device0 projectruns/power_line参数说明epochs120 对中小规模数据集够用patience15 表示连续 15 轮验证集没有提升就提前停止imgsz896 是这里最值得说的参数。输电线路里的绝缘子、防震锤在原始大图中占比非常小用默认 640 输入相当于把目标缩成几个像素所以我会把输入尺寸拉到 896让模型有更多像素去学纹理。batch8 在单卡 24G 显存下比较稳显存小就降到 4同时把 imgsz 降到 768。device0 指定第一张卡多卡可以写成 device0,1 并相应调大学习率。YOLOv8 默认会开启 mosaic、mixup 等增强。在这类小目标数据集上mosaic 能凑出包含多个小目标的混合图对提升召回帮助明显。如果发现训练初期 loss 不稳定把 mosaic 概率降到 0.5 即可留一半原图让模型先学基础特征不要一上来就迷信全开增强。4. 电力输电线路训练避坑指南5个高频翻车点的现象、原因与解决4.1 中文路径导致图片读取失败现象解压后的目录名是“电力输电线路目标检测数据集”训练一开始就跑出 ImageNotFound 错误或者数据缓存阶段直接卡住。原因Windows 下中文路径和 Linux 服务器的编码方式不一致OpenCV 读取含中文的路径时经常会读不到文件。解决解压后立刻把所有目录名改成英文比如 power_line_dataset、images、labels用 os.rename 批量处理旧目录不要手动一个个改。改完重新生成一次 train.txt 和 val.txt保证里面的路径也全是英文。4.2 真值框越界导致 loss 变 None现象训练到第三个 epoch 时 loss 突然变成 nan或者验证阶段 mAP 直接归零。原因原始标注在图片边缘标注工具把框拖出了边界转换脚本又没做 clipYOLOv8 在读取标签做增强时拿到了非法坐标。解决在 VOC 转 YOLO 的脚本里对 x_center、y_center 做 0 到 1 的 clamp对 w 和 h 也做 clamp并且加一个 0.001 的下限。训练前再跑一遍标签扫描把所有 w 或 h 小于 0.001 的样本过滤掉宁可丢几张图也不能让脏标签污染训练集。4.3 classes.txt 顺序不一致导致错检现象训练 loss 正常验证 mAP 也不低但混淆矩阵对角线之外有密集的误差防震锤频繁被识别成绝缘子。原因数据处理时用的是 A 顺序的 classes.txt训练 yaml 里却按 B 顺序写的 names同一个数字在不同文件里对应不同类别模型学到的标签语义全是错的。解决只信任压缩包内 classes.txt 这一份。用一个 Python 脚本读它自动生成 data.yaml 的 names 字段不要再手动维护映射表。这个脚本值得长期留档换任何数据集都能复用。4.4 小目标漏检严重召回率上不去现象mAP50 看着不错但 recall 一直低于 0.7防震锤和间隔棒这类小件大量漏检。原因输入尺寸默认 640 时这类目标只有十几个像素特征被下采样抹掉了。解决把 imgsz 提高到 896或者直接用 YOLOv8 的 P2 模型比如 yolov8s-p2.pt它多了一个 4 倍下采样特征层对小目标更友好。更通用的方案是 patch 推理把原图切成 1024x1024 的 tile分别检测再合并结果。输电线路巡检图像通常分辨率很高这个切图技巧能立竿见影地提升小目标召回。4.5 显存 OOM 中断训练现象训练到一半爆出 CUDA out of memory但 batch 和 imgsz 看起来都不算大。原因YOLOv8 默认开启 AMP 混合精度部分老显卡对自动混合精度的支持不完善加上 mosaic 增强会临时生成更大的拼接图峰值显存远超理论值。解决把 batch 降到 4imgsz 降到 768同时设置 workers4 减少数据加载瓶颈。如果还是不稳定就关闭 AMP把训练收敛到一定程度后再手动开启微调。最稳的做法是正式训练前先跑一个 20 步的 smoke test观察显存峰值再决定要不要加大 batch。5. 验证调优的进阶技巧用mAP50-95和混淆矩阵定位误检5.1 先看 mAP50-95再看 mAP50刚接触目标检测的人习惯只盯 mAP50在输电线路场景里这是不够的。电力部件形状细长容易出现定位偏差mAP50 只要求框和真值 IoU 超过 0.5很多错位明显的框也算正确。更严格的是 mAP50-95它把 0.5 到 0.95 之间十个 IoU 阈值做了平均能真实反映框的定位精度。yolo val modelruns/power_line/weights/best.pt datapower_line.yaml imgsz896常规做法是训练完用 best.pt 跑一次 val输出里重点看 metrics/mAP50(B) 和 metrics/mAP50-95(B)。如果两个值差距超过 15 个百分点说明分类问题不大但框的回归不准优先提高输入尺寸或补充更贴近边缘的标注数据。5.2 用混淆矩阵找漏检源头YOLOv8 的 val 输出会在 runs/power_line/val 目录下生成 confusion_matrix.png这张图能直接告诉你哪个类别被错认成哪个类别。电力场景里常见的是绝缘子与防震锤互相混因为它们外观都是长条状。如果误检集中在相似类别之间说明特征区分度不够需要补这两个类别的难例而不是盲目增加全部数据量。5.3 自动筛选高置信度误检样本一次训练不要只看指标要回到图像层面找 badcase。我的习惯是写一个简单脚本把 val 预测结果里置信度很高但真值里完全不存在的样本单独拷贝出来。import os import shutil pred_dir runs/power_line/val/predict/labels gt_dir labels/val image_dir images/val out_dir hard_examples os.makedirs(out_dir, exist_okTrue) for pred_file in os.listdir(pred_dir): gt_file os.path.join(gt_dir, pred_file) if not os.path.exists(gt_file): image_file os.path.join(image_dir, pred_file[:-4] .jpg) if os.path.exists(image_file): shutil.copy(image_file, out_dir)逻辑说明遍历 predict/labels 下的预测文件如果真实 labels 里没有同名文件说明模型在某个位置预测了目标但真值没有把对应的原图复制到 hard_examples。文件名命名规则以实际输出为准这段脚本主要提供筛查思路。人工看一眼这些图要么是标注漏了要么是模型幻觉。如果是标注漏了补一批框再训练效果提升比盲目调参明显得多。从那以后我每次训练前都强制走一遍先看 zip 内部结构再跑数据统计最后抽三张图画框三步做完才敢点训练。希望帮到你。本文还有配套的精品资源点击获取
返回列表