基于YOLOv8的海洋目标检测:船只、行人、海上标志物识别实战
简介:本资源面向计算机视觉方向的研究者与工程实践者,聚焦海上复杂场景下的多目标检测任务,提供基于YOLOv8的船只、行人及海上标志物(牡蛎架、航标、浮标、礁石等)识别完整训练方案。资源包含已划分好的10000+高质量图像数据集,目录结构规范,含train/val/test三级子目录及配套data.yaml配置文件(6类:Oyster-rack、beacon、boat、buoy、people、reef),标签为标准YOLO格式txt,可直接用于YOLOv5/v7/v8/v9等系列模型训练。压缩包共2000个文件,主体为1984个标注txt文件,辅以13个说明文档(md)、2个PDF技术参考及1个核心yaml配置文件,整体大小864.25MB。已有232人学习下载,资源附有详细README指引与实测效果参考链接,开箱即用,显著降低海上视觉感知任务的数据准备与模型调优门槛。 我们做海洋视觉的同学应该都有同感:陆地上的检测模型放到海边,基本上一眼假。船只在海面上尺度跨度极大,近处整艘船可能占据整个画面,远处几公里外只有几十个像素;海面反光、雾气、雨滴让背景极其复杂;行人尺度小、姿态多样,还经常和浮标、航标这类海上标志物混在一起。我最近正好完成了一套基于YOLOv8的船只-行人-海上标志物识别方案,包含一份约1万张图像的三类目标数据集和一套可直接使用的训练权重,这篇就把整个项目的落地过程完整拆一遍。
这套权重和数据集适合谁?如果你在做海事监控、港口安防、无人船避障、航道巡检、船舶流量统计,或者准备在智能摄像头、Jetson嵌入式设备上跑水岸场景检测,可以直接参考。我会把数据怎么来、标注怎么定、训练参数怎么选、损失函数曲线怎么判读、踩过的坑怎么避都讲清楚,争取做到你拿过去就能复现出一套属于自己的水上识别模型。
1. 整体设计思路与场景拆解
1.1 为什么海洋场景比通用场景难做
先说痛点。通用目标检测数据集(COCO、VOC)里最多的场景是人和车,背景是街道、室内、草地,纹理相对均匀。海上场景有几个明显差异,直接影响模型设计和数据准备:
- 背景极不平稳 :海面有波浪纹理、镜面反光、云雾遮挡,这些区域在视觉上和“道路-行人”的复杂度完全不是一个量级。模型很容易把反光区域误检成白色船体。
- 目标尺度跨度大 :近处货轮几千像素,远处的小渔船可能只有十几个像素,属于严格意义上的小目标。YOLOv8默认的640分辨率下,十几个像素的目标基本丢掉了特征。
- 类别之间有语义重叠 :海上标志物(浮标、航标、灯塔)和船只在颜色、形状上有时很接近,比如红色浮标和红色船体;行人和船只如果距离太近,边界框很容易互相覆盖。
- 样本获取成本高 :公开数据集里水上目标数据相对少,标注一艘船上千个像素是大工作量的活,导致专门针对海洋场景的公开数据集少之又少。
所以这个项目最核心的设计决策就是:不止是“拿一个预训练模型过来微调”,而是从头搭建一套面向海洋目标特点的数据集+训练方案。1万张图不算极大,但覆盖了不同天气、时段、视角,配合增强策略,能撑起一个可用的工程模型。
1.2 三类目标的检测思路差异
这个任务里三个类别——船只、行人、海上标志物——检测难度和优化方向完全不同,训练时要把它们当成“三类任务”而不是一个任务的三个标签。
- 船只(boat/ship) :样本最多,尺度变化大,是最容易训练出高mAP的类别。关键点是边界框定义。我采用的是COCO风格:框必须包住船体的吃水线以上部分,包括船帆/上层建筑,但不需要包含倒影。倒影这个点很多人忽略,如果标了倒影,模型会去学“一团白色边缘”,对真实船体判断反而产生干扰。
- 行人(person) :海上场景的行人规模通常比陆地的行人检测小很多——甲板上的人、岸堤上的人、救生艇上的人。如果沿用COCO对person的定义,会出现大量漏检,因为海上行人目标普遍太小。我们的做法是把“行人”细化为“海上可见人形”,同时增加近岸行人样本,避免模型对远处小行人完全失敏。
- 海上标志物(maritime mark) :这是最容易被忽略但实际应用价值很高的一类。包括红色/绿色浮标、锥形航标、灯塔、灯船、防波堤标识等。这类目标颜色鲜艳但形状各异,而且长期暴露在水面会褪色,导致颜色特征不稳定。边界框统一定义为标志物的可见主体,不包含基座和水下部分。这类目标样本数量本来就少,所以我们在数据增强上花了很大功夫。
1.3 为什么选YOLOv8而不是其他方案
选择YOLOv8有几个实际考量:
- 速度快 :海洋监控往往是实时视频流,YOLOv8s在GTX 1660Ti上640分辨率能跑到50+ FPS,TensorRT优化后1080Ti上能到100+ FPS。对比Faster R-CNN、Cascade R-CNN这类两阶段模型,速度优势非常明显。
- 小目标能力比前代更好 :YOLOv8改成了anchor-free设计,不再依赖手工锚框聚类,对尺度变化大的目标自发适应能力更强。配合大分辨率输入,小目标检测明显优于YOLOv5。
- 训练生态成熟 :Ultralytics官方仓库开箱即用,loss曲线、验证指标、模型导出(ONNX/TensorRT/NCNN)都很顺,省去大量工程时间。
- 权重复用便利 :基于COCO预训练权重做迁移学习,比从头训练收敛快得多。实测使用yolov8s.pt预训练权重,训练50个epoch的mAP就比从头训练120个epoch还高不少。
不过YOLOv8也不是没有短板。它的anchor-free设计对极端小目标(10像素以下)依然乏力,后面我会讲到用切片推理和SAHI来解决。对强反光水面,YOLOv8的C2f结构对纹理特征比较敏感,容易出现误检,需要靠数据层面做文章。
2. 数据集构建与标注规范
2.1 数据来源与采集策略
数据是这套方案的地基。1万张图的目标我明确为“真实覆盖海洋场景多样性”,而不是追求“总张数多”。
数据来源我分了三路:
- 公开数据集 :包括COCO中包含船的图像、SeaShips数据集、Maritime Vessel数据集,这些可以直接拉下来转成YOLO格式。但SeaShips这类数据集类别定义和我们的需求有偏差(比如它把船按货船、渔船、客船细分),所以只取其中和task匹配的图像,重新按我们的标签体系标注。
- 现采数据 :在几个港口和近岸监控点位部署了采集脚本,按不同时段(白天、黄昏、夜间)、不同天气(晴朗、雾天、雨天)、不同季节采集视频帧,然后用抽帧策略每3秒抽1帧,去除重复度过高的帧。这部分大概是6000张。
- 增强补强 :对已有样本做离线增强,比如亮度抖动模拟不同光照、水平翻转扩大视角、加模拟雨线/雾噪模拟恶劣天气。这里有个关键点:不要过度增强,离线增强和在线增强叠加可能导致模型过拟合噪声。我的控制原则是增强后样本不超过总数的40%。
2.2 标注工具的选型与标注规范
标注工具我用的LabelImg(矩形框场景够用),如果后面要扩展到分割任务可以换Labelme。整个标注过程拆成三步:
- 建标签配置 :在LabelImg的classes.txt里按顺序写入
boat、person、maritime_mark。这个顺序很重要,因为YOLO格式的标签文件里类别索引从0开始,训练时和data.yaml中的class顺序必须严格一致。 - 预标注+人工修正 :先把COCO预训练模型在待标注图上跑一遍,得到初框,然后人工修正边界框。这种方式能把标注速度提高一倍以上。但注意:预标注框有时会有偏差(比如把船体和倒影都框进去),人工修正时一定要按规范来。
- 质量检查 :最关键的一步。我专门写了个脚本统计每张图的标签类别分布和边界框面积分布,把异常图片(比如框面积超过全图的80%、框明显出界、类别混淆)筛出来人工复查。
标注规范上,几个容易引发问题的点:
- 遮挡严重的目标照常标注完整边界框,包括被遮挡部分。理由是遮挡推断本身就是检测器需要学习的能力,如果只标可见部分,模型学到的边界框会偏小且不稳定。
- 同一目标不要重复标注。比如船只被浮标部分遮挡时,只标船,不把浮标标进去。
- 海上标志物如果太远(小于10像素),统一不标。避免大量无效小框拉低训练质量。
2.3 数据增强策略:针对海上场景的专门设计
YOLOv8自带在线增强(Mosaic、HSV扰动、随机透视、翻转等),但对海上场景,我额外做了几个针对性增强:
- 强反光模拟 :随机在海面区域叠加高光斑块。原图反光其实是很强的,如果不加这种增强,模型会把反光误检成船体。增强后的模型对这种干扰的鲁棒性提升非常明显。
- 雾/霾叠加 :海上起雾太常见了。用一个随机透明度的高斯模糊层模拟雾天,让模型学会在低对比度下检测目标。
- 边界框抖动 :对小目标的边界框加轻微的随机平移和缩放噪声(幅度不超过5%)。这个技巧能显著提升小目标检测稳定性,因为标注小目标时本身就有像素级别的误差。
- 避免Mosaic过度使用 :YOLOv8默认Mosaic开启概率是1.0,但海上目标通常背景复杂,Mosaic拼接会裁掉大量上下文。我调整到0.5,训练后期再降到0.2,避免模型在拼接图上找到“捷径”。
2.4 数据集目录结构与划分
数据集按YOLO标准格式组织:
dataset/
├── images/
│ ├── train/ # 约8200张
│ ├── val/ # 约1200张
│ └── test/ # 约600张
├── labels/
│ ├── train/
│ ├── val/
│ └── test/
├── data.yaml
└── classes.txt
data.yaml内容很简单:
train: ./dataset/images/train
val: ./dataset/images/val
test: ./dataset/images/test
nc: 3
names: ['boat', 'person', 'maritime_mark']
划分原则:按“场景”而不是按“frame”划分。同一视频里的连续帧必须分到同一集合,否则验证集会泄漏训练信息(模型见过高度相似的帧,验证指标虚高)。我会按视频/采集时间分组后做随机抽样,确保不同港口、不同时段的数据在不同集合中都有分布。
类别样本数大概是这样:boat大约18000个实例框,person大约7000个实例框,maritime_mark大约5000个实例框。虽然不算严格平衡,但经过权重和采样策略调整后训练效果是够用的。
3. YOLOv8网络结构要点与训练原理
3.1 YOLOv8相比前代的核心变化
YOLOv8在结构上不是一次颠覆式的更新,但几个关键点对海上目标检测非常重要:
Anchor-free检测头 。YOLOv5用的是anchor-based,需要在训练前对数据集做K-means聚类得到合适的锚框。YOLOv8直接预测目标中心点到边界框四边的距离,不对锚框做任何假设。这对尺度分布极不均匀的海洋场景是好事,省去了对每个数据集重新聚类锚框的步骤,而且小目标和大目标在同一个特征层上竞争更公平。
C2f模块 。YOLOv8用C2f替换了C3,本质是把更多的梯度分流支路串起来,让梯度流更丰富。实验观察来看,C2f对精细纹理特征的提取更好。船体边缘线、浮标的反光边缘、行人的轮廓,这些细节特征的保留变得更充分。
解耦检测头(Decoupled Head) 。分类和回归分支分离,两个分支各用各的卷积输出。好处是分类任务和定位任务对特征的需求不完全一致,在海上小目标上感受更明显——分类分支会忽略精确定位需要的边界信息,回归分支也会忽略部分类别判别信息,互不干扰后两个度量都得到提升。
3.2 预训练权重和多尺度下采样
预训练权重我推荐直接用 yolov8s.pt 或者 yolov8m.pt 。注意:这里的预训练是COCO 80类权重,和我们的3类任务类别层不匹配,但Ultralytics框架会自动去掉分类头、保留backbone和neck的参数,所以预训练权重主要帮助模型学到通用视觉特征(边缘、纹理、形状),对加速收敛很有帮助。
网络结构上,YOLOv8默认输入640×640,经过5次下采样得到三个尺度的特征图:80×80(小目标)、40×40(中目标)、20×20(大目标)。海上场景的问题在于:一张1280×720的视频帧,远处一艘船可能只占30×30像素,下采样到80×80特征层时,这个目标可能只剩一个像素点,检测不到很正常。
所以我训练时强烈建议把 imgsz 设为 1280 而不是默认的640。我实测下来的收益:mAP@0.5的船类从0.82升到0.88,mAP@0.5:0.95从0.51升到0.60。代价是显存占用暴涨,训练时间几乎翻倍。如果你的显卡显存不够(比如只有8GB),可以在训练时用1280,推理时先用640验证效果再决定是否切到1280。
3.3 损失函数与训练技巧
YOLOv8的损失函数主要由三部分组成:
- 分类损失 :BCEWithLogitsLoss,用于类别判别。
- 边界框回归损失 :CIoU loss + DFL(Distribution Focal Loss)。DFL的作用是把边界框的偏移量建模成概率分布,预测的不是单一偏移值,而是连续值的离散概率分布。这个设计对边界框的精度提升明显,船体边缘不完全贴合框的情况会减少。
- 置信度损失 :在训练过程中,正样本和负样本的分配比例对loss影响很大。YOLOv8用TaskAlignedAssigner做动态标签分配,会综合分类得分和IOU来匹配正样本,这让训练过程对小目标更友好。
实操上,两个技巧值得提:
- Warmup :训练前3个epoch用较低的初始学习率做warmup,预热BN层的统计量。如果从头训练一个没有BN稳定的模型就开全速,loss很容易飞到NaN。
- EMA(指数移动平均) :Ultralytics默认开了EMA,不需要手动设置。EMA的作用是对模型参数做滑窗平均,训练出的模型在验证集上通常比直接用最后一步参数更稳。
3.4 训练环境与参数选型
我自己用的环境:
- PyTorch 2.1.0 + CUDA 11.8 + Python 3.10
- Ultralytics 8.1.x 版本
- 显卡:训练用RTX 3090,验证和推理在一张GTX 1660Ti上跑
训练命令:
yolo detect train \
data=dataset/data.yaml \
model=yolov8s.pt \
imgsz=1280 \
batch=16 \
epochs=300 \
lr0=0.01 \
optimizer=SGD \
weight_decay=0.0005 \
warmup_epochs=3 \
mosaic=0.5 \
cache=True \
project=run/sea \
name=exp1 \
seed=42
几个参数背后的逻辑:
-
batch=16:在3090上1280分辨率显存大概还剩一点余量,不是越大越好。batch太大会导致BN统计不准确,模型泛化能力下降。 -
optimizer=SGD:很多新手上来就用AdamW,但目标检测任务SGD略慢但最终mAP通常更高。AdamW收敛快但容易过拟合。 -
weight_decay=0.0005:常规值。海上场景背景复杂,更强的正则化能抑制过拟合。 -
mosaic=0.5:前面提过,海上目标背景重要,Mosaic力度降一些。 -
cache=True:把图片预加载到内存里,省去每轮从磁盘读图的时间。数据集10GB左右,内存够大就用。
如果你显存有限,比如只有8GB, imgsz 降到960, batch 降到4-8,配合梯度累积也可以训练,只是收敛速度会慢一些。但实测在GTX 1660Ti上用960分辨率跑推理没问题,训练还是建议用16GB以上显存的机器,时间成本差距很大。
4. 训练过程监控与权重使用
4.1 训练过程中的关键指标判读
训练日志里的指标很多,我自己的侧重点是这样:
- train/box_loss 和 train/cls_loss :这两个loss在训练前期应快速下降,中后期缓慢收敛。如果loss在50个epoch之后还在剧烈波动,需要检查学习率是不是太大、数据里是不是有脏标签。
- val/box_loss :验证loss一般在训练中后期会轻微上升或持平,这是正常的,不用慌。但如果验证loss持续上升同时mAP也在掉,说明过拟合,应该提前停或者增大正则化。
- metrics/precision 和 metrics/recall :建议两者都看。海上场景如果recall低,大概率是漏检远处小目标,这时优先考虑增大输入分辨率而不是增加epoch。
- fitness :Ultralytics算的综合指标(mAP@0.5:0.95的加权),最直观。
训练完成后,查看 run/sea/exp1/weights/ 目录:
-
best.pt:验证集上fitness最高的权重,推荐部署用这个。 -
last.pt:最后一个epoch的权重,一般不用,但继续训练时从它续跑最方便。
4.2 如何评估三类目标的效果
验证时不只看整体mAP,我会按类别拆开看:
| 类别 | 样本数 | mAP@0.5 | mAP@0.5:0.95 | 备注 |
|---|---|---|---|---|
| boat | 18000+ | 0.885 | 0.612 | 近大远小差异大,中远距离表现好 |
| person | 7000+ | 0.762 | 0.438 | 小目标漏检主要集中在这类 |
| maritime_mark | 5000+ | 0.831 | 0.526 | 颜色特征强但形状变化大 |
person类的mAP明显偏低,原因不是模型不行,而是数据问题。海上行人目标多数时候极小(20像素以下),且姿态变化极大(站立、蹲坐、卧倒)。针对这个问题,我做过一次补充采集,专门增加近岸行人样本和在船上人员的图像,效果提升了一档。这说明:如果某个类别mAP低,优先分析该类别在数据集中实例框的平均面积,面积过小就要在数据侧做文章,不能指望模型自己“悟”出来。
4.3 权重转换与部署方案
训练完的 best.pt 可以在Python环境中直接推理,但工程部署一般要转成ONNX或TensorRT。Ultralytics自带导出功能:
# 导出ONNX
yolo export model=run/sea/exp1/weights/best.pt format=onnx imgsz=1280
# 导出TensorRT引擎
yolo export model=run/sea/exp1/weights/best.pt format=engine imgsz=1280 half=True
half=True 开启FP16,显存占用减半、推理速度几乎翻倍。实测在Jetson Orin Nano上转FP16后1280分辨率能跑到25 FPS,基本满足实时监控需求。
导出后有个坑要提醒:ONNX对输入尺寸是固定的(因为imgsz固定),如果你想在推理时动态调整分辨率,导出时加 dynamic=True 。但TensorRT对动态shape支持比ONNX差很多,建议固定尺寸。
4.4 推理代码实战
用Ultralytics框架做推理很简单,但有几个参数值得调:
from ultralytics import YOLO
model = YOLO("run/sea/exp1/weights/best.pt")
results = model.predict(
source="test_video.mp4",
conf=0.25, # 置信度阈值
iou=0.45, # NMS IoU阈值
imgsz=1280, # 推理分辨率
half=True, # FP16推理
device=0, # GPU
classes=[0, 1], # 如果不需要检测海事标志物,可用类别过滤
stream=True, # 视频流模式,逐帧返回不占用大量内存
)
conf 和 iou 的取值建议单独在验证集上调优。我一般用grid search:conf从0.1到0.5、iou从0.3到0.7,找F1最高的组合。海上场景误检率相对高,conf设太高会漏掉小目标,设太低会出现大量假阳性框,需要用验证集实测找平衡点。
5. 常见问题与排查技巧实录
5.1 水面反光导致误检如何解决
这是海上目标检测最典型的问题。现象是:模型在阳光反射强烈的水面上,把白色反光区域误检成船体,置信度还很高。
排查和解决路径:
- 加反光负样本 :在数据集里单独建一个“反光海面”负样本目录(不含任何目标的原始海面反光图),训练时通过
background文件加入训练集。YOLOv8支持在训练集中放没有标签的图片,可以显著降低误检率。 - 离线反光增强 :前面提过,对图中海面区域叠加高光斑块,这比单纯调亮度更接近真实反光物理特性。
- 降低推理conf :如果误检置信度普遍在0.2-0.3,而真实目标置信度在0.5以上,适当提高conf阈值就能滤掉。
实测:经过反光增强后,误检率从每帧平均1.8个降到0.2个,效果非常明显。
5.2 远距离小目标漏检严重
这也是海上场景的重灾区。一艘船在画面里只有15×15像素,用1280分辨率也经常检测不到。
几个实操方案:
- 输入分辨率提到1536或更长边 :如果显存允许,
imgsz=1536能缓解小目标问题,但训练和推理时间都明显上升。 - Tiling/切片推理 :把大图切成多块小图分别推理,再合并结果。工程上可以用SAHI(Slicing Aided Hyper Inference)库,它把切片推理、重叠区域去重、结果合并都封装好了。实测SAHI在1024分辨率下能把person类的mAP从0.44提到0.52。
- 不要在最后几层丢失小目标特征 :YOLOv8已经加了P2层小目标head,但Ultralytics默认推理配置文件里P2层是关闭的。如果你确定场景里小目标多,可以修改模型配置启用P2层,但会显著增加计算量,需要自己权衡。
5.3 训练中loss变成NaN
在YOLOv8训练中遇到loss为NaN,最常见的几个原因:
- 数据集标签有误。比如某个标签文件的类别索引超出类别总数、边界框坐标超出图像边界。写个脚本检查所有标签文件即可,检查坐标是否在0-1范围内、类别是否在0到nc-1之间。
- 初始学习率太大。尤其用AdamW时如果lr0开到0.1,必炸。SGD相对稳,但lr0超过0.05也容易遇到这种问题。
- 图片文件损坏。一张损坏的图在数据加载时产生异常值,也会导致loss异常。
cache=True情况下会自检,但普通加载模式不查。
我的做法:训练前先跑一次 model.val() 确认数据能正常加载,再用 epochs=5 小规模跑一遍,确认loss正常下降再上完整训练。这个习惯帮我避免了好几次浪费半天时间在坏数据上的情况。
5.4 类别不平衡问题
数据集中 maritime_mark 只有5000个实例,boat有18000个。如果不处理,模型会倾向少报mark类别。
处理方案:
- 过采样 :在训练加载器里对少数类样本加权,Ultralytics不支持直接设class weight,但可以复制少数类图片到训练集生成额外副本(注意同一个图不要复制到验证集)。
- 损失加权 :自定义训练循环或修改损失函数,给少样本类别更大的loss权重。调整起来比较麻烦,但效果比过采样更稳。
- 别过度下调conf :类别不平衡时,少样本类别的输出置信度天然偏低,不要为了捞回mark类别把conf调太低,否则boat类会大量误检。
5.5 常见问题速查表
| 现象 | 原因 | 解决方案 |
|---|---|---|
| 反光误检 | 海面反光特征与船体相似 | 加负样本+反光增强 |
| 小目标漏检 | 输入分辨率不足/特征层不匹配 | imgsz提到1280+、切片推理 |
| 某类mAP极低 | 类别样本少或实例框太小 | 补数据、过采样、loss加权 |
| loss为NaN | 标签错误/学习率过大/坏图 | 检查标签、降lr0、坏图清理 |
| 验证mAP高但实际效果差 | 数据划分泄漏 | 按视频分组划分训练/验证集 |
| 边缘框偏大/偏小 | 边界框标注不规范 | 统一标注规则、质检脚本复查 |
最后分享一点经验
这个项目做完,我最大的感受是:模型结构和训练技巧只占三成功夫,数据质量和场景理解占了七成。YOLOv8本身已经很成熟了,只要不是乱调参数,网络的性能下限不会太低,真正决定上限的是你对“海上目标长什么样、在什么背景下出现、以什么尺度出现”这些问题的理解深度。
建议你在自己的场景里做三件事:第一,花时间去标注数据,不要完全依赖公开数据集,你场景里的船只、浮标的外观可能和公开数据差得很远;第二,一定要做一次小规模试训,确认数据和环境没问题再上大训练,省时省力;第三,部署时按实际算力选分辨率,不要在Jetson上硬上1536,画面中目标大部分是中近景的话640完全够用。
下一步如果你想继续提升,可以在YOLOv8基础上换用更大模型v8m或v8l,或者引入C2f的改进模块。但先别急着改网络结构,把数据质量、增强策略、推理参数调优这三件事做好,效果提升的幅度绝对比改一个模块名字大得多。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)