点击上面的计算机视觉车间,选择星标
第一时间送达干货
来源|集智书童
虽然深度学习促使语义分割取得显着进步,但是很多轻量级模型的精度和性能不尽令人满意。在这项工作中,作者提出了PP-LiteSeg,轻量级实时语义分割任务新模型。
具体来说,作者提出了灵活的轻量级解码器(FLD)为了减少以前解码器的计算费用。作者还提出了一个统一的注意力集成模块,以加强特征表示(UAFM),它利用空间注意力和通道注意力产生注意力权重,然后将输入特征与权重相结合。
此外,还提出了一个简单的金字塔池模块(SPPM),收集全局上下文信息,计算成本低。
实验表明,与其它方法相比,PP-LiteSeg在精度和速度之间取得了优越的权衡。CityScapes测试集上,PP-LiteSeg在NVIDIA GTX1080Ti上分别达到了72.0%的mIoU/273.6FPS和77.5%的mIoU/102.6FPS。
开源地址:
1简介
语义分割任务旨在准确预测图像中每个像素的标签。广泛应用于医学影像、自动驾驶、视频会议、半自动注释等应用。
随着深度学习的不断进步,提出了许多基于卷积神经网络的语义分割方法。FCN它是第一个通过端到端和像素对像素进行训练的全卷积网络。它提出了语义分割中的原始编解码器架构,并在后续方法中得到了广泛的应用。
为了获得更高的精度,PSPNet利用一个金字塔池化模块
聚合全局上下文信息,SFNet提出了一个flow alignment module
加强特征表达。
但由于这些模型的计算成本较高,不适合实时应用。为了加快推理速度,Espnet-v利用轻量级卷积从扩大的感觉野中提取特征。BiSeNetV2提出了bilateral segmentation network
,细节特征和语义特征分别提取。STDCSeg新的设计backboe,名为STDC
,以提高计算效率。然而,这些模型并不能在精度和速度之间取得令人满意的平衡。
在这项工作中,提出了一个人为设计的实时语义分割网络PP-LiteSeg
。如图2所示,PPLiteSeg由三个新模块组成:
(FLD)
(UAFM)
(SPPM)
编码器在语义分割模型中提取层次特征、解码器集成和上采样特征。
从编码器low-level到high-level通道数量增加,空间尺寸减小;
从解码器high-level到low-level空间大小增加;
最近,在轻量级语义分割模型的解码模块中,特征通道数保持相同。因此,本文提出了一个更灵活的轻量级解码器(FLD),它逐渐减少了通道的数量,增加了特征的空间大小。此外,提出的解码器可以很容易地根据编码器进行调整。灵活的设计平衡了编码器和解码器的计算复杂性,提高了整个模型的计算效率。
。它通常通过器中融合low-level到high-level实现特征。然而,现有方法中的集成模块通常具有较高的计算成本。在这项工作中,作者提出了一个统一的注意力集成模块(UAFM)有效加强特征表达。如图4所示,UAFM首先,使用注意模块产生权重,然后将输入特性与集成。在UAFM注意模块有两种,即空间注意模块
和通道注意力
模块充分利用了空间与通道之间的关系,具有输入特征。
,但以往的聚合模块对于实时网络来说依然很耗时。基于PPM作者设计了一个简单的金字塔池模块(SPPM),它减少了中间通道和输出通道,消除了Shortcut,并用一个add操作替换了concat操作。实验结果表明,SPPM分割精度可以在较低的计算成本下提高。
通过在Cityscapes和CamVid数据集上的大量实验评估了所提出的PP-LiteSeg。如下图所示,PP-LiteSeg实现了分割精度和推理速度之间的优越权衡。
具体来说,PP-LiteSeg在Cityscapes72测试集.0%的mIoU/273.6FPS和77.5%的mIoU/102.6FPS。
主要贡献总结如下:
提出了灵活的轻量级解码器(FLD),降低了解码器的冗余性,平衡了编码器和解码器的计算成本。
提出了统一的注意力集成模块(UAFM),利用通道和空间注意力加强特征表达。
提出了一个简单的金字塔池模块(SPPM)聚合全局上下文信息。在增加少量推理时间的同时,提高了分割精度。
在上述模块的基础上,提出了实时语义分割模型PP-LiteSeg。大量的实验证明了这一点SOTA性能。
2相关工作
2.1 语义分割
FCN它是第一个用于语义分割的完全卷积网络。它以端到端和像素训练像素。此外,任何大小的图像都可以通过FCN分割FCN后来提出了各种设计方法。
SegNet将编码器中最大的池操作应用于解码器中的上采样操作。因此,解码器中的信息被重复使用,解码器也可以产生精细的特性。
PSPNe提出金字塔池模块聚合局部和全局信息,可有效提高分割精度。
此外,最近的语义分割方法利用了Transformer结构,实现了更高的精度。
2.2 实时语义分割
为满足语义分割的实时需要,提出了轻量级模块设计、双分支架构等多种方法early-downsampling
战略、多尺度图像级联网络等。
ENet采用early-downsampling
降低处理大图像和特征图的计算成本。为提高效率,ICNet设计了多分辨率图像级联网络。
而BiSeNet细节特征和语义特征是基于双边网络分割的。双边网络是轻量级的,所以推理速度很快。
STDCSeg提出了通道减少野放大的感觉STDC模块
,并设计了有效的backbone,它可以用较低的计算成本来表示。为消除双分支网络中的冗余计算,STDCSeg以详细的GT从而进一步提高效率。
EspNetv2使用point-wise和depth-wise可分离卷积,从扩大的感觉野中学习特征,计算友好。
2.3 特征集成模块
特征集成模块常用于语义分割,主要用于加强特征表达。除了元素级的和谐和串联方法外,研究人员还提出了以下方法。
在BiSeNet中,BGA模块采用了element-wise mul
该方法融合了空间分支和上下文分支的特点。
为了使用high-level增强上下文的特点,DFANet以stage-wise
和subnet-wise
融合特征的方式。
为解决错位问题,SFNet和AlignSeg首先通过CNN模块学习转换偏移,然后将转换偏移应用于网格样本操作,产生详细的特征。SFNet详细设计flow alignment module
。AlignSeg设计了aligned feature aggregation module
和aligned context modeling module
。
FaPN特征错位问题通过应用可变形卷积来解决。
3本文方法
3.1 Flexible and Lightweight Decoder
一般来说,编码器使用一系列分为几个阶段的层来提取层次特征。low-level到high-level通道数量逐渐增加,空间尺寸逐渐减小。该设计平衡了各阶段的计算成本,保证了编码器的效率。
解码器也有几个阶段,负责集成和采样特征。虽然特征的空间大小从high-level增加到low-level,但在最近的轻量级模型中,有解码器level都保持着相同的特征通道。low-level计算阶段的成本远远大于high-level这带来了计算成本low-level阶段计算冗余。
为了提高解码器的效率,本文提出了灵活性轻量级解码器(FLD)。如图3所示,FLD逐渐将特征的通道从high-level减少到low-level层次。FLD可以很容易地调整计算成本,以实现编码器和解码器之间更好的平衡。虽然FLD中的特征通道在减少,但实验表明,PP-LiteSeg与其他方法相比,依然具有竞争力的精度。
3.2 Unified Attention Fusion Module
如上所述,融合多层次特征是实现高分割精度的关键。除了element-wise summation和concatenation
方法外,研究人员还提出了几种方法,如SFNet、FaPN和AttaNet。在这项工作中提出了一个统一的注意力融合模块(UAFM),它应用通道和空间注意力来丰富融合特征表示。
1、UAFM Framework
如图4(a)所示,UAFM利用一个注意力模块来产生权重,并通过Mul和Add操作将输入特性与相融合。
详细地说,输入特征被表示为和。是深层模块的输出,而是编码器的对应模块。请注意,它们有相同的通道数量。
UAFM首先利用双线性插值操作将上采样到相同大小,而上采样特征记为。然后,注意力模块以和作为输入,产生权重。
请注意,注意力模块可以是一个插件,如空间注意力模块、通道注意力模块等。
然后,获得注意力加权后,分别对和进行element-wise Mul
。
最后,UAFM对注意力加权后的特征进行element-wise Add
,并输出融合特征。这里将把上述过程表述为公式1:
class UAFM(nn.Layer):
"""
The base of Unified Attention Fusion Module.
Args:
x_ch (int): The channel of x tensor, which is the low level feature.
y_ch (int): The channel of y tensor, which is the high level feature.
out_ch (int): The channel of output tensor.
ksize (int, optional): The kernel size of the conv for x tensor. Default: 3.
resize_mode (str, optional): The resize model in unsampling y tensor. Default: bilinear.
"""
def __init__(self, x_ch, y_ch, out_ch, ksize=3, resize_mode='bilinear'):
super().__init__()
self.conv_x = layers.ConvBNReLU(
x_ch, y_ch, kernel_size=ksize, padding=ksize // 2, bias_attr=False)
self.conv_out = layers.ConvBNReLU(
y_ch, out_ch, kernel_size=3, padding=1, bias_attr=False)
self.resize_mode = resize_mode
def check(self, x, y):
assert x.ndim == 4 and y.ndim == 4
x_h, x_w = x.shape[2:]
y_h, y_w = y.shape[2:]
assert x_h >= y_h and x_w >= y_w
def prepare(self, x, y):
x = self.prepare_x(x, y)
y = self.prepare_y(x, y)
return x, y
def prepare_x(self, x, y):
x = self.conv_x(x)
return x
def prepare_y(self, x, y):
y_up = F.interpolate(y, paddle.shape(x)[2:], mode=self.resize_mode)
return y_up
def fuse(self, x, y):
out = x + y
out = self.conv_out(out)
return out
def forward(self, x, y):
"""
Args:
x (Tensor): The low level feature.
y (Tensor): The high level feature.
"""
self.check(x, y)
x, y = self.prepare(x, y)
out = self.fuse(x, y)
return out
2、Spatial Attention Module
空间注意力模块的动机是利用空间间关系来产生一个权重,该权重表示输入特征中每个像素的重要性。如图4(b)所示,给定输入特征,即和,首先沿着通道轴进行平均和最大化操作生成4个特征,其中维度为。然后,将这4个特征concat到一个特征上。对于concat的特征,将卷积和sigmoid运算应用于输出。空间注意力模块的公式如式2所示:
此外,空间注意力模块还可以灵活地实现,例如去除最大池化操作以降低计算成本。
Paddle实现如下:
class UAFM_SpAtten(UAFM):
"""
The UAFM with spatial attention, which uses mean and max values.
Args:
x_ch (int): The channel of x tensor, which is the low level feature.
y_ch (int): The channel of y tensor, which is the high level feature.
out_ch (int): The channel of output tensor.
ksize (int, optional): The kernel size of the conv for x tensor. Default: 3.
resize_mode (str, optional): The resize model in unsampling y tensor. Default: bilinear.
"""
def __init__(self, x_ch, y_ch, out_ch, ksize=3, resize_mode='bilinear'):
super().__init__(x_ch, y_ch, out_ch, ksize, resize_mode)
self.conv_xy_atten = nn.Sequential(
layers.ConvBNReLU(
4, 2, kernel_size=3, padding=1, bias_attr=False),
layers.ConvBN(
2, 1, kernel_size=3, padding=1, bias_attr=False))
def fuse(self, x, y):
"""
Args:
x (Tensor): The low level feature.
y (Tensor): The high level feature.
"""
atten = helper.avg_max_reduce_channel([x, y])
atten = F.sigmoid(self.conv_xy_atten(atten))
out = x * atten + y * (1 - atten)
out = self.conv_out(out)
return out
3、Channel Attention Module
通道注意力模块的关键概念是利用通道间的关系来产生一个权重,这表明了每个通道在输入特征中的重要性。如图4(c)所示,所提出的通道注意力模块利用平均池化和最大池化操作来压缩输入特征的空间维数。
此过程使用维度生成4个特性。然后,它将这4个特征沿着通道轴连接起来,并执行卷积和sigmoid运算,以产生一个权重α。简而言之,通道注意力模块的过程可以表述为公式3:
Paddle实现如下:
class UAFM_ChAtten(UAFM):
"""
The UAFM with channel attention, which uses mean and max values.
Args:
x_ch (int): The channel of x tensor, which is the low level feature.
y_ch (int): The channel of y tensor, which is the high level feature.
out_ch (int): The channel of output tensor.
ksize (int, optional): The kernel size of the conv for x tensor. Default: 3.
resize_mode (str, optional): The resize model in unsampling y tensor. Default: bilinear.
"""
def __init__(self, x_ch, y_ch, out_ch, ksize=3, resize_mode='bilinear'):
super().__init__(x_ch, y_ch, out_ch, ksize, resize_mode)
self.conv_xy_atten = nn.Sequential(
layers.ConvBNAct(
4 * y_ch,
y_ch // 2,
kernel_size=1,
bias_attr=False,
act_type="leakyrelu"),
layers.ConvBN(
y_ch // 2, y_ch, kernel_size=1, bias_attr=False))
def fuse(self, x, y):
"""
Args:
x (Tensor): The low level feature.
y (Tensor): The high level feature.
"""
atten = helper.avg_max_reduce_hw([x, y], self.training)
atten = F.sigmoid(self.conv_xy_atten(atten))
out = x * atten + y * (1 - atten)
out = self.conv_out(out)
return out
3.3 Simple Pyramid Pooling Module
如图5所示,提出了一个(SPPM)。
首先利用金字塔池化模块来融合输入特征(金字塔池化模块有3个全局平均池化操作,bin大小分别为1×1、2×2和4×4)。
然后,在输出特征之后,再进行卷积和上采样操作。对于卷积操作,kernel大小为1×1,输出通道小于输入通道。
最后,上采样的特征,并应用卷积运算来产生细化的特征。
Paddle实现如下:
class PPContextModule(nn.Layer):
"""
Simple Context module.
Args:
in_channels (int): The number of input channels to pyramid pooling module.
inter_channels (int): The number of inter channels to pyramid pooling module.
out_channels (int): The number of output channels after pyramid pooling module.
bin_sizes (tuple, optional): The out size of pooled feature maps. Default: (1, 3).
align_corners (bool): An argument of F.interpolate. It should be set to False
when the output size of feature is even, e.g. 1024x512, otherwise it is True, e.g. 769x769.
"""
def __init__(self,
in_channels,
inter_channels,
out_channels,
bin_sizes,
align_corners=False):
super().__init__()
self.stages = nn.LayerList([
self._make_stage(in_channels, inter_channels, size)
for size in bin_sizes
])
self.conv_out = layers.ConvBNReLU(
in_channels=inter_channels,
out_channels=out_channels,
kernel_size=3,
padding=1)
self.align_corners = align_corners
def _make_stage(self, in_channels, out_channels, size):
prior = nn.AdaptiveAvgPool2D(output_size=size)
conv = layers.ConvBNReLU(
in_channels=in_channels, out_channels=out_channels, kernel_size=1)
return nn.Sequential(prior, conv)
def forward(self, input):
out = None
input_shape = paddle.shape(input)[2:]
for stage in self.stages:
x = stage(input)
x = F.interpolate(
x,
input_shape,
mode='bilinear',
align_corners=self.align_corners)
if out is None:
out = x
else:
out += x
out = self.conv_out(out)
return out
3.4 模型架构
所提的PP-LiteSeg的体系结构如上图所示。
PP-LiteSeg主要由3个模块组成:encoder
, aggregation
和decoder
。
首先,给定一个输入图像,PP-Lite利用一个通用的轻量级网络作为编码器来提取层次特征。
选择STDCNet是因为其出色的性能。STDCNet有5个阶段,每个阶段的stride=2,所以最终的特征大小是输入图像的1/32。
如表1所示,提供了2个版本的PP-LiteSeg,即PP-LiteSeg-T和PP-LiteSeg-B,其中的编码器分别为STDC1和STDC2。。值得注意的是,将SSLD方法应用于编码器的训练,得到了增强的预训练权重,有利于分割训练的收敛。
其次,PP-LiteSeg采用SPPM对随机依赖进行建模。SPPM以编码器的输出特征作为输入,生成一个包含全局上下文信息的特征。
最后,PP-LiteSeg利用提出的FLD逐步融合多层次特征并输出得到的图像。
FLD由2个UAFM和一个分割头组成。为了提高效率,在UAFM中采用了空间注意力模块。
每个UAFM以2个特征作为输入,即由编码器各阶段提取的low-level特征,由SPPM或更深的融合模块生成的high-level特征。后者的UAFM输出融合的特征,下采样比为1/8。
在分割head中执行Conv-BN-Relu操作,将1/8个下采样特征的通道减少到类的数量。采用上采样操作将特征大小扩展到输入图像大小,并采用argmax操作预测每个像素的标签。并采用Online Hard Example Mining交叉熵损失对模型进行了优化。
损失函数的Paddle实现如下:
import paddle
from paddle import nn
import paddle.nn.functional as F
from paddleseg.cvlibs import manager
@manager.LOSSES.add_component
class OhemCrossEntropyLoss(nn.Layer):
"""
Implements the ohem cross entropy loss function.
Args:
thresh (float, optional): The threshold of ohem. Default: 0.7.
min_kept (int, optional): The min number to keep in loss computation. Default: 10000.
ignore_index (int64, optional): Specifies a target value that is ignored
and does not contribute to the input gradient. Default ``255``.
"""
def __init__(self, thresh=0.7, min_kept=10000, ignore_index=255):
super(OhemCrossEntropyLoss, self).__init__()
self.thresh = thresh
self.min_kept = min_kept
self.ignore_index = ignore_index
self.EPS = 1e-5
def forward(self, logit, label):
"""
Forward computation.
Args:
logit (Tensor): Logit tensor, the data type is float32, float64. Shape is
(N, C), where C is number of classes, and if shape is more than 2D, this
is (N, C, D1, D2,..., Dk), k >= 1.
label (Tensor): Label tensor, the data type is int64. Shape is (N), where each
value is 0 <= label[i] <= C-1, and if shape is more than 2D, this is
(N, D1, D2,..., Dk), k >= 1.
"""
if len(label.shape) != len(logit.shape):
label = paddle.unsqueeze(label, 1)
# get the label after ohem
n, c, h, w = logit.shape
label = label.reshape((-1, ))
valid_mask = (label != self.ignore_index).astype('int64')
num_valid = valid_mask.sum()
label = label * valid_mask
prob = F.softmax(logit, axis=1)
prob = prob.transpose((1, 0, 2, 3)).reshape((c, -1))
if self.min_kept < num_valid and num_valid > 0:
# let the value which ignored greater than 1
prob = prob + (1 - valid_mask)
# get the prob of relevant label
label_onehot = F.one_hot(label, c)
label_onehot = label_onehot.transpose((1, 0))
prob = prob * label_onehot
prob = paddle.sum(prob, axis=0)
threshold = self.thresh
if self.min_kept > 0:
index = prob.argsort()
threshold_index = index[min(len(index), self.min_kept) - 1]
threshold_index = int(threshold_index.numpy()[0])
if prob[threshold_index] > self.thresh:
threshold = prob[threshold_index]
kept_mask = (prob < threshold).astype('int64')
label = label * kept_mask
valid_mask = valid_mask * kept_mask
# make the invalid region as ignore
label = label + (1 - valid_mask) * self.ignore_index
label = label.reshape((n, 1, h, w))
valid_mask = valid_mask.reshape((n, 1, h, w)).astype('float32')
loss = F.softmax_with_cross_entropy(
logit, label, ignore_index=self.ignore_index, axis=1)
loss = loss * valid_mask
avg_loss = paddle.mean(loss) / (paddle.mean(valid_mask) + self.EPS)
label.stop_gradient = True
valid_mask.stop_gradient = True
return avg_loss
4实验
4.1 消融实验
通过消融实验,验证了所提模块的有效性。实验在比较中选择PP-LiteSeg-B2,并使用相同的训练和推理配置。Baseline模型为无该模块的PP-LiteSeg-B2,而解码器中的特征通道数为96个,融合方法为 element-wise summation
。
通过表3可以发现,PP-LiteSeg-B2中的FLD使mIoU提高了0.17%。添加SPPM和UAFM也提高了分割精度,而推理速度略有降低。基于提出的3个模块,PP-LiteSeg-B2以102.6FPS速度达到78.21mIoU。mIoU与Baseline模型相比提高了0.71%。
图6提供了定性的比较。可以观察到,当逐个添加FLD、SPPM和UAFM时,预测的图像更符合GT。总之,本文提出的模块对于语义分割是有效的。
4.2 Cityscapes
表2给出了各种方法的模型信息、输入分辨率、mIoU和FPS。图1提供了分割精度和推理速度的直观比较。
实验结果表明,所提出的PP-LiteSeg方法在精度和速度之间实现了最先进的权衡。
具体来说,PP-LiteSeg-T1以273.6FPS速度达到了72.0%的mIoU,这意味着最快的推理速度和竞争精度。
PPLiteSeg-B2的分辨率为768×1536,达到了最好的精度,即验证集的78.2%mIoU,测试集的77.5%mIoU。此外,使用与STDC-Seg相同的编码器和输入分辨率,PPLiteSeg显示出更好的性能。
4.3 CamVid
为了进一步证明PP-LiteSeg的能力,作者还在CamVid数据集上进行了实验。与其他工作类似,训练和推理的输入分辨率是960×720。
如表4所示,PP-LiteSeg-T达到222.3FPS,比其他方法快12.5%以上。PP-LiteSeg-B的精度最好(75.0%mIoU/154.8FPS)。
总的来说,比较显示PP-LiteSeg在Camvid上实现了精度和速度之间的最先进的权衡。
5参考
[1].PP-LiteSeg: A Superior Real-Time Semantic Segmentation Model
本文仅做学术分享,如有侵权,请联系删文。
后台回复:
后台回复:
后台回复:
1.面向自动驾驶领域的多传感器数据融合技术
2.面向自动驾驶领域的3D点云目标检测全栈学习路线!(单模态+多模态/数据+代码)3.彻底搞透视觉三维重建:原理剖析、代码讲解、及优化改进4.国内首个面向工业级实战的点云处理课程5.激光-视觉-IMU-GPS融合SLAM算法梳理和代码讲解6.彻底搞懂视觉-惯性SLAM:基于VINS-Fusion正式开课啦7.彻底搞懂基于LOAM框架的3D激光SLAM: 源码剖析到算法优化8.彻底剖析室内、室外激光SLAM关键算法原理、代码和实战(cartographer+LOAM +LIO-SAM)
9.从零搭建一套结构光3D重建系统[理论+源码+实践]
10.单目深度估计方法:算法梳理与代码实现
11.自动驾驶中的深度学习模型部署实战
12.相机模型与标定(单目+双目+鱼眼)
13.重磅!四旋翼飞行器:算法与实战
14.ROS2从入门到精通:理论与实战
15.国内首个3D缺陷检测教程:理论、源码与实战
扫码添加小助手微信,可申请加入3D视觉工坊-学术论文写作与投稿 微信交流群,旨在
▲长按加微信群或投稿
▲长按关注公众号
学习3D视觉核心技术,扫描查看介绍,3天内无条件退款
圈里有高质量教程资料、可答疑解惑、助你高效解决问题