分组卷积(Group Convolution)

分组卷积将输入特征图进行分组,分别对每组的特征图进行卷积操作,得到输出的特征图,最后再将每组卷积后得到的特征图拼接在一起,就完成了整个卷积操作。

在PyTorch中,输入特征图个数与输出特征图个数需要能够被分组数整除,因为输出特征图个数需要均摊给每一组。比如输入特征图个数为X,输出特征图个数为Y,分组数为g,且g可整除X和Y,那么每一组的输入特征图个数为X/g,输出特征图个数为Y/g,总的输出特征图个数为(Y/g)*g=Y

假设输入特征图个数为6,卷积核尺寸为3*3,输出特征图个数为10,不考虑偏置项。以下操作基于本例》

普通卷积:

当分组数为1时,就是普通的卷积

1
2
3
4
5
6
7
8
9
10
import torch
import torch.nn as nn
from torchsummary import summary
class Model(nn.Module):
def __init__(self):
super().__init__()
self.conv=nn.Conv2d(in_channels=6,out_channels=10,kernel_size=3,groups=1,bias=False)
def forward(self,x):
return self.conv(x)
model=Model()

查看参数量:
Alt text

参数量计算方法:

1
2
3
4
5
1*(3*3*(6/1))*(10/1)=540
1是分组数
3*3是卷积核尺寸
6是输入特征图个数
10是输出特征图个数

分组卷积:

当分组数不为1时,就是分组卷积

1
2
3
4
5
6
7
8
9
10
import torch
import torch.nn as nn
from torchsummary import summary
class Model(nn.Module):
def __init__(self):
super().__init__()
self.conv=nn.Conv2d(in_channels=6,out_channels=10,kernel_size=3,groups=2,bias=False)
def forward(self,x):
return self.conv(x)
model=Model()

查看参数量:
Alt text

参数量计算方法:

1
2
3
4
5
2 * ((3*3*(6/2))*((10/2)))=270
2是分组数
3*3是卷积核尺寸
6是输入特征图个数
10是输出特征图个数

可以看到,我们分了两组,参数量也减半。

深度可分离卷积(Depthwise Separable Convolution)

深度可分离卷积包含两个步骤:逐通道卷积逐点卷积

假设输入特征图个数为6,卷积核尺寸为3*3,输出特征图个数为12,不考虑偏置项。以下操作基于本例:

逐通道卷积(Depthwise Convolution)

当分组卷积的分组数=输入特征图个数=输出特征图个数时,就得到了逐通道卷积:

1
2
3
4
5
6
7
8
9
10
import torch
import torch.nn as nn
from torchsummary import summary
class Model(nn.Module):
def __init__(self):
super().__init__()
self.conv=nn.Conv2d(in_channels=6,out_channels=6,kernel_size=3,groups=6,bias=False)
def forward(self,x):
return self.conv(x)
model=Model()

查看参数量:
Alt text

参数量计算方法:

1
2
3
4
6 * ((3*3*(6/6))*((6/6)))=54
6是分组数,等于输入特征图个数
3*3是卷积核尺寸
6是输入特征图个数,也是输出特征图个数

逐点卷积(Pointwise Convolution)

逐点卷积就是普通的1*1卷积(分组数为1)

1
2
3
4
5
6
7
8
9
10
import torch
import torch.nn as nn
from torchsummary import summary
class Model(nn.Module):
def __init__(self):
super().__init__()
self.conv=nn.Conv2d(in_channels=6,out_channels=12,kernel_size=1,groups=1,bias=False)
def forward(self,x):
return self.conv(x)
model=Model()

查看参数量:
Alt text

参数量计算方法:

1
2
3
4
5
1 * ((1*1*(6/1))*((12/1)))=72
1是分组数,等于输入特征图个数
1*1是卷积核尺寸
6是输入特征图个数
12是输出特征图个数

至此,逐通道卷积与逐点卷积的参数量之和为:54+72=126

深度可分离卷积可以描述为:

1
2
3
4
5
6
7
8
9
10
11
import torch
import torch.nn as nn
from torchsummary import summary
class Model(nn.Module):
def __init__(self):
super().__init__()
self.depthwise_conv=nn.Conv2d(in_channels=6,out_channels=6,kernel_size=3,groups=6,bias=False)#groups=in_channels=out_channels
self.pointwise_conv=nn.Conv2d(in_channels=6,out_channels=12,kernel_size=1,groups=1,bias=False)
def forward(self,x):
return self.pointwise_conv(self.depthwise_conv(x))
model=Model()

查看参数量:
Alt text

参数量也是54+72=126

由此可见,深度可分离卷积正是逐通道卷积与逐点卷积的叠加。

普通卷积

1
2
3
4
5
6
7
8
9
10
import torch
import torch.nn as nn
from torchsummary import summary
class Model(nn.Module):
def __init__(self):
super().__init__()
self.conv=nn.Conv2d(in_channels=6,out_channels=12,kernel_size=3,groups=1,bias=False)
def forward(self,x):
return self.conv(x)
model=Model()

查看参数量:
Alt text

参数量计算方法:

1
2
3
4
5
1 * ((3*3*(6/1))*((12/1)))=648
1是分组数
3*3是卷积核尺寸
6是输入特征图个数
12是输出特征图个数

看,输出特征图个数一样的前提下,普通卷积的参数量远多于深度可分离卷积。

正因为深度可分离卷积的参数量较少,使得它在轻量化网络中得到了广泛应用,有时间可以盘点下这些轻量化网络。

参考: