YOLOv11多任务改造:检测分割计数一体的工程实践

YOLOv11多任务改造:检测分割计数一体的工程实践 简介这份资源面向计算机视觉开发者和算法工程师是一份围绕YOLOv11同时实现目标检测、图像分割与目标计数的联合训练技术文档。PDF共48页涵盖了YOLOv11的网络结构、多任务框架设计、三大任务分支的损失函数、数据集标注与预处理、联合训练与优化策略以及实验结果分析和交通、工业、安防、农业、医疗、零售等应用案例。资源为一个PDF文件压缩包大小约2.2MB支持目录章节跳转并可在阅读器左侧大纲中快速定位各章节。目前已有123人学习下载。文档将检测、分割、计数整合到同一框架中细致拆解了任务相关性、共享特征层与融合策略有助于开发者降低多模型部署成本提升多任务视觉方案的研发效率。1. 多任务框架为什么值得为 YOLOv11 做一次改造做工业视觉的同行应该都有同感一个看似简单的质检或统计需求拆开以后往往同时需要目标检测、实例分割和数量统计。比如产线上的工件计数光知道“有几个”不够还得标出每个工件的位置和像素轮廓用来计算缺陷面积或判断工件是否变形。传统做法是部署两个甚至三个独立模型检测一个、分割一个、计数再写一段后处理。模型一多显存、延迟、标注成本和维护成本都是问题更深层的问题在于三个模型各说各话——分割结果和检测框对不上计数又基于两套坐标体系联合调试时非常痛苦。多任务联合训练的思路是把检测、分割、计数放进同一个 YOLOv11 框架里共享主干特征分别输出边界框、掩膜和密度图。这么做最直接的好处有三个一是推理时只需一次前向延迟和显存消耗都大幅下降二是分割和计数可以借用检测分支的语义特征小目标场景下往往比单独训练更稳三是标注数据可以异构使用有框的图贡献给检测有掩膜的图贡献给分割不必等全部标注齐了才开工。本文从网络结构设计、损失函数平衡、训练工程细节到部署验证把这套方案完整拆开讲。2. 做一个多任务 YOLOv11先想清楚结构怎么改2.1 YOLOv11 做多任务基座的优势共享主干与任务冲突的边界YOLOv11 的设计对多任务本身是友好的。它的主干里 C3k2 模块和 SPPF 结构负责多尺度特征提取Neck 部分用 PAN-FPN 把高层语义和低层纹理融合到一起天然为检测和分割两类任务提供了共同的中间表示。分割任务需要高分辨率的空间信息检测任务需要语义明确的区域特征这两者在 FPN 的 P3、P4、P5 层级上正好可以各取所需。计数任务则更特殊它需要的不是某一个层级的特征而是对密度和空间分布的全局感知因此通常放在 FPN 融合后的高层特征上。不过共享主干也不是没有代价。检测头关注的是“哪里是目标、边界在哪”分割头关注的是“像素属于哪个目标”计数头关注的是“单位区域里有多少个目标”。三个优化目标共享一套特征如果某一分支的 loss 量级过大梯度就会把共享特征往一个方向猛拉其他分支的性能就会明显下降。这就是多任务学习的“跷跷板”效应。后文会给出通过损失权重动态调度来压制这一效应的方法但结构上首先要避免从同一个特征层直接接出三个头而是让分割和计数分别从不同层级引出减少梯度对流冲突。2.2 三种输出头的常见设计方案与返回格式对比常见的多任务头设计是检测头沿用 YOLOv11 的解耦头输出边界框坐标和类别概率分割头在 P3 层上并行一个掩膜分支输出原型掩膜prototype masks和掩膜系数解码时两者做矩阵乘法再上采样得到逐像素掩膜结果。计数头则有两条技术路线可选一种是直接在高层特征上回归密度图另一种是在检测结果上做实例嵌入embedding后去重计数。两者各有适用场景对比如下计数方案依赖分支对拥挤场景的适应性额外标注成本密度图回归独立计数头强适合小目标密集场景需要点标注或密度图标注检测后计数检测头弱目标重叠时易漏检无需额外标注实例嵌入计数分割检测头中等依赖掩膜质量需要实例标注实际项目里我一般会优先选择密度图回归作为计数分支尤其是目标成片出现、互相遮挡严重的场景。密度图对“有多少个”非常敏感而对“准确边界在哪”不敏感这与分割头对边界的高要求形成互补梯度的竞争也要温和很多。图中右侧展示的是典型输出结构左侧输入图像经 Backbone 和 Neck 提取特征后三个头并行输出 bbox 列表、mask tensors 与 density map最终由后处理统一整理成结构化结果。2.3 原型掩膜与密度图的联合学习思路在 YOLOv11 里做分割头最常见且可靠的方案是 YOLOv8-seg 以来的原型掩膜思路模型在 P5 低分辨率层上预测一组与类别无关的原型掩膜然后在检测分支的解耦头里额外回归每个实例的掩膜系数用矩阵乘法把系数与原型组合起来。这个设计的好处是很自然地复用了检测头的锚框与类别信息掩膜系数只负责“组合”不需要独立预测整个像素空间。在多任务框架里我把密度图回归头挂在 P4 层之后用一个 1×1 卷积将特征压缩为单通道密度响应避免高分辨率层上产生过多的参数同时把密度头的感受野控制在接近目标平均尺寸的范围内。2.4 各头损失函数的组合与梯度冲突的控制联合训练时损失函数由四部分组合检测框损失 Lbox、分类损失 Lcls、掩膜损失 Lmask 和密度图损失 Ldensity。其中 Lbox 常用 CIoU 或不带 anchor 的分布损失Lcls 用 BCELmask 用带 logits 的逐像素 BCELdensity 则用 MSE 或更具结构相似度的 SSIM 类损失。总损失的计算方式是加权求和但权重并不是固定的具体的动态调度算法需要单独实现细节放到下一章。梯度冲突的控制另一个有效手段是梯度裁剪与梯度冻结前 20 个 epoch 只训练 Backbone 和检测/分割头密度头暂不参与反向传播等主干特征相对稳定后再激活计数分支能明显减少前期训练崩溃的概率。3. 联合训练的实现细节损失权重调度与最小可训练代码3.1 多任务检测头、分割头与计数头的 PyTorch 最小实现为了说明架构组织方式这里给一个最小化示例。实际使用时需要把类注册到 YOLOv11 的检测框架中但核心结构是一致的import torch import torch.nn as nn class MultiTaskHead(nn.Module): def __init__(self, in_channels, num_classes, num_prototypes32): super().__init__() # 检测头输出边界框与类别概率 self.det_head nn.Sequential( nn.Conv2d(in_channels, 64, 3, padding1), nn.BatchNorm2d(64), nn.SiLU(inplaceTrue), nn.Conv2d(64, 4 num_classes, 1) # 4个回归量 类别数 ) # 分割头输出原型掩膜 self.seg_head nn.Sequential( nn.Conv2d(in_channels, 64, 3, padding1), nn.BatchNorm2d(64), nn.SiLU(inplaceTrue), nn.Conv2d(64, num_prototypes, 1) ) # 计数头输出密度图单通道 self.count_head nn.Sequential( nn.Conv2d(in_channels, 128, 3, padding1), nn.BatchNorm2d(128), nn.SiLU(inplaceTrue), nn.Conv2d(128, 1, 1) ) def forward(self, x): det_out self.det_head(x) proto self.seg_head(x) density self.count_head(x) return det_out, proto, density这个实现的要点有三个检测头的输出通道是4 num_classes前四个通道回归中心点坐标和宽高分割头输出原型掩膜通道数由 num_prototypes 控制计数头直接输出单通道密度图大小与输入特征图一致。实际使用时检测和分割头会分别放在 FPN 的不同层级上并把 mask coefficients 从检测特征中分离出来而不是像这里一样把所有任务都挂在同一个特征图上。3.2 损失权重的动态调度从固定权重到自适应加权固定权重最省事但效果很难调到最优。密度图损失的量级通常远小于检测框损失因为密度值在 0 到 1 之间的像素很多Sigmoid 输出天然被压缩而 CIoU 损失在训练初期轻松到 10 以上。直接把权重固定为 1密度分支几乎学不到东西。常见的自适应加权策略有两种uncertainty-based weighting 和 gradient normalization前者实现简单后者对多任务收敛更均匀。核心思路是每个分支的可学习参数 sigma 控制该分支在总损失中的权重当某个分支不确定性高训练波动大时其贡献自动降低。实现如下class AdaptiveWeightedLoss(nn.Module): def __init__(self, num_tasks3): super().__init__() self.log_sigma nn.Parameter(torch.zeros(num_tasks)) def forward(self, losses): precision torch.exp(-self.log_sigma) weighted precision * losses self.log_sigma return torch.mean(weighted) # 用法示例 loss_fn AdaptiveWeightedLoss(3) total_loss loss_fn(torch.stack([box_loss, mask_loss, density_loss]))这段代码里每个任务都有一个可学习的 log_sigma。当某个任务 loss 很高时梯度会让它的 sigma 变大从而降低 precision抑制该任务在当前 step 的影响反之 loss 已经很低的任务会被赋予更高权重继续优化。这个机制适合在主干已经趋于稳定后开启如果一上来就在多任务框架里用前几十个 epoch 的权重更新会很混乱因为检测分支还在学 anchor 匹配密度分支却已经提前收敛。3.3 训练启动参数与多分支学习率的差异设置不同头的收敛速度差异很大检测头需要较多 epoch 才能把边界框回归稳定下来分割头在高分辨率特征上收敛较快密度头则需要更高的学习率才能在稀疏标注下学会有效响应。实际训练时我会为三个头设置不同的 lr 倍率配置如下python train.py \ --model yolov11_multitask.yaml \ --data dataset/coco_multi.yaml \ --epochs 300 \ --batch-size 16 \ --lr 0.001 \ --lr-gamma 0.1 \ --lr-decay-epochs 180,240 \ --multitask --multi-lr [det_head:1.0,seg_head:1.2,count_head:2.0] \ --loss-weight [box:1.0,cls:0.5,mask:0.8,density:0.03]其中的 multi-lr 参数需要在训练代码里通过参数组param_group实现把不同头对应的模型参数放进不同的参数组设置不同的初始学习率。密度头的 lr 是检测头的两倍是因为密度图回归对梯度响应比较钝在小目标密集的场景尤其如此。另一个需要注意的参数是 loss-weight 中的 density初期设得很小等检测和分割的损失能下降到正常量级后再逐步把 density 的权重调上去。如果一开始就给很大密度权重模型会倾向于把整个特征图都预测成大致均匀的密度计数反而失去空间准确性。3.4 训练日志里该盯的几个关键指标多任务训练比单任务更容易被“表面收敛”欺骗。只看总损失下降是不够的因为某一个分支的 loss 可能会掩盖另一个分支的停滞。我一般会在日志里单独输出每个分量的损失并在每个 epoch 结束时跑一次验证集记录 mAP50、mAP50-95、mask IoU 和 MAE 四个指标。如果出现检测 mAP 还在涨但分割 mask IoU 停滞不前的现象需要先确认是不是 mask 分支的学习率太低检查该分支的梯度范数是否远小于其他分支。如果 density MAE 前期不降偶尔还会上升但要先判断训练是否处于 warmup 阶段此时主干还在剧烈变化密度层的参数被反复冲刷不一定代表方案有问题。等主干稳定后再观察 20 个 epoch 的提升趋势。4. 数据组织、增强策略与训练排错4.1 异构标注数据的组织方式检测、分割、计数各取所需多任务联合训练最大的红利之一是可以把不同来源的数据集放到同一个训练流程里。有些历史数据只有目标框标注没有掩膜有些数据则有点标注可以直接生成密度图。组织数据时要避免的坑是强制所有数据都具备三种标注这会让数据准备成本剧增。正确做法是为每个样本维护一个标注掩码指明当前样本可用的标注类型损失计算时只计算有效部分。例如 mask 损失只在具有实例掩膜的样本上回传梯度密度损失只在有点标注或密度图标注的样本上回传。代码里对应的处理方式是为每张图的 label dict 里增加usable_tasks字段训练循环里据此 mask 掉对应 loss。4.2 数据增强的取舍Mosaic、Copy-Paste 与多尺度训练检测任务里 Mosaic 增强效果很好但对多任务框架未必全是正面。Mosaic 拼接四张图缩小了目标相对尺寸对小目标检测有帮助可分割任务需要像素级的空间连续性拼接后图像在边界处会出现语义突变掩膜分支很容易在这些位置产生误分割。多任务框架里我建议对 Mosaic 做降级使用前 150 个 epoch 开启 Mosaic进入后 150 个 epoch 后关闭改用 Copy-Paste 和随机裁剪的组合。Segment 相关的增强比如随机旋转、缩放、色彩抖动保留但对于密度计数分支过强的几何增强会让密度图计算失真因为旋转后目标数量没变但空间密度分布变了标注的密度值却仍是对原图计算得到的因此在增强时对 count_head 的输出需要同步做仿射变换。对应到代码上就是在构建增强 pipeline 时将 count_head 用 warpaffine 与图像同时变换。4.3 类别不平衡与部分目标漏标问题多任务学习里类别不平衡更加隐蔽因为三个任务的样本分布可能是错位的。一个目标同时有框、掩膜和密度标注的数据永远只是少数大量数据只有一个或两个任务可用。容易出的问题来自漏标比如只有检测标注的数据里某些目标没有标记对计数分支来说该区域密度为零但模型实际上看到了目标。这会把密度分支的训练信号污染得很厉害。有一个不彻底的解法是将只有检测框的样本也生成一个粗糙的密度图用高斯核在框中心点打一个峰值标注质量虽然下降了但梯度方向整体仍然正确。这比让密度分支完全忽略这些样本要好因为完全忽略意味着密度分支只在一小半数据上训练很容易过拟合。4.4 多任务训练失败的常见表现与排查顺序训练中遇到的最典型问题有三个。第一是检测分支正常收敛但分割分支预测的全是背景先检查 seg_head 所在层的梯度是否有效再确认 mask 损失是否被 loss-weight 压得过低。第二是密度图输出了整片高亮或全黑这通常是密度损失和检测损失量级差异过大导致密度分支参数分布极端建议把密度输出加一个 Sigmoid 层并且调大权重。第三类是训练后期出现检测 mAP 和分割 IoU 一起掉线往往是总学习率衰减后所有分支的更新都变小但多任务里某个分支的损失还在高位此时不要盲调总 lr单独为该分支提高学习率倍率更有效。排查时按梯度范数、分支 loss 曲线、验证集指标的顺序走先定位是网络结构问题还是优化策略问题。有条件的场景可以选用自动化剪枝来缩小模型体积但要在训练完成之后做不要和联合训练同时进行。多任务本身的约束就已经很多此时再加上剪枝引入的精度损失很难判断是哪个改动导致了退化。5. 计数精度的验证方法与部署优化5.1 计数准确度评估指标的三种计算方式联合训练框架下计数任务最终输出的是一个密度图验证时需要把密度图转换为数量值。最直接的方法是对密度图逐像素求和后取整但这会受到背景噪声影响。更稳定的是先对密度图做阈值化处理过滤掉低于置信阈值的响应再求和。评估指标上我用 MAE 和 RMSE 两个指标前者反映平均计数偏差后者对大误差更敏感。如果督场景里关注的是小目标密集区还建议加上按区域划分的归一化误差即把图像分成若干网格分别计算每个网格的计数误差并求平均。训练结束后统一用一段独立验证脚本来跑避免在训练循环里重复计算影响效率。以下是我常用的验证片段import numpy as np def count_metrics(pred_density, gt_density): pred_count pred_density.squeeze().sum() gt_count gt_density.squeeze().sum() abs_err np.abs(pred_count - gt_count) mae abs_err.mean() rmse np.sqrt((abs_err ** 2).mean()) return mae, rmse # 在验证集上逐张调用 for img, gt in val_loader: pred model(img)[2] # 计数头输出 mae, rmse count_metrics(pred, gt)5.2 多任务推理导出TensorRT 与多输出节点的处理技巧部署阶段多任务模型导出时最麻烦的是多输出节点的命名和 shape 处理。以 TensorRT 导出为例模型输出至少有三个张量N x 4 x 8400 的检测输出、N x 32 x 160 x 160 的原型掩膜或直接解码后的 mask、以及 N x 1 x H x W 的密度图。导出时建议把后处理逻辑简化直接输出原始张量让端侧代码做解码。密度图输出时通常不需要保持 float32转为 float16 后对计数精度影响不大但显存占用直接减半。如果帧率要求很高可以将密度图输出尺寸下调四倍即只在 P5 层上生成低分辨率置信图再在端侧用双线性插值放大到原图尺寸。这样做计数误差会略微上升但对大多数变电站巡检、园区车辆统计这类场景完全够用。5.3 数据配比独立验证一次控制一个变量最后一个值得强调的技巧是对多任务数据配比单独做消融实验。训练前我会把数据分成三组只有框标注、框加掩膜、框加密度点。先用框标注单独把检测头训练 50 个 epoch再依次加入掩膜和密度标注。每加一组数据就对四个指标做一次完整验证。之前遇到过的一个情况加密度标注后整体计数 MAE 下降了掩膜 IoU 和检测 mAP 都掉了看起来像是数据变多反而联合训练效果变差。排查后发现问题出在密度标注的样本大多是密集场景图像整体质量偏低和原数据分布不一致。把密度标注按场景比例下采样后重新训练三个任务的指标才回到同一量级。多任务框架里训练集的构成即是超参数值得用最严谨的方式对待。本文还有配套的精品资源点击获取