ARTICLE DETAIL

资讯详情

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

坦克检测数据集实战:1521张图单类标注,YOLO训练避坑指南

坦克检测数据集实战:1521张图单类标注,YOLO训练避坑指南 简介这是一份面向目标检测初学者与算法工程师的坦克检测数据集采用Pascal VOC与YOLO双格式标注可直接用于YOLO系列模型的训练与验证适合军事目标识别、遥感图像分析等场景的入门实践与算法调优。压缩包共2000个文件包含1521张jpg图片、1521个VOC格式xml标注文件及479个YOLO格式txt标注文件整体约103.15MBxml与txt分别对应两种主流框架的读取需求无需额外转换即可投入训练。数据集仅含tank一个类别共标注2220个矩形框使用labelImg人工绘制标注准确度较高。目前已有655人学习下载可作为单类别检测任务的轻量级训练样本帮助读者快速验证模型结构、调试数据增强与评估指标节省自行采集与标注的时间成本。1. 坦克检测数据集1521 张图、2220 个框单类标注能不能直接开训如果你正在做目标检测数据集的选型尤其是想找一个单类别、标注干净、拿来就能跑 YOLO 的小型数据集验证训练链路这份坦克检测数据集值得先看一眼。它的核心参数很直白1521 张 jpg 图片对应 1521 个 VOC 格式 xml 和 1521 个 YOLO 格式 txt标注类别只有一类tank总框数 2220标注工具是 labelImg标注方式是矩形框。也就是说平均每张图约 1.46 个目标属于典型的小目标数量、单类别场景。它解决的不是“我要训一个通用检测器”的问题而是“我想快速验证数据管线、损失函数收敛、anchor 匹配是否正常”的问题。适合两类人一是刚入门 YOLO、想拿真实标注数据跑通训练和推理的新手二是手里有坦克/装甲类检测需求想先拿一份标注质量尚可的数据做 baseline 的从业者。需要提前说清楚这是个人标注数据准确度高是相对而言不代表零漏标零误标后面会讲怎么自查。2. 拆包先看目录VOC 与 YOLO 双格式到底怎么对应2.1 文件结构与命名规则拿到压缩包后第一件事不是急着写训练脚本而是把目录结构摸清楚。从项目正文给出的文件列表看根目录下有说明.txt和一批tank_xyxr_*.txt这些 txt 是 YOLO 格式的标注文件命名规则是tank_xyxr_序号.txt。对应的 jpg 图片和 VOC 格式 xml 应该与这些 txt 同名只是扩展名不同。常见做法是解压后得到三个平行目录JPEGImages放 jpgAnnotations放 xmllabels放 txt。如果压缩包里没有分目录而是全部平铺那就需要自己按扩展名归类。先执行一遍清点确认数量对得上# 统计三类文件数量确认是否都是 1521 find . -name *.jpg | wc -l find . -name *.xml | wc -l find . -name *.txt | wc -l # 检查是否有图片没有对应标注或标注没有对应图片 for f in *.jpg; do base${f%.jpg} [ -f ${base}.xml ] || echo 缺 xml: $base [ -f ${base}.txt ] || echo 缺 txt: $base done这段脚本的逻辑很直接先按扩展名计数三个数字都应该是 1521再做交叉检查任何一张图缺 xml 或 txt 都会被打印出来。参数上唯一要注意的是工作目录必须在文件所在目录执行否则for f in *.jpg匹配不到。如果输出一堆“缺 xml”说明压缩包内部分目录需要先find . -name *.jpg -exec mv {} ./JPEGImages/ \;这类操作归拢但归拢前先备份避免同名覆盖。2.2 VOC xml 与 YOLO txt 的字段映射VOC 的 xml 里关键字段是filename、size下的width/height/depth以及每个object下的name和bndbox的xmin/ymin/xmax/ymax。YOLO 的 txt 每行是class_id x_center y_center width height全部归一化到 0~1。这份数据类别只有tank一类所以 class_id 恒为 0。转换关系是x_center (xmin xmax) / 2 / img_width y_center (ymin ymax) / 2 / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_height常见坑是 xml 里的size字段和实际图片尺寸不一致。labelImg 正常保存时不会出这个问题但个人标注数据里偶尔会有图片被替换而 xml 没更新。验证方法是用 PIL 读一遍图片尺寸和 xml 里的size对比import os import xml.etree.ElementTree as ET from PIL import Image def check_size(ann_dir, img_dir): mismatches [] for xml_file in os.listdir(ann_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(ann_dir, xml_file)) root tree.getroot() w int(root.find(size/width).text) h int(root.find(size/height).text) img_name root.find(filename).text img_path os.path.join(img_dir, img_name) if not os.path.exists(img_path): mismatches.append((xml_file, 图片不存在)) continue iw, ih Image.open(img_path).size if (w, h) ! (iw, ih): mismatches.append((xml_file, fxml{w}x{h}, img{iw}x{ih})) return mismatches for item in check_size(Annotations, JPEGImages): print(item)逻辑说明遍历所有 xml读出标注尺寸和实际图片尺寸不一致就记录。参数上ann_dir和img_dir按实际目录名改。如果输出为空说明尺寸一致可以放心用 YOLO txt如果有不一致优先以实际图片尺寸为准重新生成 txt而不是改 xml因为训练时读的是图片。3. 从 VOC 到 YOLO转换脚本、划分训练集与 data.yaml3.1 自己写一遍转换脚本别直接信现成 txt虽然压缩包里已经带了 YOLO 格式 txt但我一般会自己再转一遍目的是验证已有 txt 和 xml 是否一致。如果直接拿现成 txt 训练万一某几行坐标越界或类别写错loss 会异常但不报错排查起来很费时间。下面这个脚本把 VOC 转成 YOLO并顺带做坐标合法性检查import os import xml.etree.ElementTree as ET CLASSES [tank] def voc_to_yolo(xml_path, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.iter(object): cls_name obj.find(name).text.strip() if cls_name not in CLASSES: continue cls_id CLASSES.index(cls_name) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 裁剪到图片范围内防止越界 xmin max(0, min(xmin, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) if xmax xmin or ymax ymin: continue xc (xmin xmax) / 2 / img_w yc (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {xc:.6f} {yc:.6f} {w:.6f} {h:.6f}) return lines逻辑说明CLASSES只放tank所以 class_id 恒为 0坐标先裁剪再判断有效性避免负宽高输出保留 6 位小数和 YOLO 官方格式一致。参数上img_w/img_h必须从实际图片读取不能从 xml 的size读原因上一节讲过。转换完可以和自己手里的 txt 做逐行对比差异超过阈值的样本单独看一眼。3.2 划分训练集、验证集与 data.yaml1521 张图不算多按 8:1:1 划分训练集约 1216 张验证集和测试集各约 152 张。划分时要注意同一场景的连续帧不要跨集否则验证集精度会虚高。常见做法是按文件名排序后间隔采样而不是随机打乱import os import random random.seed(42) imgs sorted([f for f in os.listdir(JPEGImages) if f.endswith(.jpg)]) random.shuffle(imgs) n len(imgs) train imgs[:int(n*0.8)] val imgs[int(n*0.8):int(n*0.9)] test imgs[int(n*0.9):] for split, items in [(train, train), (val, val), (test, test)]: with open(f{split}.txt, w) as f: for name in items: f.write(f./images/{name}\n)逻辑说明固定随机种子保证可复现输出的是图片路径列表YOLO 训练时通过替换扩展名找 labels。参数上0.8/0.9是划分比例数据量小的时候可以改成 7:2:1验证集大一点更能反映真实水平。data.yaml写法如下path: ./tank_dataset train: train.txt val: val.txt test: test.txt nc: 1 names: [tank]nc是类别数这里必须写 1names顺序要和转换脚本里的CLASSES一致否则类别索引错位训练能跑但预测全错。4. 训练参数怎么设单类别小数据集的避坑配置4.1 模型选型与输入尺寸单类别、2200 多个框不建议一上来就上大模型。常见做法是从 YOLOv8n 或 YOLOv5s 起步参数量小、收敛快单卡 8G 显存就能跑。输入尺寸设 640 是默认值但如果你的坦克目标在图中占比很小比如远距离拍摄可以提到 960 或 1280代价是显存和训练时间上升。判断依据是统计一下 YOLO txt 里 width 和 height 的分布import os import numpy as np ws, hs [], [] for f in os.listdir(labels): if not f.endswith(.txt): continue for line in open(os.path.join(labels, f)): parts line.strip().split() if len(parts) 5: ws.append(float(parts[3])) hs.append(float(parts[4])) print(width 中位数:, np.median(ws), 90分位:, np.percentile(ws, 90)) print(height 中位数:, np.median(hs), 90分位:, np.percentile(hs, 90))如果中位数小于 0.05说明目标偏小640 下可能只剩几十像素建议提高输入尺寸或改用带 P2 小目标层的模型。参数上np.percentile(ws, 90)看的是 90% 的框有多小比平均值更能反映长尾。4.2 训练命令与关键超参以 YOLOv8 为例训练命令如下yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ warmup_epochs3 \ patience20 \ cacheTrue逻辑说明epochs100配合patience20验证集 20 轮不提升就早停避免过拟合lr00.01是初始学习率小数据集可以降到 0.005 更稳cacheTrue把图片缓存到内存1521 张图完全放得下能明显加快训练。参数上batch16是 8G 显存的保守值显存够可以加到 32。如果 loss 在前几轮就 nan优先检查 txt 里有没有坐标大于 1 或负值而不是调学习率。4.3 训练过程看什么指标单类别检测重点看三个box_loss是否稳定下降、mAP50是否在 30 轮后进入平台、precision和recall是否严重失衡。如果 recall 远低于 precision说明漏检多可能是目标太小或 anchor 不匹配如果 precision 低说明误检多可能是背景类样本不足。这份数据只有 tank 一类误检通常来自类似坦克的车辆或建筑可以在验证集里把误检样本挑出来看。5. 避坑与排查个人标注数据最容易翻车的五件事5.1 现象训练 loss 正常但 mAP 始终为 0原因data.yaml里names顺序和 txt 里的 class_id 对不上或者 txt 里 class_id 不是 0。这份数据只有一类正常情况 class_id 全是 0但如果转换脚本里CLASSES写成了别的顺序就会错位。解决用awk {print $1} labels/*.txt | sort -u看唯一类别值应该只有 0再核对data.yaml的names长度等于nc。5.2 现象部分图片训练时报“坐标越界”原因xml 里的 bbox 超出图片边界或者图片尺寸和 xml 不一致。个人标注时手滑把框拉到图外很常见。解决在转换脚本里加裁剪逻辑如 3.1 节所示同时跑一遍 2.2 节的尺寸检查把不一致的样本单独处理。5.3 现象验证集精度很高但实际推理漏检严重原因训练集和验证集划分时同一场景的连续帧被分到了两边导致验证集和训练集高度相似。解决按文件名或时间戳分组划分而不是逐张随机。如果文件名里有序号可以按序号区间切分保证同一段视频的帧只出现在一个集合里。5.4 现象训练到一半显存爆了原因cacheTrue把图片全部读进内存如果图片分辨率很高内存先爆或者batch设太大。解决先看图片平均尺寸超过 1920 宽就把cache关掉改用cachediskbatch从 8 开始试逐步加。5.5 现象推理时框的位置整体偏移原因训练时用了 letterbox 填充推理时没有做同样的预处理或者输入尺寸和训练不一致。解决推理时保持imgsz和训练一致用官方predict接口会自动处理 letterbox如果自己写前处理记得把填充后的坐标映射回原图。6. 进阶验证用混淆矩阵和单图推理确认数据可用性训练完之后别只看 mAP 数字。我一般会做两件事一是跑混淆矩阵二是挑几张有代表性的图做单图推理肉眼确认框的位置和置信度。混淆矩阵在 YOLO 训练结束时会自动生成单类别情况下主要看背景被误判为 tank 的比例。如果背景误检高说明负样本不够可以从验证集里挑一些没有坦克的图加进训练集作为背景样本。单图推理命令yolo detect predict \ modelruns/detect/train/weights/best.pt \ sourcetest_images/ \ conf0.25 \ iou0.5 \ saveTrue逻辑说明conf0.25是置信度阈值单类别场景可以降到 0.2 看召回iou0.5是 NMS 的 IoU 阈值如果坦克密集排列可以降到 0.4 减少框合并。参数上source可以指向单张图或目录输出默认在runs/detect/predict。还有一个容易被忽略的验证点把 YOLO txt 反算回 VOC 坐标和原始 xml 对比看最大偏差。如果偏差超过 1 像素说明转换或取整有问题。这个检查能兜住大部分标注格式的玄学问题。从那以后我每次拿到新数据集都强制走一遍“计数→尺寸检查→类别检查→反算对比”这四步再开始训练。希望帮到你。本文还有配套的精品资源点击获取
返回列表