☰
YOLO船舶检测实战:数据集解析与训练避坑指南
2026/10/9 4:52:23 网站建设 项目流程

简介:面向YOLO系列算法的多类别船舶检测数据集,涵盖航空母舰、潜水艇、游船、集装箱船、散货船、帆船等常见船型,既适合刚接触目标检测的初学者练习完整训练流程,也适用于研究者评估不同YOLO版本在船舶识别任务上的效果,可广泛用于海上交通监控、船舶目标检测等场景。压缩包共2000个文件,以XML标注文件为主,并配套YOLO格式的TXT标签,两种格式内容对应同一批样本,便于在检测框架间灵活切换;整体大小约231.66MB,目录层级清晰,可按需取用。数据集已完成训练集与验证集划分,内置data.yaml配置文件,可直接适配YOLOv5、YOLOv7、YOLOv8、YOLOv9、YOLOv10、YOLO11等主流算法,帮助节省数据整理与格式转换时间,开箱即可启动训练。每个标签都包含目标类别索引、归一化中心点坐标以及宽高比例,既满足YOLO训练输入要求,也可通过VOC XML格式进行可视化检查,方便核对标注准确性并理解坐标换算逻辑,便于后续扩展数据增强或迁移学习。已有157人学习下载,适合需要快速获取高质量船舶标注数据、开展目标检测实验或进行模型比对的开发者使用。

1. 用 yolo 算法做船舶检测:这个数据集值不值得下

做船舶目标检测的人多半经历过一个尴尬阶段:HRSC2016 类别单一、场景偏遥感,DOTA 又全是任意框、上手门槛高,真正拿来复现 yolo 系列算法时总差一口气。这份数据集一共 12122 张图像,标注了航空母舰、潜水艇、游船、集装箱船、散货船、帆船等七个类别,而且同时给出 yolo 格式(txt)和 voc 格式(xml)两套标签,外加划分好的训练集、验证集、测试集和 data.yaml 配置文件。对新手来说,解压后就能直接跑通 yolov5/v8/v9/v10/v11 的训练流程;对老手来说,省掉了一周的数据清洗和格式转换时间,拿到手先做校验再调参,省下的时间全花在刀刃上。

2. 数据集结构与标签格式:先搞清楚里面装了什么

2.1 先检查目录结构,别急着开训

这类数据集最常见的目录约定是 images 和 labels 分开放,train / val / test 各自对应。解压后我建议你先做一件事:不急着打开训练脚本,而是按下面的路径把结构过一遍。

dataset/ ├── data.yaml ├── images/ │ ├── train/ (带图像) │ ├── val/ (带图像) │ └── test/ (带图像) ├── labels/ │ ├── train/ (对应txt) │ ├── val/ (对应txt) │ └── test/ (对应txt) └── annotations/ └── xml/ (voc格式xml)

如果你解压后的实际目录层级和上面有出入,不要慌,以 data.yaml 里的路径为准。一般做法是 data.yaml 里写相对路径,训练时会按 yaml 文件的path字段做拼接。我的习惯是先把 data.yaml 打开读一遍,确认train、val、test三个字段指向的目录确实存在且图像数量对得上。

这里有一个关键点:txt 格式的标签是归一到 0~1 之间的比例值,而 xml 格式里的bndbox是像素坐标。这两种格式并存是这份资源最省事的地方——训练直接吃 txt,检查和可视化用 xml,不需要你手工换算。

2.2 yolo 格式的 txt 标签:一行一个目标

yolo 格式的核心写法是<class> <x_center> <y_center> <width> <height>。注意,后面的四个数值全部是相对于图像宽高的比例,不是像素值。比如一张宽 1920、高 1080 的图,某个目标框中心在像素 (960, 540),框宽 300、高 200,那么对应的一行就是:

0 0.5000 0.5000 0.1563 0.1852

这里 class 从 0 开始编号。第一个0表示这个目标属于第 0 类,也就是 data.yaml 里 names 列表的第一个类别。如果你的类别列表是['aircraft_carrier', 'submarine', 'cruise', 'container_ship', ...],那么0就是航空母舰,1是潜水艇,以此类推。

训练时模型的损失函数会直接拿这个归一化坐标和预测框对比,所以最怕的就是有人把 txt 里的坐标理解成像素值去改,一改就废。我的习惯是打开任意一张图的 txt 文件,先看第一列数字是否都在 0~类别数-1 范围内,再看后面四个数是否都在 0~1 之间,如果出现大于 1 的值,说明标签有问题,要回头检查生成脚本。

2.3 voc 格式的 xml 标签:标注内容的原始依据

xml 是标准的 voc 结构,里面包含了图像名、图像尺寸、以及每个目标的类别和边界框像素坐标。示例如下:

<annotation> <filename>img_0608_620.jpg</filename> <size> <width>1920</width> <height>1080</height> <depth>3</depth> </size> <object> <name>aircraft_carrier</name> <bndbox> <xmin>860</xmin> <ymin>450</ymin> <xmax>1160</xmax> <ymax>650</ymax> </bndbox> </object> </annotation>

xml 的价值不在于训练,而在于校验和可视化。比方说你怀疑某张图的 txt 标签框错了,打开对应 xml 一看就能确认原始标注的真实位置。另一个常见用途是把它转成 coco json 格式,直接用 mmdetection 或 detectron2 做对比实验。很多从 voc 时代过来的老代码也只认 xml,这份数据集两种格式都留了,省得你自己写转换脚本再担心转换出错。

提示:txt 和 xml 是同一份标注的两种表达,修改标注时只改其中一份,训练时就会遇到“标签对不上”的隐蔽问题。后面避坑章里我会专门讲这个。

3. 从零训练一个 yolov8 船舶检测模型:命令与参数

3.1 确认 data.yaml:类别名和 nc 必须对齐

训练任何 yolo 系列模型,第一步都是把 data.yaml 配好。这份数据集的摘要里明确写了适用 yolov5、yolov7、yolov8、yolov9、yolov10、yolo11,也就是说配置文件通用。打开 data.yaml 后,你应该看到类似下面的内容:

# data.yaml path: ./dataset train: images/train val: images/val test: images/test nc: 7 names: ['aircraft_carrier', 'submarine', 'cruise', 'container_ship', 'mengla', 'bulk_carrier', 'sailboat']

我一般会重点核两处:第一,nc是不是和names的长度一致;第二,names的顺序是否和 txt 标签里的第一列索引一一对应。数据集标题里列出的七个类别顺序基本就是最终 names 的参考顺序,如果你训练后发现某个类别的预测结果总对不上,第一嫌疑就是这里。

注意一个细节:data.yaml 里的路径写法和你终端执行命令的位置有关。如果你在项目根目录下运行训练命令,path: ./dataset就表示当前目录下的 dataset 文件夹;如果你的数据集放在别的路径,建议直接改为绝对路径,避免在服务器上反复调试路径问题。

3.2 用 ultralytics 跑训练:一条命令起步

安装 ultralytics 后,训练命令非常简洁。以 yolov8n 为例:

pip install ultralytics yolo detect train \ data=dataset/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ project=ship_det \ name=yolov8n_baseline

参数说明:

  • model=yolov8n.pt:加载预训练权重,n 是最轻量级版本,显存占得小,适合先把流程跑通。
  • epochs=100:船舶检测的收敛速度比通用目标检测稍慢,100 轮起步比较稳妥。如果验证集 mAP 还在涨,就追加到 150~200。
  • imgsz=640:这是默认输入尺寸。后面避坑章会讲到,远距离小目标场景下可以考虑拉到 960 或 1280。
  • batch=16:如果你的显卡是 8GB 显存,建议调到 8;如果 12GB 以上,16 没问题。
  • project和name:指定输出目录,方便多次实验对比。

第一次跑通后,我建议你在runs/detect/yolov8n_baseline/目录下看两个文件:results.png和confusion_matrix.png。前者能看到 loss 收敛趋势和各类 mAP 曲线,后者能直接看清哪两类船容易互相混。

如果你的模型卡在 zero epoch 上久久不开始训练,多半是cache=True在提前缓存数据,第一次会比较慢,属正常现象。

3.3 验证和推理:mAP 怎么看、预测怎么跑

训练结束后,先跑验证脚本:

yolo detect val \ data=dataset/data.yaml \ model=ship_det/yolov8n_baseline/weights/best.pt \ imgsz=640

验证输出里你主要看两个指标:

  • mAP50:IoU 阈值 0.5 下的平均精度,反映“框有没有大致对”的水平。
  • mAP50-95:IoU 从 0.5 到 0.95 逐档取平均,对框的精准度要求更高,船舶检测里这类更接近实际业务要求。

如果 mAP50 高但 mAP50-95 明显低,说明框的位置基本准但边缘贴合不够好,可以尝试加大iou损失权重或转向更大模型。推理单张图用:

yolo detect predict \ model=ship_det/yolov8n_baseline/weights/best.pt \ source=dataset/images/test/ \ save=True

预测结果会保存到 runs/detect/predict 目录,建议挑一批远景图、港口密集图和近景大船图各几张,肉眼确认漏检情况。这一步成本很低,但对判断模型真实泛化能力很关键。

4. 标签格式互转与数据校验:别让模型在脏数据上白跑

4.1 两种格式并存的真实原因

有人会问:既然训练只吃 txt,那 xml 是不是多余的?实际不是。yolo 格式紧凑、训练友好,但可读性差,而且很多第三方工具(比如 LabelImg、Roboflow 导出、coco 转换脚本)默认吃 voc 或 coco。手上同时握着 xml 和 txt,意味着你可以随时做三件事:可视化校验标注、转成 coco 跑检测框架、以及用 xml 重建被误改的 txt。

4.2 一个稳妥的 VOC 转 YOLO 脚本

如果你拿到别的数据集只有 xml,或者你想把这份数据集的 xml 当作“原始事实”重新生成一套 txt,下面这个脚本是常见做法:

import xml.etree.ElementTree as ET import os # 类别顺序务必与 data.yaml 中的 names 保持一致 CLASSES = ['aircraft_carrier', 'submarine', 'cruise', 'container_ship', 'mengla', 'bulk_carrier', 'sailboat'] def voc_to_yolo(xml_file, output_dir): tree = ET.parse(xml_file) root = tree.getroot() img_w = int(root.find('size/width').text) img_h = int(root.find('size/height').text) txt_lines = [] for obj in root.iter('object'): name = obj.find('name').text.strip() if name not in CLASSES: continue cls_id = CLASSES.index(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 width = (xmax - xmin) / img_w height = (ymax - ymin) / img_h # 防止数值越界 x_center = min(max(x_center, 0.0), 1.0) y_center = min(max(y_center, 0.0), 1.0) width = min(max(width, 0.0), 1.0) height = min(max(height, 0.0), 1.0) txt_lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}") if txt_lines: txt_name = os.path.splitext(os.path.basename(xml_file))[0] + '.txt' with open(os.path.join(output_dir, txt_name), 'w') as f: f.write('\n'.join(txt_lines))

这段代码的核心逻辑是:从 xml 里读出像素坐标,除以图像宽高得到比例值,再按顺序拼接成 yolo 格式的一行。几个容易写错的点我解释一下:

  • size/width和size/height必须用 xml 里记录的值,而不是用cv2.imread重新读图。因为有些图像经过压缩或缩放后,磁盘上的尺寸和 xml 记录的尺寸不一致,以 xml 为准才和原标注匹配。
  • 类别索引用的是CLASSES.index(name),所以脚本里的类别顺序必须和 data.yaml 的 names 完全一致,否则训练时你会看到“航母的框全落在帆船类别里”这种诡异结果。
  • 坐标加了一层 min/max 截断,防止个别标注越界导致训练 loss 异常。

4.3 训练前强制做三件事

我的血泪经验是,不管资源描述写得多完整,训练前必须做数据校验。尤其是从网上下载的数据集,宁可多花半小时检查,也不要让模型在一个有问题的数据上白跑几十轮。

第一件事是统计每个类别的目标数量。用下面这段代码把 labels 目录下所有 txt 的第一列统计一遍:

cat dataset/labels/train/*.txt | awk '{print $1}' | sort | uniq -c

这个命令会把每个类别索引的出现次数列出来,哪类船多哪类船少一目了然。如果某个类别只有一两百个目标,训练时基本学不到东西,类间不均衡会导致验证集上该类别 mAP 长期在低位。

第二件事是抽查可视化。挑十几张图,把标签框画上去看看标注边界是否贴合船体。画框脚本很简单:读图,读 txt,把归一化坐标换算回像素坐标,用 cv2.rectangle 画框。这一步主要是排除“标签存在但框偏了”的情况,这种问题训练时看不出来,但推理时会很致命。

第三件事是检查 txt 和 xml 的对齐情况。比如随机抽 50 张图,分别统计两个格式下的目标数量,如果数量对不上,说明有一份标签被单独改过,需要以 xml 为准重建 txt。

提示:这三步做完再开训练,基本能排除 80% 以上“没看到错在哪但模型一直不收敛”的伪玄学问题。

5. 避坑指南:12122 张图训练中的常见问题与排查

5.1 训练能跑完,但预测时“航母变帆船”

现象:loss 正常下降,val mAP 也不差,推理时却出现大量错类,尤其是把航母和帆船混淆,或者集装箱船和散货船混淆。

原因:data.yaml 里 names 的顺序和 txt 标签里的类别索引对不上。如果训练脚本里 names 是['sailboat', 'container_ship', ...],而 txt 里原本的0代表航母,模型学到的和标签语义完全错位。

解决:打开任意一个 txt 文件,看第一列数字对应的到底是哪个类别,再回看 xml 里的 name 字段确认。最保险的办法就是用上面的 voc_to_yolo 脚本以 xml 为源重新生成整套 txt,彻底消除索引错位风险。

5.2 训练时框数量明显偏少,远距离小船全被忽略

现象:训练日志里 per image targets 很少,某些批次甚至只统计到几个目标;验证集上小目标召回率奇低。

原因:船舶检测场景大量目标是远距离小目标,在 640x640 输入下,一条小渔船可能只占十几个像素,特征经过多次下采样后在特征图上几乎消失。

解决:把imgsz提升到 960 或 1280,同时把batch减半。显存不够时开启amp=True,并且使用cache=False避免缓存占用额外显存。实测这一类场景下 imgsz 从 640 提到 1280,小目标的 recall 提升非常明显,代价是训练时间翻倍,属于正常取舍。

5.3 训练 batch 开大点直接 OOM 翻车

现象:设置 batch=32 后,刚开始训练就报 CUDA out of memory。

原因:模型输入尺寸越大,每一张图占用的显存越多。batch 和 imgsz 同时拉满,8GB 显卡根本扛不住。

解决:8GB 显存以imgsz=640, batch=8起步,12GB 可以到batch=16;用amp=True能省 20%~30% 显存。如果还想增大 batch,就上梯度累积,常见做法是把 batch 设成 8,然后累积 4 个 step,等效于 batch=32 的效果,显存占用却不变。

5.4 验证集 mAP 还行,实拍图一测就露馅

现象:训练和验证都在数据集上表现不错,到了自己的实拍图片或视频上,漏检、误检大量出现。

原因:数据集和实际场景存在分布偏差。比如训练图多为近景俯拍,实拍却包含大量雾天、夜间、遮挡密集场景。

解决:不要只盯着 val 的指标,抽 100 张自采图片单独建一个小型测试集,用前面讲的 predict 命令跑一遍,按漏检类别统计。如果集装箱船漏检多,就单独给这类补一些形态增强,常见做法是开启hsv_h=0.015、hsv_s=0.7等颜色增强,并拉大scale=0.5让模型见过不同尺度的船。

5.5 改过 xml 标签,但训练结果没有任何变化

现象:手动改了几张图的 xml 标注,重新训练后发现 val 指标和之前一模一样,好像改动没生效。

原因:训练用的根本是 txt 标签,xml 只是“看起来存在”的副本。改 xml 不会同步更新 txt。

解决:每一轮人工修正后都跑一次 voc_to_yolo 脚本,让 txt 从 xml 重新生成。从那以后我把这个步骤焊死在流程里:每次改标注,第一件事是重建 txt,第二件事是重新统计类别数量,第三件事才是开训练,顺序错一步后面全是坑。

6. 进阶:用混淆矩阵和分类别 mAP 做针对性调优

基础流程跑通后,真正拉开效果差距的是分析训练产物。我每次训练完第一件事是打开confusion_matrix_normalized.png,看哪两类船互相污染最严重。船舶类别里最常出问题的是集装箱船和散货船,两者外形相似度高、标注边界也容易重叠;观察矩阵里这两个类别之间的误召回数值,如果明显高于其他组合,说明训练数据里这些形态的区分度不够。

第二步是在 validation 输出里按类别看 mAP,用下面的方式过滤结果:

yolo detect val \ data=dataset/data.yaml \ model=ship_det/yolov8n_baseline/weights/best.pt \ imgsz=1280 \ save_json=True

开启save_json=True后,结果会输出每类的 AP 数值,逐个类别拉出来看,哪类低就针对哪类补强。如果潜水艇的 AP 明显拉低,多半是该类别实例数太少或多数目标极小,此时可以考虑对该类别做过采样拼接,或者把输入分辨率再往上提一档,甚至可以切回较大模型如 yolov8m 对比一次。

最后一个实用技巧是用tune模式跑一轮超参数搜索,yolo 内置了遗传调参,常见命令如下:

yolo detect tune \ data=dataset/data.yaml \ model=ship_det/yolov8n_baseline/weights/best.pt \ epochs=30 \ imgsz=960 \ iterations=20

它会自动搜索 lr、mosaic、hsv 增强等参数,生成一份更贴合船舶数据分布的超参组合。这个搜索跑完后,再用搜出的结果重新训练完整轮数,通常比手动调参更省心。

从那以后,我每次跑船舶检测项目都强制走一遍固定流程:解压后先校验标签,训练中盯小目标指标,训练完必看混淆矩阵和分类别 mAP,再决定要不要动数据增强参数。这套流程帮我在好几批数据上少走了弯路,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询