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

资讯详情

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

遥感舰船检测数据集实战:从VOC/YOLO格式到YOLO模型部署

遥感舰船检测数据集实战:从VOC/YOLO格式到YOLO模型部署 简介这份数据集面向遥感影像目标检测与舰船识别任务提供Pascal VOC与YOLO两种通用标注格式可直接用于训练和验证YOLO系列、SSD、Faster R-CNN等模型适合高校实验室、算法工程师及竞赛选手。压缩包共2000个文件含1999个xml标注文件和1个txt说明文件整体109.32MB。该数据集共涉及2238张jpg图片每张均附有VOC与YOLO两种格式标注覆盖航空母舰、辅助舰、集装箱船、巡洋舰、驱逐舰、护卫舰、潜艇、油轮等17类舰船细粒度类别划分便于开展多分类与多尺度检测研究。VOC格式可对接标准评测工具YOLO格式直接用于YOLOv5、YOLOv8等主流训练流程省去格式转换时间。目前已有834人学习下载按文件夹组织清晰适合快速搭建舰船识别实验。1. 从这个压缩包到能跑的检测模型一份遥感舰船数据集的正确用法如果你手上正躺着这样一份卫星遥感舰船检测数据集VOCYOLO格式2238张17类别.7z先别急着把它当成“收藏品”存起来。实际项目里很多团队拿到这类数据的第一反应是解压、看图、然后丢进 YOLO 里跑一轮最后被各种格式错位、类别串号、小目标漏检折腾到怀疑人生。这个标题真正有价值的部分不是“2238张”“17类”这两个数字而是它同时给了你在 VOC 边界框标注和 YOLO 训练标注两条路上的落地能力。本文要做的就是把这份.7z从“能解压的数据”变成“能出结果的训练集”再把训练出的模型调到真正能部署在港口、近海监控场景里的状态。适合谁看刚接触遥感目标检测的工程师以及手里有数据集但没跑通完整 YOLO 流程的入门团队。2. 先拆开.7zVOC和YOLO两套标注的结构、解压与一致性校验2.1 Linux和Windows下解开7z包命令与“别用-e”的教训这份数据用.7z压缩最常见的原因就是压缩比高遥感图像动不动就几百 MB 甚至上 GB。Windows 用户直接用 7-Zip 右键解压就行但要注意解压路径别带中文空格混排的深层目录否则后面 YOLO 读路径时经常莫名其妙报No such file or directory。Linux 服务器上常用的是 p7zip先确认装了没有sudo apt install p7zip-full # 只看看包内结构不解压 7z l 卫星遥感舰船检测数据集VOCYOLO格式2238张17类别.7z | head -40 # 真正解压到目标目录 7z x 卫星遥感舰船检测数据集VOCYOLO格式2238张17类别.7z -o./shipdata这里最值得说的是-o参数的坑-o和输出路径之间不能有空格。写成-o ./shipdata7z 会把所有文件解压到当前目录而不是你想要的shipdata文件夹里。后面你再按./shipdata去找数据就会一脸懵。另一个教训是别用7z e它会丢目录结构把全部文件平铺到一个目录里VOC 的 Annotations、JPEGImages 层级全乱。只有你需要把散文件抽出来单独看某张图片时才用7z e。解压完先别急看一眼根目录有没有README、classes.txt或labels.txt。这 17 个类别的具体顺序决定后面data.yaml里names字段的写法和顺序。我见过太多人跳过这一步直接按自己猜的类别名训练最后模型预测结果驴唇不对马嘴。2.2 看懂两套格式XML像素坐标与TXT归一化坐标怎么换算VOC 格式的标注文件是 XMLYOLO 格式的标注文件是 TXT两者描述的是同一个框但坐标系完全不同。VOC 里存的是像素绝对坐标YOLO 里存的是相对于图片宽高的归一化中心点坐标。先看一个标准的 VOC XML 对象annotation filename000001.jpg/filename size width1024/width height768/height /size object namecargo/name bndbox xmin120/xmin ymin80/ymin xmax360/xmax ymax220/ymax /bndbox /object /annotationYOLO 格式里对应的一行是这个0 0.234375 0.1953125 0.234375 0.18229166666666666换算关系很简单中心点 x (xmin xmax) / 2 / width中心点 y (ymin ymax) / 2 / height宽 (xmax - xmin) / width高 (ymax - ymin) / height。以刚才的例子x 中心是 240 / 1024 0.234375y 中心是 150 / 768 0.1953125宽 240 / 1024 0.234375高 140 / 768 0.1822917。手工验一遍你对这套数据集的信任度会立起来。同时也要明白VOC 和 YOLO 双格式存在的意义不只是转换而是方便你用不同工具链。比如你要用 LabelImg 复核标注它原生读写 VOC 更顺手你要直接开训 YOLOTXT 是唯一输入格式。双格式就意味着你不用自己写转换脚本但前提是两份标注本身一致——这一点恰恰最容易出问题。2.3 校验脚本把XML转成YOLO坐标逐行比对2238份txt拿到双格式数据集我做的第一件事永远是校验两份标注是否吻合。2238 张图靠肉眼不可能查完用脚本把 VOC 的 XML 转成 YOLO 坐标再和现成的 TXT 逐行对比差异超过 0.001 就打印出来。这个阈值留的是浮点误差不是容错空间。#!/usr/bin/env python3 # check_voc_yolo.py # 用法: python check_voc_yolo.py VOC/Annotations labels import os import sys import glob import xml.etree.ElementTree as ET voc_dir sys.argv[1] if len(sys.argv) 1 else VOC/Annotations yolo_dir sys.argv[2] if len(sys.argv) 2 else labels def voc_label_to_yolo(xml_path): tree ET.parse(xml_path) root tree.getroot() w int(root.find(./size/width).text) h int(root.find(./size/height).text) objects [] for obj in root.findall(object): name obj.find(name).text bnd obj.find(bndbox) xmin float(bnd.find(xmin).text) ymin float(bnd.find(ymin).text) xmax float(bnd.find(xmax).text) ymax float(bnd.find(ymax).text) cx (xmin xmax) / 2 / w cy (ymin ymax) / 2 / h bw (xmax - xmin) / w bh (ymax - ymin) / h objects.append((name, cx, cy, bw, bh)) return objects xml_files sorted(glob.glob(os.path.join(voc_dir, *.xml))) for xml_path in xml_files: stem os.path.splitext(os.path.basename(xml_path))[0] txt_path os.path.join(yolo_dir, stem .txt) if not os.path.exists(txt_path): print(fMISSING TXT: {stem}) continue voc_boxes voc_label_to_yolo(xml_path) with open(txt_path, r) as f: yolo_lines [line.strip() for line in f if line.strip()] if len(voc_boxes) ! len(yolo_lines): print(fCOUNT DIFF: {stem} voc{len(voc_boxes)} yolo{len(yolo_lines)}) continue for i, (name, cx, cy, bw, bh) in enumerate(voc_boxes): parts yolo_lines[i].split() if len(parts) 5: print(fBAD LINE: {txt_path} line {i}) continue cls_idx, cx_p, cy_p, bw_p, bh_p int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) diff max(abs(cx - cx_p), abs(cy - cy_p), abs(bw - bw_p), abs(bh - bh_p)) if diff 0.001: print(fMISMATCH: {stem} line {i} cls{name} voc({cx:.5f},{cy:.5f},{bw:.5f},{bh:.5f}) fyolo({cx_p:.5f},{cy_p:.5f},{bw_p:.5f},{bh_p:.5f}) diff{diff:.5f}) print(check done)脚本逻辑不复杂逐个读 XML把像素坐标换算成归一化坐标再和同名的 TXT 逐行比较。注意比较时不要只比圆圈里数字的小数点后一位浮点精度不到三位会误报。COUNT DIFF是最常见的错误说明 XML 里框了 2 个目标TXT 里只有 1 个框或者反过来。出现这种情况多半是某张图在标注阶段后期被改过但两份标注没有同步更新。跑完这个脚本如果 2238 份全部通过恭喜这份数据可以进入训练环节如果有几百个文件报错说明这个包要么在制作时转换不完全要么后期被人手动改过。别硬着头皮用先用 LabelImg 对着问题图片重新看一眼保留有问题列表再决定是删图还是手工补标。3. 用2238张图认真训一个模型类别分布、数据划分与增强策略3.1 统计17类分布先给数据量做一次体检2238 张对说的 17 个类别的遥感舰船检测来说不算大也不算小。真正决定训练效果的是每个类别的样本数而不是笼统的总数。类别不平衡在遥感场景里几乎是常态港口里货船多到上百艘某些军用舰船可能只有几十个实例。先用最简单的脚本统计全部 YOLO 标签文件里的类别分布#!/usr/bin/env python3 # class_dist.py import glob from collections import Counter yolo_dir labels counter Counter() for txt_path in glob.glob(f{yolo_dir}/*.txt): with open(txt_path, r) as f: for line in f: line line.strip() if not line: continue cls line.split()[0] counter[cls] 1 for cls_id, cnt in sorted(counter.items(), keylambda x: int(x[0])): print(fclass {cls_id}: {cnt} instances)这里直接按数字编号统计不要先映射类别名因为 YOLO 的 TXT 第一列永远是类别索引。统计结果出来以后对照压缩包里的classes.txt看对应的是哪些船型。如果发现个别类只有几十个框后面训练时就要特别小心这类容易学不出来或者被模型当成背景漏掉。对于这种类宁可先不参与训练也不要让它把整体 loss 带偏。3.2 按YOLO训练要求整理目录并生成data.yamlUltralytics YOLO 的训练结构要求images和labels下各自有 train/val 子目录比例一般是 8:2 或 85:15。2238 张图不算多按 15% 做验证集约 335 张。这里有个遥感数据的特殊注意事项不要按文件名顺序直接切前 85%遥感图经常按采集区域或时间批次命名顺序切分会导致 train 和 val 里出现高度相似的同一块水域验证成绩虚高。必须先随机洗牌再切。#!/usr/bin/env python3 # split_dataset.py import os import random import shutil random.seed(42) src_images images src_labels labels dst_root dataset val_ratio 0.15 all_files [f for f in os.listdir(src_images) if f.endswith((.jpg, .png, .jpeg))] random.shuffle(all_files) n_val int(len(all_files) * val_ratio) val_files all_files[:n_val] train_files all_files[n_val:] def move(file_list, split_name): img_dir os.path.join(dst_root, images, split_name) lbl_dir os.path.join(dst_root, labels, split_name) os.makedirs(img_dir, exist_okTrue) os.makedirs(lbl_dir, exist_okTrue) for img in file_list: stem os.path.splitext(img)[0] src_img os.path.join(src_images, img) src_lbl os.path.join(src_labels, stem .txt) dst_img os.path.join(img_dir, img) dst_lbl os.path.join(lbl_dir, stem .txt) shutil.copy2(src_img, dst_img) if os.path.exists(src_lbl): shutil.copy2(src_lbl, dst_lbl) else: print(fWARNING: label not found for {img}) move(train_files, train) move(val_files, val) print(ftrain images: {len(train_files)}, val images: {len(val_files)})复制而不是移动文件是因为原始压缩包最好保留一份原样训练过程中改错了还能回到原始标注重新来。random.seed(42)不只是为了好看没有固定种子你每次运行划分结果都不同后面调参时对比模型好坏就没有意义了。同一份训练集、同一个模型、不同的验证集对比出来的指标完全不能参考。目录整理好之后写data.yaml# data.yaml path: ./dataset train: images/train val: images/val nc: 17 names: 0: 以压缩包classes.txt第一行为准 1: 第二行 # 不要猜不要用网上的通用类别名一个必须提醒的细节YOLO 的names列表顺序必须和 TXT 第一列的类别索引严格一致。类别名是给人看的类别索引是给模型看的。很多人偷懒直接用网上找的“渔船、货船、油轮、军舰”之类列表但实际数据集制作者可能把军用舰船排在了索引 0你排的索引 0 是渔船训练时模型学的类别和标签对不上最好的结果也是准确率一塌糊涂。从压缩包里的classes.txt复制不要手打字。再补一句环境相关。如果你在 Anaconda 里面装 YOLO 训练环境建议新建一个干净环境conda create -n yolo python3.11 -y conda activate yolo pip install ultralytics不要盲目用一个很老的基础环境Python 3.8 以下在 Windows 上装 torchvision 经常需要手动找 whl 包非常容易翻车。让 ultralytics 自动拉 torch 版本是最省心的方案。3.3 小目标导向的训练参数imgsz、mosaic与旋转增强遥感舰船检测和普通地面目标检测最大的区别是目标小。一张 2000×2000 的卫星图里一艘 30 米的小渔船可能只占 20×40 像素。你用 YOLO 训练时默认输入尺寸是 640×640整图压缩之后小船直接缩成几个像素理论上就废了。常见做法是把imgsz提到 1280虽然训练慢一点但对小目标召回率的提升非常明显。yolo detect train datadata.yaml modelyolov8s.pt \ epochs100 imgsz1280 batch16 \ mosaic1.0 degrees90 fliplr0.5 scale0.2 \ patience20 workers8mosaic1.0是 YOLO 默认开启的增强把四张图拼在一起训练增加背景多样性。但遥感舰船检测里mosaic 会让本来就很小的目标变得更碎尤其是边框跨了拼图切分线时目标被切成一半标注还留在图上。如果训练发现 val 的 mAP 一直上不去先把 mosaic 调到 0.5 甚至 0.3 试试。degrees90意味着模型会看到旋转 0、90、180、270 度四个朝向的船遥感图像无所谓上下这个增强不损失语义还能让模型对船头朝向的适应力更强。scale0.2控制尺度变化幅度遥感目标本身就是多尺度这个值可以给到 0.3~0.5但再大容易让货船变得像渔船反而引入新噪声。训练过程中重点盯 val 曲线的形态。YOLO 训练日志里会有box_loss、cls_loss、dfl_loss三条曲线别一上来就琢磨改 yolo 损失函数先确认这三条曲线都是稳步下降的。如果val_cls_loss在某个 epoch 后不降反升那是过拟合信号把patience调低或者增加平移、翻转增强而不是瞎改损失函数里的权重。4. 遥感舰船数据集训练避坑指南5个最容易翻车的地方4.1 7z压缩包密码正确却一直报错别在密码上耗时间现象解压时反复输入密码7-Zip 或 p7zip 持续报“密码错误”或“CRC 失败”但密码来源很可靠复制粘贴也确认过没有多余空格。原因最常见的是压缩包本身不完整下载过程断点续传导致尾部数据缺失7z 在解压到末尾校验时把问题误报成密码错误其次是文件名里有特殊字符终端或 Windows 资源管理器对非 UTF-8 编码的中文文件名处理不正常。解决先不输入密码单独测试文件头数据是否正常7z t 文件名.7z若测试在 40% 左右停下且不报密码问题说明头部完整、body 损坏重新下载源文件如果扩展名是.7z.001、.7z.002这种分卷形式必须把全部分卷放在同一目录下再解压缺少任意一卷都会报密码错误。用 7-Zip 打开压缩包查看文件名如果里面文件名乱码多半是编码问题尝试换用脚本解压或重命名压缩包为纯英文名解决。4.2 XML和TXT框对不上先怀疑格式错位再怀疑复现误差现象第 2.3 节的校验脚本跑出了大量MISMATCH报错集中在少部分图上。原因同一张图的 VOC XML 和 YOLO TXT 可能来自不同版本的标注比如后期某人改过船框边界但没有更新另一份标注也可能是数据包在格式转换时用了脚本对不同尺寸的图片统一套用了某个宽度导致归一化坐标全部偏移。解决先把diff阈值放宽到 0.01确认到底是“系统性偏差”还是“个别极端值”。系统性偏差说明整个转换管线有问题需要重新生成个别极端值说明是人工标注修改后未同步按文件名手工修复即可。修复之后建议只用一套格式作为标准。我一般以 VOC XML 为准因为它保留了像素坐标改起来更直观YOLO TXT 在每次训练前重新生成。4.3 类别编号错位船型预测串了的典型案例现象训练后的模型在测试图上把货船预测成军舰而且置信度还挺高。原因训练时data.yaml的names顺序和标签文件里的类别索引对不上。比如数据包里classes.txt第 0 行是cargo而你写 yaml 时自作聪明把warship放在第 0 位模型学到的“cargo”特征被强行贴上了“warship”的标签。这种错误不会明显拉低 loss因为模型学到的映射关系本身是自洽的所以训练过程完全看不出来。解决第 3.2 节强调过yaml 里的names直接复制压缩包自带文件。训练前用一段小代码把训练集里前 5 个 TXT 的第一列取出来和 yaml 里对应位置的类别名打印比对一眼。跑一次推理前也从验证集里抽三张图用yolo predict输出并人工确认类别名这一步能救回大量部署阶段的尴尬。4.4 小目标丢检查完imgsz再查置信度门限别改损失函数现象验证集 mAP 看着不错但实际测试图上小船全部漏掉只检出大船的框。原因和解决这是遥感舰船检测中最典型的矛盾。先用 1280 或 1536 的 imgsz 重训一遍看 mAP 是否上升如果没变再确认测试推理时是不是用了默认conf0.25。小船因为像素少神经网络给出的置信度天然比大船低0.25 门限可能把它滤掉。把 conf 降到 0.1 试试代价是背景虚警增多此时用 NMS 的 iou 阈值 0.6 来控制重复框而不是继续拉高 conf。这两个检查做完还有漏检再考虑是不是训练时mosaic把小目标切碎了。不要一上来就把锅甩给 yolo 损失函数遥感数据集的问题九成出在分辨率与门限上损失函数反而是最不需要动的那一环。4.5 显存不够调小batch size之前先做这两件事现象训练报CUDA out of memory宿主机 GPU 显存只有 8GB。常见做法是一路把 batch size 降到 4但 mAP 跟着掉模型越训越差。原因batch size 过小时BatchNorm 的统计量不稳定梯度更新方向噪声太大模型收敛质量明显变差。解决顺序应该是先把imgsz从 1280 降回 640同时加上cacheTrue让数据预加载进内存而不是每次从磁盘读这一步能省不少显存再把 batch size 从 16 降到 8如果还是不够换yolov8n或yolov5su这种更轻量的模型而不是继续压缩 batch。最后手段才是batch4 accumulate4用梯度累积模拟批次大小保住训练稳定性。提示显存不够优先降 imgsz 而不是降 batch这条适用于绝大多数遥感小目标训练场景。5. 训练完了怎么定阈值用置信度门限和NMS把漏检与误检调到能用的程度模型训练完best.pt出来并不代表可以直接部署。YOLO 推理默认conf0.25、iou0.45这两个参数在不同场景下需要重新调。比如近岸港口监控里漏掉一艘小船是严重事故只能容忍少量虚警那么置信度门限就要往下走反过来做海面普查背景噪声多虚警成本高门限就得往上抬。先看验证生成的F1_curve.png横轴是置信度纵轴是 F1 分数曲线最高点对应的置信度就是理论最优起点。yolo predict modelruns/detect/train/weights/best.pt \ sourcetest_imgs imgsz1280 \ conf0.12 iou0.6 \ save_txt save_conf下表是针对遥感舰船场景的基准设定思路场景conf 起点iou 起点调节依据港口监控、小船漏检代价高0.08~0.150.6漏检率明显降低后再逐步升 conf 压虚警常规巡检、人工复审0.20~0.300.5以 F1_curve 峰值附近为准全自动告警、无人复核0.40~0.500.45宁可漏不可错虚警会淹没告警通道调阈值不是一次性的。我在项目里的习惯是先把conf0.1跑一遍全部测试图统计虚警数量在可接受范围内后每次加 0.02 重新跑直到出现第一个漏检止住再用这个值回验验证集 F1。这个过程的判断依据是save_conf输出的 txt它会保留模型对每个框的原始置信度方便你离线做后续的置信度重分析而不必每次重跑推理。调整时注意NMS 的 iou 阈值影响的是重叠框的合并程度船上目标密集时 iou 调到 0.6 以上稀疏时保持 0.45 即可不用动得太频繁。最后说一个我一直保留的习惯每训好一个版本就把它的混淆矩阵、F1 曲线、PR 曲线全部归档并在文件名里标好比best.pt的置信度设定值。这样对方问“为什么这个模型漏了渔船”你能立刻翻出证据而不是重新跑一遍测试去猜。数据集的格式只是起点真正决定项目成败的是你在训练之后有没有把阈值、增强和验证这三件事当成一个整体去打磨。希望帮到你。本文还有配套的精品资源点击获取
返回列表