ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

YOLO船舶检测数据集实战:从数据解压到模型调优的完整指南

YOLO船舶检测数据集实战:从数据解压到模型调优的完整指南 简介这份YOLO船舶目标检测数据集面向计算机视觉学习者、目标检测算法实践者及水上场景相关项目开发者用于训练和验证单类别船舶检测模型。数据集已按train、val、test完成划分并附带data.yaml配置文件类别仅含boatyolov5、yolov7、yolov8等主流框架可直接读取并启动训练省去自行标注与目录整理的成本。压缩包共2000个文件以1817个txt标注文件、178张jpg图像和5个yaml配置为主整体约114.65MB标注与图像一一对应便于快速构建训练流水线。内容覆盖皮划艇、独木舟、赛艇、冰川皮划艇、摩托艇等多种水上目标场景图像背景与光照条件较为丰富有助于提升模型泛化能力。目前已有1521人学习下载适合需要现成船舶检测数据、快速验证算法效果或开展课程实验的读者参考使用。1. 拿到 yolo-boat-detect-dataset-1.zip 之后船舶目标检测数据集到底该怎么用如果你手里已经有一个yolo-boat-detect-dataset-1.zip第一反应大概率是解压看看里面有什么然后直接丢给 YOLO 训练。但真正跑过船舶目标检测的人都知道水面场景的坑比陆地上多得多——反光、尾迹、远处小目标、密集停靠这些都会让模型在验证集上看起来还行一上真实视频就翻车。这个数据集的价值不在于“有船”而在于它把水面场景的标注边界固定下来了让你能把精力放在模型选型、输入分辨率和后处理上而不是从零标几千张图。这篇文章面向两类人一是刚拿到这个 zip、想快速跑通 YOLOv8 训练和推理的工程师二是已经做过通用目标检测、但第一次接触船舶场景、想知道水面检测和 COCO 那套有什么本质区别的人。我会按“先看清数据长什么样 → 转成 YOLO 能吃的格式 → 配环境跑训练 → 调参和排查 → 进阶验证”的顺序讲每一步都给出可复现的命令和参数。你不需要先看完 YOLO 算法讲解 PPT但需要有一台带 NVIDIA 显卡的机器或者至少能用 CPU 跑通推理。2. 先看清 zip 里的目录结构和标注格式别急着写 data.yaml2.1 解压后先做三件事统计类别、看图片尺寸、抽查标注拿到yolo-boat-detect-dataset-1.zip不要直接unzip完就写data.yaml。我一般会先建一个工作目录解压后立刻做三件事统计类别分布、看图片分辨率范围、随机抽 20 张图把标注框画出来。这三步能提前暴露 80% 的格式问题。mkdir -p ~/boat_work cd ~/boat_work unzip yolo-boat-detect-dataset-1.zip -d boat_raw find boat_raw -maxdepth 3 -type d | head -30 find boat_raw -name *.jpg -o -name *.png | wc -l find boat_raw -name *.txt | wc -l第一行建工作目录第二行解压到boat_raw。第三行看目录层级通常数据集会分成images/和labels/或者train/、val/下面再分。第四、五行分别统计图片和标注文件数量两者应该一致如果差很多说明有图没标或者标了没图后面训练会报错。接下来统计类别。YOLO 格式的标注是每行class_id x_center y_center width height坐标是归一化到 0~1 的。用一条 Python 脚本就能把类别分布和图片尺寸一起看了import os, glob from collections import Counter from PIL import Image root os.path.expanduser(~/boat_work/boat_raw) label_files glob.glob(os.path.join(root, **, *.txt), recursiveTrue) counter Counter() sizes Counter() for lf in label_files: with open(lf) as f: for line in f: parts line.strip().split() if len(parts) 5: counter[parts[0]] 1 img_files glob.glob(os.path.join(root, **, *.jpg), recursiveTrue) for img in img_files[:200]: with Image.open(img) as im: sizes[im.size] 1 print(类别分布:, counter.most_common()) print(图片尺寸 top5:, sizes.most_common(5))这段脚本先收集所有.txt标注按第一个字段类别 id计数。然后抽前 200 张图统计分辨率。如果类别分布严重偏斜比如某一类占 90%后面训练要加类别权重或者做重采样。如果图片尺寸差异很大比如从 640 到 4000 都有那统一 resize 到 640 会损失远处小船的信息需要权衡。2.2 判断标注是 YOLO 原生格式还是需要转换yolo-boat-detect-dataset-1.zip名字里带 yolo大概率已经是 YOLO 格式但不能假设。判断方法很简单打开一个.txt如果每行是 5 个浮点数且都在 0~1 之间就是 YOLO 格式如果是xmin ymin xmax ymax的整数像素值那是 VOC 格式如果是 JSON 带bbox字段那是 COCO 格式。常见做法是写一个转换脚本兜底。下面这个函数把 VOC 的 XML 转成 YOLO 的 txt如果你发现标注是 XML 就用它import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, out_txt, class_map): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) lines [] for obj in root.iter(object): cls obj.find(name).text if cls not in class_map: continue bnd obj.find(bndbox) xmin float(bnd.find(xmin).text) ymin float(bnd.find(ymin).text) xmax float(bnd.find(xmax).text) ymax float(bnd.find(ymax).text) xc (xmin xmax) / 2.0 / w yc (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{class_map[cls]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) with open(out_txt, w) as f: f.write(\n.join(lines))class_map是类别名到 id 的映射比如{boat: 0, ship: 1}。注意归一化时除以的是原图宽高不是 resize 后的。如果数据集里图片尺寸不一致这一步必须逐图读尺寸不能用一个固定值。2.3 划分 train/val 时别按图片随机分要按场景分很多人划分数据集就是random.shuffle然后 8:2 切分。船舶数据集的坑在于同一段水域、同一艘船在不同帧里几乎一样随机切分会让训练集和验证集高度相似验证指标虚高。我一般会先按视频来源或拍摄日期分组再在组间划分。import os, glob, random, shutil root os.path.expanduser(~/boat_work/boat_raw) img_dir os.path.join(root, images) lbl_dir os.path.join(root, labels) out_root os.path.expanduser(~/boat_work/boat_yolo) groups {} for img in glob.glob(os.path.join(img_dir, *.jpg)): key os.path.basename(img).split(_)[0] # 假设文件名前缀是场景 id groups.setdefault(key, []).append(img) keys list(groups.keys()) random.seed(42) random.shuffle(keys) split int(len(keys) * 0.8) train_keys, val_keys keys[:split], keys[split:] for phase, ks in [(train, train_keys), (val, val_keys)]: for k in ks: for img in groups[k]: base os.path.splitext(os.path.basename(img))[0] lbl os.path.join(lbl_dir, base .txt) if not os.path.exists(lbl): continue shutil.copy(img, os.path.join(out_root, images, phase, os.path.basename(img))) shutil.copy(lbl, os.path.join(out_root, labels, phase, base .txt))这里用文件名前缀当场景 id如果你的数据集命名没有规律可以改用图片的感知哈希或 EXIF 时间做分组。关键是让验证集里的船和训练集里的船不在同一段连续视频里。3. 把数据集接进 YOLOv8环境、data.yaml 和第一次训练3.1 环境配置Anaconda 建环境 ultralytics 安装的版本坑YOLOv8 的环境配置比 YOLOv5 简单但仍有几个容易翻车的点。我一般用 conda 建一个干净环境Python 选 3.10因为 3.11 以上有些 torch 版本还没跟上。conda create -n boat_yolo python3.10 -y conda activate boat_yolo pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python pillow python -c import torch; print(torch.__version__, torch.cuda.is_available())第三行装 CUDA 11.8 对应的 torch具体 cu 版本要看你显卡驱动支持到哪。第四行装 ultralytics它会自带 YOLOv8 的模型定义和训练入口。最后一行验证 GPU 是否可用如果输出False先别急着训练检查驱动和 CUDA 版本匹配。提示如果你用的是 50 系显卡需要 CUDA 12.1 以上的 torch把cu118换成cu121。装完 torch 再装 ultralytics避免它自动拉一个 CPU 版 torch 覆盖掉。3.2 写对 data.yaml路径、类别数和 names 的顺序data.yaml是 YOLOv8 训练的数据入口写错一个字段就会报Dataset not found或者类别对不上。标准写法如下path: /home/user/boat_work/boat_yolo train: images/train val: images/val nc: 2 names: 0: boat 1: shippath是数据集根目录train和val是相对路径。nc是类别数必须和 names 的长度一致。names的 key 从 0 开始顺序要和标注里的 class_id 完全对应。如果你不确定类别顺序回到 2.1 的统计脚本看counter里的 id 和实际类别名。常见错误是把names写成列表[boat, ship]YOLOv8 也支持但如果你后面要导出 ONNX 或者做后处理字典形式更不容易乱。另外path不要用~YAML 不展开波浪号写绝对路径。3.3 第一次训练用 yolov8n 跑通别一上来就上大模型第一次训练的目标是跑通流程不是刷指标。用yolov8n.pt预训练权重输入 640batch 根据显存调先跑 10 个 epoch 看 loss 曲线。yolo detect train \ data/home/user/boat_work/boat_yolo/data.yaml \ modelyolov8n.pt \ epochs10 \ imgsz640 \ batch16 \ device0 \ projectboat_runs \ nameexp1data指向 yamlmodel是预训练权重会自动下载。epochs10先看趋势imgsz640是船舶检测的常用起点batch16如果显存不够就降到 8 或 4。device0指定第一块 GPUCPU 训练去掉这个参数但会很慢。训练开始后重点看三个输出box_loss是否稳定下降、cls_loss是否震荡、mAP50是否在验证集上升。如果box_loss下降但mAP50不动大概率是验证集划分有问题或者标注框有系统性偏移。3.4 训练完先做推理验证用测试图片看实际效果训练结束后权重在boat_runs/exp1/weights/best.pt。别只看 mAP拿几张验证集里的图和几张没见过的图跑推理看框的位置和置信度。yolo detect predict \ modelboat_runs/exp1/weights/best.pt \ source/home/user/boat_work/boat_yolo/images/val \ conf0.25 \ saveTrue \ projectboat_pred \ nameval_checkconf0.25是置信度门限船舶场景建议先设低一点因为远处小船置信度普遍偏低。saveTrue会把画框后的图存到boat_pred/val_check。打开几张看有没有漏检的密集小船、有没有把浪花当成船、框有没有偏。注意如果发现大量重复框检查 NMS 的iou参数默认 0.7密集停靠场景可以降到 0.5。如果发现小目标全漏把imgsz提到 1280 再试但显存占用会翻倍。4. 船舶场景的调参与排查置信度、分辨率和数据增强怎么设4.1 置信度门限和 NMS 的联动为什么你的船框总是叠在一起船舶检测里最常见的问题是密集停靠时框叠在一起。YOLOv8 默认conf0.25、iou0.7在 COCO 上没问题但船与船之间间距小NMS 的 IoU 阈值太高会导致相邻船的框互相抑制或者保留重复框。我一般会做一组对比固定模型只改conf和iou在验证集上跑推理统计框数量和目视效果。for conf in 0.15 0.25 0.35; do for iou in 0.5 0.6 0.7; do yolo detect predict \ modelboat_runs/exp1/weights/best.pt \ source/home/user/boat_work/boat_yolo/images/val \ conf$conf iou$iou saveFalse \ projectboat_pred namegrid_${conf}_${iou} done done跑完后对比grid_*目录里的结果。经验是conf0.15~0.2能召回远处小船但会引入浪花误检iou0.5~0.6能分开密集船但可能把一艘长船切成两个框。最终值要在你的业务场景里权衡没有万能参数。4.2 输入分辨率640 够不够什么时候必须上 1280船舶检测的目标尺度跨度很大近处船占半张图远处船可能只有十几个像素。YOLOv8 在imgsz640时下采样 32 倍后最小特征图是 20x20十几个像素的船在特征图上只剩不到一个格子漏检是必然的。判断方法统计验证集里标注框的宽高分布看有多少框的宽度小于 32 像素。import glob, os small 0 total 0 for lf in glob.glob(/home/user/boat_work/boat_yolo/labels/val/*.txt): with open(lf) as f: for line in f: parts line.strip().split() if len(parts) 5: w float(parts[3]) * 640 # 假设原图 resize 到 640 if w 32: small 1 total 1 print(f小目标占比: {small}/{total} {small/total:.2%})如果小目标占比超过 20%建议训练时imgsz1280或者用yolov8m以上的模型配合 P2 检测头。代价是显存和推理时间1280 的显存占用大约是 640 的 4 倍。4.3 数据增强水面反光和尾迹让模型学偏怎么用增强压住YOLOv8 默认开启 mosaic、HSV 抖动、随机翻转。船舶场景里HSV 抖动太强会让模型把水面反光当成目标特征mosaic 会把不同场景的船拼在一起可能引入不真实的上下文。我一般会先关掉 mosaic 跑一轮 baseline再逐步加回来yolo detect train \ data/home/user/boat_work/boat_yolo/data.yaml \ modelyolov8n.pt \ epochs50 imgsz640 batch16 device0 \ mosaic0.0 hsv_h0.015 hsv_s0.3 hsv_v0.3 \ projectboat_runs nameexp_no_mosaicmosaic0.0关闭马赛克hsv_h0.015降低色调抖动hsv_s和hsv_v保持默认。跑完对比exp1和exp_no_mosaic的验证集 mAP 和目视效果。如果关掉 mosaic 后 mAP 下降但误检减少说明你的场景更看重准确率可以保持关闭或设mosaic0.3折中。4.4 排查训练不收敛从 loss 曲线到标注可视化的检查顺序训练不收敛的表现是 loss 震荡或者 mAP 一直为 0。排查顺序我一般按这个来第一步看标注有没有画错。用labelimg或者自己写脚本把 YOLO 格式的框画回图上确认框的位置和类别对。import cv2, os img_path /home/user/boat_work/boat_yolo/images/train/0001.jpg lbl_path /home/user/boat_work/boat_yolo/labels/train/0001.txt img cv2.imread(img_path) h, w img.shape[:2] with open(lbl_path) as f: for line in f: cid, xc, yc, bw, bh map(float, line.split()) x1 int((xc - bw/2) * w) y1 int((yc - bh/2) * h) x2 int((xc bw/2) * w) y2 int((yc bh/2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(check.jpg, img)第二步确认data.yaml里的nc和names与标注一致。第三步把学习率降到 0.001 跑几个 epoch 看 loss 是否下降。第四步检查图片是否损坏用cv2.imread批量读一遍。5. 避坑与常见问题船舶数据集训练里最容易翻车的 5 个点5.1 现象训练 loss 正常下降但验证集 mAP 始终为 0原因通常是验证集的标注路径没对上或者val指向的目录里没有对应的.txt。YOLOv8 在找不到标注时会静默跳过不报错。解决在data.yaml同级目录跑一个检查脚本确认images/val里的每张图在labels/val都有同名 txt。import os, glob img_dir /home/user/boat_work/boat_yolo/images/val lbl_dir /home/user/boat_work/boat_yolo/labels/val missing [] for img in glob.glob(os.path.join(img_dir, *.jpg)): base os.path.splitext(os.path.basename(img))[0] if not os.path.exists(os.path.join(lbl_dir, base .txt)): missing.append(base) print(缺失标注:, missing[:10], 共, len(missing))5.2 现象推理时框的位置整体偏移或者框比船大一圈原因多半是标注归一化时用错了宽高。比如图片被 resize 过但标注还是按原图尺寸算的或者 VOC 转 YOLO 时把xmax-xmin写成了xmax。解决回到 2.1 的可视化脚本把框画回原图肉眼确认。如果整体偏移检查归一化分母如果框偏大检查宽高计算。5.3 现象训练到一半显存爆了报 CUDA out of memory原因可能是batch太大、imgsz太高或者验证时没有释放缓存。YOLOv8 默认在训练结束后跑验证验证的 batch 可能比训练还大。解决把batch减半或者设valFalse先跑完训练再单独验证。另外可以在训练命令里加cacheFalse避免把图片全部缓存到显存。5.4 现象模型把浪花、云、岸边的白色物体当成船原因是负样本不足或者 HSV 增强让模型学到了颜色而不是形状。船舶数据集里如果正样本全是船没有包含浪花和云的负样本图模型就会过拟合到“白色块船”。解决往训练集里加一些没有船的纯水面、纯天空图标注为空 txt。YOLOv8 支持空标注文件会当作负样本。另外降低hsv_v的抖动幅度让模型更依赖边缘特征。5.5 现象从 Hugging Face 或网盘下载的数据集解压后文件名乱码原因是 zip 里的中文文件名编码和本地系统不一致。Linux 下用unzip -O GBK或者unzip -O CP936指定编码。解决如果已经乱码用 Python 的zipfile重新解压并指定编码import zipfile with zipfile.ZipFile(yolo-boat-detect-dataset-1.zip) as z: for info in z.infolist(): info.filename info.filename.encode(cp437).decode(gbk) z.extract(info, boat_raw_fixed)6. 进阶验证用切片推理和混淆矩阵确认模型在真实视频里能不能用训练指标好看不代表视频里能用。我一般会做两件事一是用saic或sahi做切片推理看小目标召回有没有提升二是跑混淆矩阵看类别之间有没有互相误判。切片推理的思路是把大图切成重叠的小块分别推理再合并。对船舶场景远处小船在整图推理时可能只有几个像素切片后在小块里占比变大更容易被检测到。pip install sahifrom sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction model AutoDetectionModel.from_pretrained( model_typeyolov8, model_pathboat_runs/exp1/weights/best.pt, confidence_threshold0.2, devicecuda:0 ) result get_sliced_prediction( test_boat.jpg, model, slice_height640, slice_width640, overlap_height_ratio0.2, overlap_width_ratio0.2 ) result.export_visuals(export_dirsahi_out)slice_height和slice_width是切片大小overlap是重叠比例0.2 表示相邻切片有 20% 重叠避免目标被切断。跑完后对比整图推理和切片推理的框数量如果切片后多出很多远处小船说明你的场景需要切片或者更高输入分辨率。混淆矩阵用 YOLOv8 自带的验证命令就能出yolo detect val \ modelboat_runs/exp1/weights/best.pt \ data/home/user/boat_work/boat_yolo/data.yaml \ imgsz640 batch16 \ projectboat_val nameconf_matrix \ plotsTrueplotsTrue会在输出目录生成混淆矩阵图。重点看boat和ship之间有没有大量误判如果有说明两个类别的视觉特征在你的数据里区分度不够可以考虑合并成一个类或者补充更多区分性样本。我自己的习惯是每次训练完先不看 mAP先拿一段真实视频抽 50 帧跑推理把漏检和误检的帧截出来按场景归类。如果漏检集中在远处小船就上切片或提分辨率如果误检集中在浪花就补负样本。这个习惯帮我省了很多次“指标好看但上线翻车”的后悔药。希望帮到你。本文还有配套的精品资源点击获取
返回列表