做目标检测的同行应该都有同感,无人机、船舶、车辆这几类任务里,船舶检测算是比较“折磨人”的一种:目标尺度跨度极大、海面背景复杂、天气干扰严重,公开数据集又少得可怜。最近整理了一套包含3000张图像的YOLO海事监测数据集,专门针对船舶检测任务做了标注,覆盖多种常见船型和不同海况场景,训练和评估都直接可用。这篇文章就围绕这套数据集的构成思路、标注细节、训练配置和应用扩展写一写,把我踩过的坑和总结的经验一并放出来,给正在搞海事监测、港口安防或者船载视觉的同学做个参考。
1. 数据集设计与选型思路
1.1 为什么需要专门的海事船舶检测数据集
船舶检测和普通的通用目标检测有不少差异。通用数据集里的目标大多是行人、车辆、猫狗这类尺度相对稳定的物体,而船舶在海面上出现时,尺度变化极其夸张。一艘万吨货轮在远海可能只有几十个像素,在近港坡道上却可以撑满整个画面;渔船、快艇、帆船之间外形差异很大,部分船型相似度又很高,比如拖轮和渔船在某些角度下容易混淆。海面的反光、浪花、雾气、雨雪也会严重干扰检测器提取特征。
通用数据集如COCO或者直接拿预训练权重来跑,在船舶场景下经常会出现两个问题:一是小目标召回率低,远海的小船直接被漏检;二是误检率高,把浪花、浮标、岸上的建筑物当成了船。究其原因,还是通用数据里没有覆盖足够多的海事场景样本。定制专门的船舶检测数据集,本质上是给模型提供“见过世面”的机会,让它学会在海杂波、逆光、云雾这类复杂背景下区分真正的船舶目标。
3000张的规模,对比COCO那种十几万张的数据集确实不算大,但用于船舶这一类目标相对单一的任务,是能训练出可用模型的。关键在于数据质量而非单纯堆数量,只要充分覆盖不同船型、不同距离、不同天气和不同拍摄角度,3000张配合适当的数据增强,完全足够支撑一个轻量级海事检测系统的初始版本。
1.2 3000张图像的规模考量与类别覆盖
3000张图听起来不多,但按单张图像平均包含2~4个船舶目标来计算,标注框数量大约在8000到12000个之间,这个规模对于单一类目或者几个类目的检测任务来说,已经具备了统计意义。实际分配上,我建议把图像拆分为训练集、验证集和测试集,比例大致是8:1:1,也就是2400张训练、300张验证、300张测试。这样划分既能保证训练样本充足,又让验证和测试集具有一定代表性。
类别设计上,第一版我按最常见的场景需求设定了六类:货船、客船、渔船、集装箱船、帆船、快艇。这里有一个经验教训,类别不是越多越好。刚开始我见过有人把“拖船”“油轮”“散货船”“工程船”等全部拆分开,看起来精细,实际上标注成本高、样本不均衡严重,而且很多船型在侧面和正面视角下区别非常小,自动标注后再人工复核的工作量极大。因此,我最终选择按外观差异明显、且任务需求明确的船型归类。如果项目确实需要更精细的船舶类型识别,可以在这个基础上逐步细分,先保证大类检测稳定,再扩展细分类。
除了类别,图像的场景多样性也做了刻意控制。海况方面分成了晴天、阴天、雾天、雨天、黄昏逆光几类;视角上覆盖了岸基摄像头俯拍、无人机斜拍、船载摄像头平拍三种主流方式;水域背景则包括开阔海域、港口近岸、航道、锚地等。这样做的目的是让模型学到的是“船舶”本身的共性特征,而不是某个特定水域的背景特征,避免在换了一个港口后模型精度断崖式下降。
1.3 为什么选择YOLO格式标注
目标检测的标注格式五花八门,Pascal VOC用XML,COCO用JSON,YOLO用txt。这套数据集最终选择YOLO格式,主要基于三点考虑。
第一,YOLO生态是目前目标检测领域最活跃的生态之一,从YOLOv5到YOLOv8、YOLOv9再到最新的YOLO11,迭代速度非常快,而所有YOLO系列模型都原生支持这种txt格式,标注文件可以直接喂给模型,省去格式转换的麻烦。第二,YOLO格式足够简洁,每行代表一个目标,五个数字依次是类别编号、归一化中心点x坐标、归一化中心点y坐标、归一化宽度、归一化高度。这种纯文本结构便于脚本处理,无论是检查标注、统计类别分布还是做数据清洗,几乎不需要额外的解析库。第三,从数据发布的角度看,txt格式体积小、可读性强,哪怕使用者不开代码,直接用文本编辑器也能看懂标注内容,排查问题非常方便。
这里补充一个细节,YOLO格式的归一化坐标是基于图像宽度和高度的比值,数值范围在0到1之间。很多新手容易在这里出错,比如直接把像素坐标写进去,导致训练时边界框跑偏。标注工具如LabelImg、Labelme、Roboflow等都可以直接导出YOLO格式,导出时要注意设置正确的类别列表顺序,因为txt文件中只有类别编号,编号对应的类别名完全依赖你训练时的names列表,一旦顺序错乱,整个训练就全乱了。
2. 数据集结构与核心细节解析
2.1 目录结构与文件组织
拿到一套数据集,第一件事不是跑训练,而是把目录结构摸清楚。合理的目录组织能省掉很多后期的麻烦,也方便团队协作时统一路径引用。这套3000张数据集我按YOLO惯例做了如下组织:
ship_dataset/ ├── images/ │ ├── train/ # 2400张 │ ├── val/ # 300张 │ └── test/ # 300张 ├── labels/ │ ├── train/ # 对应的txt标注文件 │ ├── val/ │ └── test/ ├── data.yaml └── README.md图像与标注文件同名对应,后缀不同,这是YOLO训练时查找标注文件的基本约定。images和labels两个目录下各自再按train、val、test划分,结构清晰,Mydata.yaml中也只需要指定对应路径。
data.yaml的内容很短,但非常关键,它告诉YOLO去哪里找图、有哪些类别。一个典型的配置如下:
train: ship_dataset/images/train val: ship_dataset/images/val test: ship_dataset/images/test nc: 6 names: ['cargo_ship', 'passenger_ship', 'fishing_boat', 'container_ship', 'sailing_boat', 'speedboat']在训练前务必检查data.yaml里的路径是绝对路径还是相对路径,以及names顺序和标注文件里的类别编号是否一致。这个错误非常隐蔽,尤其是当你复用之前的配置文件而忘了改names顺序时,模型会在训练时把“货船”认成“帆船”,而且loss初期还不容易暴露问题。
2.2 标注文件的内容与人工质检
每张图像对应的txt标注文件是本数据集的核心资产。拿一条典型标注来看:
2 0.583984 0.512207 0.235352 0.129883这个文件里共一行,总共有五种数值。第一个数字2代表类别编号,对应data.yaml中的第三个类别即fishing_boat。后面四个数分别是归一化后的x中心、y中心、宽度、高度。假设原始图像宽度为1920,高度为1080,那么还原回像素坐标就是:
- x中心 = 0.583984 × 1920 = 1121.25
- y中心 = 0.512207 × 1080 = 553.18
- 目标宽度 = 0.235352 × 1920 = 451.88
- 目标高度 = 0.129883 × 1080 = 140.27
左上角坐标 = (1121.25 - 225.94, 553.18 - 70.13) ≈ (895.31, 483.05),右下角坐标 ≈ (1347.19, 623.32)。
质检环节有一个容易忽略的规则:目标框的所有坐标必须在0到1之间,而且宽度、高度必须大于0。如果标注工具分心操作失误,可能会产生轻微越界或者宽高为负的坏框。训练时YOLO会做一系列坐标裁剪和惩罚,但坏框多了会拉低模型收敛速度。我一般会写一段Python脚本来批量检查所有txt文件,主要做三件事:统计每个文件的行数、检查是否有越界坐标、解析类别编号是否超出合法范围。这个脚本只需几十行,但能避免训练跑了一半才发现数据异常的灾难。
2.3 图像多样性与难例挖掘
数据集的价值不仅体现在数量上,更体现在难例的覆盖上。这里的“难例”包括三类:一是小目标,即占比面积小于几十个像素的船舶;二是遮挡目标,比如船与船部分重叠,或者被桥墩、码头设施遮挡;三是低对比度目标,比如灰色船体与灰色海面融为一体,或者逆光下只剩轮廓。
这套数据集中我特意保留了一定比例的难例。从实操角度看,小目标检测的难点在于定位框的偏移容忍度极低,几个像素的偏差就可能让IoU从0.7跌到0.3。针对小目标,YOLO本身有P2检测头或小目标增强策略,但数据预处理上更有效的做法是采用马赛克增强和自适应缩放。马赛克增强会随机拼接四张图,让模型在缩放过程中看到很多不同尺度的目标,对小目标训练有明显帮助。
遮挡目标的处理则更依赖标注的严格性。如果一条船被另一条船挡住了40%的船体,我仍然会给出一个完整的包围框,而不是只标可见部分,因为完整包围框能为模型提供更明确的回归目标,学习效果更好。当然,如果遮挡超过80%,这种样本我倾向于直接剔除,以免引入过多噪声。
低对比度目标有时连人都容易看漏,更别说机器。对于这类难例,建议在标注时使用图像增强工具把亮度、对比度临时调高,确认目标轮廓后再标注。训练阶段则配合适当的色彩抖动、亮度扰动等增强手段,让模型逐渐适应这类水下岸边的复杂背景。
3. 实操:从环境搭建到模型训练
3.1 基于PyCharm的YOLO环境搭建
很多刚接触YOLO的同学会遇到一个尴尬的问题:教程看了不少,但环境总是配不好。这里我给出我个人使用PyCharm搭建YOLO训练环境的一套稳定流程。
第一步,创建Python虚拟环境。打开PyCharm,新建项目,选择Virtualenv作为解释器,Python版本建议选择3.8到3.11之间的版本,不建议直接用最新的3.12或3.13,部分YOLO依赖库可能尚未适配。虚拟环境的好处是隔离依赖,避免不同项目之间的包冲突。
第二步,安装PyTorch。这一步是重中之重,一定要先安装与CUDA版本匹配的PyTorch。个人经验是用CPU跑3000张图片的YOLO训练基本不现实,一个epoch要将近半小时,几十个epoch耗时不可接受。推荐去PyTorch官网获取对应CUDA版本的安装命令,例如:
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118注意这里的cu118代表CUDA 11.8版本,需要你先用nvidia-smi查看自己电脑的显卡驱动支持的CUDA版本,再选择匹配的PyTorch版本。
第三步,安装YOLO训练框架。如今YOLO有多种选择,Ultralytics YOLOv8和最新的YOLO11使用起来最便捷,直接通过pip安装即可。
pip install ultralytics安装完成后,在Python环境中执行import ultralytics不报错,说明基本环境就绪。如果你使用的是YOLOv5,则需要先克隆仓库再安装依赖,方式略有差异,但原理一致。
3.2 数据划分与训练配置
数据划分虽然是常规操作,但有一些细节直接影响训练效果。我先把3000张图随机分配为2400张训练、300张验证、300张测试,但同时做了分层采样,确保每一类船舶在训练集、验证集、测试集中的比例大致相近,避免出现快艇样本全落在验证集而训练集完全没有这种情况。分层采样可以用sklearn的train_test_split配合stratify参数实现。
训练配置我以Ultralytics YOLOv8为例说明。在PyCharm中新建一个训练脚本train.py,核心配置代码如下:
from ultralytics import YOLO model = YOLO("yolov8s.pt") results = model.train( data="ship_dataset/data.yaml", epochs=100, imgsz=640, batch=16, lr0=0.01, augment=True, patience=20, project="runs/ship_detect", name="yolov8s_ship", )这里有几个参数值得展开讲。首先是imgsz,训练分辨率的选择。考虑到船舶数据里小目标不少,我建议至少用640,如果显存允许,用960或1280会更好,因为更高的输入分辨率能保留更多小目标细节,但代价是训练时间和显存占用显著增加。在8GB显存的显卡上,batch设为16,imgsz设为640,YOLOv8s模型可以勉强跑起来;如果显存不足,可以把batch降到8,或换更轻量的yolov8n模型。
pretrained权重选择和epochs设置也有关联。加载yolov8s.pt作为预训练权重,模型参数已经有了通用视觉特征的基础,通常可以在100个epoch内收敛。我设置的patience=20表示验证集指标在20个epoch内没有提升就提前停止训练,避免过度拟合和额外时间浪费。
3.3 训练过程中的损失函数与收敛观察
YOLO训练过程输出的loss曲线并不是一个单一值,而是由多个损失项加权组合的。以YOLOv8为例,loss大致分为边框损失和分类损失,其中边框损失又有坐标回归损失和分布焦点损失。许多人看到loss曲线忽高忽低就慌了,其实在开启马赛克增强等强数据增强策略的前期,loss波动属于正常现象,因为每一批参与训练的图像都是随机拼接的,不同batch之间的难度差异本身就很大。关键要看整体趋势是下降的,以及验证集上的指标是否逐步提升。
另一个常被忽略的观察点是box_loss、cls_loss和dfl_loss这几个分项。如果cls_loss下降很慢,说明类别区分存在问题,比如货船与集装箱船在某些视角下的特征相似,模型难以区分。此时不要盲目加大学习率,而是先检查标注是否存在错误,再看类别样本是否均衡。如果box_loss始终居高不下,则可能是目标框的初始预测尺度与数据集目标尺度差异过大,可以考虑调整anchor或者提高输入分辨率。
在训练中我还习惯开启plots=True,让YOLO自动保存训练曲线、混淆矩阵、PR曲线等重要图表。这些图表在训练结束后可以帮我们直观定位是漏检为主还是误检为主。比如从混淆矩阵中看到货船类别有大量样本被预测成集装箱船,那么既有可能在类别定义上边界模糊,也有可能是训练样本不够,需要针对性补充这类易混淆样本。
3.4 测试与评估指标解读
一个epoch训练结束后,模型会保存多个权重文件,其中best.pt代表验证集指标最优的权重,last.pt代表最后一个epoch的权重。通常我们用best.pt来做最终测试。
测试阶段常用的命令是:
from ultralytics import YOLO model = YOLO("runs/ship_detect/yolov8s_ship/weights/best.pt") metrics = model.val(data="ship_dataset/data.yaml", split="test") print(metrics.box.map, metrics.box.map50, metrics.box.map75)这里的map指mAP@IoU=0.5:0.95,是目标检测领域最综合的指标;map50则是IoU阈值为0.5时的平均精度,更贴近实际工程中的检测效果。船舶检测场景下,我通常更关注map50和小目标mAP值,因为实际部署时很少会要求非常苛刻的IoU阈值,只要框能大概框住船体就算检测成功。同时也要注意,mAP指标体现的是整体性能,如果某个类别的AP奇低,测试报告会单独显示,这时就要回到数据层面寻找原因,而不是盲目调参。
4. 应用场景与落地方向
4.1 海事监测与港口安防
船舶检测数据最直接的落地场景是海事监测,包括港口、码头、航道、锚地的实时监控。传统的人工盯监控方式效率非常低,一个人同时看十几路画面,很容易漏掉异常事件。基于YOLO的船舶检测系统能做到自动识别进入监控区域内的船只,并联动轨迹跟踪、越界报警等功能。
部署时要注意算力问题。岸基摄像头的视频流分辨率通常为1080P或4K,直接对全分辨率推理非常吃算力,更合理的做法是先通过运动检测或者区域设置锁定感兴趣区域,然后只在区域内执行目标检测,或者对视频流进行tile切分,将大图切成多个小块分别推理,再合并结果。
港口安防场景还有一个特殊需求,就是对夜间或者低照度环境的适应。如果摄像头本身没有红外补光,YOLO模型在纯暗环境下的检测能力会大幅度下降。此时需要用红外/热成像摄像机训练另建的模型,或者在数据集里加入低照度增强样本。不要指望一个白天训练出来的模型能直接搞定夜间场景,这点在项目规划时就要想清楚。
4.2 船舶识别与交通管理
除了安防,船舶识别在交通管理场景中同样价值巨大,比如内河航道、海上风电场周边海域的船舶动态监管。通过对船舶进行实时检测和分类,监管中心可以掌握不同船型的分布情况,预测航线冲突风险,还能辅助统计各航道的货运流量。
这类应用对检测速度要求较高,通常在边缘计算设备上部署,比如NVIDIA Jetson AGX Orin这类平台。在Jetson上运行YOLO模型时,可以先将PyTorch模型导出为TensorRT引擎,推理速度能提升几倍。导出时记得使用与设备上CUDA、TensorRT版本匹配的PyTorch,否则容易出现版本不兼容的报错。简单来说,TensorRT的加速原理是把模型网络结构固化,合并算子和层,同时使用低精度计算如FP16或INT8。对于船舶检测任务,FP16精度基本无损,INT8如果标定不好则可能掉点明显,我个人建议优先使用FP16。
4.3 扩展到实例分割、多模态AI分析
3000张的船舶检测数据集虽然以检测框为主,但它完全可以作为起点向更多方向扩展。一方面,可以基于YOLO实例分割模型如YOLOv8-seg对船舶进行像素级分割,此时需要把现有的边界框标注转化为分割掩码。对于轮廓相对规则的船舷、船体,可以通过轮廓提取辅助生成掩码,但人工修正仍是必要的环节。实例分割对船舶的精细轮廓感知更强,后续还可以用于计算船舶的长度、宽度等物理量,在电子围栏和桥梁防撞预警中非常有用。
另一方面,“船舶检测+多模态AI分析”是近年的热门方向。检测模型负责从图像中定位船的位置和类别,而多模态模型可以进一步利用文本描述、AIS数据、气象数据等融合判断船舶行为是否异常。比如,当检测系统识别到一艘渔船出现在禁止捕捞水域时,可以联动AIS信息判断该船是否开启了定位系统,再结合航速、航向判断是否存在潜在违规行为。这种从“看得见”到“看得懂”的升级,往往是海事智能化项目的核心卖点。
5. 常见问题与避坑指南
5.1 标注数据常见问题与质检方案
标注环节的坑比训练环节多得多。先列几个我的亲身体验:
坐标边界越界。标注时手滑拖出了图像边界,或者使用半自动标注后没有修剪坐标,导致宽高值超过1.0,训练时模型会收到无效回归目标。解决办法就是前面提到的质检脚本,在训练前统一扫描一遍。
类别编号与names错位。这个坑非常隐蔽,尤其是在多人协作标注时,不同人导出的类别顺序可能不一致。比如有人把集装箱船放在第3号,有人放在第4号,合并数据后没有统一转换,模型就会在不知不觉中学到错误映射。建议所有标注完成后,写一个脚本读取每张图的类别编号分布,与names列表逐一比对。
小目标框不完整。当船很小且边缘与海天线重叠时,标注员容易漏标或只框住半条船。这类标注在训练中会产生错误的“假阳性”暗示,模型学到的是“半条船也算船”。对于小目标,宁可多花时间放大图像逐像元检查,也不要草率地随手画框。
还有一个值得注意的细节是图像的EXIF信息。部分图像可能被旋转过,标注工具虽然会自动处理旋转,但如果你手工修改过图像分辨率导致尺寸变化,而txt文件里的归一化坐标没有同步重新计算,那么标注会整体偏移。稳妥的方案是统一数据流水线:所有图像先统一预处理尺寸,再开始标注。
5.2 训练过程的疑难杂症与排查
训练不收敛是最让人头疼的问题。症状表现为loss居高不下或者反复震荡。第一步要排查数据读取是否正常,打开YOLO训练日志里的样本预览图,确认图像和标注框是否对应。有时候看到框画在图上位置正确,但类别编号错了,这类错误肉眼是看不出来的,需要统计每个类别的大致目标尺寸范围进行合理性判断。
还有一种情况是验证集指标不错但测试集指标崩了。这多半是数据划分不均匀,或者验证集太小导致参考意义有限。建议把验证集从300张扩大到400张左右,测试集保持200张,并严格分层。另外要注意,训练时如果用了大量数据增强,比如旋转、透视变换,而验证集没有经过同样的增强,那么训练域和测试域本身就不一致,某些情况下会导致验证集指标虚高。
显存不足也是常见问题。除了降低batch和图像尺寸,还有一个办法是开启梯度累积。Ultralytics YOLO中可以通过accumulate参数控制累积步数,相当于用小batch模拟大batch的效果。实测中梯度累积对收敛效果的影响通常小于直接降低batch,但能帮助显存不足的机器顺利完成训练。
5.3 部署推理与精度调优建议
模型训练完成只是第一步,部署环节同样有学问。当你在PC上测试视频流正常,换到Jetson或者工控机上却出现卡顿或掉帧,大概率是模型推理引擎没有针对性优化。如果设备支持TensorRT,务必导出engine格式。如果使用OpenCV的DNN模块读取权重,也要注意模型输入尺寸必须与训练尺寸保持一致,比如都用640,不要以为换个输入尺寸也能跑就随意改,精度损失往往就是在这里发生的。
推理阶段的另一个痛点是置信度阈值和NMS阈值的设置。默认的conf=0.25、iou=0.45用于通用场景尚可,但在海事场景下,由于海面反光和云雾干扰,误检框往往集中在低置信度区间,调高conf到0.4甚至0.5能显著降低误报率,但也会牺牲一部分小目标召回的精度。我的建议是使用验证集绘制置信度与F1曲线图,选择F1最高点的阈值作为初始部署值,再根据实际场景微调。海面风浪大的日子误报会升高,所以最好让运维人员可以远程调整这个参数,而不是写死在代码里。
最后还有一个增强精度的经典技巧:TTA,也就是测试时增强。YOLO自带TTA模式,推理时会使用原始图加多个变换后图像的预测结果进行融合,能提升一点精度但对算力要求成倍增加,适用于对实时性要求不高的离线分析。如果做实时部署,TTA基本不现实,更建议在数据丰富度和标注质量上多下功夫。
最后分享一点个人的实操体会
这套3000张的船舶检测数据集,我在实际使用中最大的感悟是:不要神化“更大更全”的数据集,也不要低估“小而精”的价值。船舶检测任务里,模型能力的上限往往不由训练集的整体数量决定,而是由最难的那些样本决定的。花时间把难例标注扎实,把类别分布理清楚,比盲目追加数千张普通场景图收益高得多。很多朋友拿到数据后第一件事就是开训,但我强烈建议先花两个小时做数据质检和分布统计,跑一轮短周期训练,看看loss曲线和验证集指标是否正常,再决定是否进入正式的百轮训练。这个习惯帮我避免了不少返工的时间浪费。
另外,如果你打算把这套数据作为基础数据继续扩展,可以考虑在图像中增加红外模态,或者用半自动标注将这3000个检测框转化为分割掩码,实现更多元的下游应用。我自己后续就在准备把分割标注补上去,用来计算船舶航向角和投影长度,希望这套数据能成为更多海事视觉项目的起点,而不是只停留在检测演示阶段。