常规卷积:卷积核与输入的每个通道都进行卷积操作;
假设输入层为一个大小为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对输入feature map的每个通道分别使用一个卷积核,然后将所有卷积核的输出再进行拼接得到它的最终输出,如图:
还用上面那个例子,这里的Filter的数量与上一层的Depth相同。所以一个三通道的图像经过运算后生成了3个Feature map,参数量为:
3x3x3=27
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。
可以使用 torch.nn.Conv2d() 中的卷积组参数 groups,来实现深度可分离卷积。groups 参数是用于控制输入和输出的连接的,表示要分的组数(in_channels 和 out_channels 都必须能被 groups 参数整除)。例如:
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))