资讯动态

FCN论文精读:全卷积化、上采样与跳跃连接如何奠定语义分割范式

发布时间:2026/10/14 19:57:35 来源:尧图企业网站定制
1. 为什么FCN这篇论文值得反复精读语义分割这个方向如果你只打算精读一篇开山之作那大概率就是FCN。2015年CVPR上那篇《Fully Convolutional Networks for Semantic Segmentation》把当时如日中天的分类网络直接改造成了像素级预测器一举把语义分割从拼图式的工程活变成了端到端可训练的问题。我第一次读它的时候还在用滑窗加超像素那一套做分割读完最大的感受是原来分割可以这么偷懒——不用设计复杂的区域合并规则不用手工提特征直接把分类网络的全连接层换成卷积层输出一张热力图就完事了。这篇笔记不是论文的逐段翻译而是我作为一个实际做过分割项目的人把这篇论文里真正影响后续工程实践的几个关键点拆开来讲。适合谁看如果你刚接触语义分割想搞明白为什么现在的分割网络都长这样或者你已经用过DeepLab、U-Net、SegFormer但没回头看过FCN那这篇笔记能帮你把很多设计选择的源头对上号。关键词就三个全卷积化、上采样、跳跃连接。这三个词基本撑起了后面十年分割网络的骨架。我读这篇论文有个习惯先不看实验表格先问自己一个问题如果让我在2015年做分割我会怎么设计然后拿论文的方案跟我的直觉对比差异最大的地方往往就是最有价值的地方。FCN最反直觉的一点是它没有为分割设计任何新模块所有东西都是从分类网络借来的。这种借不是偷懒而是一种深刻的洞察分类网络学到的特征本身就包含了足够的空间信息只是被全连接层给压扁了。2. 全卷积化到底改了什么从分类到分割的那一刀2.1 全连接层为什么是分割的拦路虎先说说分类网络的结构。以当年的VGG16为例前面是一堆卷积和池化最后接三个全连接层输出1000维的类别概率。问题出在全连接层它要求输入是固定尺寸的向量所以网络必须把最后一层卷积的特征图展平这就丢掉了空间结构。你想想一张224×224的图经过多次下采样最后特征图可能是7×7×512展平后变成25088维向量再经过全连接层。这个过程里每个像素来自哪里的信息被彻底打散了。对于分类任务这没问题反正只需要知道图里有没有猫。但对于分割你需要知道哪个像素是猫空间信息就是命根子。所以FCN做的第一件事就是把最后三个全连接层全部换成卷积层。具体怎么换全连接层本质上是一个矩阵乘法输入是展平的向量输出是类别数。如果把它看成一个卷积操作输入是7×7×512的特征图用一个7×7×512×4096的卷积核去卷输出就是1×1×4096效果完全等价。区别在于卷积形式不要求输入固定尺寸你可以喂一张更大的图进去输出就变成了一张空间上的概率图。这个替换在代码上极其简单但在概念上是一次跃迁。我当年第一次自己动手改的时候用PyTorch把VGG的fc6、fc7换成卷积跑通之后发现输出不再是1×1000而是W×H×1000每个位置都有1000类的得分。那一刻我才真正理解什么叫全卷积——网络不再区分特征提取和分类两个阶段整张网络就是一个从像素到像素的映射。2.2 卷积化之后的感受野与输出尺寸替换之后有个细节必须算清楚输出特征图的大小和感受野的关系。假设输入是H×W经过VGG的5次下采样每次池化步长2特征图变成H/32×W/32。也就是说输出图上每个点对应原图32×32的区域。这个32倍的下采样率是FCN-32s的由来也是它精度不够高的根本原因——太粗糙了。感受野的计算也得心里有数。VGG16到conv5理论感受野大概是212×212但有效感受野要小得多。这意味着每个输出点虽然看到了很大一片区域但真正起决定作用的还是中心附近。这个认知对后面理解跳跃连接为什么有效很关键浅层特征感受野小但空间精度高深层特征感受野大但空间精度低两者互补。我在实际项目里踩过一个坑直接把分类网络拿过来做分割发现小目标分割得一塌糊涂。后来分析就是因为32倍下采样后一个32×32以下的目标在输出图上可能只占一个点甚至半个点信息早就丢了。FCN论文里其实也承认了这个问题所以才有了后面的跳跃连接。这个坑让我明白分割网络的下采样率不能无脑照搬分类网络得根据你的目标尺度来定。2.3 从1×1输出到热力图推理阶段的尺寸处理全卷积化之后推理阶段有个很实用的特性输入尺寸可以任意。你喂一张500×500的图输出就是大概16×16的得分图500/32≈15.6。但这里有个工程上的细节论文里提到了但容易被忽略当输入尺寸不能被32整除时输出尺寸会向下取整导致边界像素没有对应的预测。解决办法是padding或者干脆把输入resize到32的倍数。我自己在部署的时候习惯把输入统一resize到512×512这样输出正好是16×16再上采样32倍回512×512。但这里有个精度损失resize本身会引入插值误差对于细长目标比如电线杆影响明显。后来我改成用原图尺寸做padding到32的倍数推理完再crop回去边界效果好不少。这个技巧论文里没明说是我在实际部署中试出来的。另外全卷积化带来的一个隐藏好处是训练和推理可以不一致。训练时用224×224的小图省显存推理时用全尺寸图拿更好的结果。这种灵活性在分类网络里是不存在的分类网络训练和推理的输入尺寸必须一致或者做特殊处理。这一点在我做高分辨率遥感图像分割时特别有用训练用256×256的patch推理直接上2048×2048的全图网络完全兼容。3. 上采样与跳跃连接精度是怎么一步步找回来的3.1 反卷积上采样的参数初始化策略全卷积化之后输出是H/32×W/32的粗得分图要变回原图尺寸就得上采样。FCN用的是反卷积也叫转置卷积步长32核大小64。这里有个关键细节反卷积的权重怎么初始化论文里明确说了用双线性插值核来初始化而不是随机初始化。这个选择非常聪明。为什么因为随机初始化的反卷积在训练初期会产生棋盘格伪影而且收敛慢。用双线性插值初始化相当于给网络一个至少不比双线性插值差的起点训练只是在这个基础上微调。我实测过用双线性初始化比随机初始化收敛快大概30%的迭代次数而且最终精度也略高。这个技巧后来成了分割网络上采样的标配几乎所有人都这么干。反卷积的另一个坑是输出尺寸的计算。给定输入尺寸in步长s核大小kpadding p输出尺寸是 (in-1)×s k - 2p。以FCN-32s为例输入16×16s32k64p0输出是 (16-1)×3264 544。但原图是512所以还得crop一下。这个尺寸对不齐的问题在实际写代码时经常让人抓狂我的经验是要么把反卷积参数设计得刚好对齐要么就在后面接一个crop层别硬算。3.2 FCN-16s和FCN-8s跳跃连接的具体融合方式FCN-32s精度不够论文提出了两个改进版本FCN-16s和FCN-8s。核心思想就是跳跃连接把深层的粗预测和浅层的细特征加起来。具体操作是先把pool5的输出上采样2倍变成和pool4一样的尺寸然后跟pool4的预测逐元素相加再一起上采样16倍。FCN-8s同理再多融合一层pool3。这里有个实现细节论文里写得比较简略融合之前每个pool层后面都接了一个1×1卷积把通道数降到类别数比如21类。这个1×1卷积的作用是给每个尺度一个独立的打分器让网络自己学习每个尺度该贡献多少。我一开始以为直接相加就行后来发现不加这个1×1卷积不同尺度的特征通道数不一致根本加不起来。这个细节在复现的时候如果漏了代码直接报错。融合方式论文用的是逐元素相加element-wise addition不是拼接concatenation。相加的好处是参数量少计算量小坏处是要求两个特征图通道数完全一致而且信息融合是线性的。后来很多网络改用拼接比如U-Net效果也不错。我个人的经验是如果显存够拼接通常比相加好一点点因为网络可以自己学习怎么组合但如果追求轻量化相加更划算。FCN选相加我猜主要是为了控制参数量毕竟当年显存很金贵。3.3 跳跃连接为什么有效从梯度流和特征互补两个角度跳跃连接为什么能提点论文里的解释是combine coarse, semantic information from a deep, coarse layer with fine, spatial information from a shallow layer。翻译成人话就是深层知道是什么浅层知道在哪里两者一结合就既知道是什么又知道在哪里。但从工程角度我觉得还有两个原因论文没展开。第一是梯度流跳跃连接给浅层提供了一条更短的梯度回传路径缓解了梯度消失。虽然FCN不算特别深VGG16也就16层但如果没有跳跃连接浅层的卷积层收到的梯度信号确实会弱一些。第二是训练稳定性多尺度融合相当于一个隐式的集成每个尺度都有自己的预测融合之后方差更小训练更稳。我在实际项目里做过一个消融实验去掉跳跃连接mIoU直接掉了6个点。而且掉得最多的是边界区域和小目标这跟理论分析完全吻合。还有一个有意思的现象FCN-8s比FCN-16s只高了2个点左右但计算量增加不多所以性价比很高。再往下融合pool2、pool1提升就非常有限了甚至可能因为浅层特征太低级而引入噪声。所以后来大家基本停在8s这个粒度。4. 训练策略里的那些魔鬼细节4.1 用分类网络初始化为什么不能从零训练FCN论文里所有实验都是用VGG16的分类权重初始化的不是从零训练。这个选择太重要了我甚至觉得这是FCN能work的前提之一。语义分割的标注数据在当时甚至现在都很少PASCAL VOC也就一万多张从零训练一个这么深的网络过拟合是必然的。用分类权重初始化相当于把ImageNet上学到的通用视觉特征迁移过来。卷积层学到的边缘、纹理、形状这些底层特征对分割同样有用。我试过从零训练FCN在VOC上mIoU只能到40多而用预训练权重能到60多差距巨大。而且从零训练还特别容易陷入局部最优损失曲线经常卡住不动。这里有个实操细节替换全连接层为卷积层时fc6和fc7的权重怎么初始化论文里说是从fc6、fc7的权重reshape过来的但卷积核的空间维度是随机初始化的。因为全连接层的权重是二维的4096×25088而卷积核是四维的7×7×512×4096没法直接对应。我的做法是把全连接权重按输出通道切分每个输出通道对应一个7×7×512的卷积核空间维度用小的随机数填充。这样至少输出通道的响应模式是保留的比完全随机好。4.2 类别不平衡与损失函数的选择分割任务天然存在类别不平衡背景像素往往占80%以上小目标类别可能只有几百个像素。FCN用的是逐像素的交叉熵损失没有做任何重加权或OHEM。这在当时是标准做法但实际训练时你会发现网络很快就把所有像素预测成背景因为这样损失就已经很低了。论文里提到他们用了whole image的训练方式就是每次喂一整张图而不是随机crop。这样做的好处是每个batch里包含了所有类别的像素一定程度上缓解了不平衡。但即便如此小类别的分割效果还是差。我后来做项目时会在交叉熵基础上加一个中值频率平衡median frequency balancing的权重给稀有类别更高的权重mIoU能提2-3个点。还有一个细节论文里说他们用了momentum 0.9学习率1e-4weight decay 5e-4batch size 20。这些超参在当年是合理的但现在看来学习率偏小训练很慢。我复现的时候把学习率提到1e-3用poly衰减收敛快很多。不过要注意学习率大了之后反卷积层容易不稳定输出出现NaN这时候要么降低学习率要么给反卷积层单独设一个更小的学习率。4.3 数据增强与训练技巧FCN论文里提到的数据增强很有限随机mirror、随机crop、随机缩放。但就是这些简单的增强对最终精度贡献很大。我做过对比不做任何增强mIoU掉5个点以上。随机缩放特别有用因为分割网络对尺度很敏感多尺度训练能让网络学到尺度不变性。还有一个论文里没提但实际很有用的技巧在训练后期把输入尺寸逐渐增大。比如前一半迭代用256×256后一半用384×384。这样做的好处是前期小图训练快后期大图微调边界。这个技巧在DeepLab系列里被正式化了叫poly学习率大尺寸微调但FCN时代大家还没系统总结。另外关于batch normalizationFCN原文用的是VGG16没有BN。后来我复现时加了BN发现训练稳定很多可以用更大的学习率。但要注意BN在batch size小的时候效果不好如果显存只够跑batch size 4那还不如不加。我一般建议batch size至少8再加BN否则用group norm替代。5. 复现过程中最容易翻车的几个地方5.1 反卷积输出尺寸对不齐的排查复现FCN最常遇到的问题就是尺寸对不齐。比如输入512×512经过5次下采样变成16×16上采样32倍应该是512但反卷积算出来是544。这时候如果你直接跟原图算损失就会报维度不匹配。解决办法有两个一是调整反卷积的output_padding参数让它输出512二是在反卷积后面接一个crop把多出来的边界切掉。我推荐用output_padding因为crop会丢掉边界信息。具体设置output_padding stride - kernel 2*padding input_size - output_size算出来是32-64016-16 -32不对我重新算一下。实际上PyTorch的ConvTranspose2d输出公式是 (in-1)stride - 2padding kernel output_padding。要输出512in16stride32kernel64padding0则 (16-1)*32 - 0 64 output_padding 48064output_padding 544output_padding。要等于512output_padding得是-32这不可能。所以只能改kernel或padding。我的做法是把kernel改成32padding改成8这样 (16-1)*32 - 16 32 480-1632496再加output_padding16就是512。这个计算每次都要重新推建议写个函数自动算。5.2 预训练权重加载时的通道匹配问题加载VGG16预训练权重时fc6和fc7的权重是二维的要reshape成四维卷积核。但这里有个坑VGG16的fc6输入是25088维7×7×512输出4096维。reshape成卷积核就是7×7×512×4096。但如果你用的输入尺寸不是224×224pool5的输出尺寸就不是7×7那这个reshape就对不上了。比如输入384×384pool5输出是12×12fc6的权重就应该是12×12×512×4096但预训练权重只有7×7的怎么办我的做法是把7×7的卷积核用双线性插值放大到12×12。虽然不完美但比随机初始化好。或者更简单训练和推理都用224×224的倍数避免尺寸不匹配。我一般用224×224训练推理时用全卷积方式跑大图这样预训练权重完全兼容。5.3 训练不收敛的常见原因与解决路径训练FCN不收敛我遇到过几种情况。第一种是损失一直不降大概率是学习率太大反卷积层梯度爆炸。解决办法是给反卷积层单独设一个小的学习率比如主网络的1/10。第二种是损失降了但mIoU不涨这通常是过拟合了需要加数据增强或dropout。第三种是损失震荡一般是batch size太小或者学习率衰减策略不对试试把batch size调大或者用cosine衰减。还有一种隐蔽的情况损失正常下降但预测结果全是背景。这往往是类别不平衡导致的网络找到了一个偷懒的解。解决办法是在损失里给稀有类别加权或者用focal loss。我试过focal loss对FCN这种密集预测任务效果一般不如直接加权交叉熵。6. 从FCN到现代分割网络哪些设计被继承下来了6.1 全卷积化成为分割网络的默认范式FCN之后几乎所有语义分割网络都是全卷积的。DeepLab系列、PSPNet、U-Net、SegFormer无一例外。全卷积化从一个技巧变成了常识。现在你如果看到一个分割网络还有全连接层那基本可以判定它是外行做的。这个范式的确立FCN功不可没。但全卷积化也有代价它要求输出和输入有严格的空间对应关系所以网络不能有全局池化这种破坏空间结构的操作。这也是为什么分割网络的下采样率不能无限增大一般到1/32就到头了再大空间信息就丢光了。后来Transformer进来之后用attention做全局建模才部分绕开了这个限制但全卷积的骨架还是保留着。6.2 跳跃连接与多尺度融合的演化FCN的跳跃连接是加法融合后来演化出了很多变体。U-Net用的是拼接而且是对称的编码器-解码器结构融合更充分。DeepLabv3用的是ASPP加解码器本质也是多尺度融合。PSPNet用的是金字塔池化在不同尺度上做池化再融合。这些方法的共同祖先都是FCN的跳跃连接。我在实际项目里选融合方式一般看两个因素显存和精度要求。显存紧就用加法精度优先就用拼接。还有一个经验融合的层数不是越多越好一般融合2-3个尺度就够了再多收益递减。FCN-8s融合了3个尺度pool3、pool4、pool5这个数量在大多数任务上都是合适的。6.3 对后续研究的启发与局限FCN最大的启发是分割可以端到端训练不需要手工设计区域合并规则。这个思想直接催生了后来的实例分割Mask R-CNN、全景分割。另一个启发是分类网络的特征可以迁移到分割这开启了预训练微调的范式一直沿用至今。但FCN也有明显的局限。第一是感受野固定不能自适应目标尺度后来DeepLab用空洞卷积解决。第二是边界粗糙因为上采样是双线性插值后来有人用CRF后处理再后来用边界感知损失。第三是计算量大全卷积在高分辨率图上很慢后来有人用轻量骨干网络解决。这些局限在论文里其实都有提及作者也给出了初步的解决方向只是当时没有展开。我读这篇论文最大的收获不是具体的技术细节而是一种思维方式当你想解决一个新问题时先看看能不能把已有工具改造一下而不是从头造轮子。FCN没有发明任何新算子就是把分类网络的全连接换成卷积就开创了一个方向。这种借力的思路比任何具体技术都更有生命力。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑