☰
YOLOv11多任务学习:检测与分割同框落地实战指南
2026/10/5 2:37:35 网站建设 项目流程

简介:这份PDF文档面向计算机视觉方向的学习者与工程开发者,聚焦多任务学习框架下YOLOv11同时实现目标检测与实例分割的完整工程实践,适合具备一定深度学习基础、希望掌握单阶段检测与像素级分割融合方案的中高级读者。文档共39页,以1个PDF文件形式打包,压缩包约2.19MB,支持目录章节跳转、阅读器左侧大纲显示与章节快速定位,查阅体验完整流畅。内容从多任务学习与目标检测、实例分割基础讲起,梳理YOLO系列发展脉络与YOLOv11骨干、颈部、头部架构设计,重点展开特征共享机制、检测分支与掩码预测分支的融合设计及多任务损失权衡,并覆盖环境搭建、数据准备、模型训练、评估与部署全流程,配合代码实现解析与COCO及自定义数据集实验结果分析,还整理了常见问题与解决方案。目前已有99人学习,可为读者提供从原理到落地的系统参考与排错思路。

1. 多任务学习框架下 YOLOv11 的检测与分割同框落地

很多团队在做视觉项目时,都会遇到一个尴尬局面:检测模型跑一遍,分割模型再跑一遍,同一张图进两次网络,显存翻倍、延迟翻倍,维护两套权重和两套预处理逻辑。多任务学习框架的价值就在这里——用一套骨干网络同时输出目标框和实例掩码,YOLOv11 的检测头与分割头共享 Backbone 和 Neck,推理时一次前向就能拿到两类结果。这个方案适合已经有 YOLOv11 检测基础、想把手里的分割需求合并进同一条流水线的工程师,也适合刚接触实例分割、想找一个能直接跑通的多任务基线的新手。下面按「结构怎么理解 → 数据怎么准备 → 训练怎么配 → 坑在哪 → 怎么验证」的顺序,把这条工程路径讲透。

2. YOLOv11 多任务结构拆解:检测头与分割头怎么共享骨干

2.1 从单任务到多任务:共享什么、分离什么

YOLOv11 在 Ultralytics 体系里本身就是一个可扩展的多任务框架,检测、分割、姿态、分类共用同一套 Backbone(CSP 风格的卷积堆叠)和 Neck(PAN-FPN 结构)。多任务的关键设计原则是:底层特征共享,高层任务头分离。

具体来说,Backbone 负责提取从边缘纹理到语义对象的通用特征,Neck 负责多尺度特征融合,这两部分被检测头和分割头共同使用。到了 Head 阶段才分叉:检测头输出边界框回归和类别置信度,分割头输出原型掩码(prototype masks)和掩码系数(mask coefficients)。分割头的工作方式是把高分辨率特征图上采样后,与一组学习到的原型掩码做线性组合,得到每个实例的像素级掩码。

这种设计的工程意义在于:你不需要维护两套 Backbone 权重,显存占用和推理延迟相比「检测模型 + 分割模型」的串联方案有明显优势。代价是分割头的原型掩码分辨率有限,对特别精细的边缘(比如细长物体、密集重叠目标)可能不如专用分割网络。

提示:如果你只需要检测,不要加载分割头权重;多任务训练时分割头的梯度会回传到共享 Backbone,可能对检测精度产生轻微扰动,这个后面避坑章节会展开。

2.2 网络结构图里真正要关注的三个位置

看 YOLOv11 网络结构图时,很多人会被密密麻麻的模块名淹没。对多任务落地来说,只需要盯住三个位置:

第一个是 Backbone 最后一个 stage 的输出,它决定了语义特征的丰富程度。YOLOv11 在这个位置用了 C2PSA 模块(带注意力的跨阶段部分连接),对小目标和遮挡目标有一定帮助。

第二个是 Neck 的三个输出尺度(P3、P4、P5),检测头和分割头都从这里取特征。P3 分辨率最高,负责小目标;P5 语义最强,负责大目标。

第三个是分割头的原型掩码分支,它通常从 P3 特征出发,经过几次卷积和上采样,生成固定数量的原型掩码。掩码系数则由检测头旁边的分支预测,每个实例一组系数,与原型掩码做矩阵乘法后得到最终掩码。

理解这三个位置之后,你在改配置文件、调通道数、换注意力模块时就知道动哪里会影响检测、动哪里会影响分割。

2.3 用 Ultralytics 加载多任务模型的最小命令

Ultralytics 把多任务模型的加载封装得很简洁。检测模型和分割模型分别对应不同的预训练权重后缀,分割模型用-seg后缀。

# 安装 Ultralytics(建议在虚拟环境里操作) pip install ultralytics # 加载 YOLOv11 分割模型(同时具备检测与分割能力) # yolo11n-seg.pt 是 nano 版本,适合快速验证 yolo segment predict model=yolo11n-seg.pt source='https://ultralytics.com/images/bus.jpg' save=True

这段命令做了三件事:下载或加载yolo11n-seg.pt权重,对指定图片做一次前向推理,把带检测框和分割掩码的结果保存到runs/segment/predict/目录。save=True控制是否保存可视化结果,source可以是单张图、目录、视频流地址。

参数说明:model指定权重文件,换成yolo11s-seg.pt、yolo11m-seg.pt就是不同规模;source支持本地路径和 URL;save=True保存标注后的图片或视频。如果你只想拿推理结果数据而不保存可视化,可以用 Python API 直接取results[0].boxes和results[0].masks。

from ultralytics import YOLO model = YOLO("yolo11n-seg.pt") results = model("bus.jpg") # results[0].boxes 是检测结果,results[0].masks 是分割结果 boxes = results[0].boxes.xyxy # 边界框坐标 masks = results[0].masks.data # 掩码张量,形状为 (实例数, H, W) print(f"检测到 {len(boxes)} 个目标,掩码形状 {masks.shape}")

这段代码展示了多任务输出的读取方式。boxes.xyxy给出每个目标的左上角和右下角坐标,masks.data是二值掩码张量。注意掩码的分辨率通常与输入图像尺寸一致,如果后续要做像素级后处理,需要确认是否需要 resize 回原图尺寸。

3. 数据集准备:检测标注与分割标注怎么对齐

3.1 分割数据集格式与目录结构

YOLOv11 分割训练需要的数据格式是 YOLO 风格的 txt 标注,每行格式为:

<class_id> <x1> <y1> <x2> <y2> ... <xn> <yn>

其中坐标是归一化后的多边形点坐标,不是边界框。检测标注则是<class_id> <cx> <cy> <w> <h>。多任务训练时,Ultralytics 会根据标注文件的内容自动判断是检测还是分割任务——如果一行里有多于 5 个数值,就按多边形处理。

推荐的数据集目录结构:

dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml

data.yaml的内容:

path: ./dataset train: images/train val: images/val nc: 3 names: 0: person 1: car 2: dog

nc是类别数,names是类别名映射。这个文件是训练入口,路径写错是最常见的翻车原因之一。

3.2 从检测标注转分割标注的脚本

很多团队手里只有检测标注(边界框),没有多边形标注。一种常见做法是用边界框生成粗略的多边形(矩形四角),先跑通多任务训练流程,后续再逐步替换成精细标注。

import os import cv2 import numpy as np def bbox_to_polygon_label(bbox_line, img_w, img_h): """ 把 YOLO 检测标注的一行转成多边形标注行。 bbox_line: "class_id cx cy w h"(归一化) 返回: "class_id x1 y1 x2 y2 x3 y3 x4 y4"(归一化) """ parts = bbox_line.strip().split() cls_id = parts[0] cx, cy, w, h = map(float, parts[1:5]) # 还原为像素坐标 x1 = (cx - w / 2) * img_w y1 = (cy - h / 2) * img_h x2 = (cx + w / 2) * img_w y2 = (cy + h / 2) * img_h # 矩形四角,归一化 poly = [ x1 / img_w, y1 / img_h, x2 / img_w, y1 / img_h, x2 / img_w, y2 / img_h, x1 / img_w, y2 / img_h, ] return cls_id + " " + " ".join(f"{v:.6f}" for v in poly) def convert_dataset(img_dir, label_dir, out_dir): os.makedirs(out_dir, exist_ok=True) for fname in os.listdir(label_dir): if not fname.endswith(".txt"): continue img_path = os.path.join(img_dir, fname.replace(".txt", ".jpg")) if not os.path.exists(img_path): continue img = cv2.imread(img_path) h, w = img.shape[:2] with open(os.path.join(label_dir, fname)) as f: lines = f.readlines() new_lines = [bbox_to_polygon_label(l, w, h) for l in lines if l.strip()] with open(os.path.join(out_dir, fname), "w") as f: f.write("\n".join(new_lines)) convert_dataset("./dataset/images/train", "./dataset/labels/train", "./dataset/labels/train_seg")

这段脚本的逻辑是:读取每张图对应的检测标注,把归一化的中心点加宽高还原成像素坐标下的矩形四角,再重新归一化写成多边形格式。参数说明:img_dir是图片目录,label_dir是原始检测标注目录,out_dir是输出目录。注意这个转换只生成矩形掩码,适合快速验证流程,不适合对掩码精度有要求的场景。

3.3 数据增强对多任务的影响

Ultralytics 默认开启 mosaic、mixup、随机缩放等增强。对检测任务来说这些增强通常有益,但对分割任务,mosaic 拼接会改变实例的像素分布,可能让掩码边界变得不连续。我的经验是:多任务训练时把mosaic概率适当降低(比如从 1.0 降到 0.5),mixup在分割任务上慎用。

在训练配置里可以这样覆盖:

from ultralytics import YOLO model = YOLO("yolo11n-seg.yaml") # 从结构文件开始训练 model.train( data="data.yaml", epochs=100, imgsz=640, mosaic=0.5, # 降低 mosaic 概率 mixup=0.0, # 关闭 mixup degrees=10.0, # 轻微旋转 translate=0.1, scale=0.5, )

mosaic=0.5表示 50% 的概率做 mosaic 拼接,mixup=0.0关闭 mixup。imgsz=640是输入分辨率,分割任务对分辨率比检测更敏感,如果显存允许可以提到 1024。

4. 训练配置与参数调优:从 nano 到 medium 的实操路径

4.1 训练命令与关键参数含义

多任务训练和纯检测训练在命令层面几乎一样,区别在于模型结构文件和数据集标注格式。

yolo segment train model=yolo11n-seg.yaml data=data.yaml epochs=100 imgsz=640 batch=16 device=0

参数逐个说明:model指定结构文件或预训练权重,用.yaml是从头训练,用.pt是加载预训练权重做微调;data是数据集配置文件;epochs是训练轮数;imgsz是输入尺寸;batch是批大小,显存不够就往下调;device=0指定第一块 GPU。

如果要从预训练权重微调:

yolo segment train model=yolo11n-seg.pt data=data.yaml epochs=100 imgsz=640 batch=16 device=0 lr0=0.001

lr0是初始学习率,微调时通常比从头训练小一个数量级。Ultralytics 默认会用余弦退火调度,lrf控制最终学习率比例。

4.2 损失函数组成与权重调整

YOLOv11 分割任务的损失由三部分组成:检测损失(边界框回归 + 分类)、分割损失(掩码 BCE + Dice)、以及可选的分布焦点损失。Ultralytics 内部已经配好了默认权重,一般不需要手动改。但如果你发现检测精度正常、分割掩码质量差,可以检查分割损失是否被检测损失压制。

一个实用的观察方法是看训练日志里box_loss、seg_loss、cls_loss三条曲线的下降趋势。如果seg_loss下降很慢甚至震荡,可能是学习率偏大或者掩码标注质量有问题。

4.3 小目标场景下的参数调整

小目标检测和分割是多任务里比较难的部分。YOLOv11 的 P3 特征图负责小目标,但如果目标在图像中占比极小,P3 的感受野可能仍然不够。几个可调的方向:

提高输入分辨率是最直接的手段,imgsz=1024或1280能让小目标占据更多像素。代价是显存和推理时间增加。

调整 Anchor 或使用 Anchor-Free 策略。YOLOv11 本身是 Anchor-Free 的,但可以通过修改reg_max参数影响边界框回归的范围。

增加 P2 检测层。在结构文件里把 P2 特征也接入 Neck 和 Head,能提升小目标召回,但会显著增加计算量。

# 在 yolo11-seg.yaml 基础上增加 P2 的简化示意 # 实际修改需要同步调整 Neck 和 Head 的通道数 head: - [-1, 1, nn.Upsample, [None, 2, "nearest"]] - [[-1, 3], 1, Concat, [1]] # 与 P2 特征拼接 - [-1, 3, C3k2, [256, False]]

注意:修改网络结构后,预训练权重不能直接加载,需要重新训练或只加载 Backbone 部分权重。

4.4 训练过程监控与早停

Ultralytics 默认在验证集指标不再提升时触发早停(patience=50)。多任务训练时,建议同时关注检测 mAP 和分割 mAP,不要只看其中一个。如果检测 mAP 在涨但分割 mAP 停滞,可能是分割头学习率不够或者掩码标注有问题。

训练日志里metrics/mAP50(B)是检测指标,metrics/mAP50(M)是分割指标。两个都看,才能判断多任务是否真的在互相促进而不是互相拖累。

5. 多任务训练避坑:从显存爆炸到掩码错位的排查记录

5.1 显存溢出:batch 和 imgsz 的取舍

现象:训练启动后几秒内报 CUDA out of memory,或者训练中途突然 OOM。

原因:多任务模型的显存占用比纯检测模型高,因为分割头需要维护高分辨率特征图和原型掩码。batch=16加imgsz=640在 8GB 显存上可能直接爆。

解决:先把batch降到 8 或 4,确认能跑通后再逐步往上加。如果显存仍然紧张,把imgsz降到 512。另一个技巧是开启amp=True(自动混合精度),Ultralytics 默认开启,但某些显卡上需要手动确认。

5.2 掩码全黑或全白:标注格式的隐蔽错误

现象:训练 loss 正常下降,但推理时掩码要么全黑要么覆盖整张图。

原因:标注文件里的多边形坐标没有归一化,或者归一化时除了错误的宽高。YOLO 格式要求所有坐标在 0 到 1 之间,如果标注工具导出的是像素坐标,直接训练就会出问题。

解决:写一个检查脚本,遍历所有标注文件,确认每个数值都在 [0, 1] 范围内,且每行数值个数是偶数加一(类别 id 加成对坐标)。

import os def check_labels(label_dir): for fname in os.listdir(label_dir): if not fname.endswith(".txt"): continue with open(os.path.join(label_dir, fname)) as f: for i, line in enumerate(f): parts = line.strip().split() if len(parts) < 5: print(f"{fname} 第 {i} 行数值不足: {line.strip()}") continue coords = list(map(float, parts[1:])) if any(c < 0 or c > 1 for c in coords): print(f"{fname} 第 {i} 行坐标越界: {coords}") if len(coords) % 2 != 0: print(f"{fname} 第 {i} 行坐标个数不是偶数: {len(coords)}") check_labels("./dataset/labels/train")

5.3 检测涨点但分割不涨:梯度冲突的排查

现象:训练 50 轮后,检测 mAP 已经超过单任务基线,但分割 mAP 几乎没动。

原因:检测损失和分割损失的梯度在共享 Backbone 上可能方向不一致,检测任务主导了参数更新。另一种可能是分割头的初始化不够好。

解决:尝试给分割损失加权重(Ultralytics 内部有seg_weight参数,但不在标准配置里暴露,需要改源码或自定义训练循环)。更简单的做法是先用分割任务单独训练几轮,让分割头先学到合理的初始化,再联合训练。

5.4 推理结果保存路径混乱

现象:跑完推理后找不到保存的图片或掩码文件。

原因:Ultralytics 默认保存到runs/segment/predict/,但如果你改了project和name参数,路径会变。多次运行会生成predict2、predict3等递增目录。

解决:在推理命令里显式指定project和name,比如project=./output name=exp1,这样结果会固定保存到./output/exp1/。如果要保存掩码数据而不是可视化图片,用 Python API 取results[0].masks.data自己写文件。

5.5 预训练权重加载失败

现象:用model=yolo11n-seg.pt启动训练时报错,提示权重与结构不匹配。

原因:你修改了网络结构(比如加了 P2 层或换了注意力模块),但加载的预训练权重还是原始结构。Ultralytics 会尝试部分加载,但某些层名对不上就会跳过或报错。

解决:修改结构后,要么从头训练,要么用model.load()手动加载 Backbone 部分权重。另一种做法是先用原始结构训练一个基线,再在此基础上做结构消融。

6. 验证多任务效果:指标解读与一个实用技巧

训练完成后,验证多任务效果不能只看一个数字。检测用 mAP50 和 mAP50-95,分割用 mask mAP50 和 mask mAP50-95。Ultralytics 的验证命令会同时输出两组指标:

yolo segment val model=runs/segment/train/weights/best.pt data=data.yaml imgsz=640

输出里Box(P R mAP50 mAP50-95)是检测指标,Mask(P R mAP50 mAP50-95)是分割指标。如果检测 mAP50 在 0.8 以上但分割 mAP50 只有 0.5 左右,说明掩码质量还有提升空间,优先检查标注精度和分割头学习率。

一个我常用的验证技巧是:把同一张图分别用检测模型和分割模型跑一遍,对比检测框的位置是否一致。如果分割模型输出的框和检测模型偏差很大,说明多任务训练中检测头被分割任务干扰了,可以考虑冻结 Backbone 先单独微调检测头几轮。

另外,推理速度也是多任务方案的核心价值之一。用以下命令测一下单张图的端到端延迟:

import time from ultralytics import YOLO model = YOLO("runs/segment/train/weights/best.pt") img = "test.jpg" # 预热 for _ in range(5): model(img) start = time.time() for _ in range(50): model(img) end = time.time() print(f"平均单张推理时间: {(end - start) / 50 * 1000:.2f} ms")

这个脚本先跑 5 次预热(让 CUDA 完成初始化),再跑 50 次取平均。如果平均延迟在 20ms 以内,说明多任务方案在实时场景下是可行的。如果超过 50ms,考虑换更小的模型规模或者降低输入分辨率。

最后说一个我踩过的坑:多任务模型导出 ONNX 或 TensorRT 时,分割头的原型掩码分支有时不被某些推理引擎支持,导致导出后只有检测结果没有分割结果。导出前先用yolo export model=best.pt format=onnx试一下,确认输出节点里包含掩码相关的输出。如果不行,就保持 PyTorch 推理或者换用支持多任务输出的推理框架。

希望帮到你。

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

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

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

立即咨询