膨胀卷积,也叫空洞卷积,Dilated Convolution,也有叫 扩张卷积;

空洞卷积 是 2016在ICLR(International Conference on Learning Representation)上被提出的,本身用在图像分割领域,被deepmind拿来应用到语音(WaveNet)和NLP领域,它在物体检测也发挥了重要的作用,对于小物体的检测十分重要

 

普通卷积 

先来看看 普通卷积 

卷积后需进行池化 pooling,池化除了 降维、提升局部位移鲁棒性 等作用外,还有 增大感受野 的作用,

但是在 pooling 时,会丢失一部分信息,特别是一些 细节信息 和 小目标信息,对任务造成一定的影响:

1.在图像识别任务中,丢失细节可能降低准确率

2.在目标检测任务中,小目标检测 受影响较大

3.在语义分割任务中,下采样后的上采样无法还原这些信息

 

如果没有 pooling,感受野 太小,

不利于 在图像需要全局信息、语音文本需要较长的 sequence 信息依赖 等问题

 

感受野

卷积模块 输出结果中 一个元素 对应 输入层的 区域大小,代表 卷积核 在 图像上看到的区域大小,感受野 越大,包含的 上下文关系 越多,【通俗理解就是 视野更广阔了,看问题更全面了 等等】

 

空洞卷积

为了避免 pooling 的影响,提出了 空洞卷积

VS 普通卷积

空洞卷积其实 就是在 普通卷积核中间 插 0,看起来像中间有洞,故称空洞;

空洞卷积与 普通卷积 参数量 相同;

空洞卷积与 普通卷积 输出的 feature map 大小相同;

 

膨胀率 

空洞卷积中引入 膨胀率 dilation_rate 的概念,也叫 扩张率,空洞数,

膨胀率 代表 将 原来的一个 元素 扩展到 多少倍,扩展后的 卷积核尺寸 dilation_rate*(kernel_size - 1)+1,

如下图

a为 普通卷积核为 3x3,感受野 9;

b 膨胀率为 2,原先1个元素变成2个,5x5,感受野 25;

c 膨胀率为 3,原先1个元素变成3个,7x7,感受野 49;

 

保持数据结构不变

pooling 操作一般会改变 数据尺寸,在 图像分割 领域,下采样后需上采样,数据尺寸会发生变化,而空洞卷积只有卷积,没有池化,可以保持数据结构不变

 

空洞卷积的问题与优化 

栅格效应 Gridding Effect

 

 

这张图代表了3层空洞卷积,每层在 原始输入 上 参与计算的 像素个数,可忽略,直接看下面的图 

 

多个相同膨胀率的空洞卷积堆叠

左侧从下往上看,相当于一个卷积网络,每次卷积采用 膨胀率为 2 的空洞卷积,

右侧是卷积后的统计分析,整个图代表 原始输入,每个格子代表一个像素,格子里的值代表 3次卷积后,该像素被 计算的次数,

可以看到有些 像素 是没有参与计算的,造成了大量的信息丢失,影响最终效果,

论文中称 卷积核 不连续

 

多个不同膨胀率的空洞卷积堆叠

左侧膨胀率分别为 1 2 3,右侧所有像素都参与计算了,信息利用率大大增强,

同时感受野 等于 上个图,大于 下个图(普通卷积)

 

多个普通卷积堆叠

感受野明显小很多 

 

Hybrid Dilated Convolution(HDC) 

混合膨胀卷积,

空洞卷积会产生 栅格效应,需要 设计 膨胀率 使得卷积核能够覆盖所有像素,HDC 用于解决这一问题。

HDC 要求 膨胀率 满足如下要求

1.满足公式和约束

Mi 表示 第 i 层 最大 可使用 的 膨胀率,ri 表示 第 i 层的膨胀率, n 表示 膨胀卷积核  的 个数,

下面分别表示 膨胀系数 为 [1 2 5] 和 [1 2 9]

 

2.锯齿结构

dilated rate设计成了锯齿状结构,例如[1, 2, 5, 1, 2, 5]这样的循环结构 

锯齿状本身的性质就比较好的来同时满足小物体大物体的分割要求(小 dilation rate 来关心近距离信息,大 dilation rate 来关心远距离信息)。

这样卷积依然是连续的,依然能满足VGG组观察的结论,大卷积是由小卷积的 regularisation 的 叠加。

3.公约数不能大于 1

叠加的膨胀卷积的膨胀率dilated rate不能有大于1的公约数(比如[2, 4, 8]),不然会产生栅格效应

 

下面代码用于 画 上面的图

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()

 

 

 

 

 

 

参考资料:

https://www.cnblogs.com/pinking/p/9192546.html  膨胀卷积与IDCNN                      言简意赅,有图 有代码,适合入门

https://www.bilibili.com/video/BV1Bf4y1g7j8?spm_id_from=333.337.search-card.all.click     b站视频,讲得很清楚,特别是 gridding effect 问题 及 HDC 策略

https://blog.51cto.com/u_15072927/4308099                        基本和 上个视频对应,建议先看视频再看该文章

 

 

https://blog.csdn.net/qq_27586341/article/details/103131674  膨胀卷积(Dilated convolution)

 

https://blog.csdn.net/qq_35495233/article/details/86638098  NLP进阶之(七)膨胀卷积神经网络

https://zhuanlan.zhihu.com/p/113285797  吃透空洞卷积(Dilated Convolutions)

https://www.zhihu.com/question/54149221/answer/1683243773  如何理解空洞卷积(dilated convolution)?

https://zhuanlan.zhihu.com/p/89425228  空洞(扩张)卷积(Dilated/Atrous Convolution)