从头写一个YOLOv8船舰检测最容易被忽略的其实是数据。模型选型反而简单大家条件差不多YOLOv8就是当前性价比最高的那个选择。我为什么敢这么说因为这个项目我从数据收集、打标签、训练调参到导出部署全流程跑了两遍那些只贴一张架构图、给你两句yolo train命令的分享基本不会告诉你真正让模型在海面场景里涨点的是哪几个参数哪些数据会让你白白浪费几天算力。这篇文章就把一个基于YOLOv8的船舰检测系统从零到落地完全拆开。不是什么纯理论是我实际做这个项目时的完整记录包括数据怎么攒、标签怎么打、训练看什么曲线、小目标老是漏检怎么办、最后怎么导出到线上跑起来。后面直接给了代码和配置照着抄就能少踩一半坑。适合两类人看一类是要交作业、要跑通完整检测流程的学生或开发者另一类是已经在做目标检测但觉得自己的模型在船舰这类特殊场景下精度不够、误检多想找优化思路的工程师。我尽量把为什么这么做也讲清楚你后面碰到类似场景也知道该怎么调整。1. 为什么船舰检测场景绕不开YOLOv81.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/x4300万以上较慢离线高精度数据量大、计算资源多再用我最终选的是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 python3.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 dataship.yaml modelyolov8s.pt epochs200 imgsz640 batch16 device0几个参数需要解释清楚modelyolov8s.pt使用官方预训练的权重作为起点。千万别从头训练因为COCO预训练权重里已经包含了对物体一般特征的抽象从这里起步船舰特征能快速迁移训练几百轮很快收敛。从零随机初始化的话同等数据量下效果差很多。epochs200船舰数据集规模不算特别大200轮足够。我一般配合早停机制当验证集指标连续30轮不提升就自动停止。batch16根据显卡显存调整。在16G显存以下建议8或16显存越大可以越大前提是别爆显存。imgsz640默认值精度和速度的平衡点。如果你的场景里远处小船特别多建议改成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到960mAP50大约提升了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 modelbest.pt formatonnx simplifyTrue imgsz960导出后可以用下面的Python代码快速验证一遍效果from ultralytics import YOLO model YOLO(best.pt) results model.predict(test.jpg, conf0.3, iou0.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 engineyolo export modelbest.pt formatengine device0 halfTrue imgsz960TensorRT导出后推理速度能提升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, conf0.3, iou0.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在imgsz960下训练200轮然后导出ONNX部署。这条路径我已经完整验证过指标够用踩坑最少。最后再分享一个小技巧训练完不要急着交差。挑十几张最典型的包含小型船只的图片做推理测试把检测效果可视化看一下。很多时候mAP看着漂亮但你的场景里偏偏有那种所有模型都认不出来的极端目标。可视化检查一遍比盯十个指标都管用。