图像分割是机器视觉任务的一个重要基础任务,在图像分析、自动驾驶、视频监控等方面都有很重要的作用。图像分割可以被看出一个分类任务,需要给每个像素进行分类,所以就比图像分类任务更加复杂。此处主要介绍 DL-based 方法。
mmsegmentation 分割网络结构通用模式:
encoder:输入图像→resize到特定大小→输入 backbone→得到特征图
可选:decode_head[0]:特征图→FCN→类别特征图→求带权重的 loss(权重0.4)
可选:decode_head[1]:特征图→特定解码头(psp/ocr等)→类别特征图→求带权重的 loss(权重1.0)
分割主要面临的问题及对应的已有解决方案:
参考文献:
[1] Image Segmentation Using Deep Learning: A Survey
[2] A survey of loss functions for semantic segmentation
论文:Fully convolutional networks for semantic segmentation
语义分割可以看做一个分类模型,唯一复杂的地方在于需要给图像中的每个像素进行分类。
分类模型一般都是接受相同大小的输入图像,摒弃空间坐标。其使用的全连接层可以被看成一个核能够覆盖全部元素的卷积层。
所以,是否能使用全卷积网络来替代全连接网络呢?
答案是可以的,使用全卷积网络还避免了必须使用“相同大小的输入”的需求,两个网络的转换如图 2 所示。
FCN 方法是第一个基于深度学习的分割方法,只包含卷积层,可以输入任意大小的图然后生成其分割结果图。
如何从分类任务到 dense FCN:
backbone:VGG-16(和 VGG-19 的效果类似)或 GoogLeNet(使用最后的 loss 层)
使用 backbone 的方式:去掉最后一层分类层,使用 1x1 卷积将通道数变成分割的类别数(PASCAL 中使用 21,其中一类为背景),然后使用 deconvolution 层上采样。
将类别和位置进行结合:
如图 3 所示,作者采用将层级特征进行结合的方式来精细化分割任务在空间上的类别预测。
详解:
从图 4 也能看出,将浅层特征和深层特征进行结合之后,能够让网络将全局和局部的特征进行结合,用全局的特征来指导局部特征的预测。
贡献:该方法是图像分割的一个重要的转折点,证明了深度学习网络可以端到端的实现对不同大小图像的语义分割。
缺点:FCN 模型速度较慢,无法支持实时的语义分割;没有考虑全局上下文信息;无法方便的扩展到 3D 图像。
论文:Parsenet: Looking wider to see better
FCN 为使用卷积网络来实现端到端的语义分割提供了很好的思考方式,但却忽略了很重要的全局上下文信息,但在 FCN 中添加全局上下文信息也很容易,所以 ParseNet 使用平均特征来加强特征,并且这种特征为语义分割带来了很好的提升。
ParseNet 贡献点:
使用全局上下文信息来来解决局部误识别问题,且引入的计算量非常小,和标准 FCN 基本一致。
① 添加全局特征的方式:parsenet 使用早融合的方式
如图 1e 所示,unpool(也就是重复的扩大)全局特征到和原特征相同大小,然后 concat 起来,一起去学习分类结果。
全局特征和原特征都分别学习分类,然后将两个预测的分类结果结合起来,作为最终的分类结果。
如果忽略结合特征后的训练过程,则两种融合方式的结果差不多,但是在某种特征辅助下的局部特征才能判断出正确的分类结果,所以,如果先训练再整合则造成的损失是不可逆的。
② 为什么要归一化
如图 3 所示,当要结合两组特征时,如果两者的 scale 和 norm 相差很大的话,则大的一方容易吞掉小的一方,如果使用归一化,则能较好的避免该问题,且训练更稳定。
还有很多图像分割是基于卷积 encoder-decoder 结构的,可以分为两个大类:通用分割、医学图像分割
论文:Deconvolutional semantic segmentation
FCN 有两点不足的地方:
FCN 网络只能处理单个尺度,因为其感受野是固定的,所以比该感受野大很多或者小很多的目标,就会出现一些误识别(包括分裂化和类别错误)。也就是说其 label 的预测仅仅基于局部信息,如此一来,就会出现:
大目标:和其他类别的相似像素的类别错误,如下图 1a 所示
小目标:被误分类为背景像素,如下图 1b 所示
输入解卷积层的 label map 中,目标的细节结构被丢失或平滑掉了,且恢复大小的手段也很简单。FCN 中,label map 仅仅为 16x16 大小,然后使用双线性插值来复原并生成分割结果图。
因此,本文的作者提出了 deconvolutional 语义分割网络:
encoder 使用 VGG-16 中的卷积层
decoder 使用反卷积层来生成分割图,反卷积层是由 deconvolution、 unpooling 和 ReLU 组成的。
Deconvolutional semantic segmentation 的框架结构:
如图 2 所示,框架主要由两部分组成:convolution 和 deconvolution
① Deconvolution Network for Segmentation
Unpooling
卷积网络中的 pooling 能够增加感受野,降低计算量,但也会丢失位置信息,但语义分割中的位置信息还是很重要的。为了解决该问题,作者使用了 pooling 的反操作,并且将特征图复原到 pooling 之前的大小,如图 3 所示。在 pooling 的过程中,会记录最大值的来源未知,unpooling 的时候会再把 pooling 结果放到对应的位置上去。
Deconvolution
unpooling 操作是一个扩大后的稀疏 map,deconvolution 才做能够将该稀疏 map 稠密化。
deconvolution 是 convolution 的反向操作,能够将单个输入扩展成多个输出,如图 3 所示,所以 deconvolution 的输出是一个扩大且稠密的特征图。
deconvolution 的层级结构能够捕捉不同尺度的特征,低层捕捉目标的大体轮廓,高层捕捉类别精细特征。
② Deconvolution 网络分析:
下图 4 可视化了网络的输出,可以看出,deconvolution 能够从粗到细提取目标的特征,unpooling 层通过追踪最强响应的位置来捕捉 example-specific 结构,能够高效的重建目标的细节信息。deconvolution 层能够捕捉 class-specific 形状,经过 deconvolution 后,与目标密切相关的激活特征被放大,而其他区域的噪声被抑制。这两者的结合能更有利于准确的 maps 的生成。
图 5 也展示了不同网络的输出效果,和 FCN-8s 相比,本文的网络结构预测更为精准
论文:SegNet: A Deep Convolutional Encoder-Decoder Architecture for Image Segmentation
语义分割是像素级的分类任务,也出现了很多方法能够实现端到端的预测,直接得到每个像素的预测类别,但其结果大都比较粗糙。其问题主要是由 max-pooling 和 下采样导致丢失了一些信息。
作者为了将具有全局信息的低分辨率特征图更好的映射到输入大小的分辨率,这种映射需要很好的保留边界的位置信息。
所以本文构建了 SegNet,来实现高效且精细的语义分割。
关键点:
结构:
Encoder:VGG-16 网络(去掉最后一层分类层),共 13 层卷积层,
卷积结构为 Conv+BN+ReLU,下采样使用最大池化来实现(2x2,s=2),但一般会丢失位置信息,逐级增加的损耗(boundary detail)非常不利于语义分割的结果
语义分割中边界非常重要,所以,encoder 中尽可能多的保留边界信息非常重要。考虑到内存和效率问题,有一些方法提出保留最大值对应的位置来保留位置信息,所以本文也使用了这种方法。
Decoder:每个 encoder 都会对应一个 decoder,所以 decoder 也共有 13 层,通过使用 memorized max-pooling 的方式,来上采样。解码的结构如图 3 所示,
分类层:最后一层 decoder 的输出会输入 multi-class soft-max 分类器,为每个像素来生成对应的类别预测
Original HRNet 论文:Deep High-Resolution Representation Learning for Human Pose Estimation
HRNetV2 论文:High-Resolution Representations for Labeling Pixels and Regions
HRNet 通过将由高到低的分辨率特征结合起来的方式,实现对高分辨率特征的保留,并且不同分辨率的特征也进行了信息交换。所以有很多分割的方式使用 HRNet 作为 backbone,来为模型中引入上下文信息的开拓能力。
深度学习中有高层特征也有低层特征,有很多研究都证明了,低分辨率的高级特征适合于分类任务,高分辨率的低级特征对密集预测有重要意义,如姿态估计、目标检测、语义分割等。
最开始提出的 Original HRNet 仅仅使用了 high-resolution 的特征表达,如图所示,本文对 Original HRNet 做了一些修改,并行使用了 high-to-low resolution 的特征表达,也叫 HRNetV2。
Original HRNet:
HRNetV2:
这里展示一组 HRNetV2 输出的分辨率:
分割如何使用这组多分辨率特征:上采样到相同大小,也就是最大的特征图大小,然后 concat,最后得到如下大小,再送入 decode_head 来使用:
HRNetV2 结构:
如上图 1 所示,总共有 4 stages,每个stage结构类似,都是 multi-resolution block,都是高分辨率特征和低分辨率特征的结合。
第一个 stage 的输入:原图经过两层 3x3 的卷积进行特征提取后的图(1/4原图大小)
multi-resolution block 主要包括两个模块:
HRNet 的对比:
下图也通过定量分析,验证了 HRNetV2 和 HRNetv2p 确实优于 HRNetV1。
论文:U-net: Convolutional vnetworks for biomedical image segmentation
训练深度学习网络需要大量的数据,语义分割的难点在于:高分辨率特征和低分辨率特征的结合,也可以看做是低层位置信息和高层语义信息的结合,缺一不可,所以两者如何联合使用是一个很值得研究的问题
本文在 FCN 的基础上,进行了一些修改和优化,能够保证在使用少量数据的基础上产生更准确的分割结果。
FCN 的主要核心:
U-Net 的主要改进在于:
U-Net 框架结构:
如图 1 所示,U-Net 和 FCN 结构类似,也分为 encoder 和 decoder 模块,网络结构中只有卷积和池化层,包括两个路径:
特点:
论文:V-net: Fully convolutional neural networks for volumetric medical image segmentation
论文:Feature Pyramid Networks for Object Detection
FPN 最初被提出的时候是被用于目标检测的,目标检测任务中的目标大小不一,尺度相差也比较大,但由于计算量和内存的原因,特征金字塔在很长一段时间并没有被广泛使用。所以本文的作者就提出了 FPN 模块,可以嵌入任何网络结构(如 Faster R-CNN 等),并且计算开销很小。
结构:
FPN 的特点:晚融合
也就是在每层和上层融合后,分别进行结果预测,然后再把结果进行融合
具体细节:
之后也被用于分割任务。FPN 能在只增加很少计算量的情况下,融合多尺度金字塔特征。之后,使用两个多层 MLP 来生成分割 mask。
论文:Pyramid Scene Parsing Network
Pyramid Scene Parsing Network(PSPNet),也是一个多尺度网络,能更好的学习全局上下文信息。
Scene Parsing 是基于语义分割的,也是计算机视觉的基础,需要给每个像素分配其类别。其难点在于场景和类别的多样性。
下图 2 的第一行展示了一种误识别:将船识别为了车
这种误识别可能是由于船和车的目标具有一定的相似性,但作者考虑,如果模型能考虑该目标(船)的上下文信息,则会发现船一般会和河流离得近,也许会降低误识别。
主要贡献:
提出了pyramid pooling module (PPM) 模块,聚合不同区域的上下文信息,从而提高获取全局信息的能力。
现有的深度网络方法中,某一个操作的感受野直接决定了这个操作可以获得多少上下文信息,所以提升感受野可以为网络引入更多的上下文信息。
框架:
Step1: 使用global averag pooling得到不同尺度的特征,PPM模块融合了4个不同尺度的特征:
Step2: global average pooling 之后,每层都接一个1x1的卷积来降低通道维度。
Step3: 上采样到和原图相同的尺寸,然后和进入PPM头之前的feature map 进行concat 来预测结果。
Mask-RCNN 可以实现 object instance segmentation,在 COCO 数据集上取得了很好的成绩。
还有很多基于 Mask-RCNN 的实例分割结构。如 R-FCN、DeepMask、PolarMask、CenterMask 等。
膨胀卷积(Dilated,也叫 atrous 卷积)为卷积网络引入了一个新的参数——膨胀率。膨胀卷积能够在不引入额外参数的情况下,提高感受野,所以有很多实时的分割网络都会使用膨胀卷积。如 DeepLab 家族[78]、multi-scale context aggregation[79]、dense upsampling convolution and hybrid dilatedconvolution(DUC-HDC)[78]、densely connnected atrous spatial pyramid pooling(ASPP)[81] 和 efficient neural network(ENet)[82]。
论文:Semantic image segmentation with deep convolutional nets and fully connected crfs
本文认为语义分割的主要问题:
本文的方法:
本文认为语义分割的主要问题:
Deeplab v2 有三个关键点:
ASPP 框架结构: 4 组并行且使用不同膨胀率的卷积
论文:Rethinking atrous convolution for semantic image segmentation
改进:
本文认为语义分割的主要问题(Deeplab 系列提出的问题基本一样):
DeeplabV3 的解决方法:膨胀卷积(多种膨胀率)+ SPP
语义分割的高级语义特征是需要从深层的低分辨率特征图中提取的,对比了使用和不使用膨胀卷积的情况:
对 ASPP 的修改:
使用全局平均池化的原因:
ASPP 是使用不同的膨胀率来捕捉不同尺度的信息,但是当膨胀率变大的时候,有效的元素就越少,也就是间隔越大,会有很多权重落到特征图外,无法起作用,极端情况就是这个3x3的卷积的效果类似于一个1x1的卷积。
解决方式:
在最后一层特征图使用全局平均池化,将该 “image-level” 的特征输入 1x1 卷积中+BN 中,再上采样到需要的大小
修改后的 ASPP 如下图 5 所示,有两个部分:
论文:Encoder-decoder with atrous separable convolution for semantic image segmentation
语义分割中的两个常用策略:
Deeplabv3+:将这个两个策略结合起来
Deeplabv3+ 和 Deeplabv3 的联系:
改进的动机:
框架结构:
卷积网络是提取 local 特征的网络结构,为了捕捉长距离的依赖关系,作者提出了 non-local 的方法。可以和现有的结构结合,用于分类、目标检测、分割、姿态估计等。
non-local 的核心思想是计算特征图中的每个点和其他点的相关关系,也就是空间上的注意力。
论文:PSANet: Point-wise Spatial Attention Network for Scene Parsing
上下文信息聚合的来提升感受野的操作,在 ParseNet、ASPP、PPM 等都有用到。
PSANet 中,作者提出了 point-wise spatial attention,来自适应的聚合长距离上下文信息。
论文:Ocnet: Object context network for scene parsing
论文:Dual attention network for scene segmentation
为了同时捕捉空间和通道的相关性,DANet 使用并行的方法,在通道和空间同时使用了 non-local 模块。两个模块如图 3 所示。
论文:Object-Context Representations for Semantic Segmentation
动机:
语义分割任务中,每个像素的类别是该像素所属目标的类别,所以作者提出了和目标本身类别相关的方法,来给每个像素分配类别。
方法(以 cityscapes 为例):这里最终的加权可以看做是像素和类别之间的加权
语义分割研究的一个重要分支就是抽取某个位置周边的上下文语义信息:
下面对比了 DANet 和 OCR 的特征图对比,可以看出 OCR 的单个特征图会聚焦于某个特定类别,DANet 的特征图中类别的信息不明确。
论文: Rethinking semantic segmentation from a sequence-to-sequence perspective with transformers
SETR 是首个在分割任务上超越 CNN 的 Transformer 网络结构,该论文作者认为,虽然 FCN 及其衍生网络都取得了较好的成绩,但本质上都是 encoder-decoder 的结构,所以,该论文作者想要将分割任务构建成一个 sequence-to-sequence 的结构,所以就提出了一个纯 Transformer 的结构(无 CNN 和分辨率降低)。
自从 ViT 证明了 Transformer 在图像分类任务上的的效果后,催生了很多相关的研究。而分割任务可以看做对逐个像素的分类任务,和图像分类有很强的关系,所以 SETR 使用 ViT 作为 backbone,然后使用 CNN 来进行特征图恢复。
框架结构:
Decoder的作用:生成和原图大小一致的2维分割结果
所以,这里需要把 encoder 的特征 Z 从 H W 256 \frac{HW}{256} 256HW reshape 成 H 16 × W 16 × C \frac{H}{16} \times \frac{W}{16} \times {C} 16H×16W×C。
方法一:Naive upsampling (Naive)
① 将transformer得到的特征 Z L e Z^{L_e} ZLe 映射到分割类别数(如cityscape就是19)
1x1 conv + sync batch norm (with relu) + 1x1 conv
② 使用双线性插值进行上采样,然后计算loss
方法二:Progressive UPsampling (PUP)→ 效果最好
使用渐进上采样,使用卷积核上采样交替变换来实现,为了避免直接上采样多倍带来的误差,这个上采样方法每次只上采样2倍,也就是说如果要把大小为
H
16
×
W
16
\frac{H}{16} \times \frac{W}{16}
16H×16W 的
Z
L
e
Z^{L_e}
ZLe 上采样到原图大小,需要进行4次操作。
方法三:Multi-Level feature Aggregation(MLA)
论文: Pyramid vision transformer: A versatile backbone for dense prediction without convolutions
但 ViT 有一些不足:
所以 PVT 提出了一种金字塔的 Transformer,能够适用于密集预测。PVT 和 ViT 的主要不同在于:
框架结构:
整体框架结构如图 3 所示:
论文:SegFormer:Simple and Efficient Design for Semantic Segmentation with Transformers
PVT、Swin 和 Twins 等方法,主要考虑设计 encoder,忽略了 decoder 带来的提升,所以作者提出了 SegFormer,同时考虑了效果、效率、鲁棒性,使用了 encoder-decoder 的模式。
框架结构:
SegFomer 和 SETR 的区别:
Hierarchical Transformer Encoder
作者设计了一系列的 Mix Transformer encoders (MiT),MiT-B0 到 MiT-B5,结构相同,大小不同,MiT-B0 是最轻量级的,可以用来快速推理,MiT-B5 是最重量级的,可以取得最好的效果。
MiT 灵感来源于 ViT,但为适应分割做了一些优化。
① 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
② 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 相同大小的结果。
③ Efficient Self-Attention
encoder 中计算量最大的就是 self-attention 层,所以作者使用了文献 [8] 中提出的方法,使用了一个 reduction ratio R R R 来降低序列的长度:
④ Mix-FFN
ViT 使用 position encoding(PE) 来引入局部位置信息,但是 PE 的分辨率大小是固定的,所以当测试不同于训练图像大小的图像时,需要插值,这样会导致准确率下降。
作者认为 PE 在语义分割中是不需要的,引入了一个 Mix-FFN,考虑了零填充对位置泄露的影响,直接在 FFN 中使用 3x3 的卷积,格式如下:
Mix-FFN 在 FNN 中使用了 3x3 的卷积和 MLP,并且也证明了 3x3 的卷积能够保留位置信息。
Lightweight ALL-MLP Decoder
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 中更好的作用的原因在于感受野。
这里以训练 cityscapes 为例,来展示其中的要点。
代码运行:
python tools/train.py local_configs/segformer/B1/segformer.b1.1024x1024.city.160k.py
① 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)
)
)
)
② 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)
)
NeurIPS 2021
论文:HRFormer:High-Resolution Transformer for Dense Prediction
HRFormer 是一个针对密集预测任务提出的方法,不同于 ViT 系列只使用低分辨率特征的方法。
ViT 系列的方法在分类任务上的优势有目共睹,其将图像切分为小块,分别提取小块中的特征,网络输出是单个分辨率的,缺失了处理多尺度目标的能力。
HRFormer 能够提取多尺度空间信息,为密集预测提供多分辨率特征表达。
HRFormer block:
如何减少计算量:
HRFormer 框架结构:
HRFormer 的输出包含了 4 种不同分辨率的特征图,对于不同的任务,作者罗列了不同的 head 设计方法:
论文:PoolFormer: MetaFormer is Actually What You Need for Vision
Transformer 最近在计算机视觉任务上展示了很好的效果,大家基本上都认为这种成功来源于基于 self-attention 的结构。但又有文章证明,只使用 MLP 也能达到很好的效果,所以作者假设 Transformer 的效果来源于 transformer 的结构,而非将 token 进行融合交互的模块。
所以,作者使用简单的 spatial pooling 模块替换了 attention 模块,来实现 token 之间的信息交互,称为 PoolFormer,也能达到很好的效果。
在 ImageNet-1K 上达到了 82.1% 的 top-1 acc。
作者使用 PoolFormer 证明了他们的猜想,并且提出了 “MetaFormer” 的概念,也就是一种从 Transformer 中抽象出来的结构,没有特殊的 token mixer 方式。
框架结构:
1、MetaFormer
MetaFormer 其实是 Transformer 的一个抽象,其他部分和 Transformer 保持一致,token mixer 方式是不特殊指定的。
① 首先,输入
I
I
I 经过 embedding:
X
=
I
n
p
u
t
_
E
m
d
(
I
)
X=Input\_Emd(I)
X=Input_Emd(I)
② 然后,将 embedding token 输入 MetaFormer blocks,该 block 包含两个残差 sub-blocks
Y = T o k e n _ M i x e r ( N o r m ( X ) ) + X Y=Token\_Mixer(Norm(X))+X Y=Token_Mixer(Norm(X))+X
2、PoolFormer
作者为了证明猜想,使用了非常简单的 pooling 算子来实现 token mixer,没有任何可学习参数。
假设输入形式为 T ∈ R C × H × W T\in R^{C\times H \times W} T∈RC×H×W,channel 维度在前,则 pooling 操作如下, k k k 为 pooling 大小:
作者使用 PoolFormer 作为 Semantic FPN 的主干网络,使用 mmsegmentation 训练的结果如下。超越了基于 CNN 的网络。
可以支持 5 类任务:分类、分割、检测、姿势识别、人体。
对于分割任务,共支持 21 个类别,训练和验证各 1464 和 1449 张图:
vehicles, household, animals, aeroplane, bicycle, boat, bus, car, motorbike, train, bottle, chair, dining table, potted plant, sofa, TV/monitor, bird, cat, cow, dog, horse, sheep, and person
是 VOC 2010 检测比赛的扩充集,包含 400 个类别,三个大类(objects、stuff、hybrids),经常只使用 59 个常见类别。
共包括 91 个类别,328k 图像,2.5 million 带 label 的实例。
检测任务共包含 80 个类别,82k 训练,40.5 验证。
街景数据集,包含 5k 精细标注数据,20k 粗糙标注数据。标注了 30 个类别。
类别定义:
包含 20k 训练,2k 验证数据,共 150 个类别。
Mapillary Vistas 数据集包含 66 类共 25,000 张高分辨率街景场景的数据,其中有 37 个类是以实例区分的标签。数据总量是 cityscapes 的5倍之多,包括不同天气、季节、时间。采集方式包括手机、摄像机、电脑、运动相机等。
对于 K+1 个类别(K 类目标,1 类背景),PA 计算如下:
其中, p i j p_{ij} pij 是第 i 个类别预测为第 j 个类别的总量
是 PA 的扩展,对每个类别分别计算,然后求平均
是语义分割中的一个很重要的衡量指标,表示的是预测的 map 和 gt map 之间的交并比:
I o U = T P T P + F P + F N IoU = \frac{TP}{TP+FP+FN} IoU=TP+FP+FNTP
语义分割中,IoU是用mask来评价的:
GT:
Prediction:
Intersection:
Union:
mIoU:各个类别的IoU的均值
mIoU 的计算:
混淆矩阵:
真实情况 | 预测结果 | 预测结果 |
---|---|---|
真实情况 | 正例 | 反例 |
正例 | TP | FN |
反例 | FP | TN |
混淆矩阵的对角线上的值表示预测正确的值,IoU是只求正例的IoU,如何找出和正例有关的混淆矩阵元素呢,可以通过划线法来得到,
假设有N个类别,则混淆矩阵就是一个NxN的矩阵,对角表示TP,横看真实,竖看预测。
假设一个3类的预测输出的混淆矩阵如下所示:
真实 | 预测 | 预测 | 预测 |
---|---|---|---|
真实 | 0 | 1 | 2 |
0 | 3 | 0 | 0 |
1 | 0 | 2 | 1 |
2 | 0 | 1 | 2 |
真实 | 预测 | 预测 | 预测 |
---|---|---|---|
真实 | 0 | 1 | 2 |
0 | a | b | c |
1 | d | e | f |
2 | g | h | i |
精确率:
p
r
e
c
i
s
i
o
n
0
=
a
/
(
a
+
d
+
g
)
precision_0 = a/(a+d+g)
precision0=a/(a+d+g)
p
r
e
c
i
s
i
o
n
1
=
e
/
(
b
+
e
+
h
)
precision_1 = e/(b+e+h)
precision1=e/(b+e+h)
p
r
e
c
i
s
i
o
n
2
=
i
/
(
c
+
f
+
i
)
precision_2 = i/(c+f+i)
precision2=i/(c+f+i)
p
r
e
c
i
s
i
o
n
0
=
3
/
(
3
+
0
+
0
)
=
1
precision_0 = 3/(3+0+0)=1
precision0=3/(3+0+0)=1
p
r
e
c
i
s
i
o
n
1
=
2
/
(
2
+
1
+
0
)
=
2
/
3
precision_1 = 2/(2+1+0)=2/3
precision1=2/(2+1+0)=2/3
p
r
e
c
i
s
i
o
n
2
=
2
/
(
2
+
1
+
0
)
=
2
/
3
precision_2 = 2/(2+1+0)=2/3
precision2=2/(2+1+0)=2/3
召回率:
r
e
c
a
l
l
0
=
a
/
(
a
+
b
+
c
)
recall_0 = a/(a+b+c)
recall0=a/(a+b+c)
r
e
c
a
l
l
1
=
e
/
(
d
+
e
+
f
)
recall_1 = e/(d+e+f)
recall1=e/(d+e+f)
r
e
c
a
l
l
2
=
i
/
(
g
+
h
+
i
)
recall_2 = i/(g+h+i)
recall2=i/(g+h+i)
r
e
c
a
l
l
0
=
3
/
(
3
+
0
+
0
)
recall_0 = 3/(3+0+0)
recall0=3/(3+0+0)
r
e
c
a
l
l
1
=
2
/
(
2
+
1
+
0
)
=
2
/
3
recall_1 = 2/(2+1+0)=2/3
recall1=2/(2+1+0)=2/3
r
e
c
a
l
l
2
=
2
/
(
2
+
1
+
0
)
=
2
/
3
recall_2 = 2/(2+1+0)=2/3
recall2=2/(2+1+0)=2/3
CPA:Class Pixel Accuracy(每类的像素精度)→按类别计算正确的像素占总像素的比例
P
i
=
对角线值
/
对应列的像素总数
P_i = 对角线值/对应列的像素总数
Pi=对角线值/对应列的像素总数
p
0
=
3
/
(
3
+
0
+
0
)
=
1
p_0 = 3/(3+0+0) = 1
p0=3/(3+0+0)=1
p
1
=
2
/
(
0
+
2
+
1
)
=
0.67
p_1 = 2/(0+2+1) = 0.67
p1=2/(0+2+1)=0.67
p
2
=
2
/
(
0
+
1
+
2
)
=
0.67
p_2 = 2/(0+1+2) = 0.67
p2=2/(0+1+2)=0.67
MPA:Mean Pixel Accuracy→每类正确分类像素比例的平均
M
P
A
=
s
u
m
(
p
i
)
/
类别数
=
(
1
+
0.67
+
0.67
)
/
3
=
0.78
MPA = sum(p_i)/类别数 = (1+0.67+0.67)/3=0.78
MPA=sum(pi)/类别数=(1+0.67+0.67)/3=0.78
IoU:
划线法:
I
o
U
0
=
3
/
(
3
+
0
+
0
+
0
+
0
)
=
1
IoU_0 = 3/(3+0+0+0+0) = 1
IoU0=3/(3+0+0+0+0)=1
I
o
U
1
=
2
/
(
0
+
2
+
1
+
1
+
1
)
=
0.5
IoU_1 = 2/(0+2+1+1+1) = 0.5
IoU1=2/(0+2+1+1+1)=0.5
I
o
U
2
=
2
/
(
0
+
1
+
2
+
2
+
1
)
=
0.5
IoU_2 = 2/(0+1+2+2+1) = 0.5
IoU2=2/(0+1+2+2+1)=0.5
代码方法:
S
A
∪
B
=
S
A
+
S
B
−
S
A
∩
B
S_{A\cup B} = S_A+S_B-S_{A\cap B}
SA∪B=SA+SB−SA∩B
I
o
U
0
=
3
/
[
(
3
+
0
+
0
)
+
(
3
+
0
+
0
)
−
3
]
=
1
IoU_0 = 3/[(3+0+0)+(3+0+0) - 3] = 1
IoU0=3/[(3+0+0)+(3+0+0)−3]=1
I
o
U
1
=
2
/
[
(
0
+
2
+
1
)
+
(
1
+
2
+
1
)
−
2
]
=
0.5
IoU_1 = 2/[(0+2+1)+(1+2+1) - 2]= 0.5
IoU1=2/[(0+2+1)+(1+2+1)−2]=0.5
I
o
U
2
=
2
/
[
(
0
+
1
+
2
)
+
(
0
+
2
+
1
)
−
2
]
=
0.5
IoU_2 = 2/[(0+1+2)+(0+2+1) - 2] = 0.5
IoU2=2/[(0+1+2)+(0+2+1)−2]=0.5
mIoU:
m I o U = s u m ( I o U i ) / c l a s s mIoU = sum(IoU_i)/class mIoU=sum(IoUi)/class
import numpy as np
class IOUMetric:
"""
Class to calculate mean-iou using fast_hist method
"""
def __init__(self, num_classes):
self.num_classes = num_classes
self.hist = np.zeros((num_classes, num_classes))
def _fast_hist(self, label_pred, label_true):
# 找出标签中需要计算的类别,去掉了背景
mask = (label_true >= 0) & (label_true < self.num_classes)
# np.bincount计算了从0到n**2-1这n**2个数中每个数出现的次数,返回值形状(n, n)
hist = np.bincount(
self.num_classes * label_true[mask].astype(int) +
label_pred[mask], minlength=self.num_classes ** 2).reshape(self.num_classes, self.num_classes)
return hist
# 输入:预测值和真实值
# 语义分割的任务是为每个像素点分配一个label
def evaluate(self, predictions, gts):
for lp, lt in zip(predictions, gts):
self.hist += self._fast_hist(lp.flatten(), lt.flatten())
#miou
iou = np.diag(self.hist) / (self.hist.sum(axis=1) + self.hist.sum(axis=0) - np.diag(self.hist))
miou = np.nanmean(iu)
#其他性能指标
acc = np.diag(self.hist).sum() / self.hist.sum()
acc_cls = np.nanmean(np.diag(self.hist) / self.hist.sum(axis=1))
freq = self.hist.sum(axis=1) / self.hist.sum()
fwavacc = (freq[freq > 0] * iu[freq > 0]).sum()
return acc, acc_cls, iou, miou, fwavacc
是所有类别 IoU 求平均
类似于 IoU:
当为二值 map 时,等于 F1 score
代码链接:https://github.com/shruti-jadon/Semantic-Segmentation-Loss-Functions
分割的 loss 函数可以被大体分为 4 个大类:
Cross-entropy 是衡量两个概率分布的距离的量,一般被用作分类的目标函数,分割作为一个像素级别分类的任务,也同样可以使用该函数作为目标函数。
1、Binary Cross-entropy
信息量:用来衡量一个事件所包含的信息大小,一个事件发生的概率越大,则不确定性越小,信息量越小。公式如下,从公式中可以看出,信息量是概率的负对数:
h
(
x
)
=
−
l
o
g
2
p
(
x
)
h(x) = -log_2p(x)
h(x)=−log2p(x)
熵:用来衡量一个系统的混乱程度,代表系统中信息量的总和,是信息量的期望,信息量总和越大,表明系统不确定性就越大。
E n t r o p y = − ∑ p l o g ( p ) Entropy = -\sum p log(p) Entropy=−∑plog(p)
交叉熵:用来衡量实际输出和期望输出的接近程度的量,交叉熵越小,两个概率分布就越接近。
L
=
∑
c
=
1
M
y
c
l
o
g
(
p
c
)
L = \sum_{c=1}^My_clog(p_c)
L=c=1∑Myclog(pc)
其中,M表示类别数,
y
c
y_c
yc是label,即one-hot向量,
p
c
p_c
pc是预测概率,当M=2时,就是二元交叉熵损失
pytorch中的交叉熵:
Pytorch中CrossEntropyLoss()函数的主要是将softmax-log-NLLLoss合并到一块得到的结果。
交叉熵loss可以用到大多数语义分割场景中,但他有一个明显的缺点,对于只用分割前景和背景时,前景数量远远小于背景像素的数量,损失函数中y=0的成分就会占据主导,使得模型严重偏向背景,导致效果不好。
语义分割 cross entropy loss计算方式:
输入 model_output=[2, 19, 128, 256] # 19 代表输出类别
标签 target=[2, 512,1024]
1、将model_output的维度上采样到 [512, 1024]
2、softmax处理:这个函数的输入是网络的输出预测图像,输出是在dim=1(19)上计算概率。输出维度不变,dim=1维度上的值变成了属于每个类别的概率值。
x_softmax = F.softmax(x,dim=1)
3、log处理:
log_softmax_output = torch.log(x_softmax)
4、nll_loss 函数(negative log likelihood loss):这个函数目的就是把标签图像的元素值,作为索引值,在temp3中选择相应的值,并求平均。
output = nn.NLLLoss(log_softmax_output, target)
如果第一个点真值对应的3,则在预测结果中第3个通道去找该点的预测值,假设该点预测结果为-0.62,因为是loss前右负号,则最终结果变为0.62。其余结果依次类推,最后求均值即时最终损失结果。
二值 Cross-entropy 公式如下:
其中,
y
^
\hat{y}
y^ 是预测的结果,
y
y
y 是真实结果
2、Weighted Binary Cross-entropy
Weighted Binary Cross-entropy(WCE) 是二值 cross-entropy 函数的变体,正样本会通过加权系数来得到权重的加强。适用于分布较倾斜的数据,如下图所示。
Weighted Binary Cross-entropy 公式如下:
其中:
2、Balanced Cross-entropy
Balanced cross entropy (BCE) 类似于 WCE,唯一的不同在于正样本,
BCE 的公式如下:
其中:
β
=
1
−
y
H
∗
W
\beta = 1-\frac{y}{H*W}
β=1−H∗Wy,相比原来的loss,在样本数量不均衡的情况下可以获得更好的效果。
Focal loss 可以被看做 Binary Cross-entropy loss 的变体,特点如下:
Focal loss 其实是源于 Cross-entropy loss 的,目的是解决样本数量不平衡的情况:
一般分类时候通常使用交叉熵损失:
C
r
o
s
s
E
n
t
r
o
p
y
(
p
,
y
)
=
{
−
l
o
g
(
p
)
,
y
=
1
−
l
o
g
(
1
−
p
)
,
y
=
0
CrossEntropy(p,y)=
为了解决正负样本数量不平衡的问题,我们经常在二元交叉熵损失前面加一个参数 α \alpha α。负样本出现的频次多,那么就降低负样本的权重,正样本数量少,就相对提高正样本的权重。因此可以通过设定 α \alpha α的值来控制正负样本对总的loss的共享权重。 α \alpha α取比较小的值来降低负样本(多的那类样本)的权重。即:
C
r
o
s
s
E
n
t
r
o
p
y
(
p
,
y
)
=
{
−
α
l
o
g
(
p
)
,
y
=
1
−
(
1
−
α
)
l
o
g
(
1
−
p
)
,
y
=
0
CrossEntropy(p,y)=
虽然平衡了正负样本的数量,但实际上,目标检测中大量的候选目标都是易分样本。这些样本的损失很低,但是由于数量极不平衡,易分样本的数量相对来讲太多,最终主导了总的损失。
因此,这篇论文认为易分样本(即,置信度高的样本)对模型的提升效果非常小,模型应该主要关注与那些难分样本 。一个简单的想法就是只要我们将高置信度样本的损失降低一些, 也即是下面的公式:
F
o
c
a
l
_
L
o
s
s
=
{
−
(
1
−
p
)
γ
l
o
g
(
p
)
,
y
=
1
−
p
γ
l
o
g
(
1
−
p
)
,
y
=
0
Focal \_Loss =
当 γ = 0 \gamma=0 γ=0 时,即为交叉熵损失函数,当其增加时,调整因子的影响也在增加,实验发现为2时效果最优。
假设取 γ = 2 \gamma=2 γ=2,如果某个目标置信得分p=0.9,即该样本学的非常好,那么这个样本的权重为 ( 1 − 0.9 ) 2 = 0.001 (1-0.9)^2=0.001 (1−0.9)2=0.001,损失贡献降低了1000倍。
为了同时平衡正负样本问题,Focal loss还结合了加权的交叉熵loss,所以两者结合后得到了最终的Focal loss:
F
o
c
a
l
l
o
s
s
=
{
−
α
(
1
−
p
)
γ
l
o
g
(
p
)
,
y
=
1
−
(
1
−
α
)
p
γ
l
o
g
(
1
−
p
)
,
y
=
0
Focal loss =
取 α = 0.25 \alpha=0.25 α=0.25 在文中,即正样本要比负样本占比小,这是因为负样本易分。
单单考虑alpha的话,alpha=0.75时是最优的。但是将gamma考虑进来后,因为已经降低了简单负样本的权重,gamma越大,越小的alpha结果越好。最后取的是alpha=0.25,gamma=2.0
https://zhuanlan.zhihu.com/p/49981234
class FocalLoss(nn.Module):
def __init__(self, gamma=0, alpha=None, size_average=True):
super(FocalLoss, self).__init__()
self.gamma = gamma
self.alpha = alpha
if isinstance(alpha,(float,int,long)): self.alpha = torch.Tensor([alpha,1-alpha])
if isinstance(alpha,list): self.alpha = torch.Tensor(alpha)
self.size_average = size_average
def forward(self, input, target):
if input.dim()>2:
input = input.view(input.size(0),input.size(1),-1) # N,C,H,W => N,C,H*W
input = input.transpose(1,2) # N,C,H*W => N,H*W,C
input = input.contiguous().view(-1,input.size(2)) # N,H*W,C => N*H*W,C
target = target.view(-1,1)
logpt = F.log_softmax(input)
logpt = logpt.gather(1,target)
logpt = logpt.view(-1)
pt = Variable(logpt.data.exp())
if self.alpha is not None:
if self.alpha.type()!=input.data.type():
self.alpha = self.alpha.type_as(input.data)
at = self.alpha.gather(0,target.data.view(-1))
logpt = logpt * Variable(at)
loss = -1 * (1-pt)**self.gamma * logpt
if self.size_average: return loss.mean()
else: return loss.sum()
Dice Loss:
D
i
c
e
_
L
o
s
s
=
1
−
D
i
c
e
_
C
o
e
f
f
i
c
i
e
n
t
Dice\_Loss = 1-Dice\_Coefficient
Dice_Loss=1−Dice_Coefficient
Dice 系数:
根据 Lee Raymond Dice命名,是一种集合相似度度量函数,通常用于计算两个样本的相似度(值范围为 [0, 1]),公式如下,分子有2是因为分母加了两次TP:
D i c e C o e f f i c i e n t = 2 ∣ X ∩ Y ∣ ∣ X ∣ + ∣ Y ∣ DiceCoefficient = \frac{2|X \cap Y|}{|X|+|Y|} DiceCoefficient=∣X∣+∣Y∣2∣X∩Y∣
其中, ∣ X ∣ |X| ∣X∣和 ∣ Y ∣ |Y| ∣Y∣分别表示集合的元素个数,分割任务中,两者分别表示GT和预测。
所以 Dice Loss 公式如下:
D i c e L o s s = 1 − 2 ∣ X ∩ Y ∣ ∣ X ∣ + ∣ Y ∣ DiceLoss = 1-\frac{2|X \cap Y|}{|X|+|Y|} DiceLoss=1−∣X∣+∣Y∣2∣X∩Y∣
从IoU过度到Dice:
IoU的算式:
I o U = T P T P + F P + F N IoU = \frac{TP}{TP+FP+FN} IoU=TP+FP+FNTP
简单的说就是,重叠的越多,IoU越接近1,预测效果越好。
Dice 系数:
D i c e _ C o e f f i c i e n t = 2 ∣ X ∩ Y ∣ ∣ X ∣ + ∣ Y ∣ Dice\_Coefficient = \frac{2|X \cap Y|}{|X|+|Y|} Dice_Coefficient=∣X∣+∣Y∣2∣X∩Y∣
所以:
D
i
c
e
_
C
o
e
f
f
i
c
i
e
n
t
=
2
×
T
P
T
P
+
F
N
+
T
P
+
F
P
Dice\_Coefficient = \frac{2 \times TP}{TP+FN+TP+FP}
Dice_Coefficient=TP+FN+TP+FP2×TP
所以我们可以得到Dice和IoU之间的关系了,这里的之后的Dice默认表示Dice Coefficient:
I o U = D i c e 2 − D i c e IoU=\frac{Dice}{2-Dice} IoU=2−DiceDice
这个函数图像如下图,我们只关注0~1这个区间就好了,可以发现:
需要注意的是Dice Loss存在两个问题:
训练误差曲线非常混乱,很难看出关于收敛的信息。尽管可以检查在验证集上的误差来避开此问题。
Dice Loss比较适用于样本极度不均的情况,一般的情况下,使用 Dice Loss 会对反向传播造成不利的影响,容易使训练变得不稳定。
所以在一般情况下,还是使用交叉熵损失函数。
Dice coefficient 通常被用来度量两幅图像的相似度,Dice loss 公式如下:
其中,分母分子分别加 1,是为了保证边缘情况的可用性,如当
y
=
p
^
=
0
y=\hat{p}=0
y=p^=0 时。
Tversky index(TI) 也可以被看成 Dice coefficient 的一种扩展:
当 β = 1 / 2 \beta=1/2 β=1/2 时,就是 Dice coefficient。
Tversky loss 公式如下: