『算法学习』空洞卷积

本文深入探讨了空洞卷积(又称扩张卷积)的概念、原理及其在网络设计中的应用,对比了其与传统卷积的区别,特别是在感受野、信息保留及多尺度分割方面的优势,并介绍了在TensorFlow和MXNet框架中实现空洞卷积的具体方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、空洞卷积的提出

空洞卷积(atrous convolutions)又名扩张卷积(dilated convolutions),向卷积层引入了一个称为 “扩张率(dilation rate)”的新参数,该参数定义了卷积核处理数据时各值的间距。

该结构的目的是在不用pooling(pooling层会导致信息损失)且计算量相当的情况下,提供更大的感受野。 顺便一提,卷积结构的主要问题如下:

池化层不可学

内部数据结构丢失;空间层级化信息丢失。

小物体信息无法重建 (假设有四个pooling layer 则 任何小于 2^4 = 16 pixel 的物体信息将理论上无法重建。)

而空洞卷积就有内部数据结构的保留和避免使用 down-sampling 这样的特性,优点明显。

二、空洞卷积原理

如下如,卷积核没有红点标记位置为0,红点标记位置同正常卷积核。

假设原始特征为feat0,首先使用扩张率为1的空洞卷积生成feat1,feat1上一点相对feat0感受野为3*3(如图a);

然后使用扩张率为2的空洞卷积处理feat1生成feat2(如图b),使第一次空洞卷积的卷积核大小等于第二次空洞卷积的一个像素点的感受野,图b即feat1上一个点综合了图a即feat0上3*3区域的信息,则生成的feat2感受野为7*7,即整个图b深色区域;

第三次处理同上,第二次空洞卷积的整个卷积核大小等于第三次空洞卷积的一个像素点的感受野,图c即feat2上每个点综合了feat0上7*7的信息(感受野),则采用扩张率为3的空洞卷积,生成的feat3每一个点感受野为15*15。

相比较之下,使用stride为1的普通3*3卷积,三层之后感受野仅仅为(kernel-1)*layer+1=7

三、空洞卷积问题

感受野跳跃

我们对同一张图连续三次使用扩张率为1的空洞卷积,观察整张图的中心点的感受野(如下图)

很明显,感受野不连续(我们上一小结的例子就没这个问题,所以空洞卷积依赖网络设计)。

小尺度物体检测

类似第一个问题,仍然需要调整扩张率的组合来解决这个问题。

四、网络设计研究

第一个特性是,叠加卷积的 dilation rate 不能有大于1的公约数。比如 [2, 4, 6] 则不是一个好的三层卷积,依然会出现 gridding effect。

第二个特性是,我们将 dilation rate 设计成 锯齿状结构,例如 [1, 2, 5, 1, 2, 5] 循环结构。

第三个特性是,我们需要满足一下这个式子: M_i=\max[M_{i+1}-2r_i,M_{i+1}-2(M_{i+1}-r_i),r_i]

其中 r_i 是 i 层的 dilation rate 而 M_i 是指在 i 层的最大dilation rate,那么假设总共有n层的话,默认 M_n=r_n 。假设我们应用于 kernel 为 k x k 的话,我们的目标则是 M_2 \leq k ,这样我们至少可以用 dilation rate 1 即 standard convolution 的方式来覆盖掉所有洞。

一个简单的例子: dilation rate [1, 2, 5] with 3 x 3 kernel (可行的方案)

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

单分支设计的研究

通向标准化设计:Hybrid Dilated Convolution (HDC),可以很好的满足分割需要,如下图所示,

 

多分支研究解决多尺度分割

仅仅(在一个卷积分支网络下)使用 dilated convolution 去抓取多尺度物体是一个不正统的方法。比方说,我们用一个 HDC 的方法来获取一个大(近)车辆的信息,然而对于一个小(远)车辆的信息都不再受用。假设我们再去用小 dilated convolution 的方法重新获取小车辆的信息,则这么做非常的冗余。

基于港中文和商汤组的 PSPNet 里的 Pooling module (其网络同样获得当年的SOTA结果),ASPP 则在网络 decoder 上对于不同尺度上用不同大小的 dilation rate 来抓去多尺度信息,每个尺度则为一个独立的分支,在网络最后把他合并起来再接一个卷积层输出预测 label。这样的设计则有效避免了在 encoder 上冗余的信息的获取,直接关注与物体之间之内的相关性。

 

五、常用框架API介绍

TensorFlow接口

tf.nn.atrous_conv2d(value, filters, rate, padding, name=None)

value: 指需要做卷积的输入图像,要求是一个4维Tensor,具有[batch, height, width, channels]这样的shape,具体含义是[训练时一个batch的图片数量, 图片高度, 图片宽度, 图像通道数]

filters: 相当于CNN中的卷积核,要求是一个4维Tensor,具有[filter_height, filter_width, channels, out_channels]这样的shape,具体含义是[卷积核的高度,卷积核的宽度,图像通道数,卷积核个数],同理这里第三维channels,就是参数value的第四维

rate: 要求是一个int型的正数,正常的卷积操作应该会有stride(即卷积核的滑动步长),但是空洞卷积是没有stride参数的,这一点尤其要注意。取而代之,它使用了新的rate参数,那么rate参数有什么用呢?它定义为我们在输入图像上卷积时的采样间隔,你可以理解为卷积核当中穿插了(rate-1)数量的“0”,把原来的卷积核插出了很多“洞洞”,这样做卷积时就相当于对原图像的采样间隔变大了。具体怎么插得,可以看后面更加详细的描述。此时我们很容易得出rate=1时,就没有0插入,此时这个函数就变成了普通卷积。

padding: string类型的量,只能是”SAME”,”VALID”其中之一,这个值决定了不同边缘填充方式。

函数默认stride=1,无法改变。

结果返回一个Tensor,填充方式为“VALID”时,返回[batch,height-2*(filter_width-1),width-2*(filter_height-1),out_channels]的Tensor,填充方式为“SAME”时,返回[batch, height, width, out_channels]的Tensor。

测试代码如下:

img = tf.constant(value=[[[[1],[2],[3],[4]],
                          [[1],[2],[3],[4]],
                          [[1],[2],[3],[4]],
                          [[1],[2],[3],[4]]]],dtype=tf.float32)
img = tf.concat(values=[img,img],axis=3)

filter = tf.constant(value=1, shape=[3,3,2,5], dtype=tf.float32) 
out_img1 = tf.nn.atrous_conv2d(value=img, filters=filter, rate=1, padding='SAME') 
out_img2 = tf.nn.atrous_conv2d(value=img, filters=filter, rate=1, padding='VALID') 
out_img3 = tf.nn.atrous_conv2d(value=img, filters=filter, rate=2, padding='SAME') 
#error 
#out_img4 = tf.nn.atrous_conv2d(value=img, filters=filter, rate=2, padding='VALID') 
with tf.Session() as sess:
    print('rate=1, SAME mode result:')
    print(sess.run(out_img1))
    print('rate=1, VALID mode result:') 
    print(sess.run(out_img2)) 
    print('rate=2, SAME mode result:')
    print(sess.run(out_img3)) # error #print 'rate=2, VALID mode result:' #print(sess.run(out_img4))

扩张率为1时,空洞卷积等价于普通卷积。对于SAME和VALID模式计算方式如下图所示,

扩张率为2的VALID模式计算过程,

扩张率为2的VALID模式会报错,此时卷积核大于图片,无法卷积。

MXNet接口

MXNet卷积操作自带扩张率参数,详见文档

MXNet的通道存储与TensorFlow不太一致,所以我们打印一下(对比上面的图,可以体会到为什么除了tf外大多框架把通道放在第二维),

import  mxnet as mx
import mxnet.ndarray as nd

img = nd.array([[[[1],[2],[3],[4]],
                [[1],[2],[3],[4]],
                [[1],[2],[3],[4]],
                [[1],[2],[3],[4]]]])
img = nd.concat(img, img, dim=-1)
img = nd.transpose(img, axes=(0, 3, 1, 2))

w = nd.ones([5, 2, 3, 3])
b = nd.array([0 for _ in range(5)])
img
[[[[1. 2. 3. 4.]
   [1. 2. 3. 4.]
   [1. 2. 3. 4.]
   [1. 2. 3. 4.]]

  [[1. 2. 3. 4.]
   [1. 2. 3. 4.]
   [1. 2. 3. 4.]
   [1. 2. 3. 4.]]]]
<NDArray 1x2x4x4 @cpu(0)>
nd.Convolution(img, w, b, kernel=w.shape[2:], num_filter=w.shape[0], stride=(1, 1), pad=(1,1), dilate=(1,1))
[[[[12. 24. 36. 28.]
   [18. 36. 54. 42.]
   [18. 36. 54. 42.]
   [12. 24. 36. 28.]]

  [[12. 24. 36. 28.]
   [18. 36. 54. 42.]
   [18. 36. 54. 42.]
   [12. 24. 36. 28.]]

  [[12. 24. 36. 28.]
   [18. 36. 54. 42.]
   [18. 36. 54. 42.]
   [12. 24. 36. 28.]]

  [[12. 24. 36. 28.]
   [18. 36. 54. 42.]
   [18. 36. 54. 42.]
   [12. 24. 36. 28.]]

  [[12. 24. 36. 28.]
   [18. 36. 54. 42.]
   [18. 36. 54. 42.]
   [12. 24. 36. 28.]]]]
<NDArray 1x5x4x4 @cpu(0)>
nd.Convolution(img, w, b, kernel=w.shape[2:], num_filter=w.shape[0], stride=(1, 1), pad=(2,2), dilate=(2,2))
[[[[16. 24. 16. 24.]
   [16. 24. 16. 24.]
   [16. 24. 16. 24.]
   [16. 24. 16. 24.]]

  [[16. 24. 16. 24.]
   [16. 24. 16. 24.]
   [16. 24. 16. 24.]
   [16. 24. 16. 24.]]

  [[16. 24. 16. 24.]
   [16. 24. 16. 24.]
   [16. 24. 16. 24.]
   [16. 24. 16. 24.]]

  [[16. 24. 16. 24.]
   [16. 24. 16. 24.]
   [16. 24. 16. 24.]
   [16. 24. 16. 24.]]

  [[16. 24. 16. 24.]
   [16. 24. 16. 24.]
   [16. 24. 16. 24.]
   [16. 24. 16. 24.]]]]
<NDArray 1x5x4x4 @cpu(0)>

六、参考来源

Multi-scale Context Aggregation by Dilated Convolutions

【Tensorflow】tf.nn.atrous_conv2d如何实现空洞卷积?

如何理解空洞卷积(dilated convolution)?

 

<think>好的,我现在要详细思考用户关于传统空洞卷积的问题。用户想了解其算法原理、实现方法以及在深度学习中的应用。首先,我需要回忆空洞卷积的基本概念,然后整理相关知识,确保准确无误。 首先,传统卷积和空洞卷积的区别。传统卷积的卷积核在输入据上是连续扫描的,而空洞卷积通过在卷积核元素之间插入空洞(即间隔)来扩大感受野,同时不增加参量。这一点是关键,因为更大的感受野有助于捕捉更广泛的上下文信息,这在图像分割、目标检测等任务中非常重要。 接下来是算法原理。空洞卷积学表达式需要明确。假设原始卷积核大小为$k \times k$,扩张率为$d$,那么实际等效的卷积核大小变为$k' = k + (k-1)(d-1)$。例如,3x3的卷积核,d=2时,等效大小为3 + 2*1 = 5,即5x5的等效感受野。这里可能需要用公式表示,比如新的位置计算方式,考虑扩张后的位置索引。 然后是实现方法。在代码层面,空洞卷积可以通过设置卷积层的dilation参来实现。例如,在PyTorch中,nn.Conv2d的参dilation默认为1,设置为更大的值即可实现空洞卷积。需要给出一个简单的代码示例,比如定义一个带有空洞卷积的层,并解释参设置的影响。 接下来考虑深度学习空洞卷积的应用场景。比如在图像分割任务中,如DeepLab系列网络使用空洞卷积来保持高分辨率特征图的同时扩大感受野,从而更准确地定位物体边界。此外,WaveNet中使用空洞卷积来捕捉长距离依赖,处理音频序列。 还需要注意空洞卷积可能带来的问题,比如网格效应(gridding),即当扩张率较大时,卷积核的有效覆盖区域可能出现不连续,导致信息丢失。这时候可能需要结合其他技术,如混合不同扩张率的空洞卷积层,或者使用跳跃连接来缓解。 在解释过程中,要引用用户提供的资料中的相关内容。比如引用[^3]提到的传统全连接网络参冗余的问题,而空洞卷积通过扩大感受野减少参量的优势之对比。同时,引用[^2]中关于深度可分离卷积的运算量计算方式,可以空洞卷积的参量进行比较,强调空洞卷积在保持参量的同时增加感受野的优点。 最后,生成相关问题时要围绕用户可能感兴趣的扩展点,如应用场景、不同框架的实现差异、普通卷积的对比等,确保问题具有针对性,帮助用户进一步探索。</think>### 传统空洞卷积详解 #### 1. 算法原理 空洞卷积(Dilated Convolution)是一种通过**插入空洞(间隔)**扩大感受野的卷积方法,**不增加参量**。其核心思想是在卷积核元素之间插入$d-1$个零值空洞,扩张率为$d$($d=1$时为普通卷积)。 学表达式为: $$(F *_{d} k)(p) = \sum_{s+dt=p} F(s)k(t)$$ 其中$d$为扩张率,$k$为卷积核,$F$为输入特征图[^3]。 例如: - 当$d=2$时,$3\times3$卷积核等效于覆盖$5\times5$区域的稀疏核 - 感受野计算:$r = (k-1) \times d + 1$ #### 2. 实现方法 在主流框架中通过设置`dilation`参实现: ```python # PyTorch实现示例 import torch.nn as nn dilated_conv = nn.Conv2d( in_channels=64, out_channels=128, kernel_size=3, dilation=2 # 设置扩张率 ) ``` 此时实际覆盖的输入区域为$5\times5$,但参量仍保持$3\times3$[^2]。 #### 3. 深度学习应用 - **图像分割**:DeepLab系列通过堆叠空洞卷积保持特征图分辨率 - **时序建模**:WaveNet利用指增长扩张率捕获长距离依赖 - **医学影像**:处理大尺寸图像时保持局部细节全局上下文平衡 #### 4. 优势局限 ✓ 优势: - 线性增加感受野($O(d)$) - 保持参效率(参量仅$O(k^2)$) ✗ 局限: - 扩张率过大会产生**网格效应**(相邻采样点无重叠) - 需配合跳跃连接等结构优化信息流[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值