用 DeepLab-V3+ 模型做 Cityscapes 的 segmentation 任务,in Keras/TensorFlow 2.8

文章目录

前言

DeepLab-V3+ 是一个很好的 segmentation 架构。→ arXiv 论文链接在此
可以用 Inception-ResNet V2 作为脊柱 backbone,搭建一个 DeepLab-V3+ 模型,在 Cityscapes 数据集上做 segmentation 任务,效果如下。

  1. 在训练集上的效果:
    说明:每一行 3 张图片,其中左图为数据集的原图,最右图为模型预测的结果,即 segmentation mask,中间的图片是把原图和 mask 重叠的结果,以方便检查 segmentation mask 的准确度。
    用 DeepLab-V3+ 模型做 Cityscapes 的 segmentation 任务,in Keras/TensorFlow 2.8
    用 DeepLab-V3+ 模型做 Cityscapes 的 segmentation 任务,in Keras/TensorFlow 2.8
  2. 在验证集上的效果:
    用 DeepLab-V3+ 模型做 Cityscapes 的 segmentation 任务,in Keras/TensorFlow 2.8
    用 DeepLab-V3+ 模型做 Cityscapes 的 segmentation 任务,in Keras/TensorFlow 2.8
  3. 在测试集上的效果:
    用 DeepLab-V3+ 模型做 Cityscapes 的 segmentation 任务,in Keras/TensorFlow 2.8

下面用 Keras/TensorFlow 2.8 来搭建这个 DeepLab-V3+ 模型。

; 1. DeepLab-V3+ 的主体架构。

DeepLab-V3+ 的结构图如下。

用 DeepLab-V3+ 模型做 Cityscapes 的 segmentation 任务,in Keras/TensorFlow 2.8

DeepLab-V3+ 的主体架构有 3 个关键点:

  1. 模型分为前后 2 个部分。前半部分用于对图片进行理解(即上面结构图的左半部分),后半部分则用于给图片各个区域进行分类(上面结构图的右半部分),生成 mask。前半部分也叫做编码器 encoder,而后半部分也叫做解码器 decoder。
  2. 编码器顶部,使用了 ASPP 模块(Atrous Spatial Pyramid Pooling)。ASPP 的主要作用是,借助 atrous convolution(也叫 dilation convolution)使得模型有 5 种不同大小的感受域(3 个空洞卷积,1 个普通卷积,再加 1 个平均池化),将小的特征信息和大的特征信息进行混合,从而获得更好的场景信息 semantic information。 ASPP 的另一个名字是 DSPP(Dilated Spatial Pyramid Pooling)。
  3. 在解码器 decoder 部分,从 encoder 获得了一个底层特征 low-level features,作为输入注入到解码器中(即上面结构图中,左右两部分中间位置,进行横向连接的那个箭头)。这个底层特征的作用,是使得模型预测结果 mask 中,各个类别的轮廓更加准确。(BTW,顺着这个思路,我对 DeepLab-V3+ 模型做了一点修改,增加了一个底层特征注入到解码器中,以使得各个类别的轮廓更清晰,似乎有一点作用。用 keras.utils.plot_model 查看模型结构时,可以看到我增加的这个连接。)

  4. 使用 Cityscapes 数据集。

  5. Cityscapes 数据集的内容很多。在这个模型中,只需要用到数据集的 2 个文件夹, “leftImg8bit_foggy ” 和 “gtFine”。

  6. 需要制作 colormap 文件。colormap 中保存的是颜色映射关系,即 Cityscapes 的 34 个类别和 34 种颜色的一一对应关系。我已经根据 Cityscapes 官方的颜色对应关系制作好了这个 colormap_cityscapes.json 文件,可以直接使用。如果是应用到其它数据集,需要自行制作其它数据集的 colormap 文件。

  7. 环境配置和使用方法。

为了方便训练模型和调试,需要使用 Jupyter Lab,在 cityscapes_deeplab_v3plus.ipynb 文件中进行模型训练。
而其它函数,则集中放在 Python 文件 deeplab_v3_plus.py 中。
TensorFlow: 2.8

使用时,只需要在 Jupyter Lab 中打开 cityscapes_deeplab_v3plus.ipynb 文件操作即可。下面为 Jupyter Lab 中训练模型部分的图示。

用 DeepLab-V3+ 模型做 Cityscapes 的 segmentation 任务,in Keras/TensorFlow 2.8
如果显存不够大,可以设置以下2项:
[En]

If the video memory is not large enough, you can set the following 2 items:

  1. 设置图片大小。在 deeplab_v3_plus.py 中,设置 MODEL_IMAGE_SIZE(实际设置 model_image_height 即可,会自动计算 MODEL_IMAGE_SIZE)。MODEL_IMAGE_SIZE 是输入给模型的图片大小。
  2. 设置批次大小。在 cityscapes_deeplab_v3plus.ipynb 文件中,设置 BATCH_SIZE 。

如果是单张 RTX 3090 显卡,可以使用模型默认的设置,MODEL_IMAGE_SIZE = 512 x 1024 , BATCH_SIZE = 8。如果显存不够,可以降低图片大小为 320 x 640,或降低批次大小。

; 4. 参考代码。

我的模型参考了 Keras 官方代码。→ 官方代码链接在此 官方代码使用的是一个不同的 backbone 和一个不同的数据集,有兴趣的同学可以学习一下。
需要注意的是,官方代码中有一点不足,即在对标签 mask 进行图片缩放操作时,使用了 bilinear 插值方法。但实际上,应该使用最邻近点插值方法(在 TensorFlow 中为 tf.image.ResizeMethod.NEAREST_NEIGHBOR。这个问题属于吴恩达在深度学习课程中所说的 subtlety,即容易被忽视的要点)。经验证后发现,使用官方代码的插值方法,会使得准确度下降 2%。

举例说明为什么只能用最邻近点插值方法。假设 mask 图片中有一个桌子,桌子上面放着一个杯子。桌子的类别为 0,而杯子的类别为 2。当使用 bilinear 或 bicubic 插值方法时,可能会插入一个值 1,但是类别 1 有可能是代表了一辆火车。这时 mask 中就会出现一个错误的情形,即一辆火车被夹在一个桌子和一个杯子中间,从而导致模型学习到一些错误的信息。反之,如果使用最邻近点插值方法,就不会出现这种问题。

  1. github 下载链接。

下面是创建 DeepLab-V3+ 模型的主函数,其它完整数据,可以在 github 下载。→ 下载链接在此

def inceptionresnetv2_deeplabv3plus(model_image_size, num_classes,
                                    rate_dropout=0.1):
    """使用 Inception-ResNet V2 作为 backbone,创建一个 DeepLab-V3+ 模型。

    Arguments:
        model_image_size: 一个整数型元祖,表示输入给模型的图片大小,格式为
            (height, width, depth)。注意对于 Inception-ResNet V2,要求输入图片的像
            素值必须转换到 [-1, 1] 之间。
            为了便于说明数组形状,这里假设模型输入图片的大小为 (512, 1024, 3)。
        num_classes: 一个整数,是模型中需要区分类别的数量。
        rate_dropout: 一个浮点数,范围是 [0, 1],表示 SpatialDropout2D 层的比例。

    Returns:
        model: 一个以 Inception-ResNet V2 为脊柱的 DeepLab-V3+ 模型。
"""

    keras.backend.clear_session()

    model_input = keras.Input(shape=(*model_image_size, 3))
    conv_base = keras.applications.InceptionResNetV2(
        include_top=False,
        input_tensor=model_input)

    low_level_feature_backbone = conv_base.get_layer(
        'activation_4').output

    for _ in range(2):
        low_level_feature_backbone = convolution_block(
            low_level_feature_backbone)
        low_level_feature_backbone = keras.layers.Conv2DTranspose(
            filters=256, kernel_size=3,
            kernel_initializer=keras.initializers.HeNormal())(
            low_level_feature_backbone)

    low_level_feature_backbone = convolution_block(
        low_level_feature_backbone, num_filters=256, kernel_size=1)

    if rate_dropout != 0:
        low_level_feature_backbone = keras.layers.SpatialDropout2D(
            rate=rate_dropout)(low_level_feature_backbone)

    encoder_backbone = conv_base.get_layer('block17_10_ac').output

    encoder_backbone = dilated_spatial_pyramid_pooling(encoder_backbone)

    decoder_backbone = keras.layers.Conv2DTranspose(
        256, kernel_size=3, kernel_initializer=keras.initializers.HeNormal())(
        encoder_backbone)

    decoder_backbone = keras.layers.UpSampling2D(
        size=(4, 4), interpolation='bilinear')(decoder_backbone)

    if rate_dropout != 0:
        decoder_backbone = keras.layers.SpatialDropout2D(rate=rate_dropout)(
            decoder_backbone)

    x = keras.layers.Concatenate()(
        [decoder_backbone, low_level_feature_backbone])

    for _ in range(2):
        x = convolution_block(x)

        if rate_dropout != 0:
            x = keras.layers.SpatialDropout2D(rate=rate_dropout)(x)

    x = keras.layers.UpSampling2D(size=(4, 4), interpolation='bilinear')(x)
    if rate_dropout != 0:
        x = keras.layers.SpatialDropout2D(rate=rate_dropout)(x)

    down_sampling_1 = model_input
    down_sampling_1 = convolution_block(down_sampling_1, num_filters=8,
                                        separableconv=False)
    for _ in range(2):
        down_sampling_1 = convolution_block(down_sampling_1, num_filters=8)

    if rate_dropout != 0:
        down_sampling_1 = keras.layers.SpatialDropout2D(rate=rate_dropout)(
            down_sampling_1)

    x = keras.layers.Concatenate()([down_sampling_1, x])
    for _ in range(2):
        x = convolution_block(x, num_filters=64)

    model_output = keras.layers.Conv2D(
        num_classes, kernel_size=(1, 1), padding='same')(x)

    model = keras.Model(inputs=model_input, outputs=model_output)

    return model

Original: https://blog.csdn.net/drin201312/article/details/123353599
Author: 杭州的平湖秋月
Title: 用 DeepLab-V3+ 模型做 Cityscapes 的 segmentation 任务,in Keras/TensorFlow 2.8

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/514080/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球