ARTICLE DETAIL

建站实战干货

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

244张电塔图训练YOLOv8:从VOC转YOLO到遥感检测的完整避坑指南

2026/9/23 21:21:18 拓冰建站 浏览量
244张电塔图训练YOLOv8:从VOC转YOLO到遥感检测的完整避坑指南 简介面向遥感目标检测与电力设施巡检方向的开发者与研究者这份数据集提供了244张电塔遥感影像及对应标注统一采用Pascal VOC和YOLO两种格式方便直接接入主流检测框架。包内共734个文件包括244张jpg原图、244个xml标注文件、244个txt标注文件以及若干辅助说明文件压缩包整体78.4MB体积适中适合快速下载与迭代实验。数据集由labelImg工具画框标注唯一类别dianta共包含504个真实框既可用于电塔检测模型的训练与验证也可作为算法评测的基准样本。目前已有280人学习下载适用于入门目标检测或电力巡线场景的模型调优与精度对比。请注意资源仅保证标注准确合理不承诺模型精度使用时应结合自身任务验证。1. 244 张电塔图能不能训 YOLO做电力巡检的算法工程师大概率都收到过类似名字的压缩包遥感图像电塔检测数据集VOCYOLO格式244张1类别.7z。先别急着嫌 244 张太少这个数据量对电塔这类目标其实是“能训但要看手法”的临界点。电塔在遥感影像里形态明确、尺寸相对稳定拿它先把完整流程跑通再逐步扩数据是这个数据集最常见的用法。这篇笔记按拿到压缩包之后的真实动作来写怎么拆包看格式为什么要从 VOC 转 YOLO怎么用 YOLOv8 把它练起来以及 244 张小数据最容易踩的一串坑。核心就一句话这个数据集不是给你直接上生产的是给你把流程验证明白、把坑提前踩平的。2. 拆开 .7z 先看清两套标注VOC 和 YOLO 的真实结构这种压缩包解压之后最常见的交付方式是把图片和 VOC 标注放在两个目录里图片是.jpg标注是.xml有些包会顺带给你一个labels目录里面是已经转好的 YOLO 格式.txt。不管给没给你都得先搞清楚两套格式各自描述了什么东西否则后面训练报错都不知道去哪查。第一步永远是解压后别急着跑训练先看目录骨架。2.1 解压后先看这层jpg、xml、txt 三种文件各管什么我拿到过的这类电塔数据解压后一般长这样. ├── JPEGImages/ │ ├── tower_001.jpg │ ├── tower_002.jpg │ └── ... ├── Annotations/ │ ├── tower_001.xml │ ├── tower_002.xml │ └── ... └── labels/ # 有些包会预生成有些没有 ├── tower_001.txt ├── tower_002.txt └── ...JPEGImages放原始遥感图像Annotations放 VOC 格式的 XML 标注labels放 YOLO 格式的 TXT 标注。三者的文件名一一对应靠主文件名关联。为什么大多数数据集交付用 VOC 而不是直接用 YOLO 格式因为 VOC 的 XML 可读性好labelimg 等标注工具原生支持且能携带difficult、truncated这类附加信息而 YOLO 格式每张图只有一个小 TXT信息量少但训练读取速度快不需要解析 XML 树。所以常见做法是交付用 VOC训练前再转 YOLO。2.2 VOC 的 XML 长什么样一个电塔标注的逐字段拆解用文本编辑器打开任意一个 XML结构大致如下annotation folderJPEGImages/folder filenametower_001.jpg/filename size width1024/width height768/height depth3/depth /size object nametower/name bndbox xmin412/xmin ymin233/ymin xmax687/xmax ymax510/ymax /bndbox /object /annotationsize里的宽高是后面转换 YOLO 坐标的基准务必以 XML 里的值为准不要自己另猜。object里name是类别名电塔数据一般只有tower一类bndbox是目标的像素坐标边界框左上角(xmin, ymin)右下角(xmax, ymax)。一个容易忽略的点有些标注员会在同一张图里标多个电塔object节点会出现多次转换脚本要按循环处理不能只取第一个。2.3 YOLO 的 TXT 才是训练时要吃的格式归一化坐标怎么算YOLO 格式每一行只存五个数class_id, x_center, y_center, width, height。注意后面四个数全部归一化到 0~1 区间单位不是像素而是相对图像宽高的比例。转换公式如下设图像宽高为W和Hx_center (xmin xmax) / 2.0 / W y_center (ymin ymax) / 2.0 / H width (xmax - xmin) / W height (ymax - ymin) / H对应上面那组 XML计算结果是0 0.536621 0.483724 0.268555 0.360677这里0是类别编号因为只有tower一类所以编号从 0 开始。归一化坐标的好处是模型不关心输入图像的分辨率训练时无论是 640 还是 1280框的相对位置不变。选型理由也要说清楚VOC 负责“给人看”YOLO 负责“给模型快速读”。244 张小数据转换这步做得干净后面训练能省掉大量排错时间。3. 把 VOC 转成 YOLO转换脚本、目录重建与数据划分这一章是全文最该抄作业的部分。拿到这种数据集你至少要经过三步整理目录、转换标注、划分数据。三步都做对训练才不会被路径和格式问题拦在半路。3.1 先立好目录骨架images 与 labels 一一对应不少人在这一步翻车图片在JPEGImages/标注在Annotations/训练时 YOLO 按数据配置文件去找图片路径结果标签目录对不上直接报错。我一般先把数据整理成统一的结构图片归到images/标签归到labels/文件名保持一致data/ ├── images/ │ ├── tower_001.jpg │ └── ... └── labels/ ├── tower_001.txt └── ...整理这一步看起来简单实际上是在为后面所有步骤打底。目录混乱的代价是训练时报错你不知道是路径问题还是格式问题排查成本非常高。3.2 转换脚本用 XML 生成 TXT并处理类名不一致下面这个脚本把Annotations/下所有 XML 转成 YOLO 格式的 TXT输出到labels/import os import xml.etree.ElementTree as ET # 类别列表顺序必须与后续 data.yaml 中的 names 完全一致 class_names [tower] def voc_to_yolo(xml_path, out_dir): tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) txt_name os.path.basename(xml_path).replace(.xml, .txt) out_path os.path.join(out_dir, txt_name) with open(out_path, w, encodingutf-8) as f: # 一张图里可能有多个电塔必须循环 object for obj in root.iter(object): name obj.find(name).text if name not in class_names: print(f未知类别: {name}, 文件: {xml_path}) continue cls_id class_names.index(name) 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) # 坐标归一化保留 6 位小数足够训练 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 f.write(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n) if __name__ __main__: os.makedirs(labels, exist_okTrue) xml_dir Annotations for xml_file in os.listdir(xml_dir): if xml_file.endswith(.xml): voc_to_yolo(os.path.join(xml_dir, xml_file), labels)逻辑说明脚本用ElementTree解析 XML先读size拿到图像宽高再遍历所有object节点计算归一化坐标后写入 TXT。class_names列表的索引就是类别编号顺序一旦确定后面data.yaml里必须保持一致。参数说明x_center保留 6 位小数YOLO 训练时读取浮点精度足够遇到img_w或img_h为 0 这样的脏数据建议直接打印出来人工检查不要静默跳过。一个容易漏的点如果某张 XML 里没有任何object背景图转换脚本会生成一个空 TXT 文件这是正确的YOLO 训练允许空标签图避免报错。3.3 按 7:2:1 划分数据集固定随机种子避免同源样本泄漏划分数据是 244 张图训练的重中之重。常见做法是训练集 70%、验证集 20%、测试集 10%测试集尽量留作最后评估不要参与调参。import os import random random.seed(42) # 固定随机种子保证多次跑结果可复现 image_dir images label_dir labels train_ratio 0.7 val_ratio 0.2 all_images [f for f in os.listdir(image_dir) if f.endswith(.jpg)] all_images.sort() random.shuffle(all_images) n len(all_images) n_train int(n * train_ratio) n_val int(n * val_ratio) split_map { train: all_images[:n_train], val: all_images[n_train:n_train n_val], test: all_images[n_train n_val:], } for split_name, file_list in split_map.items(): os.makedirs(fdata/{split_name}/images, exist_okTrue) os.makedirs(fdata/{split_name}/labels, exist_okTrue) with open(fdata/{split_name}.txt, w) as f: for img_name in file_list: img_src os.path.join(image_dir, img_name) label_name img_name.replace(.jpg, .txt) label_src os.path.join(label_dir, label_name) os.rename(img_src, fdata/{split_name}/images/{img_name}) if os.path.exists(label_src): os.rename(label_src, fdata/{split_name}/labels/{label_name}) print(f{split_name}: {len(file_list)} 张)逻辑说明脚本按比例随机切分图片再把对应的标签文件一起搬进data/下的训练、验证、测试子目录顺便生成一个按行存路径的.txt文件供 YOLO 直接读取。参数说明random.seed(42)让实验可复现别人跑你的代码能得到一样的划分结果。train_ratio和val_ratio按 0.7 和 0.2 设定剩下 10% 做测试集确保模型没见过的遥感图能反映真实水平。需要特别强调的是遥感图像常有同一区域多帧重叠的情况。如果只是按文件随机切分同一座电塔的不同帧可能同时落进训练集和验证集让验证指标虚高。更严谨的做法是提前看文件名前缀把同一场景的图分到同一边。244 张数据量不大这一步人工检查十分钟能省掉后面的误判。3.4 生成 data.yaml让 YOLO 知道去哪找图训练前还需一个数据配置文件YOLOv8 用 YAML 格式path: /full/path/to/data # 改成你的实际路径 train: train.txt val: val.txt names: 0: towertrain和val可以指向 3.3 生成的train.txt和val.txt也可以直接写成train/images和val/images的目录路径两种写法 YOLOv8 都认。names必须和转换脚本里class_names的顺序一致否则类别标签会错乱。到这里数据准备工作就结束了。下一步是搭建环境把 YOLOv8 训起来。4. 在 YOLOv8 上练起来环境、训练命令与 5 个必调参数数据准备好之后训练本身反而是最机械的一步。244 张图不算多但如果你直接用默认参数跑大概率会得到一份过拟合的模型。这一章把这些参数为什么这么调讲清楚。4.1 环境配置Anaconda 里跑通 YOLOv8 的最小流程YOLOv8 的安装比早期版本省心很多核心只需要ultralytics这一个包。用 Anaconda 新建环境避免污染系统 Pythonconda create -n yolo python3.10 -y conda activate yolo pip install ultralytics安装完成后先跑一个最小推理命令验证环境不要直接上训练yolo predict modelyolov8n.pt sourcehttps://ultralytics.com/images/bus.jpg能弹出检测结果说明 CUDA、PyTorch 和 ultralytics 之间没有版本冲突。环境配置阶段最常见的报错是 PyTorch 的 CUDA 版本和显卡驱动不匹配建议安装 ultralytics 前先用nvidia-smi看一眼驱动支持的 CUDA 版本再决定装哪个 PyTorch 轮子。4.2 5 个必调参数imgsz、epochs、batch、patience、cache244 张图的小数据集不要照着检测比赛的大参数抄。下面这张表是我在这种体量数据上常用的推荐值参数默认值244 张推荐值理由imgsz640640电塔在遥感图中尺寸偏大640 够用512 可加快训练但会损失细节epochs100200小数据需要更多轮次收敛配合早停控制过拟合batch1616 或 32batch 太小会让 BN 统计不稳定显存允许则用 32patience5050val 指标连续 50 轮不提升就早停省时间cacheFalseTrue244 张图全部缓存到显存或内存减少磁盘 IO 等待cacheTrue在这里很划算数据量小缓存不会爆显存但训练每轮的读取速度会明显提升。patience是 Early Stopping 的耐心值验证集损失连续多少轮不降就停小数据集上非常管用能避免无效训练到 200 轮。其他增强参数如hsv_h、degrees、flipud先用默认值不要一上来就乱调。电塔的朝向有物理约束过度的旋转增强反而会让模型学到错误特征。4.3 训练命令与 loss 曲线先别盯 mAP先看损失执行训练yolo detect train datadata.yaml modelyolov8n.pt epochs200 imgsz640 batch16 patience50 cacheTrue device0这里modelyolov8n.pt表示用 YOLOv8 nano 的预训练权重做初始化小数据集上比从零训练收敛快得多。训练结束后重点不是只看最后的 mAP而是打开runs/detect/train/results.png看三条损失曲线。YOLOv8 的损失函数主要由三部分组成box_loss是预测框与真实框的回归损失cls_loss是分类损失dfl_loss是分布焦点损失管边界框的边线精度。小数据集上你常看到的现象是train/box_loss快速降到 0.02 附近后不再明显下降正常train/cls_loss缓慢下降说明类别特征还在学习val/cls_loss先降后升而train/cls_loss还在降说明过拟合开始了这时要依赖早停或减少 epochs。如果训练日志里出现 loss 变成nan优先检查学习率是不是太大或数据里有没有坏图。244 张图训到一半崩掉先怀疑这几个点不要急着改模型结构。5. 电塔检测避坑实录小样本训练常翻车的 5 个位置这一章专门写小样本电塔检测里最容易踩的坑。每条都是实打实见过的问题按“现象 → 原因 → 解决”来记。5.1 BN 层在小数据上翻车loss 掉一半直接发散现象训练前 20 轮 loss 正常下降到第 40 轮左右 train loss 突然反弹val 曲线剧烈抖动严重时直接出现nan。原因Batch Normalization 层的统计量在小 batch 上估计不稳定244 张图还要切出验证集训练样本更少BN 的均值和方差波动大导致特征分布漂移。解决优先把 batch 从 16 提到 32显存不够就换更小的yolov8n模型训练命令里加--lr0 0.001降低初始学习率再不行就冻结 backbone 前几层让 BN 只在检测头部分更新。简单说模型越小、batch 越大、学习率越收敛保守小数据越稳。5.2 误检把电线杆当电塔类别太宽 背景样本不够现象模型在测试图上把一排电线杆、路灯杆标成 tower置信度还在 0.5 以上看起来像模型真的“确信”了。原因电塔本质是杆状结构和电线杆在视觉上高度相似而 244 张数据里几乎没有负样本图——也就是没有电塔、只有类似物的背景图。模型没见过“这是错的”的样本自然学不会区分。解决从现场巡线视频里截取一批不含电塔的遥感图打成空标签放进训练集让模型在验证时能接触到负样本推理时把置信度门限从默认 0.25 往上提到 0.45 到 0.5。这在 yolo 检测里是调整置信度门限最典型的使用场景数据里负样本不足就用门限硬压误检率。注意门限抬高会损失召回适合巡检场景宁可漏检再人工复核的诉求。5.3 转换格式时少除了图像尺寸框肉眼可见地偏现象转完 YOLO 格式用可视化脚本把框画回原图发现框整体偏移位置和大小都不对。原因转换脚本里没有读 XML 的size字段而是用了自己假设的固定宽高比如默认 1024或者把像素值当成浮点直接除以 1000坐标全部错位。解决回到 3.2 的脚本强制从size节点读取width和height并且全部转成float再计算。转完别急着训练先随机抽 10 张图把 TXT 的框画回原图看一眼这一步一分钟的事能省掉一小时的排错时间。5.4 labelimg 打标完 YOLO 格式的标训练却提示数据错误现象labelimg 标注完成后训练时报错“image not found”或“invalid label format”排查半天发现标注文件根本没生成在预期目录。原因labelimg 保存 YOLO 格式时默认把 TXT 和图片放在一起而不是放在labels/目录里还有一类常见情况是 labelimg 里的类别文件和data.yaml的 names 顺序不一致导致标注索引错乱。解决在 labelimg 打标前先创建一个classes.txt内容写tower然后在界面里加载这个文件保存时明确指定输出目录为labels/不要用默认的与图片同目录。训练前用脚本统计一下labels/下的 TXT 数量和images/下的图片数量是否一致不一致先补齐再说。5.5 验证集指标虚高切分时同源样本泄漏现象val 的 mAP50 能到 0.9 以上模型看起来很强换到一段全新的巡线视频上效果直接拉胯。原因数据切分时只按文件名随机打散但遥感数据里同一座电塔往往有多帧几乎一模一样的图同时进了训练集和验证集模型相当于“背题”了验证集根本不具备泛化评估能力。解决回到 3.3 的划分思路按文件名前缀或按场景分组把同一区域的所有图分到同一个集合里。244 张数据宁可训练集少几张也要保证验证集是从没见过的场景。6. 遥感大图怎么测切片推理与置信度调整技巧训练好的模型不能直接塞给一张几千乘几千的遥感大图。遥感影像动辄 5000 像素边长直接 reszie 成 640 会把电塔细节抹掉小目标直接消失。这一章讲两个最实用的推理技巧。6.1 滑窗切片推理把大图拆开喂给模型常见做法是滑窗裁剪把大图按 640 的窗口、15% 到 25% 的重叠率切成小块逐块送入模型检测再把结果坐标映射回原图。重叠率不能省否则电塔正好被切在边缘时检测框会被截断模型只看到半个塔误检率直线上升。切出来的小块独立推理后同一座电塔可能被多个窗口重复检测到需要用 NMS 合并。YOLO 推理命令里自带iou参数默认 0.5可以按场景微调框重合度超过这个阈值的都合并成一个。6.2 置信度门限与 NMS把误检压到巡检可接受范围推理时通过命令行参数直接调整门限yolo detect predict modelruns/detect/train/weights/best.pt sourcetest_big.tif imgsz640 conf0.45 iou0.5conf0.45比默认值 0.25 高不少在这个电塔数据集上是一个相对保守的起点。一个更稳的做法是先把验证集所有预测结果按置信度从高到低排列人工往前翻 100 个预测框看有多少是误检。如果前 100 个里误检明显少于真检门限就合适如果误检占了三分之一就继续上调直到误检率降到项目能接受的水平。在电力巡检这类场景里漏检可以靠人工复核兜底误检不进行为性能压力所以门限往里调一档并不亏。最后说一个我自己养成的习惯拿到这种 244 张小数据集第一遍永远用默认参数跑通不作任何花哨改进先看输出的结果缩略图里误检长什么样再决定是调阈值、补数据还是换模型。这个流程走完你会发现电塔检测最难的其实不是训练而是数据划分和负样本设计这两件看着不起眼的事。希望帮到你。本文还有配套的精品资源点击获取