深度学习基础知识(二)--卷积形式汇总

在之前的文章中,我们对基础卷积应该已经有了一定的理解。

深度理解卷积--使用numpy实现卷积

https://cloud.tencent.com/developer/article/1781616

深度使用卷积--使用tensorflow实现卷积

https://cloud.tencent.com/developer/article/1781618

深度理解卷积--使用im2col实现卷积

https://cloud.tencent.com/developer/article/1781621

本文主要汇总下在深度学习中常用的卷积的其他形式,比如深度可分离卷积,反卷积,可变形卷积等等。

一维卷积

一维卷积和二维卷积不是针对卷积的输入维度,而是说卷积的方向是一维的。通常在CV使用二维卷积,一维卷积常用于语音和NLP中。我们结合CV和NLP的输入来进一步理解一维卷积和二维卷积。

对于图像,卷积的输入特征是(B,H,W,C),卷积核为(K,K),在特征图两个方向(宽和高)进行相乘求和得到卷积结果。

对于文本,卷积的输入特征是(B,L,C),其中L表示文本长度,C表示每个词向量的维度。那么我们在进行卷积时,卷积核为(K,C), 只在序列长度(L)方向进行卷积。

假设输入通道数为C_in,输出通道数为C_out,一层conv1d的weight参数个数应该是C_in*K*C_out。

import torch
from torch import nn


conv1 = nn.Conv1d(in_channels=256, out_channels=128, kernel_size=3)
input = torch.randn(32, 15, 256)
# 32,256,15
input = input.permute(0, 2, 1)
print("input size", input.size())
# 32,128,13
out = conv1(input)
print("output size", out.size())
for name, param in conv1.named_parameters():
   print(name, '      ', param.size())


print===》
weight      torch.Size([128, 256, 3])
bias        torch.Size([128])

1*1卷积

1*1卷积核是卷积核中的一个特殊形式,单独提出来是因为其应用广泛,在很多网络结构中可以巧妙使用1*1卷积。

1*1卷积的主要优势有,在增加少量参数的情况下:

  1. 提升网络特征表达。利用1*1卷积+激活函数增加网络的非线性,提升网络表达能力。
  2. 升维和降维。1*1卷积不改变输入特征图的宽高,只改变通道数。可以在网络层中增加1*1卷积在调整特征图的通道维度。
  3. 跨通道信息交互。1*1卷积每个通道的权重是共享的,实际就是通道层的线性组合,相当于是通道之间的线性组合,在通道之间进行信息交互。

具体应用可参考:https://zhuanlan.zhihu.com/p/358893064

深度可分离卷积

深度可分离卷积是Mobilenet提出的轻量级网络结构,思想就是将普通卷积拆分为深度卷积(depthwise convolution)和逐点卷积(pointwise convolution)两个步骤。

假设输入特征图维度为H,W,M

  1. depthwise conv:对M个通道H,W的特征图分别使用K,K大小的卷积核进行卷积,得到M个H,W的卷积结果
  2. pointwise conv:使用N个1*1卷积核对depthwise conv的结构进行卷积,得到H,W,N的卷积结果

和标准卷积一样,输入H,W,M卷积得到H,W,N特征图,我们对比下标准卷积和深度可分离卷积的计算量和计算参数。

计算量:

标准卷积:H*W*K*K*M*N

深度可分离卷积:H*W*M*K*K+H*W*1*1*M*N

参数量:

标准卷积:M*N*K*K

深度可分离卷积:M*K*K+M*N

可以看出深度可分离卷积计算量要低于标准卷积,所以针对移动端部署场景可以使用深度可分离卷积这种轻量级网络。

深度可分离卷积实现代码:

def depthwise_separable_conv():
    in_ch = 3
    out_ch = 3
    depth_conv = nn.Conv2d(in_channels=in_ch,
                                out_channels=out_ch,
                                kernel_size=3,
                                stride=1,
                                padding=1,
                                groups=in_ch)
    in_ch = out_ch
    out_ch = 6
    point_conv = nn.Conv2d(in_channels=in_ch,
                                out_channels=out_ch,
                                kernel_size=1,
                                stride=1,
                                padding=0,
                                groups=1)


    in_ = torch.randn(1, 3, 5, 5)
    # [1,3,5,5]
    depth_out = depth_conv(in_)
    print(depth_out.size())


    # [1,6,5,5]
    point_out = point_conv(depth_out)
    print(point_out.size())

分组卷积

分组卷积最早在AlexNet中被使用,主要用于切分网络让网络能在多个GPU上并行。

group Convolution是将网络输入按通道分为g组分别进行卷积运算,其中每组运算时的输出通道也分为g组,其计算过程如下图所示:

假设输入特征图通道 C_1 ,卷积核大小为 (h_1,w_1) ,输入特征图按通道分为 g 组,输出通道为 C_2

标准卷积的参数量为 C_1*h_1*w_1*C_2

分组卷积首先将输入分为g份,每份通道为 C_1/g 个通道,对每组进行卷积,每组卷积有 C_2/g 个filter,

所以每组参数量为 C1/g*h_1*w_1*C_2/g ,g组卷积完后再concat起来。

分组卷积的参数量为 C1/g*h_1*w_1*C_2/g*g=C_1*h_1*w_1*C_2/g

所以,分组卷积可以用普通卷积 1/g 的参数量得到同样尺寸对特征图。

结合上面的深度可分离卷积,depthwise过程就是一个g=C1=C2的分组卷积,深度可分离卷积在分组卷积后面接入点了一层pointwise将各分组卷积结果结合起来。

分组卷积实现代码:

def group_conv():
    in_ch = 6
    group_ch = 3
    out_ch = 6
    # 普通卷积
    conv = nn.Conv2d(in_channels=in_ch,
                           out_channels=out_ch,
                           kernel_size=3,
                           stride=1,
                           padding=1,
                           groups=1)
    # 分组卷积
    group_conv = nn.Conv2d(in_channels=in_ch,
                           out_channels=out_ch,
                           kernel_size=3,
                           stride=1,
                           padding=1,
                           groups=group_ch)


    in_ = torch.randn(1, 6, 5, 5)
    conv_out = conv(in_)
    # [6,6,3,3]
    for name, param in conv.named_parameters():
        print(name, '      ', param.size())
    group_out = group_conv(in_)
    # [6,2,3,3]
    for name, param in group_conv.named_parameters():
        print(name, '      ', param.size())

参考:

https://zhuanlan.zhihu.com/p/65377955

反卷积

项目中有些场景需要对特征图进行上采样,提升特征图分辨率,比如语义分割等decoder端。

我们可以使用无参数学习的插值方案,也可以使用增加参数学习的反卷积方案。

反卷积又称为转置卷积,下面介绍下反卷积的原理和实现。

假设输入为X,卷积核为W,步长为1,输出为Y:

X=\\begin{bmatrix}x_{11} & x_{12} & x_{13} \\\\x_{21} & x_{22} & x_{23} \\\\x_{31} & x_{32} & x_{33}\\end{bmatrix}
W =\\begin{bmatrix}w_{11} & w_{12} \\\\w_{21} & w_{22} \\end{bmatrix}
Y =\\begin{bmatrix}y_{11} & y_{12} \\\\ y_{21} & y_{22} \\end{bmatrix}

输出Y实际就是X和W的卷积,我们将卷积核表示为系数矩阵C,其中卷积未覆盖到的位置用0填充

C =\\begin{bmatrix}w_{11} & w_{12} & 0 & w_{21} & w_{22} & 0 & 0& 0& 0\\\\0 & w_{11} & w_{12} & 0 & w_{21} & w_{22} & 0 & 0& 0 \\\\0 & 0 & 0 & w_{11} & w_{12} & 0 & w_{21} & w_{22} & 0 \\\\0 & 0 & 0 & 0 & w_{11} & w_{12} & 0 & w_{21} & w_{22}\\end{bmatrix}

将X和Y展开为列向量:

x=\\begin{bmatrix}x_{11} & x_{12} & x_{13} & x_{21} & x_{22} & x_{23} & x_{31} & x_{32} & x_{33}\\end{bmatrix} ^T
y=\\begin{bmatrix}y_{11} & y_{12} & y_{21} & y_{22}\\end{bmatrix} ^T

卷积可以表示为: y=Cx

则有: x=C^Ty

整个过程如下图所示:

实现反卷积的过程:对输入进行pad填充得到新的输入,将卷积核旋转180度,然后对新的输入进行正向卷积操作。

下面使用pytorch看下反卷积的使用

def transpose_test():
    upsample = nn.ConvTranspose2d(in_channels=1, out_channels=3, kernel_size=2, stride=1, padding=0)
    in_ = torch.randn(1, 1, 3, 3)
    out_ = upsample(in_)
    # [1, 3, 4, 4]
    print(out_.size())
    
    upsample = nn.ConvTranspose2d(in_channels=3, out_channels=3, kernel_size=2, stride=2, padding=0)
    in_ = torch.randn(1, 3, 3, 3)
    # [1, 3, 6, 6]
    out_ = upsample(in_)
    print(out_.size())

参考:

https://zhuanlan.zhihu.com/p/115070523

https://zhuanlan.zhihu.com/p/393200454

https://blog.csdn.net/tsyccnh/article/details/87357447

https://arxiv.org/pdf/1603.07285.pdf

空洞卷积

空洞卷积(dilated convolution)是针对图像语义分割问题中下采样会降低图像分辨率、丢失信息而提出的一种卷积思路。利用添加空洞扩大感受野,让原本3x3的卷积核,在相同参数量和计算量下拥有5x5(dilated rate =2)或者更大的感受野,从而无需下采样。

空洞卷积主要作用就是可以扩大感受野,不同dilation rate的空洞卷积代表了不同感受野,所以可以利用不同空洞的卷积提取多尺度信息。

上面提到反卷积是在输入填充信息,空洞卷积是在卷积核填充,如下图

需要注意到是反卷积和空洞卷积都会引起网格效应,针对小目标来说,一个解决方案就是令所叠加的卷积孔洞率不能出现大于1的公约数。

下面使用pytorch看下空洞卷积的使用:

def dilation_test():
    # 普通卷积
    conv1 = nn.Conv2d(1, 1, 3, stride=1, bias=False, dilation=1)
    # 空洞卷积,dilation就是空洞率
    dilation_conv = nn.Conv2d(1, 1, 3, stride=1, bias=False, dilation=2)
    in_ = torch.randn(1, 1, 5, 5)


    # [1,1,3,3]
    out_ = conv1(in_)
    print(out_.size())


    # [1,1,1,1],参数一样,感受野不一样,输出大小也不一样
    out_ = dilation_conv(in_)
    print(out_.size())

可变形卷积

可变形卷积是Deformable Convlolutional Networks一遍检测论文中提出的,针对图像任务中目标的尺寸,形状不规则,通过在卷积层中插入offset来增强网络特征提取能力,因为可变形卷积的加入能更好的覆盖不同形状的目标。

可变形卷积示意图:

输入特征图通过一个卷积操作得到offset,然后基于特征图和offset通过可变形卷积得到输出特征图。其中offset field的尺寸和输入特征图一致,通道数是2N是指k*k*2,k*k是卷积核大小,2?️有X和Y两个方向的offset,也就是每个点对应有2N个通道。

假设输入特征图尺寸为b,c,h,w,卷积核尺寸为k,k,则计算offset的卷积核需要k*k*2,也就是得到b,k*k*2,h,w的offset,每个特征图点有x和y两个方向的偏移量,而每个channel是用同一个偏移量。

通过下图可视化而可变形卷积引入了offset,所以卷积操作的位置会在监督信息的指导较好的适应目标的各种尺寸,形状,因此提取的特征更加丰富并更能集中到目标本身。

因果卷积

因果卷积是也就是对于输出t时刻的数据 y_{t},其输入只可能是t以及t以前的时刻的数据。因果卷积一般用于处理序列问题,根据x1......xty1.....y_{t-1}去预测yt

在实现因果卷积时,使用普通卷积公式只是在左边需要做padding,让t时刻输出不能看到未来时刻都值。但是因果卷积存在一个缺点,如果希望增大感受野就需要增大卷积核或者加深网络层;增加卷积核会增加参数量,网络层过深会带来梯度消失以及训练不稳定难收敛的问题。所以一般因果卷积会和空洞卷积一起使用,通过空洞卷积来扩大每层都感受野。

持续更新~

本站文章资源均来源自网络,除非特别声明,否则均不代表站方观点,并仅供查阅,不作为任何参考依据!
如有侵权请及时跟我们联系,本站将及时删除!
如遇版权问题,请查看 本站版权声明
THE END
分享
二维码
海报
深度学习基础知识(二)--卷积形式汇总
https://cloud.tencent.com/developer/article/1781616
<<上一篇
下一篇>>