[卷积核]空洞卷积(转)

本文介绍了空洞卷积(又称扩张卷积)的概念,旨在解决卷积神经网络中感受野扩大而不损失细节的问题。通过增加扩张率,空洞卷积能提供更大的视野,避免池化层导致的信息损失。文章讨论了空洞卷积的原理,感受野跳跃和小尺度物体检测的问题,并探讨了网络设计策略,包括多分支设计和不同框架如TensorFlow、MXNet的API使用。此外,还提出了空洞卷积在网络设计中的挑战和解决方案。

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

[卷积核]空洞卷积(转)

转自: https://www.cnblogs.com/hellcat/p/9687624.html

 


一、空洞卷积的提出


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


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





池化层不可学


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


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





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


二、空洞卷积原理


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



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


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


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


相比较之下,使用stride为1的普通33卷积,三层之后感受野仅仅为(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。


测试代码如下:



?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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外大多框架把通道放在第二维),



?
1
2
3
4
5
6
7
8
9
10
11
12
13
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)>




?
1
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)>




?
1
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)?


 

posted @ 2018-12-26 21:09 向前奔跑的少年 阅读( ...) 评论( ...) 编辑 收藏
### 多尺度并行空洞卷积概述 多尺度并行空洞卷积是一种特殊类型的卷积核结构,旨在通过调整卷积核内元素间的间距(膨胀)来实现在不增加网络复杂性的前提下对不同尺度特征的同时提[^3]。 #### 实现原理 多尺度并行空洞卷积心在于其能够在一个单一的操作中捕捉多个尺度的信息。具体来说: - **膨胀**:也称为空洞卷积,允许在网络之间保持较大的感受野而不需要显著增加参数数量或计算成本。 对于标准而言,当步幅固定时,增大卷积核尺寸会相应地扩展模型的感受野;然而这样做也会带来更多的参数和更高的计算需求。相比之下,空洞卷积通过在滤波器权重间插入零值的方式有效地增加了感知区域大小而不必实际扩大过滤器本身。 - **多尺度特性**:为了进一步增强模型对于多种尺度变化模式的理解能力,可以在同一上应用具有不同膨胀系数的一组平行排列的空洞卷积操作。这使得每一级都能够专注于特定的间分辨下的细节描述,最终融合这些来自不同次的结果以获得更全面的数据表示形式。 ```python import torch.nn as nn class MultiScaleDilatedConv(nn.Module): def __init__(self, in_channels, out_channels, kernel_size=3, dilations=[1, 2, 4]): super(MultiScaleDilatedConv, self).__init__() # 定义一组带有不同膨胀因子的卷积层 self.convs = nn.ModuleList([ nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, dilation=dilation, padding=(kernel_size//2)*dilation) for dilation in dilations]) def forward(self, x): outputs = [] for conv_layer in self.convs: output = conv_layer(x) outputs.append(output) # 将所有输出沿通道维度拼接在一起 return torch.cat(outputs, dim=1) ``` 此代码片段展示了如何创建一个多尺度并行空洞卷积模块 `MultiScaleDilatedConv` ,该模块接受输入张量 `x` 并对其进行一系列不同膨胀运算,最后将所得结果按通道方向连接起来作为整体输出。 #### 应用场景 这类技术广泛应用于计算机视觉任务中,特别是在那些需要处理跨越较大间范围内的依赖关系的任务里表现尤为出色,比如: - 自然场景文字识别 (Scene Text Recognition) - 高分辨遥感影像分析 - 医学图像分割与诊断辅助工具开发 - 物体检测与跟踪系统构建
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值