天天看点

深度可分离卷积_分组卷积(Group conv)与深度可分离卷积(Depthwise separable conv)

深度可分离卷积_分组卷积(Group conv)与深度可分离卷积(Depthwise separable conv)

写在前面

今天有一个小小的idea,需要用到分组卷积,于是我翻开了notebook,发现之前对两种卷积的分析都太简单了,这次再复习竟不知道啥是啥,于是痛定思痛,决定以后把经典的东西,都仔仔细细地写在这里,供大家批评,鞭策自己进步!

请注意题目顺序

分组卷积

深度可分离卷积,后面就知道为什么一定要按这样的顺序了。

[一]分组卷积(Group conv)

  • 论文地址:https://arxiv.org/pdf/1605.06489.pdf
  • 解释

分组卷积,即ResNeXt,是受到Inception和AlexNet的启发。Iception 的论文中提到,对于卷积来说,卷积核可以看做一个

三维的滤波器

:通道维+空间维(Feature Map 的宽和高),常规的卷积操作其实就是实现

通道相关性

空间相关性

联合映射

。Inception 模块的背后存在这样的一种假设:卷积层通道间的相关性和空间相关性是可以

退耦合

的,将它们分开映射,能达到更好的效果.具体来说,经过不同卷积路径得到的特征图之间的耦合性较低,关注的主要特征不同,可以得到互为补充的特征图,以更完整的表示图像.

以下就是论文中提到的分组卷积可视化图:

深度可分离卷积_分组卷积(Group conv)与深度可分离卷积(Depthwise separable conv)

(a)为常规卷积(b)为分组卷积

(a)是常规卷积(b)是分组卷积,得到多个互补的特征,然后将得到的特征进行concat组合,作为最终的输出特征图。

  • 举例
举个例子比较参数量:假设input.shape = [c1, H, W] output.shape = [c2, H, W] (a)常规卷积参数量= kernel_size * kernel_size * c1 * c2 (b)分组卷积参数量=kernel_size * kernel_size * (c1/g) * (c2/g) * g = ( kernel_size * kernel_size * c1 * c2 )/g其中

g

为分组数量

上面的例子我们可以清楚地看到,得到相同的output.shape,分组卷积的参数量是常规卷积的1/g,其中

g

是分组的个数(上图中是两个)。

  • 分析

官方分析:Alex认为group conv的方式能够增加 filter之间的对角相关性,而且能够减少训练参数,不容易过拟合,这类似于正则的效果。

  • 代码实现(pytorch提供了相关参数,以2d为例)
import torch
import torch.nn as nn
...
model = nn.Conv2d(in_channels = in_channel, out_channels = out_channel, 
kernel_size = kernel_size, stride = stride, padding = 1, dilation = dilation, group = group_num)
           

这里要注意,group_num要能被in_channels和out_channels整除才行,否则会报错

[二]深度可分离卷积

  • 论文地址:https://arxiv.org/pdf/1704.04861.pdf
  • 解释

深度可分离卷积是MobileNet的精髓,它由deep_wise卷积和point_wise卷积两部分组成。我以前一直觉得深度可分离卷积是极端化的分组卷积(把group数量设为Cin个就行)。但今天再次思考一下,发现他们很大的不同在于,

分组卷积

只进行

一次卷积

(一个nn.Conv2d即可实现),不同group的卷积结果concat即可,而

深度可分离卷积

是进行了

两次卷积

操作,第一次先进deep_wise卷积(即收集每一层的特征),kernel_size = K*K*1,第一次卷积总的参数量为K*K*Cin,第二次是为了得到Cout维度的输出,kernel_size = 1*1*Cin,第二次卷积总的参数量为1*1*Cin*Cout。第二次卷积输出即为深度可分离卷积的输出。

以下就是论文中提到的深度可分离卷积可视化图:

深度可分离卷积_分组卷积(Group conv)与深度可分离卷积(Depthwise separable conv)

(a)是常规卷积(b)(c)是深度可分离卷积,(b)是depth_wise conv,用于收集每一channel的特征,(c)是point_wise conv,将原通道数为Cin的feature map 调整为Cout。

  • 举例
举个例子比较参数量:假设input.shape = [c1, H, W] output.shape = [c2, H, W] (a)常规卷积参数量=kernel_size * kernel_size * c1 * c2 (b)深度可分离卷积参数量=kernel_size * kernel_size *c1 + 1*1*c1*c2

上面的例子我们可以清楚地看到,得到相同的output.shape,直观看上去,深度可分离卷积的参数量比常规卷积少了一个数量级。

  • 分析

深度可分离卷积同时考虑图像的区域和通道(深度可分离卷积首先只考虑区域depth_wise,然后再只考虑通道point_wise)实现了图像的区域和通道的完全分离。

  • 代码实现(pytorch)
import torch
import torch.nn as nn
...
model = nn.Sequential(
        nn.Conv2d(in_channels = in_channel, out_channels = in_channel, 
kernel_size = kernel_size, stride = stride, padding = 1, dilation = dilation, group = in_channel),
        nn.Conv2d(in_channels = in_channel, out_channels = out_channel kernel_size = 1, padding = 0)
        )
           

写在后面

之所以在

写在前面

中提到,本文的题目一定要先是

分组卷积

再是

深度可分离卷积

,因为在我看来后者是前者的极端情况(分组卷积的group设为in_channel,即每组的channel数量为1),尽管形式上两者有比较大的差别:

分组卷积

只进行一次卷积操作即可,而

深度可分离卷积

需要进行两次——先depth_wise再point_wise卷积,但他们本质上是一样的。

深度可分离卷积进行一次卷积是无法达到输出指定维度的tensor的,这是由它将group设为in_channel决定的,输出的tensor通道数只能是in_channel,不能达到要求,所以又用了1*1的卷积改变最终输出的通道数。这样的想法也是自然而然的,BottleNeck不就是先1*1卷积减少参数量再3*3卷积feature map,最后再1*1恢复原来的通道数,所以BottleNeck的目的就是减少参数量。提到BottleNeck结构就是想说明1*1卷积经常用来改变通道数。

这样,分组卷积和深度可分离卷积就能很好地联系起来并牢牢掌握啦!!!