常规卷积
常规卷积:卷积核与输入的每个通道都进行卷积操作;
假设输入层为一个大小为64×64像素、三通道彩色图片,经过一个包含4个Filter的卷积层,最终输出4个Feature Map,且尺寸与输入层相同。
卷积层共4个Filter,每个Filter包含了3个Kernel,每个Kernel的大小为3×3。
卷积层的参数数量可以用如下公式来计算(即:卷积核W x 卷积核H x 输入通道数 x 输出通道数):
4 × 3 × 3 × 3 = 108
深度可分离卷积
深度可分离卷积(Depthwise Separable Convolution,DSC)用于减少网络参数,提升计算效率;它的核心思想是将一个完整的卷积运算分解为两步进行,分别为Depthwise Convolution和Pointwise Convolution,详细结构如图所示:
Depthwise Convolution 逐通道卷积
常规卷积卷积核需要与输入的每个通道都进行卷积操作,而Depthwise对输入feature map的每个通道分别使用一个卷积核,然后将所有卷积核的输出再进行拼接得到它的最终输出,如图:
还用上面那个例子,这里的Filter的数量与上一层的Depth相同。所以一个三通道的图像经过运算后生成了3个Feature map,参数量为:
3x3x3=27
Pointwise Convolution 逐点卷积
Pointwise Convolution实际为1×1卷积,与常规卷积运算相同,在DSC中它起两方面的作用:
第一个作用是让DSC能够自由改变输出通道的数量;
第二个作用是对Depthwise Convolution输出的feature map进行通道融合;
用1*1的卷积组合不同深度卷积的输出,得到一组新的输出。卷积核的尺寸为 1×1×M,M为上一层的depth。这里的卷积运算会将上一步的map在深度方向上进行加权组合,生成新的Feature map。有几个Filter就有几个Feature map,参数量为:
1x1x3x4=12;
因此,深度可分离卷积的参数个数为27+12=39。
pytorch 实现可分离卷积
可以使用 torch.nn.Conv2d() 中的卷积组参数 groups,来实现深度可分离卷积。groups 参数是用于控制输入和输出的连接的,表示要分的组数(in_channels 和 out_channels 都必须能被 groups 参数整除)。例如:
- 当 groups =1 (默认值)时,就是同普通的卷积;
- 当 groups=n 时,相当于把原来的卷积分成 n 组,每组 in_channels/n 的输入与 out_channels/n 个 kernel_size x kernel_size x in_channels/n的卷积核卷积,生成 out_channels/n 的输出 ,然后将各组输出连接起来,形成完整的 out_channels 的输出;
- 当 groups = in_channels 时,每个输入通道都只跟 out_channels/in_channels 个卷积核卷积; out_channels = in_channels 时就是 Depthwise 卷积。
import torch
from torchsummary import summary
class myGroupConv(torch.nn.Module):
def __init__(self):
super(myGroupConv, self).__init__()
self.conv2d = torch.nn.Conv2d(in_channels=4,
out_channels=8,
kernel_size=3,
stride=1,
padding=1,
groups=1,
bias=False)
self.relu = torch.nn.ReLU()
def forward(self, x):
x = self.conv2d(x)
x = self.relu(x)
return x
class depthwise_separable_conv(torch.nn.Module):
def __init__(self, ch_in, ch_out):
super(depthwise_separable_conv, self).__init__()
self.ch_in = ch_in
self.ch_out = ch_out
self.depth_conv = torch.nn.Conv2d(ch_in, ch_in, kernel_size=3, padding=1, groups=ch_in, bias=False)
self.point_conv = torch.nn.Conv2d(ch_in, ch_out, kernel_size=1, bias=False)
def forward(self, x):
x = self.depth_conv(x)
x = self.point_conv(x)
return x
class mydspConv(torch.nn.Module):
def __init__(self):
super(mydspConv, self).__init__()
self.conv2d = depthwise_separable_conv(4, 8)
self.relu = torch.nn.ReLU()
def forward(self, x):
x = self.conv2d(x)
x = self.relu(x)
return x
device = torch.device("cuda" )
model_1 = myGroupConv().to(device)
summary(model_1, (4, 3, 3))
model_2 = mydspConv().to(device)
summary(model_2, (4, 3, 3))
结果:
说明:
输入尺寸:3x3x4
输出尺寸:3x3x8
- 常规卷积,group=1,参数量:3x3x4x8=288;测试代码中偏置设为False,所以不加上偏置的参数量,若设为true,则参数量还需要加上等于输出通道个数的偏执量的个数,等于296;
- 深度可分离卷积:
- 逐通道卷积:groups=输入通道数,输出通道数=输入通道数,kernel_size=3,每一个卷积核只在一个通道上进行卷积,其参数量=3×3×4=36
- 逐点卷积:kernel_size=1, 其参数量=1×1×4×8=32;
总参数量=36 + 32 = 68