语义分割与实例分割模型实战:从技术脉络到部署优化 最近把之前的项目归档翻出来文件夹里编号018正好是图像分割相关的实践记录语义分割与实例分割模型。这个题目看似老生常谈但真要在实际项目里跑通、跑稳、跑出能用的结果还是有不少门道。我这次就把当时踩过的坑、对比过的方案、留下的关键结论整理成文也顺带把两类分割任务的技术脉络梳理一遍方便后来者少走弯路。图像分割是计算机视觉里承上启下的一个方向它比分类更精细比检测更彻底。分类告诉你图里有什么检测告诉你东西在哪儿分割则直接给每个像素一个归属。语义分割把整个画面按类别划分实例分割在语义的基础上还把同类物体的不同个体切开。两者在自动驾驶、医疗影像、遥感分析、工业质检、视频编辑等领域都有大量应用也是目前很多算法工程师入门深度学习视觉方向时绕不开的课题。这篇文章适合谁看对我来说读者大概是三类人一是刚接触分割任务、想系统搞懂语义分割和实例分割区别与模型选型的学生或初级工程师二是已经跑通基础分类检测模型、准备往分割方向扩展的开发者三是项目里需要落地分割能力、正在纠结模型和部署方案的技术负责人。内容从概念、模型演进、数据与评估、实操细节一直聊到部署优化兼顾原理和工程实践。1. 语义分割与实例分割一字之差解决的问题完全不同很多人一开始会把语义分割和实例分割混为一谈毕竟名字都带“分割”。但从任务定义、输出形式到模型结构设计两者的差异非常大。理解的偏差会直接导致你在选模型、定指标、设计后处理时做出一连串错误判断所以这一块值得花点篇幅讲透。1.1 语义分割到底在分割什么语义分割的输入是一张图像输出是一张与输入同尺寸的“类别标签图”。也就是说图像里的每一个像素都会被赋予一个类别编号比如0代表背景1代表道路2代表行人3代表车辆。最终的可视化效果通常是一张把不同类别涂成不同颜色的图。这里最关键的点是语义分割不区分个体。如果画面里有三辆并排停着的车语义分割只会把所有属于“车”的像素都标成同一个类别标签不会告诉你这是第几辆车也不会在车与车之间画一条边界。它更像是一种“像素级分类”本质上是把图像分类任务从整图级别下探到了像素级别。这也决定了它的评判标准你关心的是每个像素的类别是否正确至于有多少个物体、物体之间怎么划分那不是它的职责。因此语义分割的标签制作相对简单标注时只需要按照类别把区域涂出来就行不需要逐个物体去标注。在工程上语义分割被大量用于“按区域理解场景”的任务。比如自动驾驶里的可行驶区域检测、遥感图像里的土地利用分类、医疗影像里的器官或病灶区域提取。这些场景下我们并不需要数清楚到底有几个器官或者几块病灶只需要把“哪些像素属于这个类别”搞清楚就够了。1.2 实例分割比语义分割多了什么实例分割要做的事就更多了。它不仅要判断每个像素属于哪个类别还要区分同类别里的不同个体。还是以三辆车为例实例分割会给第一辆车标上实例编号1第二辆车标上2第三辆车标上3即使它们的类别都是“car”也不能合并成一个区域。从输出的形式来看实例分割通常是“每个实例一个二值掩码”再加上这个实例的类别和置信度。在数据结构上它更接近目标检测加分割的混合体既要像检测一样输出边界框和类别又要像分割一样输出像素级的掩码。因此实例分割的标签制作成本比语义分割高一个量级。每一张训练图像上你需要对每个可见的独立物体单独标注轮廓。这个工作量在大规模数据上非常可观也是为什么很多实际项目中实例分割数据集的规模比语义分割小得多模型的鲁棒性也更难保证。从模型角度说实例分割通常需要额外的“区分实例”机制。常见的做法分两类一类是基于检测框的先检测出每个实例的边界框再在框内做掩码预测代表就是 Mask R-CNN另一类是基于像素聚类的直接预测每个像素的嵌入向量然后通过聚类把像素划分成不同实例代表是近期基于 Transformer 的掩码方案。两类路线各有取舍后面我再详细展开。1.3 全景分割顺带一提的第三种形态实际做项目时还会碰到一个叫“全景分割”的术语。全景分割想把语义分割和实例分割统一起来对背景类如道路、天空、草地做语义分割对前景类如人、车、动物做实例分割。输出是一张图背景是类别标签前景是实例标签彼此互不重叠。虽然这个项目标题只提到语义分割和实例分割但在很多开源数据集和论文里全景分割经常作为统一模型的评测任务出现。比如某个模型号称同时解决了语义和实例分割最终评测也常放在全景分割上。了解这个概念至少你在看论文和开源代码的时候不会懵。从实际开发者的角度我建议先把语义分割和实例分割分开理解透彻再考虑统一建模。一上来就追求“一个模型搞定所有任务”往往会给自己增加不必要的复杂度。2. 从FCN到Transformer分割模型技术路线梳理分割模型这几年演进极快从最初的暴力滑窗分类思路到全卷积网络的出现再到编码器-解码器、空洞卷积、多尺度特征融合以及最近的 Transformer 和掩码学习每个阶段的模型设计思想都对工程实践产生了深远影响。下面我按技术脉络梳理会重点说每个方案的适用场景和坑点。2.1 全卷积里程碑FCN为什么能端到端在 FCN全卷积网络出现之前做像素级分类最原始的办法是滑窗法把图像裁成小 patch逐个交给分类网络判断中心像素类别然后再滑到下一个位置。这种方法效率极低而且感受野有限边缘区域的分类效果很差。FCN 的核心改动是把分类网络末端的全连接层全部换成卷积层。这样一来网络不再要求输入固定尺寸输出也不再是类别概率而是一张具有空间结构的特征图。配合上采样操作就能直接输出像素级的预测图。FCN 第一次让分割任务实现了真正的端到端训练。训练时输入图像、输出分割图损失函数直接计算预测图和标注图之间的差异梯度回传更新整个网络。这个概念在现在看来平平无奇但在当时是非常革命性的。不过 FCN 的缺点也很明显上采样倍数太大细节信息丢失严重分割边界经常很粗糙。而且它没有充分考虑多尺度信息小物体容易漏掉。后来的分割模型很大程度都在解决这两个问题。在实际工作中现在已经很少有人直接拿 FCN 当基线了但理解了 FCN 的端到端思想再去看后面所有分割模型都会觉得顺理成章。2.2 U-Net与编码解码结构小样本分割的常青树U-Net 最初是医学图像分割领域的明星模型后来被广泛用到各类分割任务里即使现在很多新方法对比也依然拿 U-Net 当一个非常能打的基线。U-Net 的结构是一个对称的 U 形。左侧是编码器通过卷积和下采样逐步提取深层语义特征右侧是解码器通过卷积和上采样逐步恢复空间分辨率。关键设计在中间的“跳跃连接”编码器每层的特征图会被直接拼接到解码器对应层。这样做的好处是解码器既能拿到深层的语义信息又能拿到浅层的细粒度边缘信息最终的分割结果在边界保持上明显更好。为什么 U-Net 在小样本场景特别强医学图像、工业缺陷这类任务通常数据量不大几万张都算多的几千张甚至几百张也常见。U-Net 的参数量适中结构简单不容易过拟合同时跳跃连接带来的梯度传递非常友好训练相对稳定。这些特性让它成为小样本分割任务的第一选择。我自己在项目中的体会是如果数据量不大、目标类别清晰、不需要区分实例直接上 U-Net 或它的变体比如带残差模块或者注意力模块的版本往往是最省时间、效果也最可控的方案。后面我会在实操部分再讲它的具体配置。2.3 DeepLab系列空洞卷积与ASPPDeepLab 系列是另一条重要的技术路线它不靠跳跃连接而是靠空洞卷积来扩大感受野。空洞卷积可以在不降低特征图分辨率的前提下让卷积核覆盖到更大范围相当于在卷积核内插入空洞。DeepLab 系列里最具代表性的设计是 ASPP空洞空间金字塔池化。它用多个不同空洞率的并行卷积分支分别以不同尺度提取特征然后拼接融合。这个设计让网络能够同时捕获大物体和小物体的上下文信息在多尺度场景下效果很好。我曾经在一次道路场景分割实验中对比过 U-Net 和 DeepLabV3。道路场景的类别包括路面、车道线、行人、车辆、交通标志等尺度差异非常大。结果 DeepLabV3 在整体 mIoU 上略胜一筹尤其对远距离小物体和路缘边界预测更加稳定。但它的模型体积和推理耗时也更大部署时需要做量化或者选轻量骨干网络。DeepLab 系列的骨干网络一般用残差网络实际项目中根据算力可选 ResNet-50、ResNet-101或者 MobileNet 版的 DeepLabV3后者在移动端部署时非常常见。2.4 Mask R-CNN目标检测延伸出的实例分割前面几条路线都是语义分割的模型到了实例分割最经典的绕不开 Mask R-CNN。Mask R-CNN 的架构可以理解为“Faster R-CNN 加一个掩码分支”。Faster R-CNN 负责检测出图像里每个实例的类别和边界框Mask R-CNN 在检测的基础上额外为每个候选框内的区域预测一个二值掩码。也就是说它先用检测的方式把实例“框”出来再在框内做分割。关键细节在于 RoIAlign。Faster R-CNN 用的是 RoIPooling这个过程会做取整操作导致空间位置精度损失。Mask R-CNN 改用 RoIAlign用双线性插值来采样特征几乎没有位置信息的量化误差因此掩码预测的边界准确度大幅提升。Mask R-CNN 的输出天然是“框 类别 掩码 置信度”这样的结构化结果工程上非常友好。部署时你不需要额外写聚类逻辑拿到输出直接按置信度和类别过滤就行。这个特性让 Mask R-CNN 很长一段时间内都是工业落地里实例分割的事实标准。不过 Mask R-CNN 也有自己的短板速度偏慢因为检测分支本身计算量就大再加上逐框掩码预测推理效率远不如纯语义分割模型。如果项目中的实例数量特别多比如密集人群、密集货架上的商品逐实例预测掩码的开销会非常高。针对这个问题后续出现了很多基于点、基于聚合的替代方案但 Mask R-CNN 作为理解和落地的起点地位依然稳固。2.5 新一代Transformer与掩码学习最近几年Transformer 结构席卷 CV 领域分割任务也未能幸免。以 Mask2Former 为代表的模型把语义分割、实例分割、全景分割统一成了“掩码分类”任务不再区分是语义还是实例而是让模型直接预测一组二值掩码并为每个掩码预测一个类别标签。如果你熟悉 DETR 这类基于查询的目标检测模型会发现掩码学习的思路非常相似。模型先定义一组可学习的对象查询每个查询通过注意力机制从特征图中“读出”一个物体区域最终输出掩码和类别。训练时的二分图匹配决定了哪个查询负责哪个物体推理时则根据置信度筛选。这套方案的优势是结构统一一个模型通过不同的查询配置就能完成所有分割任务。劣势是训练难度更大对显存要求高调参经验门槛也更重。我自己的实践感受是如果你刚接触分割任务不要一上来就挑战这类模型先把经典模型跑明白再尝试新范式也不迟。从工程选型的角度看应该依据项目需求倒推模型纯语义、少量类别、追求速度优先 U-Net 或 DeepLabV3实例分割且需要结构化输出优先 Mask R-CNN追求最前沿效果且算力充裕再考虑 Transformer 方案。盲目追新是最常见的成本黑洞。3. 数据与评估指标分割任务里容易被低估的两件事分割模型训练中数据和评估指标的影响往往比模型结构更大。我见过不少团队在模型选型上反复折腾却不重视数据质量结果换哪个模型效果都差不多。这里专门把数据标注、评估指标和工具链梳理一遍。3.1 数据标注的细节与格式语义分割和实例分割的数据标注是两种完全不同的工作流。语义分割标注时标注者通常使用多边形工具或者画笔工具在图像上勾勒出每个类别的区域。标签图是一个 shape 为 HxW 的单通道图像每个像素的取值从0到类别数减一。这里最容易犯的错误是类别值从1开始标背景留0导致后续处理时黑白颠倒训练时背景类权重失衡。实例分割标注则复杂一些。每个实例有独立的轮廓多边形和类别标注结果通常保存为 JSON 或 COCO 格式。COCO 格式里每条记录包含 image_id、category_id、bbox 和 segmentation 多边形坐标。读取这类数据时需要注意坐标是否归一化、多边形是否闭合、以及掩码是从多边形 rasterize 得到的二值图还是 RLE 压缩格式这些细节会直接影响后续训练代码的编写。标注工具方面开源社区常见的选择有 LabelMe、labelme 的命令行版本、CVAT、以及 Supervisely 这类更工业化的平台。我的建议是个人实验用 labelme 足够团队协作和正式项目上手 CVAT它的任务分配、标签校验和导出格式支持更完善能省掉很多数据管理上的体力活。数据标注完成后质检环节很多人会跳过但这一步比训练本身更容易发现问题。比如类别不平衡严重的区域、边界标注粗糙、错标漏标都会在模型预测出错的地方集中暴露。建议标注完成后抽样可视化把标签图叠在原图上检查边缘对齐情况确认没问题再进入训练流程。3.2 mIoU、Dice、PQ别再只盯着损失曲线分割任务最常见的评估指标是均交并比 mIoU。计算方法是对每个类别计算预测区域和真实区域的交集面积除以并集面积然后对所有类别取平均。公式上可以写成mIoU (1/C) * sum_i [TP_i / (TP_i FP_i FN_i)]。这里的 TP、FP、FN 都是在像素级别统计的。mIoU 对类别不平衡比较敏感如果某个类别的像素占比很少它在这个类别上的错误会被平均过程稀释导致整体指标看起来不错但小类别实际效果很差。因此实际评估时我习惯同时打印每个类别的 IoU而不是只看均值。如果某个类别 IoU 特别低往往能从数据层面找到原因要么标注不全要么样本太少要么类别定义本身有歧义。医学和遥感领域常用 Dice 系数公式是 2*交集/(预测面积真实面积)。Dice 和 IoU 之间存在单调关系但 Dice 对小区域更敏感对类别不平衡的感受更强。如果项目的关键目标是小目标比如肠镜里的息肉、工业面板上很小的划痕我会更倾向用 Dice 作为训练损失和评估指标的组合。实例分割的评估则更复杂。COCO 数据集的实例分割标准指标是 AP平均精度它基于掩码 IoU 阈值比如 0.5、0.75计算精确率和召回率的综合表现。与语义分割不同实例分割必须先确定“预测实例”和“真实实例”的匹配关系匹配成功才算正样本因此 AP 对实例级别的定位精度非常敏感。全景分割则用 PQ全景质量指标把每个类别内的匹配实例质量和未匹配惩罚统一算进去。理解 PQ 的公式比死记更重要PQ sum_i IoU(匹配实例) / (匹配数量 0.5 * 未匹配数量)。它同时惩罚漏检和误检并且不区分语义还是实例统一按区域质量算分。3.3 数据增强与工具链分割任务的数据增强不能照搬分类任务的随机裁剪加翻转。因为标签是像素级对齐的任何空间变换必须同时作用于图像和标签。如果图像做了旋转标签也必须旋转同一个角度如果图像做了颜色抖动标签则不能动。因此常规做法是使用支持“同步变换”的增强库比如 imgaug 或者 albumentations。我在项目里常用的增强组合是随机水平翻转、随机缩放裁剪、轻微旋转、颜色抖动、高斯模糊、以及随机亮度和对比度调整。需要特别注意的一点是随机裁剪时必须保证裁剪区域包含足够多的前景像素否则大量训练样本都是纯背景模型会偏向把所有像素都预测为背景类。做法是先统计每个样本的前景占比或者用类别加权采样来平衡。工具链上PyTorch 生态的 Segmentation Models Pytorch 库非常值得了解它把 U-Net、FPN、PSPNet 等结构统一封装搭配 ResNet、EfficientNet 等骨干训练代码能大幅缩短。实例分割则常用 Detectron2 或者 MMDetection 的 Mask R-CNN 配置。Detectron2 的文档和示例代码质量很高适合理解实例分割的完整训练流程。4. 实战拆解一个道路场景分割Demo的核心细节下面用一个我实际做过的模拟项目来拆解操作步骤。这个项目做的是城市道路场景的分割Demo数据是自己采集并标注的小规模数据集目标包括背景、道路、车辆和行人四类。前期先用语义分割模型跑通流程后期扩展了实例分割能力来区分不同车辆和行人。4.1 场景需求与模型选型逻辑项目的核心需求是从车载摄像头画面中实时识别可行驶区域和障碍物。接收端明确要求类别只有背景、道路、车辆、行人四类不需要极致的细节边界但推理速度要快最好能在嵌入式设备上跑。基于这个需求最终选型是 MobileNetV3 骨干的 DeepLabV3。选择理由很简单类别少、任务简单不需要太深的骨干来提取极复杂的语义特征MobileNetV3 在速度和精度之间平衡很好DeepLabV3 的 ASPP 模块对多尺度场景近处大面积道路、远处微小行人有明确的优势这个组合在主流推理框架里都有现成的优化实现不需要自己造轮子。如果项目要求变成“必须数清楚每个行人、每辆车”那就得切换实例分割模型。我在这个模拟项目里做了两套模型对比DeepLabV3 for 语义分割以及 Mask R-CNN for 实例分割。下面讲的预处理和训练流程有一部分是两边通用的。4.2 数据准备与标签处理的关键步骤数据准备阶段我分成了四步走。第一步是采集。使用普通的行车记录仪和事后帧提取得到一批城市道路图像筛选掉过度曝光、完全遮挡和重复帧最终保留约 2000 张可用图像。第二步是标注。用 labelme 工具画多边形语义分割版本直接按类别涂区域实例分割版本对每个车辆和行人实例单独画多边形。这一步花了大约一周时间也让我彻底理解了为什么实例分割数据昂贵。第三步是数据划分。按场景维度划分训练集、验证集、测试集不能随机划分否则同一个路口的相似帧会同时出现在训练和测试里造成性能虚高。我当时按“路段”分组保证测试集里的场景与训练集有显著差异。第四步是格式转换。labelme 导出的是 JSON需要转成训练代码能够直接读取的格式。语义分割需要转成 PNG 标签图类别值需要重编码实例分割需要转成 COCO 格式并生成掩码 RLE 或者数组供检测框架读取。这步看起来枯燥但几乎所有项目里 90% 的 bug 都出在这建议写一个小工具并做可视化检查确认每个 mask 都贴合原图轮廓。4.3 训练配置与常见坑点训练配置上我用的是 PyTorch。语义分割模型用 Segmentation Models Pytorch 封装代码如下import segmentation_models_pytorch as smp model smp.DeepLabV3Plus( encoder_nametimm-mobilenetv3_large_100, encoder_weightsimagenet, classes4, activationsoftmax2d, ) loss_fn smp.losses.DiceLoss(modemulticlass) optimizer torch.optim.AdamW(model.parameters(), lr3e-4)这里有两个细节要提醒。第一个是损失函数的选择。我在这个四分类任务里试过交叉熵和 Dice 两种损失。单独用交叉熵时道路这类大面积类别学得很好但行人这种小目标经常漏掉单独用 Dice 时整体收敛快但边界稍显粗糙。最终我用的是 0.6 交叉熵加 0.4 Dice 的加权组合效果最稳。第二个是预训练权重的使用。MobileNetV3 的 ImageNet 预训练权重对收敛速度帮助很大但这意味着输入图像的归一化方式必须与预训练设置一致否则第一轮 loss 直接异常。我当时就遇到过归一化参数配错导致 loss 飙升的问题排查很久才发现是 mean/std 写错了。训练过程中的监控我除了看 loss 曲线还定期把验证集的预测结果可视化输出。这对分割任务尤其重要因为 loss 下降不代表每个类别的边界都合理很多结构问题从数值上看不出来但可视化一眼就能发现。下面的代码片段是一个简单的推理循环用来输出验证集预测叠加图import torch import torch.nn.functional as F import numpy as np def predict_single(model, image_tensor, device): model.eval() with torch.no_grad(): logits model(image_tensor.unsqueeze(0).to(device)) probs F.softmax(logits, dim1) pred probs.argmax(dim1).squeeze(0).cpu().numpy() return pred实例分割部分我用了 Mask R-CNN 的现成配置。因为项目只有两类前景目标对比 ResNet-50 骨干和 ResNet-101 骨干后最终选的是 ResNet-50 加 FPN。训练时需要注意收敛慢的问题Mask R-CNN 是一个多任务模型分类、回归、掩码三个任务的损失权重默认值不是最优的我调了几轮之后发现掩码损失的权重稍微加大一些能让边界更干净。4.4 两类模型的效果对比这个项目里两个模型各有胜负看需求选择。语义分割模型在推理速度上优势明显在嵌入式设备上可以达到接近实时的帧率对大面积道路的预测非常稳。但它的输出只是类别图无法回答“画面里有几辆车”“这几辆车分别在哪”。如果只是做可行驶区域检测这个模型完全够用。实例分割模型输出结构化得多直接给出每个车辆和行人的掩码和置信度。但它的推理速度快不起来在同样的嵌入式平台上帧率不到语义模型的三分之一。如果任务需要感知计数、追踪或者精细交互实例分割不可替代代价就是要接受算力消耗。我把两个模型的关键对比列成表格方便参考评估维度语义分割DeepLabV3实例分割Mask R-CNN输出内容像素类别图每个实例的掩码、类别、框、置信度能否区分个体不能能标注成本较低较高训练难度较低收敛快较高多任务需要调参推理速度快慢典型适用场景可行驶区域、全景理解行人车辆计数、精细交互分析5. 分割模型的部署与性能优化模型在服务器上跑通是一回事真正上线又是一回事。分割模型的计算量和内存占用都远高于分类模型部署阶段如果不做优化很容易出现性能不达标的问题。这一节聊几个我实际用过的优化手段。5.1 显存与速度的瓶颈分析分割模型的算力开销主要来自两部分高分辨率特征图和上采样路径。输入分辨率越大特征图越多显存占用越高而解码器里的上采样和跳跃连接会让你在推理时同时保存多个中间特征进一步加剧内存消耗。对语义分割来说最直接的降本方案是控制输入尺寸。很多任务没必要用 1024x1024 的原始分辨率去推理缩到 512x512 或 768x768 之后精度损失往往很小速度却能成倍提升。不要默认原始尺寸最优先用一组分辨率做对比实验选一个性价比最高的值。对实例分割来说速度瓶颈通常在检测分支的候选框数量。Mask R-CNN 里可以调低 RPN 的 proposal 数量从默认的 1000 降到 500 甚至 300对最终 AP 影响不大但速度提升明显。某些场景下还可以关闭掩码分支的上采样倍数直接输出低分辨率掩码再做后处理放大边界。5.2 量化、剪枝与蒸馏的实际取舍部署优化三板斧是量化、剪枝、蒸馏。量化是最常见的。把 FP32 模型转成 INT8在 TensorRT 或者 ONNX Runtime 上运行推理速度通常能提升 2-3 倍。但量化对分割模型有个隐患边界部分的激活值分布跨度大直接量化容易造成边界预测退化。解决办法一是用校准集做量化二是在量化和非量化模型之间对比 mIoU下降超过 1 个点就需要换方案。剪枝对分割模型的效果不如分类模型那么立竿见影因为分割的精度高度依赖特征图的完整度结构性剪枝容易破坏上采样路径的连通性。我自己试过几次通道剪枝精度下降明显回报有限。如果非要做轻量化更推荐一开始就选轻量骨干而不是事后剪枝。蒸馏在分割任务上的潜力常被低估。用一个大模型当教师把输出特征图或者预测分布“教”给小模型小模型可以在同等推理速度下获得明显更高的 mIoU。我在一个工业质检分割项目里做过实验MobileNetV3 学生模型从 ResNet101 教师模型蒸馏之后mIoU 涨了大约 3 个点这个收益比调参来得更实在。蒸馏的损失函数通常会结合教师和学生输出的 KL 散度以及学生的像素级硬标签损失。训练代码大致是这个思路teacher_logits teacher_model(image) student_logits student_model(image) kd_loss F.kl_div( F.log_softmax(student_logits / 4, dim1), F.softmax(teacher_logits / 4, dim1), reductionbatchmean, ) * (4 ** 2) ce_loss criterion(student_logits, mask_label) total_loss 0.5 * kd_loss 0.5 * ce_loss这里除以温度系数 4 是知识蒸馏的标准做法目的是软化教师输出的分布让小模型更容易学到类别之间的相似关系。5.3 与传统图像算法结合的后处理技巧神经网络输出的原始预测图直接使用往往不够干净合理结合传统图像处理手段能显著提升使用体验。最常见的操作是形态学处理。语义分割的输出经常会有一些孤立的小噪点比如道路中间突然出现一小块车辆误检。用开运算先腐蚀再膨胀可以去掉这种噪声让预测图变得更加规整。反过来如果预测区域内部有小洞可以用闭运算补上。连通域分析也是实例分割之外一种廉价的个体分离手段。即使模型做的是语义分割如果你只关心某几类目标的个体数量可以对这一类别的预测掩码做连通域标记每个连通域视作一个实例。该方法在目标间距较大时非常有效但目标重叠时就无能为力了这一点需要根据场景判断。边界平滑方面条件随机场曾经是分割模型标配的后处理手段能把类别标签的粗糙边界修得更加细腻。不过现在深度学习模型本身的边界预测已经相当好条件随机场带来的增益变得有限还要增加显著的计算耗时所以实际项目中我几乎不用了。6. 一些直观的实操体会这个项目做下来我最大的体会是分割任务难的不是模型而是围绕模型的一整套工程细节。数据标注质量决定了模型的上限损失函数和训练策略决定了能不能达到这个上限部署优化则决定了项目能不能真正落地。哪个环节掉链子最终都会反映在结果上。先说数据。小规模分割数据的质量把控比增加数据量更重要。我曾经只挑选了约 2000 张图像但仔细排查了边缘标注、类别一致性训练出来的效果比用 5000 张粗标数据更好。如果时间有限建议把标注质检放到和模型训练同等重要的位置。再说训练。分割模型的训练周期比分类长loss 曲线更容易骗人。无论是语义还是实例分割我都不建议只看数值指标结束训练一定要把预测图叠加在原图上肉眼过一遍关键场景。很多问题从数值上看不出来但可视化一目了然。最后说选型。不要因为某个新模型在榜单上分数高就盲目选它。榜单高分通常是建立在巨大的训练算力和数据规模之上的换到你的小数据、弱算力场景效果可能还不如经典模型。我在这个项目里最终选择了 DeepLabV3 和 Mask R-CNN 两个经典方案就是基于算力、数据、需求三者平衡的结果。如果你正准备开始一个分割任务我的建议是先想清楚你到底要语义还是实例输出再用小数据集快速跑通一个经典模型建立基线最后根据基线的不足再去升级模型或优化数据。这个流程看起来慢但实际上是最稳、最省时间的一种做法。后续如果场景数据持续扩大或者对推理速度有了更高要求再逐步引入蒸馏、量化和更轻量的网络结构整个演进路径就会非常清晰。这个编号018的项目算是把我脑海里零散的分割知识系统性落地了一遍。下次遇到类似题目我可以直接复用这套流程从数据准备到选型到部署都有明确路径可走。