MATLAB YOLOv2舰船检测实战:从数据标注到模型调优
2026/9/23 10:54:27 网站建设 项目流程

简介:这份资源面向深度学习与计算机视觉方向的学习者和研究者,提供一套基于YOLO算法的舰船目标检测完整实现方案,可用于海上救援、交通监控、军事侦察等场景下的船只自动识别练习。压缩包共60个文件,约2.33MB,其中55张jpg图片构成训练与测试样本,2个mat文件分别保存标注数据和模型参数,2个m脚本负责YOLOv2网络的训练与检测流程,另有1个txt说明文件辅助理解目录结构。已有127人学习下载。资源以SSDD舰船目标数据集为基础,覆盖不同天气、光照与海况下的舰船图像,读者可据此掌握从数据加载、模型训练到结果可视化的完整链路,并借助Matlab的矩阵运算与深度学习工具箱完成网络搭建、损失函数选择与超参数调整,进而评估检测速度与精度,形成可复用的舰船检测工程思路。

1. 舰船检测为什么值得单独拆一个 YOLO 工程

海上监控场景里,通用目标检测模型直接拿来跑舰船,误检和漏检往往同时出现——近岸的白色建筑、浪花、云层边缘都可能被当成船。这份资源把问题收窄到「舰船」这一个类别,用 YOLOv2 在 MATLAB 里跑通了一条从数据加载、模型训练到测试可视化的完整链路,压缩包内包含ship_code.ziptrain训练图目录、test测试图目录、gTruth.mat标注文件、Yolov2Model_ship.mat预训练模型以及ship_yolov2.mship_yolov2_test.m两个主脚本。它适合两类人:一是做海事方向课题、需要快速拿到可复现基线的研究生;二是手上只有 MATLAB 授权、不想折腾 Python 深度学习环境但要做目标检测验证的工程师。SSDD 舰船目标数据集的多天气、多海况样本,让这个工程在泛化性上比随便凑几十张图强不少。

2. 拆开压缩包:文件结构与 YOLOv2 舰船检测原理

2.1 目录里每个文件到底干什么

拿到ship_code.zip解压后,先别急着点运行。把目录结构理清楚,后面调参和排错会省很多时间。核心文件可以分成四类:

文件/目录类型作用
ship_yolov2.m脚本训练主入口,负责加载数据、配置网络、启动训练
ship_yolov2_test.m脚本推理入口,加载模型对test目录图片做检测并画框
Yolov2Model_ship.mat模型文件已训练好的舰船检测权重,可直接用于测试
gTruth.mat标注文件MATLAB groundTruth 对象,存储训练图的边界框与标签
train/图像目录训练集图片,编号从 1.jpg 到 50.jpg
test/图像目录测试集图片,test1.jpg 到 test5.jpg
说明.txt文本作者留下的运行说明与版本备注

train目录里 50 张图覆盖了 SSDD 数据集的典型场景:不同光照、不同海况、舰船大小差异明显。gTruth.mat是 MATLAB 图像标注工具(Image Labeler)导出的标准格式,里面存的是每个边界框的坐标和ship标签。Yolov2Model_ship.mat是已经跑完训练的模型,如果你只想验证效果,直接跑测试脚本就行,不用重新训练。

提示:gTruth.mattrain目录必须保持相对路径一致,移动文件后标注会失效,这是 MATLAB groundTruth 对象的常见坑。

2.2 YOLOv2 为什么适合舰船检测这个任务

YOLOv2 在舰船检测上的优势,核心在于它把检测变成了单阶段回归问题。输入图像被划分成 13×13 的网格,每个网格预测若干锚框(anchor box)的偏移量和置信度,最后通过非极大值抑制(NMS)输出最终框。相比 Faster R-CNN 这类两阶段方法,YOLOv2 省掉了区域建议网络,推理速度更快,对海上监控这种需要实时性的场景更友好。

舰船目标有个特点:长宽比跨度大,从接近 1:1 的小渔船到 5:1 以上的大型货轮都有。YOLOv2 用 k-means 聚类得到锚框尺寸,比手工设定更贴合数据分布。在 MATLAB 的trainYOLOv2ObjectDetector里,锚框可以通过AnchorBoxes参数指定,默认是基于 COCO 的,直接拿来检测舰船会偏。常见做法是用训练集的边界框重新聚类,或者至少根据舰船的长宽比手动调一版。

另一个关键点是输入分辨率。YOLOv2 支持多尺度训练,InputSize设成[416 416 3]是平衡速度和精度的常见选择。舰船在图像里往往只占几十个像素,分辨率太低会直接丢小目标。如果显存允许,把InputSize提到[608 608 3]对小舰船检测提升明显,但训练时间会成倍增加。

2.3 训练脚本的核心参数与执行流程

打开ship_yolov2.m,训练流程大致分四步:加载 groundTruth、划分训练验证集、配置网络层、调用训练函数。下面这段代码是典型写法,我按资源里的结构还原并加了注释:

% 加载标注数据 load('gTruth.mat'); % 得到 gTruth 对象 % 从 groundTruth 提取边界框和标签 [imds, blds] = objectDetectorTrainingData(gTruth); % 按 80/20 划分训练集和验证集 idx = floor(0.8 * numel(imds.Files)); trainImds = subset(imds, 1:idx); trainBlds = subset(blds, 1:idx); valImds = subset(imds, idx+1:end); valBlds = subset(blds, idx+1:end); % 定义网络输入尺寸和类别数 inputSize = [416 416 3]; numClasses = 1; % 只有 ship 一类 % 配置 YOLOv2 网络 lgraph = yolov2Layers(inputSize, numClasses, 'darknet19', ... 'AnchorBoxes', [16 16; 32 32; 64 64; 128 128; 256 256]); % 设置训练选项 options = trainingOptions('sgdm', ... 'MiniBatchSize', 8, ... 'InitialLearnRate', 1e-3, ... 'MaxEpochs', 60, ... 'Verbose', true, ... 'Plots', 'training-progress'); % 开始训练 detector = trainYOLOv2ObjectDetector(trainImds, trainBlds, lgraph, options); % 保存模型 save('Yolov2Model_ship.mat', 'detector');

逻辑说明:objectDetectorTrainingData把 groundTruth 对象转成图像数据存储和边界框数据存储,这是 MATLAB 训练检测器的标准入口。yolov2Layers负责搭建网络骨架,'darknet19'是 YOLOv2 的经典骨干,轻量且够用。AnchorBoxes这里给的是通用尺寸,实际用的时候建议根据trainBlds里的框做一次 k-means 聚类替换掉。

参数说明:MiniBatchSize设 8 是 4GB 显存下的保守值,显存够可以提到 16。InitialLearnRate用 1e-3 是 SGD 的常见起点,训练 loss 震荡大就降到 1e-4。MaxEpochs设 60 对 50 张图来说偏多,但 YOLOv2 收敛慢,配合验证集早停更稳妥。AnchorBoxes的五个尺寸覆盖了小、中、大舰船,如果数据里全是小目标,把大尺寸去掉能减少无效计算。

2.4 测试脚本怎么用预训练模型出结果

ship_yolov2_test.m的逻辑比训练简单得多,核心就是加载模型、读图、检测、画框:

% 加载训练好的模型 load('Yolov2Model_ship.mat'); % 得到 detector % 读取测试图片 testImg = imread('test/test1.jpg'); % 执行检测,设置置信度阈值 [bboxes, scores, labels] = detect(detector, testImg, ... 'Threshold', 0.5); % 在图上画检测框 if ~isempty(bboxes) detectedImg = insertObjectAnnotation(testImg, 'rectangle', ... bboxes, cellstr(labels), 'LineWidth', 3, 'FontSize', 18); else detectedImg = testImg; disp('当前图片未检测到舰船,可尝试降低 Threshold'); end % 显示结果 figure; imshow(detectedImg); title('舰船检测结果');

逻辑说明:detect函数返回三个值——边界框坐标、置信度分数、类别标签。Threshold控制输出门槛,设 0.5 是默认值,漏检多就降到 0.3,误检多就提到 0.7。insertObjectAnnotation是 MATLAB 自带的标注可视化函数,比手动画矩形省事。

参数说明:Threshold是测试阶段最常调的参数,没有之一。舰船检测里,海面反光和云层边缘容易产生 0.3~0.5 之间的假阳性,如果发现框画在浪花上,优先提阈值而不是改模型。detect还支持'MiniBatchSize'参数做批量推理,测试图多的时候设成 4 或 8 能快不少。

3. 从零跑通训练:数据准备、锚框聚类与训练监控

3.1 用 Image Labeler 检查 gTruth 标注是否可用

拿到gTruth.mat后第一件事不是训练,是验证标注有没有问题。MATLAB 里直接加载并可视化:

load('gTruth.mat'); % 查看标注摘要 summary(gTruth) % 可视化前 4 张图的标注框 imgIdx = 1:4; for i = imgIdx img = imread(gTruth.DataSource.Source{i}); bbox = gTruth.LabelData.ship{i}; annotated = insertShape(img, 'Rectangle', bbox, ... 'LineWidth', 4, 'Color', 'green'); figure; imshow(annotated); title(['Image ' num2str(i)]); end

逻辑说明:gTruth.DataSource.Source存的是图像路径,gTruth.LabelData.ship存的是对应的边界框。summary能快速看出有多少张图、多少个框、标签是否统一。如果LabelData里字段名不是ship,后面训练会直接报错,得先改成实际标签名。

常见问题是标注框坐标越界或宽高为负。insertShape画不出来或者报错,基本就是标注有问题。SSDD 数据集里有些小目标标注框只有几个像素宽,训练时容易被忽略,可以考虑过滤掉宽高小于 10 像素的框,或者用数据增强把小目标放大。

3.2 锚框聚类:让先验框贴合舰船尺寸分布

YOLOv2 的锚框如果直接用默认值,小舰船和大货轮的检测效果会差很多。用训练集边界框做 k-means 聚类是标准做法:

% 提取所有训练框的宽高 allBoxes = vertcat(trainBlds.LabelData{:}); wh = allBoxes(:, 3:4); % 第3、4列是宽和高 % k-means 聚类,k=5 numAnchors = 5; [~, centroids] = kmeans(wh, numAnchors, ... 'Distance', 'cityblock', 'Replicates', 5); % 按面积排序,方便对应小到大目标 [~, order] = sort(centroids(:,1) .* centroids(:,2)); anchors = centroids(order, :); disp('聚类得到的锚框尺寸:'); disp(anchors);

逻辑说明:cityblock距离比欧氏距离更适合边界框聚类,这是 YOLO 原论文的做法。Replicates设 5 是跑多次取最优,避免陷入局部最优。聚类完把锚框按面积排序,训练时小锚框负责小目标,大锚框负责大目标,逻辑更清晰。

参数说明:numAnchors设 5 是 YOLOv2 的默认值,舰船类别少,5 个够用。如果数据里目标尺寸特别集中,减到 3 个也能跑,还能减少计算量。聚类结果替换掉yolov2Layers里的AnchorBoxes参数即可。

3.3 训练过程监控:loss 曲线怎么看、什么时候停

training-progress窗口里主要看三条线:总 loss、分类 loss、定位 loss。总 loss 下降但验证 loss 开始上升,就是过拟合的信号,该停了。舰船检测里,定位 loss 通常比分类 loss 大,因为边界框回归本身更难。

如果 loss 从第一轮就居高不下,检查三件事:学习率是不是太大(降到 1e-4 试试)、锚框尺寸是不是和实际目标差太远、标注框有没有归一化问题。MATLAB 的 YOLOv2 训练器内部会做归一化,但 groundTruth 里的坐标必须是像素值,不能是归一化后的。

训练完成后,用验证集跑一遍detect,看 mAP 大概在什么水平。50 张训练图、60 轮,mAP 通常能到 0.6~0.7 左右,再往上提就得加数据或做增强。如果 mAP 低于 0.4,优先检查标注质量和锚框聚类结果,而不是盲目加轮数。

4. 避坑与排查:舰船检测工程里最容易翻车的五件事

4.1 现象:训练 loss 正常下降,但测试图一个框都画不出来

原因:detectThreshold默认是 0.5,而模型输出的置信度普遍偏低,尤其是训练轮数不够或锚框不匹配时。另一个可能是测试图和训练图的分辨率差异太大,模型没学到尺度不变性。

解决:先把Threshold降到 0.2 看有没有框出来。如果有框但位置很偏,说明锚框需要重新聚类。如果降到 0.1 还是没框,检查detect输入的图像是不是 RGB 三通道,有些测试图是灰度图,直接喂进去会报维度错误。

4.2 现象:gTruth.mat加载后报「无法识别的字段」

原因:MATLAB 版本差异导致 groundTruth 对象结构不同。R2019b 之前和之后的LabelData字段名可能不一样,或者作者保存时用了自定义标签名。

解决:load之后先whos看变量,再gTruth.LabelData看字段。字段名不是ship就手动改,或者用gTruth.LabelData.Properties.VariableNames查实际名称。实在不行,用objectDetectorTrainingData之前先gTruth = changeFilePaths(gTruth, newPath)把路径修正。

4.3 现象:训练到一半报显存不足(Out of Memory)

原因:MiniBatchSize设太大,或者InputSize设成了 608 而显卡只有 4GB。YOLOv2 的 darknet19 骨干虽然轻量,但特征图在浅层分辨率很高,显存占用比想象中大。

解决:把MiniBatchSize降到 4 甚至 2,InputSize保持 416。如果还不行,用'ExecutionEnvironment', 'cpu'先跑通流程,再换到 GPU 上调参。MATLAB 的trainingOptions里可以设'DispatchInBackground', true来异步读取数据,减少显存峰值。

4.4 现象:检测框大量重叠,同一艘船被框了好几次

原因:NMS 阈值设得太高,或者锚框之间重叠度太大。YOLOv2 的detect函数内部有 NMS,但默认阈值不一定适合舰船这种长宽比大的目标。

解决:在detect里显式设'OverlapThreshold', 0.3,比默认的 0.5 更激进,能压掉重复框。如果还有重叠,检查锚框聚类结果,把宽高比接近的锚框合并或去掉一个。

4.5 现象:换一批新图测试,效果断崖式下降

原因:SSDD 数据集虽然多样,但 50 张训练图覆盖的场景有限。新图如果来自不同海域、不同季节,光照和背景差异会导致模型泛化失败。

解决:最直接的办法是加数据。如果加不了,用 MATLAB 的augmentedImageDatastore做在线增强,随机翻转、缩放、调整亮度。舰船检测里水平翻转是安全的,垂直翻转会把船底朝上,反而不合理。亮度抖动范围控制在 ±20% 以内,太大容易把船和海面混在一起。

5. 进阶技巧:用验证集反推模型短板并做定向优化

训练完一个模型只是起点,真正决定舰船检测能不能用的,是你能不能从验证结果里读出模型到底弱在哪。我一般会写一个批量评估脚本,把验证集所有图的检测结果和真实标注做对比,按目标尺寸分桶统计漏检率:

% 批量评估验证集 threshold = 0.4; smallMiss = 0; smallTotal = 0; largeMiss = 0; largeTotal = 0; for i = 1:numel(valImds.Files) img = imread(valImds.Files{i}); gtBoxes = valBlds.LabelData{i}; [predBoxes, ~, ~] = detect(detector, img, 'Threshold', threshold); for j = 1:size(gtBoxes, 1) gtArea = gtBoxes(j,3) * gtBoxes(j,4); if gtArea < 32*32 smallTotal = smallTotal + 1; if isempty(predBoxes) || ... max(bboxOverlapRatio(gtBoxes(j,:), predBoxes)) < 0.3 smallMiss = smallMiss + 1; end else largeTotal = largeTotal + 1; if isempty(predBoxes) || ... max(bboxOverlapRatio(gtBoxes(j,:), predBoxes)) < 0.3 largeMiss = largeMiss + 1; end end end end fprintf('小目标漏检率:%.2f%% (%d/%d)\n', ... 100*smallMiss/smallTotal, smallMiss, smallTotal); fprintf('大目标漏检率:%.2f%% (%d/%d)\n', ... 100*largeMiss/largeTotal, largeMiss, largeTotal);

逻辑说明:bboxOverlapRatio算的是预测框和真实框的 IoU,低于 0.3 就算漏检。按面积 32×32 分桶,是因为 YOLOv2 在 416 输入下,小于这个尺寸的目标在特征图上只剩几个像素,检测难度陡增。跑完这个脚本,如果小目标漏检率超过 50%,而大目标低于 20%,那问题就很明确——模型对小舰船不敏感。

定向优化的手段有几个,按性价比排序:第一,把InputSize从 416 提到 608,小目标特征保留更多,代价是训练时间翻倍;第二,在锚框聚类时增加小尺寸锚框的数量,比如从 5 个锚框里分 3 个给小目标;第三,对训练集里的小目标做复制粘贴增强,把舰船抠出来贴到不同背景上,这是最直接但最费手工的办法。

还有一个容易被忽略的点:置信度门限不是全局最优的。小目标本身置信度就低,用同一个Threshold会系统性漏检。我习惯对小目标单独降阈值,比如面积小于 32×32 的框用 0.25,大框用 0.5,实测能拉回不少漏检。这个逻辑在detect之后手动过滤一遍就行,不用改模型。

从那以后我每次跑完舰船检测训练,都强制走一遍分桶评估,不看到小目标漏检率数字就不调参。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询