语义分割网络DeepLab-v3的架构设计思想 下

空洞空间金字塔池化

空洞空间金字塔池化(ASPP)的思想是提供具有多尺度信息的模型。为了做到这一点,ASPP 添加了一系列具有不同扩张率的空洞卷积。这些扩张率是被设计用来捕捉大范围语境的。此外,为了增加全局的语境信息,ASPP 还通过全局平均池化(GAP)结合了图像级别的特征。

这个版本的 ASPP 包含 4 个并行的操作。它们分别是一个 1×1 的卷积以及三个 3×3 的卷积(扩张率分别是(6,12,18))。正如我们前面所提及的,现在,特征图的标称步长(nominal stride)是 16.

在原始实现的基础上,我们使用 513 x513 的裁剪尺寸进行训练和测试。因此,使用 16 的输出步长意味着 ASPP 接收大小为 32 x32 的特征向量。

此外,为了添加更多全局语境信息,ASPP 结合了图像级别的特征。首先,它将 GAP 应用于从最后一个空洞块输出的特征上。其次,所得特征被输入到具有 256 个滤波器的 1x 1 卷积中。最后,将结果进行双线性上采样到正确的维度大小。

@slim.add_arg_scope

def atrous_spatial_pyramid_pooling(net, scope, depth=256):

"""

ASPP consists of (a) one 1×1 convolution and three 3×3 convolutions with rates = (6, 12, 18) when output stride = 16

(all with 256 filters and batch normalization), and (b) the image-level features as described in https://arxiv.org/abs/1706.05587

扫描二维码关注公众号,回复: 1819971 查看本文章

:param net: tensor of shape [BATCH_SIZE, WIDTH, HEIGHT, DEPTH]

:param scope: scope name of the aspp layer

:return: network layer with aspp applyed to it.

"""

with tf.variable_scope(scope):

feature_map_size = tf.shape(net)

# apply global average pooling

image_level_features = tf.reduce_mean(net, [1, 2], name='image_level_global_pool', keep_dims=True)

image_level_features = slim.conv2d(image_level_features, depth, [1, 1], scope="image_level_conv_1x1", activation_fn=None)

image_level_features = tf.image.resize_bilinear(image_level_features, (feature_map_size[1], feature_map_size[2]))

at_pool1x1 = slim.conv2d(net, depth, [1, 1], scope="conv_1x1_0", activation_fn=None)

at_pool3x3_1 = slim.conv2d(net, depth, [3, 3], scope="conv_3x3_1", rate=6, activation_fn=None)

at_pool3x3_2 = slim.conv2d(net, depth, [3, 3], scope="conv_3x3_2", rate=12, activation_fn=None)

at_pool3x3_3 = slim.conv2d(net, depth, [3, 3], scope="conv_3x3_3", rate=18, activation_fn=None)

net = tf.concat((image_level_features, at_pool1x1, at_pool3x3_1, at_pool3x3_2, at_pool3x3_3), axis=3,

name="concat")

net = slim.conv2d(net, depth, [1, 1], scope="conv_1x1_output", activation_fn=None)

return net

最后,各个分支的特征都被通过连接操作结合成一个单独的向量。然后使用另一个 1×1(采用 BN,和 256 个滤波器)的卷积对这个输出进行卷积。

ASPP 之后,我们将结果输入到另一个 1×1 的卷积中去生成最终的分割逻辑。

实现细节

这个实现用 ResNet-50 作为特征提取器,Deeplab_v3 采取了以下网络配置:

输出步长=16

为新的空洞残差块(block 4)使用固定的多重网格空洞卷积率(1,2,4)

在最后一个空洞卷积残差块之后使用扩张率为(6,12,18)的 ASPP。

将输出步长设置为 16 有利于可持续地快速训练。与另一个输出步长 8 相比,输出步长为 16 使得空洞残差块处理的特征图比步长为 8 时处理的特征图小四倍。

将多重网格扩张率应用于空洞残差块内部的 3 个卷积。

最终,ASPP 中的三个并行的卷积得到了不同的扩张率——(6,12,18)。

在计算交叉熵损失函数之前,我们将分割逻辑调整为输入图像的大小。正如论文中所指出的,为了保持分辨率细节,调整分割逻辑的大小比调整真实标签的大小更好。

基于原始的训练过程,我们使用一个 0.5 到 2 之间的随机因子对每个图像做了扩展。此外,我们还对缩放后的图像做了随机的左右翻转。

最终,我们为训练和测试裁剪了 513 x513 大小的图像。

def deeplab_v3(inputs, args, is_training, reuse):

# mean subtraction normalization

inputs = inputs - [_R_MEAN, _G_MEAN, _B_MEAN]

# inputs has shape [batch, 513, 513, 3]

with slim.arg_scope(resnet_utils.resnet_arg_scope(args.l2_regularizer, is_training,

args.batch_norm_decay,

args.batch_norm_epsilon)):

resnet = getattr(resnet_v2, args.resnet_model)

_, end_points = resnet(inputs,

args.number_of_classes,

is_training=is_training,

global_pool=False,

spatial_squeeze=False,

output_stride=args.output_stride,

reuse=reuse)

with tf.variable_scope("DeepLab_v3", reuse=reuse):

# get block 4 feature outputs

net = end_points[args.resnet_model + '/block4']

net = atrous_spatial_pyramid_pooling(net, "ASPP_layer", depth=256, reuse=reuse)

net =slim.conv2d(net, args.number_of_classes, [1, 1], activation_fn=None,

normalizer_fn=None, scope='logits')

size = tf.shape(inputs)[1:3]

# resize the output logits to match the labels dimensions

# net = tf.image.resize_nearest_neighbor(net, size)

net = tf.image.resize_bilinear(net, size)

return net

为了实现残差网络 block4 中具有多重网格的空洞卷积,我们仅仅改变了 resnet_utils.py 文件中的以下这段代码:

...

with tf.variable_scope('unit_%d' % (i + 1), values=[net]):

# If we have reached the target output_stride, then we need to employ

# atrous convolution with stride=1 and multiply the atrous rate by the

# current unit's stride for use in subsequent layers.

if output_stride is not None and current_stride == output_stride:

# Only uses atrous convolutions with multi-graid rates in the last (block4) block

if block.scope == "block4":

net = block.unit_fn(net, rate=rate * multi_grid[i], **dict(unit, stride=1))

else:

net = block.unit_fn(net, rate=rate, **dict(unit, stride=1))

rate *= unit.get('stride', 1)

训练

为了训练网络,我们决定使用来自于《Semantic contours from inverse detectors》的扩增版的 Pascal VOC 数据集。

训练数据由 8252 张图像组成。训练集有 5623 张,验证集有 2299 张。为了使用原始的 VOC2012 验证数据集来测试模型,我们从验证集中删去了 558 张图像。这 558 张图片也出现在官方的 VOC 验证集中。此外,我还添加了来自 VOC 2012 训练集中的 330 幅图像,它们既没出现在 5623 张训练集中,也没出现在 2299 张的验证集中。最后,8252 张图像中的 10%(大约 825 张图像)用来验证,其余的图像留着训练。

注意,这与原始论文是不一样的:这次实现没有在 COCO 数据集上预训练。此外,论文中描述到的一些训练和评估技术也没有用到。

结果

模型能够在 PASCAL VOC 验证集上得到良好的结果。

像素准确率:大约 91%

平均准确率:大约 82%

均交并比(mIoU):大约 74%

频权交并比(FWIoU):大约 86%

以下是 PASCAL VOC 验证集的图像分割的结果。

结论

语义分割无疑是计算机视觉领域中最流行的领域之一。Deeplab 提供了一个传统编码器-解码器体系架构的替代方案。它提倡在多范围的语境中使用空洞卷积学习特征。

原文链接:https://medium.freecodecamp.org/diving-into-deep-convolutional-semantic-segmentation-networks-and-deeplab-v3-4f094fa387df

参考:http://baijiahao.baidu.com/s?id=1595995875370065359&wfr=spider&for=pc

猜你喜欢

转载自blog.csdn.net/fu6543210/article/details/80724617