当前位置:   article > 正文

语义分割|学习记录(4)膨胀卷积(空洞卷积 )

膨胀卷积


前言

这篇博文主要来介绍一下膨胀卷积的知识,膨胀卷积(Dilated convolution)也叫做空洞卷积(Atrous convolution)。我第一次接触到这个词实在看deeplabv3+的论文的时候碰见的这个词,在这里进行详细的介绍。

一、膨胀卷积

下图为普通卷积:k=3,r=1,p=0,s=1
在这里插入图片描述
下图为膨胀卷积:k=3,r=2,p=0,s=1在这里插入图片描述
我们可以发现这两种卷积同样使用3*3的kernel,但是在膨胀卷积中,kernel存在间隙,我们称为膨胀因子r

膨胀卷积有什么用呢?

  • 增大感受野
  • 保持原输入特征图的W,H(如上图,实际使用时候将padding设置为1这样就可以保证特征图大小不变)

为什么要使用膨胀卷积呢?

我们在图像分类网络中一般要进行多次多倍数的下采样,比如下采样32倍,之后还要通过上采样还原会原图的大小,但是过多的下采样对我们还原回原图是有很大影响的。拿VGG16网络来举例子,在VGG16网络中通过maxpooling操作对图片进行下采样降低长宽,但是这样做会丢失一部分细节信息和特别小的目标。并且无法通过上采样还原回来。
有人会说,那我们将maxpooling层去掉可以吗?去掉maxpooling层也不是一个好方法,这样会降低感受野。
所以膨胀卷积就解决了这个问题,既可以增大感受野,还能保证输入输出特征图的高和宽不会变换。

二、gridding effect

Understanding Convolution for Semantic Segmentation
在这里插入图片描述
下图有四个卷积层:layer1到Layer4进行了3次膨胀卷积的操作卷积核为 3 ∗ 3 3*3 33,膨胀系数为r2
在这里插入图片描述
详细的我们看一下layer2上我们用到了Layer1上的哪些数据呢?
我们看到layer2上一个像素它会使用到如下图9个layer1位置上的参数
在这里插入图片描述
再看一下layer3上我们用到了Layer1上的哪些数据呢?
我们看到layer3上一个像素它会使用到如下图25个layer1位置上的参数
在这里插入图片描述
再看一下layer4上我们用到了Layer1上的哪些数据呢?
我们看到layer4上一个像素它会使用到如下图49个layer1位置上的参数
在这里插入图片描述
这时我们可以观察到,通过膨胀卷积我们利用到的底层图像像素并不是连续的,在每个非零元素间都是有间隔的,这样势必导致特征信息的丢失,这个就是gridding effect问题,我们也要想办法尽量避免gridding effect问题。

如果我们将上图的三次膨胀卷积操作的膨胀因子设计r1,r2,r3的形式:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这样设置膨胀系数后,我们所利用到的底层的信息就是连续的啦!
我们再来看一组实验,如果用普通卷积的话:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
很明显我们可以看出来在参数相同的情况下普通卷积的感受野要比使用膨胀卷积的感受野要小。

三、使用多个膨胀卷积的时候,怎样设置膨胀系数?

Understanding Convolution for Semantic Segmentation这篇论文中给出了方法。
我们在刚刚的实验中就可以看到,使用膨胀系数分别是1,2,3的膨胀卷积进行连续的卷积操作时候的效果要比使用相同膨胀系数的卷积的效果要好。那么具体该怎么设计系数呢?作者提出了HDC设计准则:
请添加图片描述
我们用实际代码来测试一下论文中这两组设置膨胀卷积系数的例子:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap


def dilated_conv_one_pixel(center: (int, int),
                           feature_map: np.ndarray,
                           k: int = 3,
                           r: int = 1,
                           v: int = 1):
    """
    膨胀卷积核中心在指定坐标center处时,统计哪些像素被利用到,
    并在利用到的像素位置处加上增量v
    Args:
        center: 膨胀卷积核中心的坐标
        feature_map: 记录每个像素使用次数的特征图
        k: 膨胀卷积核的kernel大小
        r: 膨胀卷积的dilation rate
        v: 使用次数增量
    """
    assert divmod(3, 2)[1] == 1

    # left-top: (x, y)
    left_top = (center[0] - ((k - 1) // 2) * r, center[1] - ((k - 1) // 2) * r)
    for i in range(k):
        for j in range(k):
            feature_map[left_top[1] + i * r][left_top[0] + j * r] += v


def dilated_conv_all_map(dilated_map: np.ndarray,
                         k: int = 3,
                         r: int = 1):
    """
    根据输出特征矩阵中哪些像素被使用以及使用次数,
    配合膨胀卷积k和r计算输入特征矩阵哪些像素被使用以及使用次数
    Args:
        dilated_map: 记录输出特征矩阵中每个像素被使用次数的特征图
        k: 膨胀卷积核的kernel大小
        r: 膨胀卷积的dilation rate
    """
    new_map = np.zeros_like(dilated_map)
    for i in range(dilated_map.shape[0]):
        for j in range(dilated_map.shape[1]):
            if dilated_map[i][j] > 0:
                dilated_conv_one_pixel((j, i), new_map, k=k, r=r, v=dilated_map[i][j])

    return new_map


def plot_map(matrix: np.ndarray):
    plt.figure()

    c_list = ['white', 'blue', 'red']
    new_cmp = LinearSegmentedColormap.from_list('chaos', c_list)
    plt.imshow(matrix, cmap=new_cmp)

    ax = plt.gca()
    ax.set_xticks(np.arange(-0.5, matrix.shape[1], 1), minor=True)
    ax.set_yticks(np.arange(-0.5, matrix.shape[0], 1), minor=True)

    # 显示color bar
    plt.colorbar()

    # 在图中标注数量
    thresh = 5
    for x in range(matrix.shape[1]):
        for y in range(matrix.shape[0]):
            # 注意这里的matrix[y, x]不是matrix[x, y]
            info = int(matrix[y, x])
            ax.text(x, y, info,
                    verticalalignment='center',
                    horizontalalignment='center',
                    color="white" if info > thresh else "black")
    ax.grid(which='minor', color='black', linestyle='-', linewidth=1.5)
    plt.show()
    plt.close()


def main():
    # bottom to top
    dilated_rates = [1, 2, 3]
    # init feature map
    size = 31
    m = np.zeros(shape=(size, size), dtype=np.int32)
    center = size // 2
    m[center][center] = 1
    # print(m)
    # plot_map(m)

    for index, dilated_r in enumerate(dilated_rates[::-1]):
        new_map = dilated_conv_all_map(m, r=dilated_r)
        m = new_map
    print(m)
    plot_map(m)


if __name__ == '__main__':
    main()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98

r=[1,2,3]:
在这里插入图片描述
r=[1,2,5]
在这里插入图片描述
r=[1,2,9]
在这里插入图片描述

四、膨胀系数设置建议

  • 建议是将dilation rates设置为锯齿结构,例如[1,2,3,1,2,3]
  • 膨胀系数的公约数不大于1
    例如r=[2,4,8]:
    在这里插入图片描述
    如果这样设置膨胀系数,我们得到的特征图非零像素也不是连续的。

五、是否应用HDC设计准则的分割效果对比

在这里插入图片描述
第一行是GT,第二行是ResNet-DUC model,第三行是应用HDC设计准则,很明显可以看出科学的设置膨胀系数对分割效果带来的正面影响。

六、总结

  • 介绍了什么是膨胀卷积
  • 膨胀卷积的作用
  • 为什么要使用膨胀卷积
  • 膨胀卷积膨胀系数的确定方法和影响

参考资料

up主霹雳吧啦Wz原视频
程序源码出处
Understanding Convolution for Semantic Segmentation

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/473370
推荐阅读
相关标签
  

闽ICP备14008679号