ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

YOLO算法实战:7255张电线杆杆顶数据集处理与训练全流程

2026/9/14 1:30:29 拓冰建站 浏览量
YOLO算法实战:7255张电线杆杆顶数据集处理与训练全流程 简介面向YOLO算法训练与电力巡检场景的电线杆检测数据集内含7255张带标签图像标注目标为杆顶适用于训练基于YOLO系列的目标检测模型支持智能电网维护、输电线路自动巡检等应用中的电线杆识别与状态监测。压缩包内共2000个XML标注文件与图像一一对应每个XML包含杆顶边界框及类别信息可配合YOLO格式转换脚本直接用于模型训练整个压缩包约246.3MB体积适中便于下载与本地处理。目前已有157人学习下载。数据集覆盖不同光照、角度、遮挡及复杂背景下的杆顶样本规模较大且带精确标签有助于提升模型泛化能力减少人工巡检成本为输电线路智能化管理提供高质量训练基础。同时也可作为计算机视觉目标检测教学与算法对比实验的实用数据来源。1. 为什么一个“yolo算法-电线杆数据集-7255张图像带标签-杆顶.zip”值得拆开收到这样一个 zip先别急着解压就开训。标题本身已经把价值说清楚了7255 张图像对一个垂直场景来说是中等规模比从无人机视频里逐帧抽帧再清洗省太多时间标签也带上了省掉了目标检测项目里最费钱的标注环节。把 YOLO 算法跑在这种杆顶数据集上最常见的落地场景是电力巡检、杆塔普查和城市基础设施管理模型要识别的是杆塔顶端的绝缘子、横担、金具、避雷线这些目标。和你平时用 COCO 或 KITTI 不同这类“电线杆杆顶”数据集有一个典型特征目标数量少、单个目标尺度小、背景却很杂——天空、植被、电线混在一起。这类 zip 的价值不在“能跑通 demo”而在于帮你验证下游策略标签框准不准类别分布均不均小目标到底占多少。这些不查清楚YOLO 再怎么调参也救不回来。接下来按解包、审计、转换、训练、调优的顺序走一遍每一步都给出能直接抄的命令和脚本。处理这类数据集你迟早会遇到早做总比训练到一半返工好。2. 解包以后先别训练审计 7255 张图像与标签的结构2.1 zip 解包后目录结构怎么摆压缩包名字是yolo算法-电线杆数据集-7255张图像带标签-杆顶.zip但 zip 内部的目录结构每次都不一样。拿到手先解压然后用tree看一层。unzip yolo算法-电线杆数据集-7255张图像带标签-杆顶.zip -d pole_dataset cd pole_dataset tree -L 2 -d常见结构有两种一种是images/和labels/平级另一种是train/val/下再各带images/和labels/。我一般更接受第二种因为 YOLOv8 官方约束不强但后续训练时train.txt和val.txt生成会少一层路径处理。提示如果 zip 解压后没有labels只有xml或json那说明原始标注不是 YOLO 格式直接跳到第 3 章做转换。目录靠近下面这样时可以直接开始数据审计pole_dataset/ ├── train/ │ ├── images/ │ │ ├── 0001.jpg │ │ └── ... │ └── labels/ │ ├── 0001.txt │ └── ... └── val/ ├── images/ │ └── ... └── labels/ └── ...很多打包数据集不会帮你切好 train/val只给一个总目录。这时候别急着用脚本乱切先看同名的图像和 label 文件是否一一对应同一张图不要同时出现在两个集合里否则验证集指标会虚高。2.2 用 Python 统计类别数、框数量和目标尺度YOLO 标签是老格式每一行是class_id x_center y_center width height全部归一化到 01 之间。先用一个辅助脚本把维度统计出来这一步比跑任何训练脚本都优先。# label_stats.py # 统计 YOLO txt 标签里的类别数量、目标总数和框尺度分布 import os from collections import Counter labels_dir train/labels class_counter Counter() box_count 0 tiny small medium large 0 for txt in os.listdir(labels_dir): with open(os.path.join(labels_dir, txt), r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue class_id int(parts[0]) w float(parts[3]) h float(parts[4]) box_count 1 class_counter[class_id] 1 # 按归一化后的面积粗分尺度 bbox_area w * h if bbox_area 0.001: tiny 1 elif bbox_area 0.01: small 1 elif bbox_area 0.1: medium 1 else: large 1 print(总 box 数:, box_count) print(类别分布:, class_counter) print(ftiny(0.001): {tiny}, small(0.01): {small}, medium: {medium}, large: {large})这份统计脚本的作用是提前暴露三个问题第一个是类别 id 是否连续如果 class id 只有 0 和 2说明原始标注做了类别筛掉第二个是目标尺度分布杆顶目标的bbox_area普遍小于 0.01那后面训练 imgsz 就要往大了设第三个是空标签文件比如一张图里根本没有目标对训练没有帮助该删就删。统计后的表格大致会是这样类别 id类别名假设目标数大小占比0insulator1420672%1crossarm391820%2lightning_rod15808%如果统计出来某一类占比超过 80%那么训练时如果不做类别加权模型会把多数类学得更好少数类则几乎不识别。这个意识要在训练前建立而不是等 mAP 崩了再回头查。2.3 把 txt 标签可视化回原图确认不是零点坐标统计只能告诉你数量不能告诉你框准不准。很多采集项目把标签外包少数框会偏到天空里去或者 width/height 写反。写一个快速可视化脚本把归一化坐标乘以原图宽高再画框每张图上固定画几个目标用图片拼图的方式检查。# visualize.py # 快速检查前 20 张图像的标注质量 import cv2 import glob import os image_paths sorted(glob.glob(train/images/*.jpg))[:20] out_dir check_vis os.makedirs(out_dir, exist_okTrue) for img_path in image_paths: img cv2.imread(img_path) h, w img.shape[:2] txt_path img_path.replace(images, labels).replace(.jpg, .txt) if not os.path.exists(txt_path): continue with open(txt_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue _, xc, yc, bw, bh parts x1 int((float(xc) - float(bw) / 2) * w) y1 int((float(yc) - float(bh) / 2) * h) x2 int((float(xc) float(bw) / 2) * w) y2 int((float(yc) float(bh) / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(os.path.join(out_dir, os.path.basename(img_path)), img)画出来的图如果出现大面积框不贴合、框超出图像边界那就说明标签需要清洗。这里有一个常见误区YOLO 坐标本身要求归一化但如果某个框的 x_center 写成了像素坐标可视化时会发现框全部集中在左上角小区域这种属于标签格式错误不是模型问题。3. 统一格式把原始标注转成 YOLO txt 并生成可训练的 dataset.yaml3.1 从 XML/JSON 到 YOLO 坐标的标准转换路径很多杆塔数据集的原始标签不是 YOLO txt而是 PASCAL VOC 的 XML 或 COCO 的 JSON。XML 转 YOLO 时最需要注意的是坐标换算方向VOC 存的是x_min, y_min, x_max, y_max像素值YOLO 存的是归一化中心坐标和宽高。# xml_to_yolo.py # 把 VOC XML 转成 YOLO txt import xml.etree.ElementTree as ET import os class_dict {insulator: 0, crossarm: 1, lightning_rod: 2} 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.iter(object): name obj.find(name).text if name not in class_dict: continue class_id class_dict[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) # 像素坐标转归一化中心坐标和宽高 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{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(os.path.join(out_dir, os.path.basename(xml_path)[:-4] .txt), w) as f: f.write(\n.join(lines))这段代码里最关键的是把xmin, ymin, xmax, ymax加起来除以 2 再除以图片宽高。如果漏掉除以宽高训练时读到的是像素坐标会被 YOLO 当成归一化值处理损失函数根本收敛不了。class_dict要和你最终写进 dataset.yaml 的类别顺序完全一致顺序错一个整个训练结果就是张冠李戴。建议在转换前手动打开 5 张图和 XML 核对一遍别只看文件行数对得上就放心。3.2 清洗无效框和空标签文件转换之后还要做一次过滤。常见无效框包括宽或高小于等于 0、坐标超出图像尺寸、归一化后数值大于 1。出现这些情况通常是标注工具导出错误或者图像边缘裁切导致。写一个清洗脚本把 YOLO txt 重新读一遍过滤掉非法行。# clean_labels.py # 清洗无效的 YOLO 标签行并删除空标签文件 import os import shutil labels_dir train/labels images_dir train/images clean_dir train/labels_clean os.makedirs(clean_dir, exist_okTrue) for txt in os.listdir(labels_dir): txt_path os.path.join(labels_dir, txt) with open(txt_path, r) as f: lines f.readlines() valid_lines [] for line in lines: parts line.strip().split() if len(parts) ! 5: continue _, xc, yc, w, h parts xc, yc, w, h map(float, (xc, yc, w, h)) if w 0 or h 0: continue if xc 0 or xc 1 or yc 0 or yc 1: continue if w 1 or h 1: continue valid_lines.append(line.strip()) if valid_lines: with open(os.path.join(clean_dir, txt), w) as f: f.write(\n.join(valid_lines)) else: # 空标签可以删但对应图片也从训练集移走 img_path os.path.join(images_dir, txt.replace(.txt, .jpg)) if os.path.exists(img_path): os.rename(img_path, f{img_path}.unused)清洗逻辑分为两步先做行级过滤再做文件级清理。行过滤处理的是个别坏框文件级清理处理的是整张图没有任何可用目标的情况。对杆顶数据集来说空标签文件多出现在无人机俯拍画面里杆塔被电线完全遮挡这类图对训练只有负贡献直接移走即可。3.3 写 dataset.yaml 并校验验证集文件存在YOLOv8 训练前需要一个 YAML 文件描述数据路径、类别名和类别数。这里不使用绝对路径而是用相对path指向数据集根目录配合train和val的相对路径迁移到其他机器上不用改配置。# pole_dataset.yaml path: /data/pole_dataset # 当前环境的数据集根目录 train: train/images # 训练图像目录 val: val/images # 验证图像目录 names: 0: insulator 1: crossarm 2: lightning_rod写完 yaml 后用 Ultralytics 自带工具做一次数据校验它会检查每个训练样本的标签能不能对上。yolo detect train datapole_dataset.yaml epochs0epochs0是一个小技巧不触发实际训练只走数据和标签校验流程。如果标签有问题会直接提示 “found X corrupt labels”如果一切正常会打印出各类别对应的 box 数量这和第 2.2 节统计结果对齐才算真正可以开训。提示data.yaml 里的names数量必须等于标签中出现的最大 class_id 1否则训练阶段会报 “class index out of range”。4. 用 YOLOv8 训练自定义杆顶数据集时的关键参数4.1 选模型尺寸n、s 还是 m关于 YOLO 算法选型这组数据是典型的“杆顶小目标 户外复杂背景”不是自动驾驶那种大目标高速场景。YOLOv8n 轻量型适合快速验证但它下采样倍数高小目标信息在小特征图上损失明显。我通常先无脑跑一版 YOLOv8s因为它在速度和精度之间最平衡显存需求也低。显存够大且只追求精度再往m以上走直接上x对 7255 张图来说容易过拟合收敛后在小目标上的提升未必值得多出的训练时间。4.2 训练命令里必调的四个参数用官方ultralytics包训练的命令不长但参数值要仔细设。yolo detect train \ datapole_dataset.yaml \ modelyolov8s.pt \ imgsz1280 \ epochs200 \ batch16 \ device0 \ patience50 \ workers8 \ cacheTrue这段命令里第一个重点是imgsz1280。杆顶目标在原始图像中占比小如果用默认 640 缩放目标可能只剩几个像素很难学出特征。常见处理是保持 1280 或至少 1024代价是显存占用按平方增长batch 要相应调低。第二个重点是patience50它控制早停连续 50 轮验证损失没下降就停止。训练早期不要关早停否则你会盯着 loss 反复浪费时间。batch16在单卡 A100 或 4090 上配合 1280 输入可能没问题但显存少就得降到 8 或 4。cacheTrue会把图像预先加载到内存中减少磁盘 I/O 等待前提是内存足够7255 张 JPEG 如果按每张 500 KB 算大约占用 4 GB 内存。下面是这些参数对训练的影响表参数推荐值作用坑imgsz1280保留小目标分辨率过大会爆显存epochs200配合早停使用不设 patience 会白等batch8~16影响泛化太大容易过拟合小数据集workers8数据加载线程过高会导致 CPU 瓶颈cacheTrue预加载图像内存不够会卡死4.3 训练日志里的指标怎么判读训练过程打印的metrics/mAP50(B)和metrics/mAP50-95(B)是两个核心输出。杆顶检测这类任务mAP50 更贴近实际需求因为落地的巡检框并不要求非常精确的像素边界IOU 0.5 足够判定识别成功。mAP50-95 则会严格得多头部目标还好杆顶这类小目标天然吃亏数值低于 mAP50 一半也正常。如果训练 loss 一直降但 val loss 在第 80 轮后反弹那就是过拟合信号。常见应对不是加数据而是把mosaic增强加强。YOLOv8 默认开启 mosaic对杆顶目标重叠、遮挡有正向作用但到训练后期 mosaic 会产生大量遮挡严重的合成图反而干扰验证。可以配置分段关闭# hyp_finetune.yaml mosaic: 0.8 close_mosaic: 15close_mosaic表示最后 15 轮关闭 mosaic 增强让模型在接近真实数据分布的情况下做最后一次微调。这个配置对 7255 张的中等规模数据集特别有效几乎每次把 mAP50 往上推一个点。5. 再进一步把 7255 张杆顶数据用到极致的两个技巧5.1 训练时切片把高分辨率图切成子图喂给 YOLO如果原图是 4K 无人机航拍直接缩到 1280 会导致杆顶目标只剩十几像素。常见做法是离线切图把原图按 1024×1024 的窗口切成若干子图重叠率 10%切出来的子图重新生成 YOLO 标签。这样训练数据从 7255 张膨胀到数万张。切图注意一个原则目标被切破时如果保留部分仍然大于原框的 50% 就保留否则丢掉。5.2 推理时用 SAHI 做切片推理避免漏检训练时已经切片推理阶段就不应该再把整图喂进去。用 SAHI 或自己写的 overlap 推理脚本在原图上滑动窗口检测并合并结果。对电线杆杆顶这类密集排列的目标合并时用 NMS阈值设 0.3 附近因为在相邻窗口边界容易产生重复框。# infer_slice.py # 对高分辨率图像做切片推理 import cv2 import numpy as np from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) def slice_infer(image, window_size1024, overlap0.1): h, w image.shape[:2] step int(window_size * (1 - overlap)) all_boxes [] for y in range(0, h, step): for x in range(0, w, step): crop image[y:ywindow_size, x:xwindow_size] results model.predict(crop, imgsz1024, conf0.25, verboseFalse) for box in results[0].boxes.data.cpu().numpy(): x1, y1, x2, y2, conf, cls box # 转为原图坐标 all_boxes.append([x x1, y y1, x x2, y y2, conf, cls]) return all_boxes img cv2.imread(test_4k.jpg) boxes slice_infer(img) print(检测到框数量:, len(boxes))slice_infer的循环里step控制窗口滑动速度overlap 越大漏检越少但推理时间越长。杆顶目标通常出现在图像上半部分可以先裁掉下半部分 30% 再切片能省不少耗时。窗口大小最好和训练切图一致否则模型分辨率和推理分辨率错位。最后记得把子图坐标加回偏移量别直接拿去画框。数据集的 7255 张图不会替你解决所有工程问题但把标签质量、切图策略和推理方式这几步做扎实YOLO 训练出来的杆顶检测模型在真实巡检视频里的可用度会明显高出一个台阶。本文还有配套的精品资源点击获取