从头写一个YOLOv8船舰检测,最容易被忽略的其实是数据。模型选型反而简单:大家条件差不多,YOLOv8就是当前性价比最高的那个选择。我为什么敢这么说?因为这个项目我从数据收集、打标签、训练调参到导出部署全流程跑了两遍,那些只贴一张架构图、给你两句yolo train命令的分享,基本不会告诉你:真正让模型在海面场景里涨点的是哪几个参数,哪些数据会让你白白浪费几天算力。
这篇文章就把一个基于YOLOv8的船舰检测系统从零到落地完全拆开。不是什么纯理论,是我实际做这个项目时的完整记录,包括数据怎么攒、标签怎么打、训练看什么曲线、小目标老是漏检怎么办、最后怎么导出到线上跑起来。后面直接给了代码和配置,照着抄就能少踩一半坑。
适合两类人看:一类是要交作业、要跑通完整检测流程的学生或开发者;另一类是已经在做目标检测,但觉得自己的模型在船舰这类特殊场景下精度不够、误检多,想找优化思路的工程师。我尽量把为什么这么做也讲清楚,你后面碰到类似场景,也知道该怎么调整。
1. 为什么船舰检测场景绕不开YOLOv8
1.1 传统视觉方法在海上场景有多难顶
早些时候做船舰检测,大家常用的是帧差法、背景建模、颜色分割那一套。陆地上简单监控场景还行,一到海面,基本是灾难现场。
海面本身是动态的,波浪一直动,帧差法会把波纹当成前景,导致误检率飙升;阳光、云影、雾气一来,光照剧烈变化,背景建模的模型被搅得乱七八糟。你整体用Canny边缘检测加形态学过滤吧,远处的小船边缘模糊,轮廓被噪声吃掉;近处的大船内部纹理复杂,又可能会出现一堆假轮廓。更不要说夜间、雨雾天,传统视觉方法直接失效。
我当时用传统方法加了一系列滤波规则,花了一个多月调参。最后的结果是:在晴朗白天顺光条件下,准确率勉强能看,一旦天气或光照一变化,误检率就跑到不可用的程度。老实说,在海上做目标检测,传统方法很难在产品里稳定交付。
1.2 YOLOv8相比上一代,强在哪里
深度学习检测方案里,YOLOv8是目前最适合快速落地船舰检测的,这是反复对比之后得出的结论。
官方给的YOLOv8在几个方面明显针对工程落地做了优化:主干网络部分用C2f模块替代了之前YOLOv5的C3,特征梯度回传路径更丰富,对尺度差异较大的目标更友好;检测头改成了解耦结构,分类和回归分支互相不干扰;标签分配策略全面走向anchor-free加动态匹配,这意味着不需要手调锚框参数,密集排列的船队场景下也能有更好的分配效果。
这些改动看起来不复杂,但反映到船舰检测上,直接的好处有两点:一是靠岸停泊的船挨得很近,动态分配策略能更好地把不同目标的框区分开;二是同一张图里同时存在几十米的大船和几十像素的小船,C2f和尺度融合策略让多尺度特征都更充分。单从公开评测指标来看,YOLOv8s在相近算力下通常比YOLOv5s的mAP高2到5个点,速度还不降。
1.3 选型实战:n、s、m、l到底用哪个
YOLOv8官方提供了n、s、m、l、x五个尺寸,很多新手直接选最大的x,觉得精度最高。实际跑下来,船舰检测并不全是模型越大越好。
| 模型 | 参数规模 | 典型速度 | 适用场景 | 我的建议 |
|---|---|---|---|---|
| YOLOv8n | 约300万 | 极快 | 边缘设备、实时视频流 | 精度偏低,不推荐主用 |
| YOLOv8s | 约1100万 | 快 | 普通GPU即可跑 | 首选,性价比最高 |
| YOLOv8m | 约2500万 | 中等 | 精度要求更高 | 显存充足时考虑 |
| YOLOv8l/x | 4300万以上 | 较慢 | 离线高精度 | 数据量大、计算资源多再用 |
我最终选的是YOLOv8s。主要原因是训练数据量并不算特别大,用更大的模型容易过拟合,而且推理速度对部署很友好。一个判断标准:如果你的数据量在几千到两三万张这个量级,且单卡训练,那就从s起步;如果数据量很大、算力宽裕,再考虑m。一上来就l、x,训练慢还容易吸收噪声,精度不一定比调好参数的s高。
2. 数据工程,决定了模型的上限
2.1 船舰数据从哪来、攒多少合适
训练YOLOv8模型,最怕的不是调参,而是数据质量太差。船舰检测有它的特殊难点:海面场景复杂,公开数据集数量有限,自采数据需要大量人工标注。
我当时的数据来源主要有三个方向:一是网上能找到的开源海上船只数据集,类别相对粗糙,但作为预训练打底很好用;二是通过海上监控视频、航拍视频截图抽帧,注意隔几帧抽一帧,否则相邻样本高度相似,会导致训练集和验证集信息重叠,评估指标虚高;三是标注一部分无人机航拍的港区视频,这类俯视数据对补全模型视角非常关键。
数据量怎么把握?一般来说,单类目标至少准备800到1500张带标注图片作为底数,整体的训练集建议在3000张以上。如果类别较多,比如要分货船、油轮、渔船、军舰等五六个类别,那总张数最好加到8000张以上。数量特别少的类别,还得分外留意过拟合问题。
2.2 类别体系别凭直觉拍脑袋
这是我从失败案例里总结出来的最重要的教训之一。最开始做船舰检测时,我参考某些文献把类别分得非常细:散货船、集装箱船、油轮、渔船、工作船、客运船……单看数据分布,有个别类别只有几十张样本。训练出来,mAP50指标看起来好像还行,实际一测,模型把渔船反复识别成工作船,把散货船漏一半。
为什么?因为船舰类别之间存在大量视觉相似性,特别是从侧面、远距离、逆光拍摄时,不同船型的轮廓和纹理很难区分。标注人员自己也经常分不清具体船型,标签噪声一大,模型学到的就是混乱的边界。
后来我把类别合并成三大类:货船(含集装箱船、散货船、油轮这类大型商船)、渔船、其他船舶。结果出乎意料的好——各指标都往上走,误检率下降明显。这个取舍的本质是:当你没有大量高质量细粒度标注时,让模型先把是大船还是小船、是渔船还是商船分清,比强行分船型更实用。二分类或三分类的检测系统在真实业务里完全够用,后续有足够数据再细分也不迟。
2.3 标签怎么打,YOLO格式是什么
船舰检测的标注工具,我用过LabelImg,也用过更现代的X-AnyLabeling(支持SAM辅助分割)。如果数据量上千,强烈建议用带辅助标注的工具,效率高一倍。不管用哪个工具,导出的格式尽量统一成YOLO格式,因为YOLOv8原生支持,不用转换。
YOLO格式的标签文件是文本文件,每行表示一个目标,格式是:
class_id x_center y_center width height注意四个坐标值都是归一化到0到1之间的相对值,也就是用目标中心点的相对坐标和宽高相对图片宽高的比例。比如一张1920x1080的图,一艘船的中心点在(960, 540),目标框尺寸是(480, 270),那么对应的标签就是:
0 0.5 0.5 0.25 0.25为什么会踩坑?有一回我从某个开源工具导出标签,格式直接给成了左上角和右下角的绝对坐标。YOLOv8读进去完全不报错但也不产生任何正确训练信号,loss反而不降,排查了半天。所以第一步先确认标签格式有没有错,再谈调参。
标注目录结构一定要按下面这种组织方式:
datasets/ship/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── ship.yaml训练集和验证集要在图片侧和标签侧对应同名,验证集比例建议15%到20%。我通常从全部数据里先按视频来源或场景分组,再随机划分,避免同一镜头下的连续帧同时出现在训练集和验证集里。
2.4 数据清洗不是走过场
有些样本是必须删掉的,我总结下来有几类:目标占比不到全图2%的极小目标样本,标注难度大、噪声高;严重模糊、过曝、完全看不清船的样本;同一场景下重复度极高的帧;半艘船紧贴图像边缘导致标注框被切割严重的样本。
另外,海面反光区域多,如果样本里大量出现刺眼的白光区域,模型很容易学会"看到亮斑就认为是船"。我当时专门建了一个难例集,把含有玻璃反光、浪花反光、码头白色顶棚的负样本标注为背景,然后模型误检率明显下降。这里注意,你说不清是什么目标的复杂区域,宁可不标,也不要瞎标一个类别,不然就是把噪声喂给模型。
数据增强方面,YOLOv8默认开启mosaic、随机翻转、HSV扰动等策略,对船舰场景来说,默认的就够用。我额外只增强了旋转,因为航拍和监控视角下船的方向不固定。要注意别用那种把目标压扁拉伸太狠的增强,比如极端透视变换,会把远处小船的形状破坏得一塌糊涂,反而降低精度。
3. 训练全流程,从环境到出权重
3.1 环境准备和项目结构
训练YOLOv8需要Python环境、PyTorch和ultralytics库。推荐直接用Python 3.9或3.10,配合CUDA版PyTorch,显卡驱动装对版本就行。
conda create -n ship python=3.10 conda activate ship pip install ultralytics如果显卡是NVIDIA且显存足够,安装完就可以用GPU训练;实在没有GPU运行环境,CPU也能训练,但建议把epoch数降低,先用极小样本验证全流程能跑通。
3.2 写数据配置和训练命令
数据配置文件ship.yaml内容很简单:
path: ../datasets/ship train: images/train val: images/val nc: 3 names: 0: cargo 1: fishing 2: othernc是类别数量,names是类别名字。这个配置一定要和标签里的class_id对齐,否则类别错乱到怀疑人生。
训练命令是这样:
yolo detect train data=ship.yaml model=yolov8s.pt epochs=200 imgsz=640 batch=16 device=0几个参数需要解释清楚:
model=yolov8s.pt:使用官方预训练的权重作为起点。千万别从头训练,因为COCO预训练权重里已经包含了对"物体一般特征"的抽象,从这里起步,船舰特征能快速迁移,训练几百轮很快收敛。从零随机初始化的话,同等数据量下效果差很多。epochs=200:船舰数据集规模不算特别大,200轮足够。我一般配合早停机制,当验证集指标连续30轮不提升就自动停止。batch=16:根据显卡显存调整。在16G显存以下,建议8或16;显存越大可以越大,前提是别爆显存。imgsz=640:默认值,精度和速度的平衡点。如果你的场景里远处小船特别多,建议改成960,这个会在后面详细说。
训练时日志会输出每个epoch的box_loss、cls_loss、dfl_loss,以及precision、recall、mAP50、mAP50-95。不要只盯着训练集loss,重点看验证集mAP。训练集loss一直降、验证集指标不升反降,那就是过拟合了。
3.3 训练要盯哪几个关键曲线
训练过程最值得盯的曲线,第一是验证集的mAP50和mAP50-95,这两个直接反映模型的实际检测能力;第二是验证集的召回率recall,船舰检测里漏检比误检更致命,召回率上不去,说明有小船群体性漏掉。
我当时有一个典型跑偏经历:训练到80轮时mAP50已经不错了,但recall始终卡在0.7附近。后来发现是验证集里有很多几百米外的小目标,模型在640尺度下根本看不出来。解决办法是提高训练和推理的输入尺寸。用960尺寸重新跑了一遍后,recall提升了大概五个点。
训练完成之后,会在runs/detect/train目录下生成weights/best.pt和last.pt。注意细节:best.pt是验证集指标最好的权重,last.pt是最后一轮权重;除非最后一轮恰好最好,否则一律用best.pt做推理。
4. 精度优化,船舰场景涨点实操
4.1 小目标检测是最大的坎
船舰检测最核心的难点就是小目标。与普通物体不同,船在海上很远的地方只占几十个像素,640尺度输入时,经过特征提取网络的下采样,小目标的特征空间非常有限,容易丢失。
处理办法主要有几个:
提升输入图片分辨率是第一步。把imgsz从640调整到960,甚至1280,对远处小目标有直接的提升。代价是推理速度变慢、显存占用升高。对于离线分析场景,这完全值得。我的实测数据是:imgsz从640到960,mAP50大约提升了3到5个点,recall提升更明显。
如果进一步提升到1280,收益开始递减,训练时间却大幅增加,性价比不高。因此我最后选择了960作为训练和推理的统一尺度。注意训练和推理的imgsz保持一致,不然训练用960,推理用640,等于把模型的输入分布改变了,效果反而打折。
还有一种思路是在推理时使用切片(tiling)策略,把大图切块分别推理再合并。这个方案对超大分辨率图效果不错,但会带来明显的速度损失,实时视频流场景基本不适合,我一般只在单张大幅航拍图分析时用。
4.2 类别不均衡怎么办
船舰检测数据常见的现象是货船样本很多、渔船样本很少,个别类别占比不到10%。模型天然偏向样本多的类别,导致少样本类别召回极差。
解决办法排序下来:第一优先是增加少样本类别的图片数量,哪怕是网上额外下载或从不同视频中截取,为每个少样本类别凑到足够的标注样本;其次是对少样本类别的图片做离线增强,复制粘贴、旋转、缩放,扩大这类目标在训练中的出现频率;再不行,就在训练配置里通过修改损失权重或数据采样比例来强行加权,让模型更关注这类样本。
我试过单纯靠调loss权重把渔船检测拉起来,有效果,但mAP50-95会有点波动,不如直接加数据来得干净。所以建议把重心放到数据层面。
4.3 实际业务中误检的处理思路
海面场景的误检经常集中在几种情况:海面亮斑、码头白棚、海浪浪花、云层边缘。模型会把纹理简单、边缘锐利的区域当成船。
这时候最有效的方法不是调阈值,而是收集误检样本并标注为背景,加到训练集里重新训练一轮。注意YOLO格式里背景类不需要额外标签,但你需要把对应的"有假目标"图片原图放进训练集,模型在迭代中会学到这些区域并不是目标。
另外推理时设置合适的置信度阈值也很关键。你可以在验证集上画出precision-recall曲线,找一个准确率和召回率平衡点作为阈值。船舰检测实际使用中,我一般把conf设到0.25到0.35之间,iou设到0.5。conf太低会一堆误检,太高又会漏检。
5. 部署推理,模型不是训练完就完事
5.1 导出ONNX和TensorRT
训练好的best.pt还不能直接扔到生产环境,YOLOv8的PyTorch推理在CPU上慢,在GPU上也只是够用。要真正工程化,需要先导出。
导出ONNX格式是最通用的做法:
yolo export model=best.pt format=onnx simplify=True imgsz=960导出后可以用下面的Python代码快速验证一遍效果:
from ultralytics import YOLO model = YOLO("best.pt") results = model.predict("test.jpg", conf=0.3, iou=0.5) for r in results: boxes = r.boxes for box in boxes: cls = int(box.cls[0]) conf = float(box.conf[0]) xyxy = box.xyxy[0].tolist() print(f"类别: {cls}, 置信度: {conf:.3f}, 坐标: {xyxy}")如果目标环境是NVIDIA GPU且追求极致性能,再进一步导出TensorRT engine:
yolo export model=best.pt format=engine device=0 half=True imgsz=960TensorRT导出后推理速度能提升1.5倍以上,显存占用也更低。
5.2 视频流的实时检测怎么搭
船舰检测落到实际业务里,多数是处理视频流或者定时抓拍的图片。实时视频检测的基本套路是循环读取帧,推理,画框,再输出。核心代码骨架大致长这样:
import cv2 from ultralytics import YOLO model = YOLO("best.pt") cap = cv2.VideoCapture("harbor.mp4") while cap.isOpened(): ret, frame = cap.read() if not ret: break results = model.predict(frame, conf=0.3, iou=0.5) annotated = results[0].plot() cv2.imshow("ship detect", annotated) if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows()如果视频流分辨率很高,比如4K监控,直接整帧推理速度会很慢。我的做法是先把帧缩放到模型输入尺寸,得到检测框坐标后,再按缩放比例映射回原图坐标。需要注意的是,检测框坐标是相对缩放后图像的,映射回原图时要把x、y、w、h都按原图缩放倍数放大。
5.3 几种部署方案的取舍
| 部署方式 | 推理速度 | 环境要求 | 适用场景 |
|---|---|---|---|
| PyTorch | 中等 | 需要完整深度学习环境 | 开发调试阶段 |
| ONNX Runtime | 较快 | 支持CPU/GPU | 通用生产环境 |
| TensorRT | 最快 | 仅NVIDIA GPU | 高并发、实时服务 |
| NCNN等 | 较快 | 移动端、嵌入式 | 端侧部署 |
如果你只是自己跑通项目、交个作业,PyTorch直接推理就够了。如果是部署到服务器,ONNX是性价比最高的选择,生态好、跨平台。如果服务要求高实时,再上TensorRT。移动端或边缘盒子,那得做量化并导出NCNN格式,这是另一个话题。
6. 常见问题排查,帮你省几天时间
6.1 典型问题速查表
| 问题现象 | 可能原因 | 处理办法 |
|---|---|---|
| loss不降 | 标签格式错误、学习率太大 | 先检查标签归一化坐标是否合法;把lr0降到0.001试跑 |
| 训练集指标高但验证集差 | 过拟合 | 加强数据增强、减少epoch、换更小模型 |
| 小目标大量漏检 | 输入尺寸太小 | imgsz提到960以上,或对图像做切片推理 |
| mAP高但实际识别不准 | 类别样本不均衡 | 增加少样本类别数据,必要时合并类别 |
| 推理结果比训练指标差很多 | 训练和推理尺寸不一致 | 统一imgsz,导出模型时固定输入尺寸 |
| 有大量海面反光误检 | 负样本不足 | 收集误检背景图加入训练集重新迭代 |
| 相邻船只被识别成一个目标 | NMS抑制过度 | 调低iou阈值到0.4左右,或增大输入分辨率 |
6.2 我踩过最深的几个坑
第一个坑是盲目追求大模型。我一开始直接用YOLOv8x训练,数据量只有几千张,跑了三天,结果mAP还不如后来用s模型调参的结果。不要迷信模型越大越强,数据和参数的匹配度更重要。
第二个坑是训练和推理不一致。训练时用960,导出模型时又图省事用640,导致部署后检测效果明显变差。这个问题很隐蔽,因为它不报错,只会悄悄降精度。任何时候导出和推理,都要把imgsz锁死。
第三个坑是类别定义。前面提到的船型细分导致标注不一致的问题,其实根源在标注规范。你要是决定分四类以上,前期必须给标注人员提供一份带示例图的标注规范,什么样的船叫什么名字、什么样的角度怎么标,全部写清楚。否则标注噪声会让你后期疯狂调参也补不回来。
6.3 个人经验的最后几句话
做完整套基于YOLOv8的船舰检测系统,我最直观的体会是:这类目标检测项目拼的不再是模型结构,而是数据质量和工程取舍。YOLOv8把算法层面的事情已经压得非常低,你不需要去改网络结构,也不需要手写NMS,真正拉开差距的是你有没有把数据洗干净、把类别定义合理、把阈值设对、把部署链路跑通。
如果你要我给一个最简单的复现路径,那就是:先用公开海上船舰数据集合并自采数据,做三分类,用YOLOv8s在imgsz=960下训练200轮,然后导出ONNX部署。这条路径我已经完整验证过,指标够用,踩坑最少。
最后再分享一个小技巧:训练完不要急着交差。挑十几张最典型的包含小型船只的图片做推理测试,把检测效果可视化看一下。很多时候mAP看着漂亮,但你的场景里偏偏有那种所有模型都认不出来的极端目标。可视化检查一遍,比盯十个指标都管用。