论文链接:https://arxiv.org/pdf/2105.15203.pdf
代码链接:https://github.com/NVlabs/SegFormer
语义分割是计算机视觉任务的基础,又因为分割和分类有强关系,所以产生了从分类网络变体而来的分割网络,如 FCN 是一个典型的网络结构,并且在其后也产生了很多基于 FCN 的变体。
ViT 在图像分类上的成功,催生了 SETR 网络,该网络说明了 Transformer 也能在分割任务上生效。SETR 使用 ViT 作为主干网络,然后使用 CNN 来进行特征图增大。但是 ViT 有一些不足:
基于此,有作者提出了 PVT,是 ViT 的变体,金字塔结构,能进行密集预测。PVT 的提出,超越了基于 CNN 的目标检测和语义分割。还有后续的 Swin 和 Twins,这些方法主要考虑设计 encoder,但忽略了 decoder 能带来的更多提升。
本文提出了一种 SegFormer,同时考虑了效果、效率、鲁棒性,同时使用了 encoder 和 decoder。
创新性:
本文提出的 encoder,在对分辨率不同的输入进行 inference 的时候, 没使用插值的位置编码,所以,本文提出的 encoder 能够很简单的应用于不同分辨率的测试,也不会影响性能。并且分层级的部分能够产生高分辨率和低分辨率的特征。
轻量级的 MLP decoder,能够很好的利用 Transformer 的特征,其中低层能保留局部信息,高层能保留非局部信息。将这些不同层的 MLP decoder 结合后,能够结合 local 和 global 特征,能够得到一个简单且直接的 decoder 来得到有效的特征表达。
作者在 ADE20K,cityscapes,COCO-Stuff 进行了实验。
SegFormer 的结构如图 2 所示,主要有两个模块:
作者设计了一系列的 Mix Transformer encoders (MiT),MiT-B0 到 MiT-B5,结构相同,大小不同,MiT-B0 是最轻量级的,可以用来快速推理,MiT-B5 是最重量级的,可以取得最好的效果。
MiT 灵感来源于 ViT,但为适应分割做了一些优化。
1、Hierarchical Feature Representation:
给定输入图像 H × W × 3 H\times W \times 3 H×W×3,作者使用 patch merging 的方法来得到层级特征图 F i F_i Fi,其分辨率为 H 2 i + 1 × W 2 i + 1 × C i \frac{H}{2^{i+1}} \times \frac{W}{2^{i+1}} \times C_{i} 2i+1H×2i+1W×Ci,其中 i = { 1 , 2 , 3 , 4 } i=\{1, 2, 3, 4\} i={1,2,3,4},且 C i + 1 > C i C_{i+1}>C_{i} Ci+1>Ci
2、Overlapped Patch Merging:
ViT 中,将 N × N × 3 N\times N \times 3 N×N×3 的 patch,merge 成了 1 × 1 × C 1 \times 1 \times C 1×1×C 的特征,所以,作者可以吧特征从 H 4 × W 4 × C 1 \frac{H}{4} \times \frac{W}{4} \times C_{1} 4H×4W×C1 变换到 H 8 × W 8 × C 2 \frac{H}{8} \times \frac{W}{8} \times C_{2} 8H×8W×C2。并且不重叠的 patch 会失去局部连续性,所以作者使用有重叠的 patch merging 方法。
作者的 patch size K=7,相邻 patch 的 stride 为 S=4,padding size P=1,基于此来实现有重叠的 patch merging,得到和无重叠 patch merging 相同大小的结果。
3、Efficient Self-Attention
encoder 中计算量最大的就是 self-attention 层,所以作者使用了文献 [8] 中提出的方法,使用了一个 reduction ratio R R R 来降低序列的长度:
4、Mix-FFN
ViT 使用 position encoding(PE) 来引入局部位置信息,但是 PE 的分辨率大小是固定的,所以当测试不同于训练图像大小的图像时,需要插值,这样会导致准确率下降。
作者认为 PE 在语义分割中是不需要的,引入了一个 Mix-FFN,考虑了零填充对位置泄露的影响,直接在 FFN 中使用 3x3 的卷积,格式如下:
Mix-FFN 在 FNN 中使用了 3x3 的卷积和 MLP,并且也证明了 3x3 的卷积能够保留位置信息。
SegFormer 使用 MLP 构建了一个 Decoder,能够使用 MLP 来实现 decoder 的一个重要原因是,Transformer 有比 CNN 高的感受野。
Decoder 的过程:
Effective Receptive Field Analysis:
语义分割任务中,保持大的感受野非常重要,所以作者使用 Effective Receptive Field Analysis(ERF)作为工具来可视化并解释为什么 MLP decoder 在 Transformer 上如此有效。
如图 3 所示,作者分别可视化了SegFormer 和 Deeplabv3+ 的 4个 stage 和 decoder head 的 ERF。
所以,MLP 形式的 decoder 能在 Transformer 网络中发挥比 CNN 中更好的作用的原因在于感受野。
1、SegFormer 只使用了 ImageNet-1k 预训练,SETR 中的 ViT 在 ImageNet-22k 上预训练
2、SegFormer 的 encoder 是多层级的结构,能够同时捕捉不同分辨率的特征,SETR 的 ViT encoder 只能生成单个分辨率的特征图
3、SegFormer 没有使用位置编码,SETR 使用了固定大小的位置编码,会降低准确率
4、MLP 的 decoder 比 SETR 的更小更轻量,SETR 需要多个 3x3 的卷积堆叠来实现 decoder。
这里以训练 cityscapes 为例,来展示其中的要点。
代码运行:
python tools/train.py local_configs/segformer/B1/segformer.b1.1024x1024.city.160k.py
1、Encoder 主要过程
四个 stage 的输出被 concat 成为一个 list,也就是四种不同分辨率大小的多层级特征图。
transformer block 1 如下:
ModuleList(
(0): Block(
(norm1): LayerNorm((64,), eps=1e-06, elementwise_affine=True)
(attn): Attention(
(q): Linear(in_features=64, out_features=64, bias=True)
(kv): Linear(in_features=64, out_features=128, bias=True)
(attn_drop): Dropout(p=0.0, inplace=False)
(proj): Linear(in_features=64, out_features=64, bias=True)
(proj_drop): Dropout(p=0.0, inplace=False)
(sr): Conv2d(64, 64, kernel_size=(8, 8), stride=(8, 8))
(norm): LayerNorm((64,), eps=1e-05, elementwise_affine=True)
)
(drop_path): Identity()
(norm2): LayerNorm((64,), eps=1e-06, elementwise_affine=True)
(mlp): Mlp(
(fc1): Linear(in_features=64, out_features=256, bias=True)
(dwconv): DWConv(
(dwconv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=256)
)
(act): GELU()
(fc2): Linear(in_features=256, out_features=64, bias=True)
(drop): Dropout(p=0.0, inplace=False)
)
)
(1): Block(
(norm1): LayerNorm((64,), eps=1e-06, elementwise_affine=True)
(attn): Attention(
(q): Linear(in_features=64, out_features=64, bias=True)
(kv): Linear(in_features=64, out_features=128, bias=True)
(attn_drop): Dropout(p=0.0, inplace=False)
(proj): Linear(in_features=64, out_features=64, bias=True)
(proj_drop): Dropout(p=0.0, inplace=False)
(sr): Conv2d(64, 64, kernel_size=(8, 8), stride=(8, 8))
(norm): LayerNorm((64,), eps=1e-05, elementwise_affine=True)
)
(drop_path): DropPath()
(norm2): LayerNorm((64,), eps=1e-06, elementwise_affine=True)
(mlp): Mlp(
(fc1): Linear(in_features=64, out_features=256, bias=True)
(dwconv): DWConv(
(dwconv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=256)
)
(act): GELU()
(fc2): Linear(in_features=256, out_features=64, bias=True)
(drop): Dropout(p=0.0, inplace=False)
)
)
)
2、SegFormer Head
# MLP 1 (for the feature from stage 4)
MLP(
(proj): Linear(in_features=512, out_features=256, bias=True)
)
resize
# MLP 2
MLP(
(proj): Linear(in_features=320, out_features=256, bias=True)
)
resize
# MLP 3
MLP(
(proj): Linear(in_features=128, out_features=256, bias=True)
)
resize
# MLP 4
MLP(
(proj): Linear(in_features=64, out_features=256, bias=True)
)
resize
# 特征融合
ConvModule(
(conv): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(activate): ReLU(inplace=True)
)