0%

GhostNet(CVPR2020)

本篇论文是发表于CVPR2020的一篇轻量级网络的论文,作者是华为诺亚方舟实验室,文章的总体思路比较清晰,为了减少网络计算量,作者将传统的卷积分成两步进行,首先利用较少的计算量通过传统的卷积生成channel较小的特征图,然后在此特征图的基础上,通过cheap operation(depthwise conv)再进一步利用较少的计算量,生成新的特征图,最后将两组特征图拼接到一起,得到最终的output,最终实验效果还不错,相同计算量的情况下比MobileNet- V3的效果还要更好一些。


作者:华为诺亚方舟实验室

论文链接:https://arxiv.org/abs/1911.11907

作者解读:https://zhuanlan.zhihu.com/p/109325275

开源代码tensorflow:https://github.com/huawei-noah/ghostnet

开源代码:pytorch : https://github.com/iamhankai/ghostnet.pytorch/blob/master/ghost_net.py


虽然说mobileNet或者是shuffleNet提出了使用depthwise或者是shuffle等操作,但是引入的1x1卷积依然会产生一定的计算量。

为什么1x1依然会产生较大的计算量?看卷积计算量的计算公式$n*h*w*c*k*k$,可以发现,由于c和n都是比较大的,所以会导致这个计算量也是比较大的,但是作者在分析输出的特征图的时候发现,其实有些特征图是比较相似的,如下所示,作者认为可以通过简单的变换得到。

2.png

基于此,作者得到启发,是不是每张图都需要用这么大的计算量去得到?是否可以通过cheap transformation得到这些相似的特征图?

于是乎,就诞生了GhostNet。Ghost就是说相似的特征图,犹如另一个的幽灵,可以通过简单的线性变换得到。

0.png

Ghost module结构图

上图完美的解释了Ghost究竟做了什么。

Ghost module介绍

相比于传统的卷积,GhostNet分两步走,首先GhostNet采用正常的卷积计算,得到channel较少的特征图,然后利用cheap operation得到更多的特征图,然后将不同的特征图concat到一起,组合成新的output.

那么这么处理,到底节省了多少计算量呢?下面我们分析一下。

首先,假设我们输入特征图的尺寸是h*w*c,输出特征图的尺寸是h’*w’*n,卷积核大小为k*k。

在cheap operation变换中,我们假设特征图的channel是m,变换的数量是s,最终得到的新的特征图的数量是n,那么我们可以得到等式:

$$n = m * s$$

由于Ghost的变换过程中最后存在一个恒等变换(Identity),所以实际有效的变换数量是s-1,所以上式可以得到如下公式:

$$m * (s-1) = n/s * (s-1)$$

所以我们便可以计算得到如下结果:

1.png

当然这里还有一个条件:m << n

通过这么分析,可以体会到,其实GhostNet的方法也很简单,无外乎就是将原本的乘法变成了两个乘法相加,然后在代码实现中,其实第二个变换是用depthwise conv实现的。作者在文中也提到,前面的卷积使用pointwise效率比较高,所以网络嫣然类似一个mobilenet的反过来的版本,只不过GhostNet采用了拼接的方式,进一步减少了计算量

Ghost module的pytorch代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class GhostModule(nn.Module):
def __init__(self, inp, oup, kernel_size=1, ratio=2, dw_size=3, stride=1, relu=True):
super(GhostModule, self).__init__()
self.oup = oup
init_channels = math.ceil(oup / ratio)
new_channels = init_channels*(ratio-1)

self.primary_conv = nn.Sequential(
nn.Conv2d(inp, init_channels, kernel_size, stride, kernel_size//2, bias=False),
nn.BatchNorm2d(init_channels),
nn.ReLU(inplace=True) if relu else nn.Sequential(),
)

self.cheap_operation = nn.Sequential(
nn.Conv2d(init_channels, new_channels, dw_size, 1, dw_size//2, groups=init_channels, bias=False),
nn.BatchNorm2d(new_channels),
nn.ReLU(inplace=True) if relu else nn.Sequential(),
)

def forward(self, x):
x1 = self.primary_conv(x)
x2 = self.cheap_operation(x1)
out = torch.cat([x1,x2], dim=1)
return out[:,:self.oup,:,:]

Difference from existing methods

  • 在MobileNet等网络中,基本都会使用1x1的卷积,而在GhostNet中,可以使用任何尺寸的卷积。
  • 在MobileNet中,使用了pointwise+depthwise,但是在GhostNet中,可以使用正常的卷积。
  • Ghost使用了更多的线性变换。

GhostNet结构

下图是Ghost bottleneck结构图,很类似resnet结构,不同的是channel是先升维再降维。

3.png

Ghost bottleneck

下面是GhostNet的网络结构图,可以看到channel控制的比较小,并且引入了SE结构。

4.png

GhostNet 细节

实验

作者首先采用控制变量法,测试不同的s以及d的效果。经过测试发现在s=2,d=3的情况下模型表现较好。

5.png

下表为在resnet50实验的不同网络的压缩比例以及精度对比

11.png

ImageNet效果对比

8.png

9.png

目标检测的效果

10.png