大家做深度学习这么久,肯定绕不过一个坎:语义分割。不管你是做自动驾驶、医学影像分析、遥感图像处理,还是抠图、人像分割,最终都得跟“给每个像素分类”这件事打交道。而一聊到语义分割,有两座绕不开的大山,一个是全卷积神经网络(FCN),另一个就是U-Net。
很多时候大家看论文、看博客,网络结构图能看懂,但真正到自己搭模型、调参数、做训练的时候,就会冒出各种奇怪的问题:为什么我的分割边界这么糊?为什么小目标全丢了?为什么loss降不下去?为什么显存一下就爆了?这些问题的根源,往往就藏在FCN和U-Net的设计细节里。这篇文章,我结合自己实际训练分割模型的经验,把这两个经典网络从原理到实操彻底拆一遍,把自己踩过的坑也一并交代清楚,希望能帮你真正弄懂它们,而不是只会调库。
先说清楚这篇内容适合谁。如果你刚接触图像分割,想知道FCN到底“全卷积”在哪、U-Net为什么长这样,这篇文章完全跟着走一遍就能建立起整体认知;如果你已经跑通了分割模型,但在调优阶段卡住了,这篇文章里的损失函数选择、上采样细节、显存优化技巧也会有点用。我不打算把论文里的公式全抄一遍,而是尽量用“这是干什么用的”“为什么这么做”“实际训练中怎么选”的角度来讲。
1. 先从FCN说起:它是怎么把分类网络变成分割网络的
1.1 分类网络和分割网络之间隔着一道“全连接层”的墙
想理解FCN,得先理解一个非常朴素的问题:在FCN出现之前,为什么大家做不了像素级的分类?
当时的图像分类网络(比如VGG、GoogLeNet),结构上可以分两段:前面是一堆卷积和池化,把一张大图逐步压成很小的feature map,这一步叫特征提取;后面接几个全连接层,把特征图拉成一长条向量,最后做分类。这套结构在ImageNet分类任务上表现很好,但完全没法直接用于分割,因为全连接层把空间位置信息整个打平了,输出是一个类别概率,而不是一张跟输入大小一致的像素级标签图。
你可以这样理解:分类网络只告诉我们“这张图里有一只猫”,但完全不说猫在哪里、轮廓长什么样。分割任务需要的是“这张图里每个像素分别属于猫、背景、还是别的东西”。想要达到这个目标,网络结构必须保留空间分辨率,或者说,必须设计一种方式,把特征图恢复到像素级别。
FCN的突破性想法非常直接:把分类网络尾部的全连接层全部扔掉,换成卷积层。所以它叫“全卷积神经网络”,意思就是整个网络从头到尾只有卷积、池化、上采样这类操作,没有任何全连接层。这样一来,网络输出就不再是一个固定长度的类别向量,而是一张有空间维度的分数图,每个位置对应原图上一个区域,逐像素做分类。
这一步替换看起来简单,但它带来一个连锁问题:网络中间经过多次池化,特征图已经缩得很小。以VGG16为例,输入224x224的图,到最后一个卷积层,特征图只有7x7,如果直接用这个分辨率做分割,输出就是一张7x7的粗糙标签图,根本无法还原到原图尺度。所以FCN的核心难点变成了:怎么把低分辨率的高层语义特征,恢复成高分辨率的像素级预测。
1.2 上采样方式的选择:转置卷积和插值到底应该怎么选
恢复分辨率这个操作,学名叫做“上采样”。FCN里用到了一个非常核心的层,叫转置卷积(Transposed Convolution,也叫反卷积)。很多新手第一次看到转置卷积会被绕晕,其实它理解起来不复杂。
普通卷积是把输入的一个区域“压缩”成输出上的一个点,而转置卷积做的事情正好反过来:把输入上的一个点“扩展”成输出上的一个区域。给它一个输入值,它乘以卷积核的权重,再把结果分散写到输出的多个位置上,所以它能增加特征图的空间尺寸。这个操作里面有一个很重要的细节——转置卷积是有学习参数的,卷积核里的权重可以通过反向传播更新,也就是说,网络可以自己学习用什么样的模式把特征图放大。
但实际使用中,转置卷积有个知名的副作用:会产生棋盘格伪影,就是输出图上出现那种重复的方块状纹理,尤其是在放大倍数比较高的场景里特别明显。我自己第一次用FCN训练分割模型,结果图放大了细看,边缘区域全是隐隐的格子状条纹,找了好久才定位到原因——转置卷积的权重更新过程中,相邻位置之间会出现不一致的响应,频率越高的区域越明显。
针对这个问题,现在工程上已经形成了一些更稳的替代方案。最常用的做法是“双线性插值上采样 + 普通卷积”,也就是先用双线性插值把特征图放大,再送进一个普通卷积层做精细的特征变换。这个组合既能有效放大分辨率,又不会带来棋盘格伪影,代价是需要多一个卷积层的参数量。还有一个方案是用子像素卷积(PixelShuffle),它在超分辨率任务里用得比较多,分割里相对少一些。
如果让我给建议,我会说:如果做快速原型验证,直接用转置卷积也够用;如果要做最终效果比较好的模型,优先考虑双线性插值加卷积的方案。这里不光是伪影问题,还有一个好处是双线性插值没有学习参数,减少了训练时的变量,稳定性和收敛速度都更好。
1.3 FCN的三层结构:浅层细节和深层语义如何融合
FCN论文里提出了三种融合模式,分别叫FCN-32s、FCN-16s和FCN-8s。这个命名中,后面的数字表示最终预测相对原图的下采样倍数,也可以理解成“缩放比例是1/多少”。这里的关键,是特征融合策略。
FCN-32s最简单:直接把最后一层特征图做32倍上采样回原图大小,完成预测。它的优点是结构简单,缺点是丢失了太多细节信息。因为高层特征图经过多次池化,小物体的位置信息早就没了,直接放大之后就只剩一团糊的边缘。
FCN-16s做了一次融合:把最后一层特征图先上采样2倍,然后和之前第4次池化得到的特征图相加,再一起上采样回原图。这样做的好处是,较浅层的特征带着比较清晰的边缘和位置信号,能补充一些细节。
FCN-8s更进一步:在第16s融合的基础上,再把第3次池化层的特征图也加入融合,这样浅层信息占比更大,输出分辨率从32倍变成了8倍下采样。实际效果上,FCN-8s是三种方案里分割质量最好的,因为它保留了更准确的边界位置,同时还有足够的高层语义。
我在实际实验中也明显感受到了这个趋势。用同一份训练数据、同一个后端特征提取网络,三种结构的mIoU几乎总是FCN-8s > FCN-16s > FCN-32s。这个结论很重要:它告诉我们一个设计原则,高层语义负责“知道是什么”,浅层特征负责“知道在哪里”,两个都想要,就必须在结构上做跨层融合。
不过有一点必须提醒:FCN的融合方式比较粗放,就是直接相加(element-wise sum)。具体使用中,浅层特征和深层特征的数值量级往往不一致,直接相加可能导致数值波动失衡。我自己实验时发现,如果在相加之前分别对两个分支做一个BatchNorm归一化,训练的稳定性会提高不少。这一点在原始论文里没细说,但在实际调优时很值得一试。
2. U-Net:一拍即合的编解码器对称结构
2.1 为什么说U-Net是FCN的“进化版”
如果说FCN开启了语义分割的先河,那么U-Net就是把语义分割推向成熟的关键模型。它最早是为医学图像分割设计的,但后来几乎所有领域的分割任务都验证了它的有效性。
U-Net的结构看起来像一个U形字母:左边是逐渐下采样的编码器(Contracting Path),右边是逐渐上采样的解码器(Expanding Path),中间底部是bottleneck层,左右两边用跳跃连接(Skip Connection)衔接。编码器的每一层downsample之后,特征图分辨率减半,通道数翻倍;解码器的每一层upsample之后,特征图分辨率翻倍,通道数减半。
这个结构与FCN最大的不同,在于跳跃连接的处理方式。FCN的跨层融合是直接相加,而U-Net用的是拼接(concatenation),把编码器的浅层特征图和解码器的深层特征图沿着通道维度拼在一起。拼接比相加保留的信息量更大,解码器可以同时看到“不同抽象级别”的信息:既知道全局语义,又知道边界细节。
我举个直观的类比:编码器相当于一个经验丰富的医生,能一眼看出画面里是一个器官边界;解码器相当于一个还在学习的实习生,需要看着医生说“这块区域的轮廓比较模糊,注意仔细描边”。如果只有解码器自己干活,很多边缘会画错;但有了编码器提供的轮廓草稿,解码器的工作难度大大降低。拼接操作就是把医生的建议原封不动递给实习生,而不是只给一个抽象结论。
2.2 编码器下采样的每一步都做了什么
U-Net的编码器,早期版本是基于卷积层加最大池化构建的。每一层基本由两个3x3卷积加ReLU激活组成,然后通过2x2最大池化把特征图尺寸减半。这里两个连续卷积的设计是有讲究的:一个卷积能看到的感受野太小,两个串联之后,第二层每个输出位置已经可以看到输入层5x5范围的信息,感受野更大,特征表达能力更强。
下采样过程中的通道数变化也有规律。以原始U-Net论文的配置为例,第一层64个通道,第二层128,第三层256,第四层512,到bottleneck层是1024。通道数增长是有原因的:分辨率减半意味着每个像素对应的“原始区域”变大,所以用更多通道来承载更丰富、更抽象的语义信息。这个设计思想在后面几乎所有分割模型中都被保留下来。
这里有一个实际训练中的细节:通道数越多,显存占用和计算量就越大。很多人在自己的数据集上复现U-Net时发现显存根本不够用,于是动辄把通道数缩减一半或者四分之一。这个方法可行,但要注意缩减之后模型的表达能力确实下降了。我的建议是,先在原始通道数配置下跑一个小尺寸输入,确定模型能收敛、效果逻辑正常,再根据显存限制逐步缩减,不要一上来就大刀阔斧地砍。
2.3 解码器和跳跃连接的配合哲学
解码器的工作是把低分辨率语义图逐步还原为高分辨率预测。每一步,先对来自上一层的特征图做上采样(原始U-Net用转置卷积,也可以换成双线性插值),然后把上采样结果与编码器对应层的特征图拼接,再通过两个卷积层做融合与精修。
这里有一个关键点:拼接操作要求两个特征图的空间尺寸完全一致,所以上采样的倍数必须算准确。常见做法是直接以编码器某层输出的大小为目标,让上采样后的特征图和它一样大。如果你用的是双线性插值,注意align_corners这个参数会影响结果,不同框架之间的默认设置不一样,处理不好会导致特征图尺寸偏差一个像素,训练时报维度不匹配的错。这类错误在调试时非常浪费时间,建议看到维度报错第一反应就去检查这个位置。
跳跃连接数量也有讲究。原始U-Net是每一层都做跳跃连接,但实际工程中,可以每次都连,也可以隔层连。如果你的任务边界简单、语义明确,隔层连可以减少计算量;如果任务复杂、目标尺度变化大,建议保留完整跳跃连接。我自己在实验中发现,完整跳跃连接对小目标的保留能力明显更强,因为浅层位置信息仍然通过连接路径直接传到了解码器。
2.4 U-Net家族里那些好用的变体
经典的U-Net只是起点,后来围绕它生出了一整个家族。先提几类我认为在实际项目中很常用的:
ResUnet就是把编码器的卷积块换成残差结构。残差连接能有效缓解网络加深时的梯度消失问题,所以ResUnet通常可以堆得更深,训练也更稳定。
Attention U-Net在跳跃连接处增加了注意力门控模块,让网络自动学习哪些浅层特征值得被重点利用。它特别适合目标区域占比小、背景占比大的场景,比如病灶分割、小目标检测。
unet++改名叫做嵌套U-Net,核心变化是重新设计了跳跃连接的路径,让不同层的特征在解码前就做了多次融合。它在医学分割比赛里拿了很好的成绩,不过模型体积和训练时间也相应变大了。
还有一个很常见的工程操作是换backbone,比如把U-Net的编码器换成ResNet34、ResNet50等预训练模型。这样能借助大数据集上预训练的特征迁移能力,在小数据集上往往能获得明显提升。这是我在大多数真实项目中都会采用的做法,因为从头训练一个U-Net编码器在数据量小的时候很容易过拟合。
3. 从FCN到U-Net:两者的关键差异和选型思路
3.1 结构设计上的四大差异点
FCN和U-Net虽然都是编解码思想下的产物,但设计取向有非常明显的分歧。我用表格把这几个关键差异整理出来,方便对照:
| 对比维度 | FCN | U-Net |
|---|---|---|
| 网络形态 | 非对称,只有编码器下沉后直接上采样 | 完全对称的U形编解码 |
| 跨层连接 | FCN-16s/FCN-8s做浅层特征相加融合 | 每一层都用拼接方式跳跃连接 |
| 上采样方式 | 转置卷积(后续可替换) | 转置卷积或插值+卷积 |
| 特征融合策略 | 元素级相加 | 通道维度拼接 |
| 信息保留能力 | 较浅层细节有限 | 浅层细节信息完整,边界准确 |
| 初始应用场景 | 通用图像分割 | 医学图像分割(因数据少强语义结构) |
| 参数量和计算量 | 相对较小 | 相对较大 |
这张表的最核心启示是:U-Net用更复杂的结构换来了更强的细节还原能力。FCN更适合结构相对简单、边界不是主要瓶颈的场景;U-Net在复杂边界、小目标、细节丰富的场景里几乎是默认选项。
3.2 实际选型时我会怎么做判断
那么问题来了,项目里到底用哪个?这没有绝对答案,但有一些经验可以分享。
如果你处理的是遥感图像里的建筑物提取、农业地块分割、或者工业质检里的区域分割,这些场景往往需要比较精细的边缘,我的第一选择基本都是U-Net或者它的变体。因为这类任务对你的评分指标往往基于交并比,边界画得准不准直接决定IoU高低,而U-Net的浅层特征拼接对边界精度的提升非常明显。
如果你的场景是图像级的大区域分类,比如一个大路面图像只需要分辨“哪里是车道,哪里是人行道”,不要求细致边缘,FCN是更轻量的选择。它结构简单、参数量少、训练速度快,还容易部署到算力不足的设备上。
还有一个很实用的参考维度是训练数据量。U-Net的跳跃连接和拼接结构在数据量小的时候也能稳定收敛,这在医学图像场景里早就被验证了;FCN的融合方式相对简单,数据量不足时容易出现过拟合或者特征利用率不高的问题。所以小数据集任务闭眼选U-Net,基本不会错。
3.3 输出层的设计:每类一个通道还是每像素一个类别
无论用FCN还是U-Net,网络末尾都需要一个输出层把特征变成分割结果。通常的做法是:最后一层特征图跟着一个1x1卷积,把特征图的通道数变成类别数。然后有两种常见预测方式。
第一种方式是每个类别使用独立的通道,也就是输出一个C通道的热力图,每个通道对应一类,每个空间位置在哪个通道上的值最大,就预测为哪个类别。这种方式在物体分类思维里非常自然。
第二种方式是直接把每个空间位置预测成对应类别标签,这在某些轻量部署中会用到,但训练时一般不会直接输出类别整数,因为整数标签没法做梯度回传。常见的做法还是第一种,然后在最后做argmax取类别。
关于输出层的激活函数,有一个经常被问到的点:为什么分割模型最后一层不做sigmoid或者softmax?实际上训练时我们会在损失函数里内置这个操作,比如交叉熵损失自带softmax,而模型输出层只是返回线性分数。如果你在模型末尾手写一个softmax后再喂给损失函数,可能会遇到数值稳定性的问题,反而把梯度弄数值化了。保持输出层为线性分数是最稳妥的工程习惯。
4. 实操全过程:从数据准备到训练一个能用的分割模型
4.1 数据标注和预处理:比想象中更影响效果
分割模型吃的训练数据是成对的:一张原图,一张同尺寸的标签图,标签图的每个像素值对应一个类别索引。做数据准备时,我遇到过几个特别容易踩的坑。
第一个坑是类别不平衡。如果目标区域只占整张图像的5%,背景占95%,模型很容易学会“全部预测为背景”这种偷懒策略,因为这样也能拿到很高的准确率。解决办法可以分几个方向:用带权重的交叉熵损失,对前景类别赋更高的权重;或者用Dice损失,它直接针对区域重叠程度优化,天然对前景占比不敏感;还有一个更进阶的方案是组合损失,比如Dice损失加交叉熵损失一起使用,我实验下来效果最好的是这种组合方式。
第二个坑是标签图和原图的空间对齐。有些标注工具导出的标签图会因为缩放、裁剪操作和原图产生半像素偏移,尤其是训练数据集较大的时候,这种问题不容易被发现,但训练到后期loss曲线就降不下去,呈现一个平缓的高原。排查方法很简单:写一个可视化脚本,把原图和标签图叠加显示,看看地物边缘是否完全贴合。我每次拿到新的数据集都会先做这一步。
第三个坑是数据增强的参数需要谨慎。很多图像分类里常用的增强方式,比如随机旋转、反转、颜色抖动,在分割任务里不是都能直接用。必须注意:对图像做几何变换时,标签图必须同步做完全相同的变换,否则标签和图像错位,模型永远学不对边;所以代码里要复用同一套随机参数来变换两个图。颜色抖动是可以只用于原图的,因为标签不受颜色影响。
4.2 损失函数的选择逻辑和常见组合
损失函数对分割结果的影响,很多时候比网络结构更大。这里把最常用的几种说清楚。
交叉熵损失逐像素独立计算分类误差,对每个像素一视同仁。类别不平衡时效果差,但梯度计算稳定,收敛速度尚可。
Dice损失定义是两倍预测和真实区域的重叠面积除以两个区域面积之和,它直接优化区域的重叠程度,对小目标和类别不平衡场景特别友好。但它有个问题:当目标区域非常小时,损失曲线可能抖动剧烈,因为分母很小、梯度变化被放大,需要较小的学习率。
带边界惩罚的损失函数,这类设计通常是在交叉熵基础上对边界像素额外加权,让模型更关注边界。比如可以用图像膨胀获取边界区域,对边界像素权重乘以一个大于1的系数。
我个人最常使用的组合是“交叉熵 + Dice损失”,两者按比例加和,系数比如设为1比1或者0.5比0.5。这个组合的思路是:交叉熵提供稳定的逐像素梯度,Dice损失提供更明确的区域重叠信号,互相补充。踩过一次深坑,只用Dice损失训练医学图像分割,前期loss剧烈抖动,后期又过拟合,换成组合损失后效果稳定很多。
4.3 训练策略:学习率、batch size和显存间的平衡
分割模型的训练目标比普通分类更复杂,需要更大的显存,很多参数选择也因此受限。这里有几个重要的工程经验。
Batch size在分割任务中应尽量取大,但显存往往不让。常用策略是使用梯度累积,相当于用多个小批次反向传播累积梯度后再更新参数,可以在显存有限的条件下等效增大batch size。我实际操作时,通常把batch size设为2到4,梯度累积步数设为4到8,等效batch size就能达到16左右,效果比直接小batch训练明显稳定。
学习率方面,分割模型比分类模型更容易出现后期震荡。推荐使用学习率衰减策略,比如阶梯衰减或者余弦退火。个人经验是余弦退火在分割模型上效果比较稳,后期loss曲线能更平滑地收敛到较低水平。
还有一个必须警惕的问题是过拟合。分割模型的参数量大,在小数据集上非常容易过拟合。常用的防护策略包括:数据增强、Dropout、Early Stopping、以及使用预训练编码器。其中预训练编码器是最有效的一招,相当于让网络一开始就携带了从大数据集学来的通用特征。
4.4 评估指标:为什么accuracy在分割任务上不够用
很多初学者上来就用accuracy评估分割效果,然后发现分数高得吓人却看不出模型好坏。考虑一个分割任务里目标占5%的情况,就算模型把整张图都预测成背景,accuracy也是95%,看上去很“好”。显然这个指标完全无法反映模型真实能力。
语义分割领域最核心的指标是IoU(Intersection over Union),计算预测区域和真实区域交集除以并集。每个类别单独计算IoU再求平均,就是mIoU,这是各种分割论文和比赛里的标准指标。还有一个常用指标叫Dice系数,和IoU正相关,计算方式是两倍交集除以两个集合面积之和,很多医学分割论文学者习惯用它。
我自己在项目汇报中一般同时报mIoU和Dice,因为对于类别类别严重不平衡的任务,mIoU更能反映多类别的综合表现,而Dice能突出对小目标的敏感度。训练过程中的验证集监控也是重点关注mIoU,它比loss更直观。
评估的另一个细节是:推理时输出的是每个类别的概率图,要根据阈值或argmax得到最终标签图,再与真实标签图逐像素比对。常见错误是用了不同尺寸的标签评估,或者忘记处理边缘像素导致指标偏低,这类问题需要提前用可视化脚本排除。
5. 实际项目里那些折腾人的问题与排查记录
5.1 边缘为什么总是模糊、丢失细结构
边缘模糊是分割模型最常见的问题之一,原因可能出在多个环节。如果是FCN,可能是浅层特征融合不足,处理办法是尝试更深的融合策略或换U-Net类结构。如果是U-Net但依然模糊,优先检查跳跃连接是否完整保留浅层高分辨率特征,以及在解码部分是否还有卷积层在精修边缘。
另外一个容易被忽视的原因是把数据增强里的旋转角度设得过大。过大的旋转会导致标签图里的小结构因为插值被抹平,模型学的目标本身就不清晰,输出自然模糊。我的经验是几何增强的幅度保持适中,不要为了强行增大多样性而让标签失真。
5.2 小目标分割效果差、容易丢失怎么办
在遥感图像和医学图像里,小目标多而小,经常出现整个目标被模型忽略的烦恼。排查思路大致如下:
第一,检查输入分辨率。如果原图很大被压得很小,一个小目标可能只占几个像素,几乎不可能被正确分割。方案是做大图切片训练——把大图裁成512x512或1024x1024的切片,保持目标原始像素大小,再训练模型。这个方法我在遥感分割项目里用了无数次,效果提升非常明显。
第二,检查损失函数。如果用的是纯交叉熵,小目标很容易被淹没在背景里,建议换成Dice损失或组合损失。
第三,使用多尺度输入训练。推理时可以对原图做几个尺度的缩放分别推理,再把预测结果融合。这个策略能显著提升小目标的检测率,代价是推理时间变大。
5.3 显存不够用:从模型结构到推理阶段的全面优化
显存不足是分割任务里反复出现的恶梦,尤其当你尝试大图输入或者大通道数模型的时候。我提供几条实际可落地的措施,按性价比从高到低排列。
输入图片尺寸切分是立竿见影的办法。不要试图一次性把大图塞进网络,而是做一个滑窗切片推理,最后拼回大图。1536x1536以上的图,显存不够基本就只能走这条路。训练时也可以切片训练,模型会适应固定尺寸输入,推理时再根据情况做重叠切片拼接,解决边缘效应。
更换backbone同样有效。把VGG16换成ResNet34或更轻量的MobileNet变体,能显著减少参数量和显存占用,代价是精度可能小幅下降。训练时可以用大模型蒸馏出的伪标签来提升轻量模型,这是后话。
开启混合精度训练是很多人忽略的做法。现在主流框架都支持自动混合精度,能把显存占用降低一半左右,训练速度往往还会加快。只要数据类型支持,强烈建议开。
最后是推理阶段用小尺寸切片。推理时不需要保存中间梯度,但显存占用依旧来自激活值,切片推理是最直接的手段。如果整图推理显存不够,就采用重叠切片加边缘融合,拼回去效果基本一致。
5.4 类别不平衡严重:只靠调整权重还不够
当某些类别占比极小的时候,我的处理顺序是这样的:先换损失函数(Dice或组合损失),再考虑类别权重,然后是数据层面做重采样或者合成少数类别样本,最后才是修改网络结构。千万不要一开始就试图加复杂模块,结构改动成本高,收益往往不明确。
重采样的具体操作包括两种:一种是对整张图的类别比例做采样,让每batch内各类别比例尽量均衡;另一种是做前景裁剪,对包含小目标样本的区域做局部放大。后者在医学图像和遥感图像中效果极好,因为它让模型看到了更丰富的小目标细节。
5.5 一个完整的训练配置示例
作为参考,把我最近一次做道路分割实验的配置放在这里,不算最优但足够稳。框架选PyTorch,模型是ResNet34编码器的U-Net,输入尺寸是512x512,batch size是4,梯度累积4步,优化器是AdamW,初始学习率1e-4,学习率调度用余弦退火,训练语料是4000张标注图。损失函数取“0.5倍交叉熵 + 0.5倍Dice”,训练60个epoch。评估指标在验证集上mIoU能到0.72,在测试集上0.68。前面试过纯Dice和纯交叉熵,效果都没有这个组合好。
这个配置有个需要注意的地方:AdamW的权重衰减不能设太大,我用的5e-4,太大会导致深层特征学不动,太小则有过拟合风险。学习率1e-4在分割任务里属于适中范围,如果batch size较大可以适当提升到2e-4,如果数据很少则要降低到5e-5附近。
6. 关于FCN和U-Net,我一直想说的几个经验总结
从FCN到U-Net,核心就是理解两个概念:上采样和跨层连接。上采样告诉你低分辨率特征怎么变回高分辨率,跨层连接告诉你如何在不丢失位置细节的前提下获得高层语义。把这两个概念吃透,后面再看DeepLab、PSPNet、SegFormer这些后续模型时,会发现它们都只是在解决不同侧面的问题:空洞卷积是为了增大感受野但不降低分辨率,金字塔池化是为了捕捉多尺度上下文,Transformer结构则是换了一种全局建模方式。但底层的“编码-解码”和“多尺度融合”思想都是相通的。
我个人在实际操作中最大的体会是,不要把网络结构看得太重。同样的数据、同样的训练策略,微调损失函数和输入预处理方式带来的收益往往比换个更新奇的网络大得多。结构化创新是热点,但工程落地时,踏踏实实调好数据、损失函数、训练策略,才是稳定出效果的关键路径。
最后再分享一个小技巧:每次训练结束后,除了保存模型的权重文件,我还会保存一份训练集的浅层特征可视化图,以及几组验证集图像的前后对比图。一旦后续发现模型效果下降,这些材料能帮你快速定位是新数据的问题还是模型本身的问题。这个小习惯让我避开了很多“效果突然变差又找不到原因”的局面,建议大家也试一试。