
简介这份PDF文档面向计算机视觉方向的学习者与工程开发者聚焦多任务学习框架下YOLOv11同时实现目标检测与实例分割的完整工程实践适合具备一定深度学习基础、希望掌握单阶段检测与像素级分割融合方案的中高级读者。文档共39页以1个PDF文件形式打包压缩包约2.19MB支持目录章节跳转、阅读器左侧大纲显示与章节快速定位查阅体验完整流畅。内容从多任务学习与目标检测、实例分割基础讲起梳理YOLO系列发展脉络与YOLOv11骨干、颈部、头部架构设计重点展开特征共享机制、检测分支与分割分支的融合设计及多任务损失权衡并给出环境搭建、数据准备、模型训练、评估与部署的工程步骤配合各模块代码实现解析与COCO及自定义数据集实验结果分析还整理了常见问题与解决方案。目前已有99人学习可帮助读者系统理解多任务融合思路、对照代码复现流程并积累排错经验。1. 多任务学习框架下 YOLOv11 的检测与分割同屏输出这条路值不值得走如果你正在做工业质检、自动驾驶感知或者遥感解译大概率遇到过这种局面目标检测模型跑一遍拿到框实例分割模型再跑一遍拿到掩码两个模型各自占一份显存、各自吃一份推理时间部署到 Jetson 或者边缘盒子上直接爆内存。多任务学习框架的核心思路就是让一个骨干网络同时输出检测框和实例掩码YOLOv11 的seg分支恰好提供了这个能力——它不是在检测头上硬挂一个分割头而是在训练阶段就让检测损失和分割损失联合回传共享 backbone 和 neck 的特征。这意味着你只需要维护一套权重、一次前向推理就能同时拿到boxes和masks。适合谁适合已经跑通 YOLOv11 检测、想用最小改造成本把实例分割也纳入同一条流水线的工程师也适合刚接触多任务学习、想找一个能落地、不玄学的切入点的新手。这一章不展开代码先把「为什么是 YOLOv11 而不是自己拼两个模型」这件事说清楚。YOLOv11 的seg模型本质上是一个多任务学习框架的工程化实现检测头负责回归边界框和分类分割头在检测框的基础上预测原型掩码系数两者共享 C3k2 和 SPPF 提取的特征。这种设计的好处是推理时只跑一次 backbone检测和分割的延迟几乎叠加在一个量级上而不是两倍。代价是训练时两个任务的梯度会互相拉扯小目标的分割精度往往不如单独训练的专用分割模型。所以选型时要问自己你的场景是「检测为主、分割辅助」还是「分割精度优先」前者直接上yolo11n-seg.pt微调后者可能需要考虑 Mask R-CNN 或者自己设计解耦头。我一般会建议先用yolo11s-seg跑一个 baseline看掩码的 mAP50-95 能不能接受再决定要不要加深分割分支。2. 从标注到训练YOLOv11 多任务数据管线的搭建2.1 检测框和分割掩码的标注格式怎么统一YOLOv11 的seg模型训练时标签文件里每一行同时包含检测框和分割多边形。格式是class_id x1 y1 x2 y2 ... xn yn其中多边形点按顺时针或逆时针排列归一化到 0-1 之间。检测框不需要单独写YOLOv11 在训练时会自动从多边形计算外接矩形作为检测目标。这意味着你不需要维护两份标注文件一份多边形标注就够了。常见做法是用 LabelMe 或者 CVAT 标注多边形然后转成 YOLO 格式。LabelMe 输出的是 JSON每个 shape 里有points数组。转换脚本的核心逻辑是读取 JSON对每个 shape 提取label和points归一化坐标写入 txt。下面是一个可以直接用的转换脚本import json import os from pathlib import Path def labelme_to_yolo_seg(json_path, output_dir, class_map): 将 LabelMe JSON 转为 YOLOv11 seg 格式的 txt class_map: {person: 0, car: 1, ...} with open(json_path, r, encodingutf-8) as f: data json.load(f) img_w data[imageWidth] img_h data[imageHeight] lines [] for shape in data[shapes]: label shape[label] if label not in class_map: continue cls_id class_map[label] points shape[points] # 归一化并展平 norm_points [] for x, y in points: norm_points.append(str(round(x / img_w, 6))) norm_points.append(str(round(y / img_h, 6))) lines.append(f{cls_id} .join(norm_points)) out_path Path(output_dir) / (Path(json_path).stem .txt) with open(out_path, w, encodingutf-8) as f: f.write(\n.join(lines)) # 使用示例 class_map {defect: 0, scratch: 1} for jf in Path(labels_json).glob(*.json): labelme_to_yolo_seg(str(jf), labels_txt, class_map)逻辑说明class_map必须和你的data.yaml里的names顺序一致否则训练时类别会错位。round(x / img_w, 6)保留六位小数YOLOv11 官方推荐至少四位六位更稳。如果多边形点数少于 3 个LabelMe 里可能是矩形标注这种要单独处理成四个点否则 YOLOv11 会报polygon must have at least 3 points。参数说明output_dir要和图像目录同级YOLOv11 默认在images同级的labels目录找标签。如果你的目录结构是dataset/images/train/和dataset/labels/train/那output_dir就指向dataset/labels/train/。2.2 data.yaml 里 seg 任务的关键字段YOLOv11 的data.yaml和检测任务基本一样但有一个隐藏坑task字段必须显式写成segment否则 Ultralytics 会按检测任务加载分割头不会被初始化。下面是一个最小可用的配置# dataset/data.yaml path: /home/user/dataset train: images/train val: images/val test: images/test task: segment names: 0: defect 1: scratch 2: stainpath是数据集根目录train和val是相对路径。task: segment这个字段在官方文档里提得不多但如果你用yolo train命令时没有指定tasksegment模型会默认加载检测权重分割分支的 loss 不会回传。我一般会在训练命令里也显式带上tasksegment双保险。2.3 训练命令与多任务损失权重YOLOv11 的seg训练命令和检测几乎一样只是模型换成-seg后缀yolo segment train \ modelyolo11s-seg.pt \ datadataset/data.yaml \ epochs150 \ imgsz640 \ batch16 \ device0 \ tasksegment \ projectruns/seg_train \ nameexp01 \ patience30 \ lr00.01 \ lrf0.01 \ mosaic1.0 \ copy_paste0.3关键参数说明copy_paste0.3是分割任务特有的增强它把一张图里的实例复制粘贴到另一张图对分割掩码的边界学习很有帮助但值太高会导致过拟合0.1 到 0.3 之间比较稳。mosaic1.0是默认值如果小目标多可以保持如果大目标为主可以降到 0.5。lr00.01是初始学习率lrf0.01是最终学习率因子YOLOv11 默认用余弦退火这两个值一般不用大改。训练时看什么看train/seg_loss和train/box_loss的下降曲线。如果seg_loss震荡厉害而box_loss平稳说明分割头的梯度被检测头压制了可以尝试调低box的损失权重——但 YOLOv11 没有直接暴露这个超参常见做法是冻结 backbone 前几层先训分割头几个 epoch再解冻联合训练。这个技巧在工业缺陷分割里很管用因为缺陷的边界往往比整体轮廓更重要。3. 推理与后处理一次前向拿到框和掩码3.1 Python 推理接口与结果解析训练完之后推理接口和检测任务几乎一样只是返回的Results对象里多了masks字段from ultralytics import YOLO import cv2 import numpy as np model YOLO(runs/seg_train/exp01/weights/best.pt) results model.predict( sourcetest_images/, imgsz640, conf0.25, iou0.45, retina_masksTrue, # 关键返回原图分辨率掩码 saveTrue, projectruns/seg_infer, nametest01 ) for r in results: boxes r.boxes.xyxy.cpu().numpy() # N x 4 masks r.masks.data.cpu().numpy() # N x H x W classes r.boxes.cls.cpu().numpy() # N confs r.boxes.conf.cpu().numpy() # N # 把掩码叠加到原图 img r.orig_img for i, mask in enumerate(masks): mask_resized cv2.resize(mask, (img.shape[1], img.shape[0])) color np.random.randint(0, 255, 3).tolist() img[mask_resized 0.5] img[mask_resized 0.5] * 0.5 np.array(color) * 0.5 cv2.imwrite(foverlay_{r.path.split(/)[-1]}, img)逻辑说明retina_masksTrue是关键参数。默认情况下 YOLOv11 返回的掩码是 160x160 的原型掩码需要上采样到原图尺寸。开启retina_masks后直接返回原图分辨率的二值掩码省去手动 resize 的麻烦但显存占用会高一些。如果部署在边缘设备上建议关掉retina_masks在 CPU 上用cv2.resize做上采样反而更省显存。参数说明conf0.25和iou0.45是通用起点。分割任务里iou对掩码的 NMS 影响不大因为掩码 NMS 用的是框的 IoU但conf太低会导致大量碎片掩码后处理时可以用面积过滤mask.sum() 100的掩码直接丢掉。3.2 掩码后处理的三个实用技巧第一个技巧是掩码面积过滤。YOLOv11 的seg模型在小目标上容易产生噪点掩码面积小于 50 像素的基本可以认为是误检。第二个技巧是掩码与框的对齐检查如果掩码的质心偏离检测框中心超过框宽度的 30%这个掩码大概率是错的可以丢弃。第三个技巧是多边形简化如果下游需要矢量边界用cv2.findContours加cv2.approxPolyDP把掩码转成多边形epsilon设为轮廓周长的 0.002 到 0.005 之间既能压缩点数又不丢形状。import cv2 import numpy as np def mask_to_polygon(mask, epsilon_ratio0.003): 将二值掩码转为简化多边形 mask_u8 (mask * 255).astype(np.uint8) contours, _ cv2.findContours(mask_u8, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) polygons [] for cnt in contours: if cv2.contourArea(cnt) 50: continue epsilon epsilon_ratio * cv2.arcLength(cnt, True) approx cv2.approxPolyDP(cnt, epsilon, True) polygons.append(approx.reshape(-1, 2)) return polygons这个函数返回的多边形点可以直接喂给标注工具做复核或者存成 JSON 给下游的测量模块用。epsilon_ratio越大多边形越简单但边界越粗糙。工业测量场景建议 0.001 到 0.002可视化场景 0.005 就够。3.3 导出 ONNX 和 TensorRT 时的分割头处理YOLOv11 的seg模型导出 ONNX 时输出节点比检测模型多一个output1形状是[1, 32, 160, 160]这是原型掩码。TensorRT 部署时要注意如果只用检测结果可以只解析output0如果要分割掩码需要把output1和检测框的掩码系数做矩阵乘法再上采样。Ultralytics 的export命令会自动处理这些yolo export modelbest.pt formatengine halfTrue device0 imgsz640导出 TensorRT 引擎时halfTrue开启 FP16分割掩码的精度损失通常在 1% 以内但速度提升明显。如果发现掩码边缘出现锯齿可以关掉half用 FP32 对比一下确认是量化误差还是模型本身的问题。4. 避坑与排查多任务训练里最容易翻车的五个地方4.1 现象训练 loss 正常下降但掩码全是背景原因data.yaml里task字段没写或者写成了detectYOLOv11 按检测任务加载分割头虽然存在但 loss 权重为 0梯度不回传。解决检查data.yaml和训练命令里是否都有tasksegment缺一不可。4.2 现象推理时r.masks为 None原因模型加载的是检测权重而不是分割权重。YOLO(yolo11s.pt)加载的是检测模型YOLO(yolo11s-seg.pt)才是分割模型。解决确认模型文件名带-seg后缀或者训练时用了tasksegment。4.3 现象小目标的分割掩码断裂成几块原因YOLOv11 的原型掩码分辨率是 160x160小目标在原型掩码上只占几个像素上采样后边界不连续。解决训练时把imgsz提到 1024 或 1280或者用retina_masksTrue推理但后者不解决训练时的分辨率瓶颈。更彻底的做法是修改分割头上采样倍数但这需要改模型结构不建议新手动。4.4 现象copy_paste增强导致掩码和框不对齐原因copy_paste在复制实例时如果两张图的尺度差异大粘贴后的掩码坐标没有同步缩放。解决把copy_paste降到 0.1 以下或者改用copy_paste_modeflip这个模式只做翻转粘贴尺度一致性更好。4.5 现象TensorRT 推理时掩码全黑原因导出 ONNX 时没有指定tasksegment或者 TensorRT 解析时只取了output0。解决导出命令加tasksegmentTensorRT 推理脚本里同时解析output0和output1按官方后处理逻辑做矩阵乘法。5. 把多任务框架压到 30 FPS一个 Jetson 上的调优习惯在 Jetson Orin Nano 上部署yolo11s-seg默认 FP32 推理大概 12 到 15 FPS离 30 FPS 还有距离。我一般会按这个顺序调先导出 TensorRT FP16 引擎FPS 能到 22 左右再把imgsz从 640 降到 512FPS 到 28 但小目标掩码质量下降明显最后把retina_masks关掉掩码上采样放到 CPU 上用cv2.resize做FPS 稳定在 31 到 33 之间CPU 占用增加不到 10%。这个组合在工业质检的传送带场景里够用了掩码边缘的轻微锯齿不影响面积测量。验证方法很简单用trtexec跑 benchmark 看纯推理延迟再用实际视频流跑端到端延迟。如果trtexec显示 25ms 但端到端 40ms瓶颈大概率在前后处理这时候把预处理从 Python 换成 C 或者 CUDA kernel能再挤出 5 到 8ms。我习惯在调优前先跑一遍nsys profile看清楚时间花在哪再决定动哪里不然容易白忙活。最后说一个血泪教训多任务模型的权重不要和检测模型混用。我有一次直接把yolo11s.pt的权重加载到seg模型里做初始化结果分割头随机初始化训练了 50 个 epoch 掩码 mAP 还是 0.1。后来换成yolo11s-seg.pt预训练权重10 个 epoch 就到 0.45 了。预训练权重的选择比超参调优重要得多这个后悔药我吃过一次就够了。希望帮到你。本文还有配套的精品资源点击获取