深度可分离卷积及其代码实现
深度可分离卷积及其代码实现
深度可分离卷积(Depthwise Separable Convolution)是许多轻量级网络架构(如MobileNet)的核心组件,它通过将卷积操作分解为逐通道卷积和逐点卷积两个阶段,显著减少了模型的参数量和计算量。本文将详细介绍深度可分离卷积的原理、参数量和计算量的对比,并提供具体的代码实现。
1. 普通卷积操作
假设输入为[Channel,H,W] = [3,3,3],下面所有的卷积例子设置padding=1,使得卷积后特征图尺寸不变
普通卷积:in_channel=3,out_channel=4
所以这里需要4个卷积核(输出几个通道就要几个卷积核,这里输出4个通道所以需要4个卷积核),每个卷积核的shape是3 x 3 x 3
普通卷积层的参数量为:卷积核W x 卷积核H x 输入通道数 x 输出通道数 = 3 x 3 x 3 x 4 = 108个参数
普通卷积层的计算量为:**卷积核W x 卷积核H x 输出图片W x 输出图片H x 输入通道数 x 输出通道数 = 3 x 3 x 3 x 3 x 3 x 4 = 972 **
2. 深度可分离卷积(Depthwise separable convolution)
一些轻量级的网络,如MobileNet中,会有深度可分离卷积Depthwise separable convolution。由Depthwise(DW)和Pointwise(PW)两个部分结合起来,用来提取特征feature map。相比常规的卷积操作,可以显著减少参数量和计算量
2.1 原理
深度可分离卷积可分为两个过程,分别为逐通道卷积(Depthwise Convolution)和逐点卷积(Pointwise Convolution)
- 逐通道卷积(Depthwise Convolution)
Depthwise Convolution的一个卷积核负责一个通道,一个通道只被一个卷积核卷积,这个过程产生的feature map通道数和输入的通道数完全一样,如下图所示
in_channel=3,out_channel=3,卷积核个数为3,每个卷积核的shape为1 x 3 x 3
逐通道卷积层的参数量为:卷积核W x 卷积核H x 输入通道数 = 3 x 3 x 3 = 27个参数
逐通道卷积层的计算量为:卷积核W x 卷积核H x 输出图片W x 输出图片H x 输入通道数 = 3 x 3 x 3 x 3 x 3 = 243
Depthwise Convolution完成后的feature map通道数与输入通道数相同,无法扩展feature map。而且这种运算对输入的每个通道独立进行卷积运算,没有有效的利用不同通道在相同空间位置上的特征信息。因此需要Pointwise Convolution来将这些Feature map进行组合生成新的Feature map
- 逐点卷积(Pointwise Convolution)
Pointwise Convolution与普通卷积非常相似,它的卷积核的尺寸为1×1×M,M为逐通道卷积输出特征图的通道数。所以这里的卷积运算会将逐通道卷积层输出的feature map在深度方向上进行加权组合,生成新的feature map
in_channel=3,out_channel=4,卷积核个数为4,每个卷积核的shape为3 x 1 x 1
逐点卷积层的参数量为:卷积核W x 卷积核H x 输入通道数 x 输出通道数 = 1 x 1 x 3 x 4 = 12个参数
逐点卷积层的计算量为:卷积核W x 卷积核H x 输出图片W x 输出图片H x 输入通道数 x 输出通道数 = 1 x 1 x 3 x 3 x 3 x 4 = 108
经过Pointwise Convolution之后,同样输出了通道数为4的feature map,与常规卷积的输出维度相同
上面两步合起来就是如下图:
2.2 参数量、计算量对比
常规卷积
参数量:108
计算量:972
深度可分离卷积
参数量:39 = 27 + 12
计算量:351 = 243 + 108
相同的输入,同样是得到输出通道数为4的feature map,深度可分离卷积的参数量和计算量是常规卷积的约1/3
2.3 代码实现
代码实现非常简单,关于nn.Conv2d的groups参数的作用在下面介绍
#深度可分离卷积
class DepthWiseConv(nn.Module):
def __init__(self, in_channel, out_channel):
super(DepthWiseConv, self).__init__()
#逐通道卷积
self.depth_conv = nn.Conv2d(in_channels=in_channel,
out_channels=in_channel,
kernel_size=3,
stride=1,
padding=1,
groups=in_channel#逐通道做卷积
)
#逐点卷积,卷积核大小必须为1
self.point_conv = nn.Conv2d(in_channels=in_channel,
out_channels=out_channel,
kernel_size=1,
stride=1,
padding=0,
groups=1)
#前向传播,逐通道卷积+逐点卷积
def forward(self, x):
return self.point_conv(self.depth_conv(x))
3. nn.Conv2d的groups参数
在 PyTorch 的 nn.Conv2d 中,groups 参数用于定义分组卷积(Grouped Convolution)。分组卷积允许我们将输入特征图的通道分成多个组并分别进行卷积计算
- 基本定义:
groups代表将输入通道分成groups组进行卷积操作
当groups = 1时,表示普通的卷积操作
当groups > 1时,输入通道将被分成 groups 组,每组的输入通道进行独立的卷积计算
- 计算方式:
如果in_channel为输入通道数,out_channel为输出通道数,且groups = g,那么会将输入通道分成g组,每组处理in_channel个通道
每个组使用独立的卷积核进行卷积操作,这样可以减少参数量和计算复杂度
- 示例:
假设有一个输入x,in_channel = 8 ,希望输出4个通道,并且设置groups = 4,这意味着输入的8个通道将被分成4组。每组包含2个输入通道,同时卷积得到1个输出通道
import torch
import torch.nn as nn
#创建分组卷积层
conv = nn.Conv2d(in_channels=8,
out_channels=4,
groups=4,
kernel_size=3,
stride=1,
padding=1)
#批次大小为1,通道数为8,大小为32x32
x = torch.randn(1, 8, 32, 32)
#分组卷积
out = conv(x)
#shape = (1, 4, 32, 32)
print(out.shape)