GhostNet

歡迎訪問我的個人博客,查看更多文章:https://www.wanglichun.tech/2020/03/08/ghostnet/

本篇論文是發表於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依然會產生較大的計算量?看卷積計算量的計算公式nhwckkn*h*w*c*k*k,可以發現,由於c和n都是比較大的,所以會導致這個計算量也是比較大的,但是作者在分析輸出的特徵圖的時候發現,其實有些特徵圖是比較相似的,如下所示,作者認爲可以通過簡單的變換得到。

在這裏插入圖片描述

基於此,作者得到啓發,是不是每張圖都需要用這麼大的計算量去得到?是否可以通過cheap transformation得到這些相似的特徵圖?

於是乎,就誕生了GhostNet。Ghost就是說相似的特徵圖,猶如另一個的幽靈,可以通過簡單的線性變換得到。

在這裏插入圖片描述

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=msn = m * s

由於Ghost的變換過程中最後存在一個恆等變換(Identity),所以實際有效的變換數量是s-1,所以上式可以得到如下公式:

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

所以我們便可以計算得到如下結果:

在這裏插入圖片描述

當然這裏還有一個條件:m << n

通過這麼分析,可以體會到,其實GhostNet的方法也很簡單,無外乎就是將原本的乘法變成了兩個乘法相加,然後在代碼實現中,其實第二個變換是用depthwise conv實現的。作者在文中也提到,前面的卷積使用pointwise效率比較高,所以網絡嫣然類似一個mobilenet的反過來的版本,只不過GhostNet採用了拼接的方式,進一步減少了計算量

Ghost module的pytorch代碼如下:

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是先升維再降維。

在這裏插入圖片描述

Ghost bottleneck

下面是GhostNet的網絡結構圖,可以看到channel控制的比較小,並且引入了SE結構。

在這裏插入圖片描述

GhostNet 細節

實驗

作者首先採用控制變量法,測試不同的s以及d的效果。經過測試發現在s=2,d=3的情況下模型表現較好。

在這裏插入圖片描述

下表爲在resnet50實驗的不同網絡的壓縮比例以及精度對比

在這裏插入圖片描述

ImageNet效果對比

在這裏插入圖片描述
在這裏插入圖片描述

目標檢測的效果

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章