深度学习基础知识(二)--卷积形式汇总
在之前的文章中,我们对基础卷积应该已经有了一定的理解。
深度理解卷积--使用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。
@H_419_36@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*1卷积不改变输入特征图的宽高,只改变通道数。可以在网络层中增加1*1卷积在调整特征图的通道维度。
- 跨通道信息交互。1*1卷积每个通道的权重是共享的,实际就是通道层的线性组合,相当于是通道之间的线性组合,在通道之间进行信息交互。
具体应用可参考:https://zhuanlan.zhihu.com/p/358893064
深度可分离卷积深度可分离卷积是Mobilenet提出的轻量级网络结构,思想就是将普通卷积拆分为深度卷积(depthwise convolution)和逐点卷积(pointwise convolution)两个步骤。
假设输入特征图维度为H,W,M
- depthwise conv:对M个通道H,W的特征图分别使用K,K大小的卷积核进行卷积,得到M个H,W的卷积结果
- 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
可以看出深度可分离卷积计算量要低于标准卷积,所以针对移动端部署场景可以使用深度可分离卷积这种轻量级网络。
深度可分离卷积实现代码:
@H_419_36@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将各分组卷积结果结合起来。
分组卷积实现代码:
@H_419_36@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看下反卷积的使用
@H_419_36@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看下空洞卷积的使用:
@H_419_36@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
原文地址:https://cloud.tencent.com/developer/article/1990900