图像分割这个方向我从五年前第一次跑通 FCN 的 demo 开始到现在手里迭代过十几个分割项目踩过的坑比跑过的 epoch 还多。很多人入门分割的时候最大的困惑不是“模型怎么搭”而是“这么多网络到底该选哪个、为什么选它、换了场景又该怎么改”。FCN、U-Net、SegNet、Deeplab、SegFormer 这五个名字几乎出现在每一份分割学习路线里但真正把它们串起来讲清楚“演进逻辑”和“落地取舍”的资料并不多。这篇内容就是想把这条线捋直从全卷积的开山之作到编码器-解码器的经典范式再到空洞卷积和 Transformer 的介入每一代解决了上一代的什么问题以及在广告牌分割、地毯图像分割这类实际场景里我会怎么做选型和调参。不管你是刚接触计算机视觉的新手还是已经能跑通训练但不知道怎么优化的同学应该都能从里面找到能直接用的东西。1. 图像分割到底在解决一个什么问题1.1 从分类、检测到分割的任务递进要理解分割先得把它和分类、检测摆在一起看。图像分类是给整张图打一个标签比如“这是一张猫的图”目标检测是在图上框出若干个矩形告诉你“这里有一只猫、那里有一只狗”而图像分割要求更细它要给每一个像素都分配一个类别输出的是和原图同尺寸的掩码mask。换句话说分类回答“是什么”检测回答“在哪里、有几个”分割回答“每个像素属于谁”。这个差别直接决定了网络结构的不同。分类网络最后接全连接层把空间信息压成一个向量检测网络在特征图上做回归和分类分割网络则必须保留空间分辨率因为一旦空间信息被压没了像素级的预测就无从谈起。这也是为什么 FCN 的出现是一个分水岭——它第一次证明了卷积网络可以端到端地做像素级预测而不需要全连接层。从任务形态上分割又分成语义分割、实例分割和全景分割。语义分割只关心类别不区分同一类的不同个体比如图里三只猫都标成“猫”实例分割要把每只猫分开标全景分割则是两者的结合。FCN、U-Net、SegNet、Deeplab、SegFormer 这几个主要都是语义分割范畴的骨干理解这一点很重要因为它们的损失函数、评价指标都是围绕语义分割设计的。1.2 语义分割的评价指标与损失函数做分割绕不开两个东西怎么衡量好坏怎么训练。评价指标里最常用的是 mIoU平均交并比。IoU 是预测区域和真实区域的交集除以并集mIoU 就是把每个类别的 IoU 算出来再平均。它比像素准确率更可靠因为当背景占绝大多数时像素准确率会被背景“刷”得很高但 mIoU 能暴露小类别分割差的问题。还有一个常被忽略的指标是 Dice 系数它在医学图像分割里用得特别多因为医学图像里前景病灶往往很小Dice 对不平衡更敏感。实际项目里我一般两个都看mIoU 看整体Dice 看小目标。损失函数方面最基础的是逐像素交叉熵。但它有个问题当类别极度不平衡时占多数的背景会主导梯度。解决办法有几种一是给不同类别加权二是用 Focal Loss 降低易分样本的权重三是直接用 Dice Loss 或者交叉熵和 Dice 的组合。我在做广告牌分割的时候就遇到过广告牌区域只占整图不到 5%纯交叉熵训出来的模型几乎全预测成背景换成加权交叉熵加 Dice 之后才正常收敛。提示不要一上来就堆复杂损失。先用交叉熵跑通观察每个类别的 IoU如果发现某个小类别 IoU 长期接近 0再针对性地上加权或 Dice。1.3 数据标注分割项目里最容易被低估的成本很多人以为分割的难点在模型其实真正拖垮项目进度的是标注。像素级标注比画框贵得多一张复杂场景图标注可能要十几分钟甚至更久。我见过太多团队模型选得很 fancy结果标注数据只有几百张最后效果还不如老老实实标几千张用 U-Net。标注工具上常用的有 LabelMe、CVAT 这些导出格式一般是 COCO 或者 Pascal VOC 的掩码。这里有个实操细节标注时一定要统一类别定义和边界规则。比如广告牌分割里“广告牌”到底包不包括边框、包不包括被遮挡的部分如果标注员理解不一致训出来的模型会非常不稳定。我的做法是先标 50 张做一轮对齐几个人交叉检查把边界规则写成文档再批量开工。另外数据增强在分割里几乎是必须的。随机翻转、随机裁剪、颜色抖动、旋转都是常规操作。但要注意几何变换必须同时作用在图像和掩码上而且掩码要用最近邻插值不能用双线性否则类别边界会被插值出“中间值”变成不存在的类别。这个坑我早期踩过训练 loss 一直下不去排查半天才发现是掩码插值方式错了。2. FCN全卷积网络为什么是分割的起点2.1 把全连接层换成卷积层的核心动机在 FCN 之前大家做分割的思路是“滑窗”——用一个分类网络在图上逐块滑动每滑一次预测中心像素的类别。这个方法慢得离谱而且重复计算严重。FCN 的核心贡献就一句话把分类网络末尾的全连接层换成卷积层让网络可以接受任意尺寸输入输出同尺寸的密集预测。为什么换成卷积就行因为全连接层要求输入维度固定而卷积层对空间尺寸没有要求。一个 1x1 卷积本质上就是在每个像素位置上做一次全连接但它保留了空间维度。这样一来网络输出的不再是“一个类别”而是一张“每个位置一个类别”的热力图。这个思路现在看很自然但在当时是颠覆性的。FCN 的另一个关键设计是上采样。卷积和池化会让特征图越来越小最后要恢复到原图尺寸就得靠反卷积转置卷积或者双线性插值上采样。FCN 用的是转置卷积并且提出了跳跃连接skip connection把浅层的高分辨率特征和深层的语义特征融合缓解上采样带来的细节丢失。2.2 FCN-8s 的跳跃连接结构拆解FCN 有三个版本FCN-32s、FCN-16s、FCN-8s数字代表上采样的倍率。32s 是直接把最后一层特征图上采样 32 倍效果最粗糙16s 融合了 pool4 的特征8s 进一步融合了 pool3 的特征效果最好。以 FCN-8s 为例它的流程大致是主干网络比如 VGG逐层下采样得到 1/32 分辨率的特征图这个特征图先上采样 2 倍变成 1/16和 pool4 的输出相加再上采样 2 倍变成 1/8和 pool3 的输出相加最后上采样 8 倍恢复到原图尺寸。每一次融合都是在把深层的“语义”和浅层的“位置细节”结合起来。这个设计背后的逻辑很直白深层特征知道“这是什么”但不知道“在哪里”因为分辨率太低浅层特征知道“在哪里”但不知道“是什么”。跳跃连接就是把两者的优势拼起来。这个思想后来被 U-Net、SegNet 等大量借鉴可以说是分割网络的一个通用范式。2.3 FCN 的局限与它留下的坑FCN 虽然开创了先河但它的缺点也很明显。第一上采样用的转置卷积容易产生“棋盘效应”就是输出图上出现规律性的网格状伪影原因是卷积核重叠不均匀。第二FCN 的感受野固定对多尺度目标的适应能力差大目标和小目标很难同时分好。第三跳跃连接只是简单相加融合方式比较粗糙。我在复现 FCN 的时候棋盘效应是最头疼的。后来换成双线性插值初始化转置卷积核或者干脆用插值上采样加普通卷积伪影就明显减轻了。这个经验后来在 U-Net 和 SegNet 里同样适用。所以如果你现在还在用转置卷积做上采样建议检查一下输出图有没有网格纹有的话优先考虑插值方案。3. U-Net编码器-解码器与跳跃连接的经典范式3.1 U 型结构的对称设计逻辑U-Net 最初是为医学图像分割设计的但它的影响力早就超出了医学领域。它的结构是一个对称的 U 型左边是编码器逐层下采样提取语义右边是解码器逐层上采样恢复分辨率中间用跳跃连接把编码器每一层的特征拼接到解码器对应层。为什么是拼接concatenation而不是像 FCN 那样相加这是 U-Net 的一个关键改进。拼接保留了浅层特征的完整通道信息让解码器可以自己学习怎么融合而不是被迫接受一个相加后的结果。实测下来拼接的融合效果通常比相加更好代价是通道数翻倍显存占用更高。U-Net 的另一个特点是它能在小数据集上表现很好。医学图像往往只有几百张U-Net 靠强大的数据增强和跳跃连接依然能训出可用的模型。这也是为什么很多工业缺陷检测、遥感分割项目数据量不大的时候首选 U-Net。3.2 跳跃连接在广告牌分割中的实际作用我拿广告牌分割举个例子。广告牌的特点是形状规整多为矩形、边界清晰但背景复杂可能出现在楼顶、路边、高速旁光照和角度变化大。用 U-Net 做的时候跳跃连接的作用非常明显浅层特征能提供广告牌的边缘和角点信息深层特征负责判断“这块区域是不是广告牌”。如果没有跳跃连接边界会糊成一团尤其是广告牌和背景颜色接近的时候。实操上我会在跳跃连接处加一个 1x1 卷积先降通道再拼接这样能控制显存。另外编码器用预训练的 ResNet 或 EfficientNet 比从头训 VGG 效果好很多因为预训练权重已经学到了通用的边缘和纹理特征。这一点在数据量不足时尤其关键。3.3 U-Net 变体从 U-Net 到 Attention U-NetU-Net 之后出现了大量变体。U-Net 把跳跃连接改成了密集连接中间加了多个嵌套的卷积块让不同尺度的特征融合得更充分。Attention U-Net 在跳跃连接上加了注意力门控让解码器只关注编码器里和当前任务相关的区域抑制无关背景。这些变体不是越复杂越好。U-Net 参数量大、训练慢在小数据集上容易过拟合Attention U-Net 的注意力模块会增加计算量但如果你做的是目标边界模糊的任务比如地毯图像分割里不同花纹的边界注意力机制确实能带来提升。我的建议是先用原版 U-Net 建立 baseline再根据具体问题决定要不要上变体不要一上来就堆结构。4. SegNet把池化索引玩出花的下采样-上采样方案4.1 SegNet 与 U-Net 的核心差异SegNet 和 U-Net 都是编码器-解码器结构但它们的解码方式完全不同。U-Net 靠转置卷积或插值上采样SegNet 则用了一个很巧妙的办法在编码器做最大池化的时候记录下每个池化窗口里最大值的位置索引解码器上采样时直接把值放回这些记录的位置其余位置填零。这个设计的优点是省参数。因为上采样不需要学习只是查表放值所以 SegNet 的解码器比 U-Net 轻很多。缺点是信息恢复能力有限因为非最大值位置直接填零细节会有损失。实测下来SegNet 在道路、建筑这类结构规整的场景表现不错但在边界复杂、纹理细腻的场景比如地毯花纹分割就不如 U-Net。4.2 池化索引上采样的适用边界池化索引上采样本质上是一种“稀疏恢复”。它假设最大值的位置就是最重要的信息位置这在很多情况下成立但当一个池化窗口里有多个重要目标时就会丢信息。所以 SegNet 更适合单目标或目标稀疏的场景。我在做地毯图像分割的时候试过 SegNet效果一般。地毯的花纹是连续、密集的池化索引上采样会把很多花纹细节丢掉分割出来的边界呈锯齿状。后来换成 U-Net 加空洞卷积边界才平滑下来。所以选型的时候一定要先看你的目标是不是“稀疏、规整”如果是SegNet 的轻量优势值得考虑如果是“密集、细腻”还是老老实实用 U-Net 系。4.3 SegNet 的显存优势与部署考量SegNet 最大的工程价值在于显存占用低。因为解码器不需要存储转置卷积的中间结果也不需要拼接特征图整体显存比 U-Net 小不少。这在边缘设备部署时是个实打实的优势。如果你的场景是车载、监控摄像头这类算力有限的设备SegNet 或者它的轻量变体是值得评估的。不过要注意显存省了精度可能也省了。部署前一定要在目标设备上做完整的精度-速度权衡测试不要只看论文里的 mIoU。我一般会准备两套模型一套高精度离线用一套轻量在线用根据业务对延迟的容忍度来切换。5. Deeplab 系列空洞卷积与多尺度特征提取5.1 空洞卷积如何在不降分辨率的前提下扩大感受野Deeplab 系列的核心武器是空洞卷积也叫膨胀卷积。普通卷积的感受野随层数线性增长要扩大感受野就得不断下采样但下采样会丢分辨率。空洞卷积的做法是在卷积核的元素之间插入“空洞”让同样的参数量覆盖更大的区域。比如一个 3x3 的卷积核膨胀率为 2 时实际覆盖的是 5x5 的区域但参数量还是 9 个。这个设计对分割特别友好因为分割既要大感受野判断语义又要高分辨率定位边界。Deeplab 用空洞卷积替换了部分下采样让网络在保持分辨率的同时获得大感受野。这是它相比 FCN、U-Net 的一个本质进步。5.2 ASPP 模块多尺度并行的思路Deeplab v2 提出了 ASPP空洞空间金字塔池化用不同膨胀率的空洞卷积并行处理同一张特征图再把结果拼接。膨胀率通常取 6、12、18 这种覆盖小、中、大三种尺度。这样网络就能同时“看到”不同大小的目标对多尺度场景适应性很强。Deeplab v3 对 ASPP 做了改进加入了全局平均池化分支图像级特征并且把 BN 加进了 ASPP。v3 则引入了编码器-解码器结构把 ASPP 的输出和浅层特征融合进一步改善边界。这个演进路线很清楚v1 解决感受野v2 解决多尺度v3 优化 ASPPv3 补边界。5.3 Deeplab 在复杂场景下的调参经验Deeplab 的调参有几个关键点。第一膨胀率的选择要和特征图分辨率匹配。如果特征图已经很小了再用大膨胀率卷积核会“溢出”到有效区域之外产生边界伪影。第二ASPP 的输出通道数不要设太大否则显存吃不消一般 256 就够。第三输出步长output stride建议设成 16 或 88 精度更高但更吃显存。我在做广告牌分割时用 Deeplab v3 做过对比output stride 设 16 时 mIoU 比 8 低大概 1.5 个点但速度快了近一倍。如果业务对实时性有要求16 是更划算的选择。另外Deeplab 对学习率比较敏感用多项式衰减poly比阶梯衰减更稳初始学习率我一般从 0.01 开始试。6. SegFormerTransformer 进入分割领域的轻量方案6.1 分层 Transformer 编码器的设计取舍SegFormer 是 Transformer 路线在分割里的代表。它的编码器是一个分层的 Transformer类似 PVT、Swin 的思路输出多尺度特征解码器则非常轻就是一个简单的 MLP 融合模块。这个“重编码器、轻解码器”的设计和传统 CNN 分割网络正好相反。为什么这么设计因为 Transformer 的自注意力机制本身就有全局建模能力不需要靠复杂的解码器来扩大感受野。分层结构则解决了 Transformer 计算量随分辨率平方增长的问题——通过逐层下采样把注意力计算控制在可接受范围内。SegFormer 的另一个亮点是它去掉了位置编码改用卷积式的混合注意力对不同分辨率的输入更鲁棒。6.2 SegFormer 相比 CNN 分割网络的优势场景SegFormer 在几个场景下优势明显。一是大目标、全局上下文重要的场景比如遥感图像里的大面积地块分割自注意力能捕捉长距离依赖。二是多尺度目标共存的场景分层结构天然支持。三是需要轻量部署的场景SegFormer 的 B0 版本参数量很小速度也快。但 Transformer 也有短板。它通常需要更多数据才能训好小数据集上不如 CNN 稳。而且它的归纳偏置弱对边缘、纹理这类局部特征的捕捉不如 CNN 直接。我在数据量少于两千张的时候一般还是优先 CNN数据量上来了再考虑 SegFormer。6.3 从 CNN 迁移到 Transformer 的实操注意事项如果你打算从 U-Net 或 Deeplab 迁移到 SegFormer有几个点要注意。第一优化器通常用 AdamW学习率比 CNN 小一个量级6e-5 是常见起点。第二训练轮数要够Transformer 收敛比 CNN 慢别训几十轮没效果就放弃。第三数据增强要更强因为 Transformer 对数据量更敏感。第四预训练权重很重要用 ImageNet 预训练的编码器比从头训效果好太多。另外SegFormer 的输入尺寸最好是 32 的倍数因为分层下采样有 4 次尺寸不整除会在拼接时出问题。这个细节官方代码里有处理但自己改数据管道时容易忽略。7. 五个网络怎么选一张表说清楚7.1 按数据量、场景、算力三维度选型选型没有绝对的最优只有最适合。我一般从三个维度判断数据量、场景复杂度、部署算力。网络数据需求适合场景算力需求边界精度FCN中入门学习、简单场景低一般U-Net低医学、工业缺陷、小数据中高SegNet中道路、建筑、稀疏目标低中Deeplab中高多尺度、复杂背景中高高SegFormer高大数据、全局上下文中高数据少千张以内优先 U-Net目标稀疏且要轻量优先 SegNet多尺度复杂场景优先 Deeplab数据充足且追求全局建模优先 SegFormer纯粹学习原理从 FCN 入手。7.2 迁移学习与预训练权重的使用建议不管选哪个网络预训练权重都建议用。编码器用 ImageNet 预训练的 ResNet、EfficientNet 或 MiT能省大量训练时间还能提升小数据下的泛化。加载预训练时要注意通道匹配比如你把输入从 3 通道改成 4 通道加了深度图第一层卷积的权重需要特殊处理常见做法是把多出来的通道权重初始化为原三通道的均值。微调策略上我一般先冻结编码器训几轮解码器再解冻全部一起训。这样能避免随机初始化的解码器在早期把预训练编码器的权重带偏。学习率用差分学习率编码器小、解码器大效果更稳。7.3 从 mIoU 到实际业务指标的落地转换论文看 mIoU业务看的是实际指标。比如广告牌分割业务关心的是“广告牌有没有被完整框出来”“边界误差有多大”这时候 mIoU 高不一定业务就好。我一般会额外算边界 F1 或者 Hausdorff 距离前者衡量边界贴合度后者衡量最坏情况下的边界偏差。还有一个容易被忽略的点是推理速度。mIoU 高 2 个点但推理慢一倍在很多业务里是不划算的。所以选型时一定要把精度和速度放在一起看做一条精度-速度曲线找性价比最高的那个点。8. 训练分割模型时最容易踩的五个坑8.1 掩码插值方式错误导致类别污染前面提过一次这里再强调。几何增强时掩码必须用最近邻插值。用双线性插值会让类别边界产生 0 到 1 之间的中间值这些值在计算 loss 时会被当成某个类别或者被忽略导致边界区域训练信号混乱。这个 bug 很隐蔽因为 loss 曲线看起来正常但边界精度就是上不去。8.2 类别不平衡处理过度导致背景误检加权交叉熵和 Dice 能救小类别但权重给太大模型会变得“激进”把很多背景预测成前景造成误检。我一般先把权重设成类别频率的倒数开根号而不是直接取倒数这样更温和。然后根据验证集上的误检率微调不要一次性给太猛。8.3 验证集划分不合理导致指标虚高分割数据往往来自视频或连续拍摄相邻帧高度相似。如果随机划分验证集训练集和验证集会有大量近似重复的图指标虚高。正确做法是按场景、按拍摄批次划分确保验证集和训练集在分布上有区分度。这个坑我在做地毯图像分割时踩过随机划分时 mIoU 0.85按批次划分后掉到 0.72这才是真实水平。8.4 上采样方式选择不当引入棋盘伪影转置卷积的棋盘效应前面说过。除了换插值还可以用“插值上采样 普通卷积”的组合先双线性放大再卷积细化既没有棋盘纹又能学习上采样。这个方案现在很多开源实现都在用推荐优先考虑。8.5 忽略推理阶段的尺寸对齐问题训练时输入尺寸固定推理时如果输入尺寸和训练不一致很多网络会出问题。尤其是带下采样的网络输入尺寸不是 2 的幂次倍时编码器和解码器的特征图尺寸对不上拼接会报错。解决办法是推理前把输入 padding 到合适尺寸推理后再裁回来。这个细节在部署时特别重要我见过不止一个项目因为这个问题上线后崩掉。9. 从零跑通一个分割项目的完整流程9.1 数据准备与标注规范落地第一步永远是数据。先明确类别定义写标注规范文档标 50 张做对齐再批量标。标注格式统一成一种比如 COCO 或者 VOC不要混用。标完后做一轮质量抽检随机抽 10% 检查边界和类别是否正确。数据量目标上语义分割一般每类至少几百个实例才比较稳太少的话优先考虑数据增强和迁移学习。9.2 基线模型搭建与快速验证不要一上来就调参。先用 U-Net 或 Deeplab 搭一个 baseline用默认配置跑通看 mIoU 和 loss 曲线。baseline 的作用是给你一个参照系后面所有改动都跟它比。我一般会记录 baseline 的 mIoU、每类 IoU、训练时间、推理速度作为后续优化的锚点。9.3 迭代优化与消融实验设计优化要有章法一次只改一个变量。比如先试数据增强再试损失函数再试 backbone再试上采样方式。每改一次记录指标变化做消融表。这样你才能知道哪个改动真正有用。我见过很多人一次性改一堆最后效果好不知道为啥好效果差也不知道是哪儿的锅。9.4 模型导出与部署前的检查清单部署前检查这几项输入尺寸是否固定、预处理是否和训练一致归一化参数尤其重要、输出后处理是否正确argmax 还是阈值、推理速度是否达标、显存占用是否可接受。归一化不一致是最常见的部署事故训练用 ImageNet 均值方差部署忘了加指标直接崩。导出 ONNX 或 TensorRT 后一定要用同一张图对比导出前后的输出确保数值一致。10. 一些关于学习路径的个人建议如果你是刚入门我的建议是先别急着追新。把 FCN 的论文读一遍自己用 PyTorch 复现一个最小版本理解全卷积和上采样然后手撸一个 U-Net把跳跃连接、损失函数、数据管道都跑通再去看 SegNet 的池化索引、Deeplab 的空洞卷积和 ASPP、SegFormer 的 Transformer 编码器。这个顺序是沿着“感受野-多尺度-全局建模”的主线走的每一步都在解决前一步的遗留问题理解起来会顺很多。代码方面不要只跑开源仓库的 demo。试着改一改比如把 U-Net 的 backbone 换成 ResNet把损失换成 Dice把上采样换成插值观察指标怎么变。这种“改-测-想”的循环比看十篇论文都管用。分割这个方向理论重要但手感更重要而手感只能从一次次跑实验里来。最后说一个我自己的习惯每做一个项目我都会建一个实验记录表记下每次改动的配置、指标、耗时和结论。时间长了这张表就是你自己的经验库下次遇到类似场景翻一翻就知道该从哪儿下手。分割网络更新很快但“先看数据、再定基线、小步迭代、重视部署”这套方法论几年下来一直没变过。