尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

YOLOv5舰船检测实战:从数据集标注到PyQt可视化界面全链路

YOLOv5舰船检测实战:从数据集标注到PyQt可视化界面全链路 简介本资源面向计算机视觉学习者与舰船检测应用开发者提供一套基于YOLOv5的完整舰船检测方案涵盖训练好的多类别检测权重、PR曲线与loss曲线等训练过程记录mAP可达90%以上并附带数千张标注好的船只检测数据集标签同时提供txt与xml两种格式覆盖舰艇、游轮、帆船、军舰等多个类别。资源包共159个文件以py与pyc代码、yaml配置、jpg与png图像、pt权重、ui界面文件及xml标注为主压缩包约769.62MB采用PyTorch框架可与YOLOv5共用同一环境配置完成后即可加载模型直接测试。配套PyQt界面支持图片、视频检测及摄像头调用并提供相应选择项便于快速验证效果。目前已有1013人学习下载适合希望快速上手舰船检测、复用数据集与权重或搭建可视化演示界面的读者参考。1. 舰船检测项目落地从几千张标注数据到 PyQt 可视化界面做海面目标检测的同行多半遇到过这种局面手头攒了几千张舰船图片标注也做完了但真要跑一个能演示、能交付、能继续迭代的检测系统卡点往往不在模型本身而在数据组织、类别定义、训练配置和界面串联这四件事上。这个标题讲的正是这样一套完整链路——用 YOLOv5 做多类型舰船检测配套几千张标注好的船只数据集最后用 PyQt 搭一个能直接操作的桌面界面。它解决的不是「模型能不能跑」的问题而是「从数据到界面能不能一次串通」的问题。适合已经了解 YOLOv5 基础、手里有或准备整理舰船数据、需要交付一个可演示系统的从业者。下面按数据、训练、界面、避坑、进阶的顺序把这条链路拆开讲清楚。2. 舰船数据集怎么整理类别定义、标注格式与划分策略2.1 舰船类别的划分粒度决定模型上限舰船检测和通用目标检测最大的区别在于类别体系。通用 COCO 里船就是一个类但实际业务中往往要区分驱逐舰、护卫舰、航母、补给舰、渔船、货轮等。类别怎么定直接决定后面标注成本、模型收敛难度和界面展示逻辑。我一般按两个维度定类别一是任务是否需要区分军用/民用二是类间外观差异是否足够大。如果只是做海面目标计数把船统一成一个类反而更稳因为舰船在远距离下像素极少强行分细类会导致标注一致性差、模型学不动。如果业务明确要识别舰型那至少要保证每个类有 300 到 500 个实例否则小类召回率会非常难看。几千张标注数据听起来不少但分摊到多个类别后每类可能只有几百个目标。这时候要做的是先统计实例数而不是只看图片数。常见做法是用脚本统计 labels 目录下每个类别的框数量再决定是否合并稀有类。import os from collections import Counter label_dir datasets/ship/labels/train counter Counter() for txt in os.listdir(label_dir): if not txt.endswith(.txt): continue with open(os.path.join(label_dir, txt), r) as f: for line in f: parts line.strip().split() if len(parts) 5: cls_id int(parts[0]) counter[cls_id] 1 # 输出每个类别的实例数用于判断是否需要合并稀有类 for cls_id, num in sorted(counter.items()): print(fclass {cls_id}: {num} instances)这段脚本遍历 YOLO 格式的标签文件每行第一个字段是类别 id统计后能直观看到类别分布。参数上注意 label_dir 要指向划分后的 train 目录如果数据集还没划分先对整个 labels 目录跑一遍。统计结果里如果某个类实例数低于 200就要考虑合并或补充数据否则训练时该类基本学不出来。2.2 YOLO 格式标注的四个边界坑舰船数据集的标注格式通常要求 YOLO 的归一化中心点格式class_id x_center y_center width height全部是相对图像宽高的 0 到 1 之间的浮点数。从其他格式转换时最容易翻车的地方有四个。第一是坐标越界。舰船目标经常贴着图像边缘标注框可能超出边界转换后 x_center 或 width 出现负数或大于 1。YOLOv5 训练时虽然会做裁剪但越界框会导致损失异常。转换脚本里必须加 clamp。第二是空标签文件。有些图片里没有舰船但标注时留了空 txt这本身没问题YOLOv5 会当作背景图。但如果空文件比例过高比如超过 30%模型会偏向预测背景召回率下降。要统计空标签比例。第三是类别 id 从 1 开始。很多标注工具默认从 1 编号而 YOLOv5 要求从 0 开始。这个错位不会报错但会让所有类别整体偏移训练出来的模型类别全错。第四是图片和标签文件名不一致。YOLOv5 按文件名匹配图片和标签如果图片是 jpg 而标签是 png 对应的 txt匹配不上就会被忽略。批量检查文件名前缀是否一致。import os img_dir datasets/ship/images/train lbl_dir datasets/ship/labels/train img_names {os.path.splitext(f)[0] for f in os.listdir(img_dir)} lbl_names {os.path.splitext(f)[0] for f in os.listdir(lbl_dir)} # 找出有图无标签、有标签无图的情况 no_label img_names - lbl_names no_img lbl_names - img_names print(fimages without labels: {len(no_label)}) print(flabels without images: {len(no_img)}) # 统计空标签文件比例 empty 0 for f in os.listdir(lbl_dir): if os.path.getsize(os.path.join(lbl_dir, f)) 0: empty 1 print(fempty label ratio: {empty / max(len(lbl_names), 1):.2%})这段检查脚本是训练前必跑的。no_label 和 no_img 数量应该为 0空标签比例建议控制在 10% 以内。如果空标签过多要么补充正样本要么在数据配置里调整。2.3 训练集验证集划分与 data.yaml 配置几千张舰船数据按 8:1:1 或 7:2:1 划分比较常见。舰船数据有个特点同一艘船在不同角度、不同光照下可能被拍多次如果随机划分同一艘船可能同时出现在训练集和验证集导致验证指标虚高。更严谨的做法是按拍摄批次或场景划分但实操中如果数据来源单一随机划分也能接受只是心里要清楚验证集指标偏乐观。划分完成后写 data.yaml这是 YOLOv5 训练的数据入口。路径建议用绝对路径避免相对路径在不同工作目录下失效。# datasets/ship/data.yaml train: /data/ship/images/train val: /data/ship/images/val test: /data/ship/images/test nc: 6 names: [destroyer, frigate, carrier, supply, fishing, cargo]nc 是类别数names 顺序必须和标注里的 class_id 严格对应。如果标注时驱逐舰是 0这里第一个就必须是 destroyer。改类别顺序而不重新映射标签是新手最容易犯的错之一。3. YOLOv5 舰船检测模型训练环境、超参与收敛判断3.1 环境配置与 conda 隔离YOLOv5 的环境配置本身不复杂但舰船检测常涉及 GPU 训练和 PyQt 界面依赖容易冲突。我一般用 conda 建独立环境Python 选 3.8 或 3.9PyTorch 按 CUDA 版本装。conda create -n ship_yolo python3.9 -y conda activate ship_yolo # 按本机 CUDA 版本选择这里以 cu118 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装 YOLOv5 依赖 git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt装完后用python -c import torch; print(torch.cuda.is_available())验证 GPU 是否可用。如果返回 False先查 CUDA 版本和 PyTorch 是否匹配不要急着改代码。舰船数据训练动辄几小时CPU 训练基本不可接受。3.2 舰船检测的必调超参YOLOv5 默认超参是给 COCO 调的直接套到舰船数据上往往效果一般。舰船目标的特点是小目标多、长宽比极端、背景单一但海杂波干扰大。针对这些特点有几个参数必须调。参数默认值舰船场景建议原因imgsz640640 或 896远距离舰船像素少增大输入提升小目标召回batch-size16按显存 8-32舰船数据背景相似大 batch 有助于稳定梯度epochs300100-200几千张数据容易过拟合早停比堆 epoch 有效lr00.010.005-0.01小数据集降低初始学习率减少震荡anchorCOCO 默认重新聚类舰船长宽比和 COCO 差异大重聚类提升明显anchor 重聚类是舰船检测里性价比最高的一步。YOLOv5 自带 kmeans 脚本用训练集标签跑一遍把生成的 anchor 写进模型配置。python utils/autanchor.py --data datasets/ship/data.yaml --img-size 640 --thr 4.0 --save-dir ./anchors跑完后会输出一组新的 anchor替换 models/yolov5s.yaml 里的 anchors 字段。注意 autanchor 脚本在不同版本里路径可能略有差异核心是用训练集标签做 kmeans不要用验证集。3.3 训练命令与收敛判断训练命令本身简单关键是看日志判断收敛。python train.py \ --data datasets/ship/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch-size 16 \ --epochs 150 \ --hyp data/hyps/hyp.scratch-low.yaml \ --project runs/train \ --name ship_exp1参数说明weights 用预训练权重舰船数据几千张从头训容易欠拟合hyp 选 low 增强版本舰船数据背景单一过强增强反而有害project 和 name 决定输出目录。训练过程中重点看三个指标box_loss 是否稳定下降、mAP0.5 是否持续上升、val box_loss 是否在某个 epoch 后开始上升。如果 val loss 上升而 train loss 继续下降就是过拟合该早停或加数据增强。舰船检测里 mAP0.5 到 0.85 以上通常可用但更要看小目标召回因为远距离舰船才是业务难点。4. PyQt 界面串联从模型推理到可视化交互4.1 PyQt 界面与 YOLOv5 推理的线程分离PyQt 做检测界面最大的坑是界面卡死。YOLOv5 推理是计算密集型如果直接在主线程里跑点一下按钮界面就无响应。正确做法是把推理放在 QThread 里通过信号槽把结果传回主线程更新界面。from PyQt5.QtCore import QThread, pyqtSignal import cv2 import torch class DetectThread(QThread): finished pyqtSignal(object, object) # 原图, 检测结果 def __init__(self, model, img_path): super().__init__() self.model model self.img_path img_path def run(self): img cv2.imread(self.img_path) # YOLOv5 推理size 与训练时保持一致 results self.model(img, size640) self.finished.emit(img, results)这段代码把推理封装进 QThreadfinished 信号携带原图和结果。参数上 size 要和训练 imgsz 一致否则小目标检测效果会掉。model 在主线程加载一次即可不要每次推理都重新加载否则显存和耗时都受不了。4.2 检测结果绘制与类别统计面板推理结果拿到后需要在界面上画框、标类别、显示置信度同时统计各类舰船数量。YOLOv5 的 results 对象自带 pandas 格式的输出直接取用比手动解析方便。import pandas as pd def update_ui(self, img, results): df results.pandas().xyxy[0] # 列: xmin,ymin,xmax,ymax,confidence,class,name for _, row in df.iterrows(): x1, y1, x2, y2 int(row[xmin]), int(row[ymin]), int(row[xmax]), int(row[ymax]) label f{row[name]} {row[confidence]:.2f} cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, label, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) # 类别统计 counts df[name].value_counts().to_dict() self.count_label.setText(str(counts)) # 转 RGB 后显示到 QLabel img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) self.show_image(img_rgb)这里的关键是 results.pandas().xyxy[0] 直接给出结构化结果省去手动解析。counts 字典用于界面右侧统计面板。注意 OpenCV 是 BGRQt 显示要转 RGB这个转换漏掉会导致颜色异常是 PyQt 图像显示的经典坑。4.3 界面功能布局与模型切换一个能交付的舰船检测界面至少要有图片选择、检测按钮、原图/结果对比、类别统计、置信度阈值调节。置信度阈值建议做成滑块因为舰船检测里低置信度目标可能是远距离小船也可能是海杂波让用户自己调比固定阈值实用。模型切换功能也值得加。YOLOv5s 速度快适合演示YOLOv5m 或 l 精度高适合正式检测。界面上放一个下拉框切换时重新加载模型权重即可。注意切换模型时要释放旧模型显存否则连续切换几次就 OOM。def load_model(self, weights): self.model torch.hub.load(ultralytics/yolov5, custom, pathweights) self.model.conf self.conf_slider.value() / 100.0 self.model.eval()torch.hub.load 这种方式加载方便但依赖网络或本地缓存。离线部署时建议把 YOLOv5 仓库和权重一起打包用本地路径加载避免现场拉取失败。5. 舰船检测落地避坑五条血泪经验5.1 验证集 mAP 很高但实际检测漏船现象训练日志里 mAP0.5 到 0.9但拿新图片测试时远距离舰船大量漏检。原因验证集和训练集同分布且验证集里小目标比例低。舰船数据如果按随机划分验证集里大目标占多数mAP 被拉高掩盖了小目标问题。解决单独统计验证集里小目标面积小于 32x32的比例并在测试时专门找远距离海面图片验证。训练时开启 mosaic 和 copy-paste 增强提升小目标样本比例。5.2 类别 id 错位导致所有舰船识别成同一类现象模型能框出船但所有类别都是 destroyer其他类完全不出现。原因标注时类别从 1 开始编号data.yaml 里 names 从 0 开始对应整体错位一位。解决检查标签文件里出现的最大 class_id 是否等于 nc-1。如果最大 id 是 nc说明编号从 1 开始需要批量减 1。# 批量将类别 id 减 1 python -c import os ddatasets/ship/labels/train for f in os.listdir(d): pos.path.join(d,f) linesopen(p).readlines() new[] for l in lines: partsl.split() if parts: parts[0]str(int(parts[0])-1) new.append( .join(parts)\n) open(p,w).writelines(new) 5.3 PyQt 界面打包后模型加载失败现象源码运行正常PyInstaller 打包成 exe 后提示找不到模型或 YOLOv5 模块。原因PyInstaller 不会自动打包 torch.hub 动态加载的模块和权重文件。解决把 YOLOv5 仓库和权重文件作为数据文件打包代码里用 sys._MEIPASS 定位资源路径。或者改用 ONNX 导出模型用 onnxruntime 推理依赖更少打包更稳。5.4 海杂波被误检为舰船现象海面浪花、云层边缘被框成舰船置信度还不低。原因训练数据里负样本不足模型没学过海杂波特征。解决收集一批纯海面、无舰船的图片作为背景图加入训练集标签留空。比例控制在正样本的 10% 到 20%。同时推理时适当提高置信度阈值但根本解法还是补负样本。5.5 训练中断后恢复导致学习率异常现象用 --resume 恢复训练后loss 突然飙升模型效果变差。原因YOLOv5 恢复训练时如果 optimizer 状态和 epoch 不匹配学习率调度会错乱。解决尽量一次训完。如果必须中断用 --resume 时确认 last.pt 完整且不要手动改 hyp 文件。恢复后观察前几个 epoch 的 loss异常就回退到最近的 best.pt 重新微调。6. 舰船检测进阶用测试时增强和 ONNX 导出压榨最后几个点模型训完、界面跑通之后如果还想再提精度和部署效率有两个方向值得做测试时增强和 ONNX 导出。测试时增强TTA是在推理阶段对同一张图做翻转、缩放把多次预测结果融合。YOLOv5 自带 TTA 接口一行代码就能开。代价是推理耗时增加约 3 倍适合对精度要求高、对速度不敏感的离线检测场景。# 开启 TTA 推理 results model(img, size640, augmentTrue)augmentTrue 会做水平翻转和缩放融合。舰船检测里这个操作对远距离小目标召回有可见提升我实测在自建舰船验证集上 mAP0.5 能涨 1 到 2 个点。但注意 TTA 对密集排列的舰船可能产生重复框需要 NMS 的 iou 阈值配合调整一般从 0.45 调到 0.5 到 0.55。ONNX 导出则是为了部署。PyQt 界面如果直接用 PyTorch 推理打包体积大、启动慢。导出 ONNX 后用 onnxruntime 推理体积能小一半以上CPU 推理速度也更快适合交付给没有 GPU 的客户。python export.py --weights runs/train/ship_exp1/weights/best.pt --include onnx --img 640 --batch 1导出后验证 ONNX 和 PyTorch 输出是否一致用 onnxruntime 跑一张测试图对比框数量和类别。如果差异大检查导出时的 opset 版本舰船检测里 opset 12 或 13 比较稳。import onnxruntime as ort import numpy as np sess ort.InferenceSession(best.onnx) input_name sess.get_inputs()[0].name # 预处理要和训练一致resize 640, 归一化, NCHW blob np.random.randn(1, 3, 640, 640).astype(np.float32) outputs sess.run(None, {input_name: blob}) print([o.shape for o in outputs])这段只是验证 ONNX 能跑通实际预处理要按 YOLOv5 的 letterbox 方式做否则框位置会偏。ONNX 输出的解码逻辑和 PyTorch 不同需要自己写 NMS 后处理这是导出部署里最费时间的部分。最后说个习惯舰船检测项目里我永远会在训练前把数据集统计脚本跑一遍把类别分布、空标签比例、图片标签匹配情况打印出来存档。这个习惯帮我省过至少三次通宵排查——有一次就是类别 id 从 1 开始训练了两天才发现所有类别全错。数据这关过了后面模型和界面都是顺水推舟的事。希望帮到你。本文还有配套的精品资源点击获取
返回列表