【小白學PyTorch】11 MobileNet詳解及PyTorch實現


文章來自微信公眾號【機器學習煉丹術】。我是煉丹兄,歡迎加我微信好友交流學習:cyx645016617。

@


本來計划是想在今天講EfficientNet PyTorch的,但是發現EfficientNet是依賴於SENet和MobileNet兩個網絡結構,所以本着本系列是給“小白”初學者學習的,所以這一課先講解MobileNet,然后下一課講解SENet,然后再下一課講解EfficientNet,當然,每一節課都是由PyTorch實現的。

1 背景

Mobile是移動、手機的概念,MobileNet是Google在2017年提出的輕量級深度神經網絡,專門用於移動端、嵌入式這種計算力不高、要求速度、實時性的設備。

2 深度可分離卷積

主要應用了深度可分離卷積來代替傳統的卷積操作,並且放棄pooling層。把標准卷積分解成:

  • 深度卷積(depthwise convolution)
  • 逐點卷積(pointwise convolution)。
    這么做的好處是可以大幅度降低參數量和計算量。

2.2 一般卷積計算量

我們先來回顧一下什么是一般的卷積:

先說一下題目:特征圖尺寸是H(高)和W(寬),尺寸(邊長)為K,M是輸入特征圖的通道數,N是輸出特征圖的通道數。

現在簡化問題,如上圖所示,輸入單通道特征圖,輸出特征圖也是單通道的, 我們知道每一個卷積結果為一個標量,從輸出特征圖來看,總共進行了9次卷積。每一次卷積計算了9次,因為每一次卷積都需要讓卷積核上的每一個數字與原來特征圖上對應的數字相乘(這里只算乘法不用考慮加法)。所以圖6.18所示,總共計算了:

\(9*9=3*3*3*3=81\)

如果輸入特征圖是一個2通道的 ,那么意味着卷積核也是要2通道的卷積核才行,此時輸出特征圖還是單通道的。這樣計算量就變成:

\(9*9*2=3*3*3*3*2=162\)

原本單通道特征圖每一次卷積只用計算9次乘法,現在因為輸入通道數變成2,要計算18次乘法才能得到輸出中的1個數字。現在假設輸出特征圖要輸出3通道的特征圖。 那么就要准備3個不同的卷積核,重復上述全部操作3次才能拿的到3個特征圖。所以計算量就是:

\(9*9*2*3=3*3*3*3*2*3=486\)

現在解決原來的問題:特征圖尺寸是H(高)和W(寬),卷積核是正方形的,尺寸(邊長)為K,M是輸入特征圖的通道數,N是輸出特征圖的通道數。 那么這樣卷積的計算量為:

\(H*W*K*K*M*N\)

這個就是卷積的計算量的公式。

2.2 深度可分離卷積計算量

  • 深度可分離卷積(Depthwise Separable Convolution,DSC)

假設在一次一般的卷積中,需要將一個輸入特征圖64×7×7,經過3×3的卷積核,變成128×7×7的輸出特征圖。計算一下這個過程需要多少的計算量:

\(7*7*3*3*64*128=3612672\)

如果用了深度可分離卷積,就是把這個卷積變成兩個步驟:

  1. Depthwise:先用64×7×7經過3×3的卷積核得到一個64×7×7的特征圖。注意注意!這里是64×7×7的特征圖經過3×3的卷積核,不是64×3×3的卷積核!這里將64×7×7的特征圖看成64張7×7的圖片,然后依次與3×3的卷積核進行卷積;
  2. Pointwise:在Depthwise的操作中,不難發現,這樣的計算根本無法整合不同通道的信息,因為上一步把所有通道都拆開了,所以在這一步要用64×1×1的卷積核去整合不同通道上的信息,用128個64×1×1的卷積核,產生128×7×7的特征圖。

最后的計算量就是:

\(7*7*3*3*64+7*7*1*1*64*128=429632\)

計算量減少了百分之80以上。

分解過程示意圖如下:

在圖中可以看到:

  • (a)表示一般卷積過程, 卷積核都是M個通道,然后總共有N和卷積核,意味着輸入特征圖有M個通道,然后輸出特征圖有N個通道。
  • (b)表示depthwise過程, 總共有M個卷積核,這里是對輸入特征圖的M個通道分別做一個卷積,輸出的特征圖也是M個通道的;
  • (c)表示pointwise過程,總共有N個\(1 \times 1\)的卷積核,這樣來整合不同通道的信息,輸出特征圖有N個通道數。

2.3 網絡結構


左圖表示的是一般卷積過程,卷積之后跟上BN和ReLU激活層,因為DBC將分成了兩個卷積過程,所以就變成了圖右這種結構,Depthwise之后加上BN和ReLU,然后Pointwise之后再加上Bn和ReLU。


從整個網絡結構可以看出來:

  • 除了第一層為標准的卷積層之外,其他的層都為深度可分離卷積。
  • 整個網絡沒有使用Pooling層。

3 PyTorch實現

import torch
import torch.nn as nn
import torch.nn.functional as F


class Block(nn.Module):
    '''Depthwise conv + Pointwise conv'''
    def __init__(self, in_planes, out_planes, stride=1):
        super(Block, self).__init__()
        self.conv1 = nn.Conv2d\
            (in_planes, in_planes, kernel_size=3, stride=stride, 
             padding=1, groups=in_planes, bias=False)
        self.bn1 = nn.BatchNorm2d(in_planes)
        self.conv2 = nn.Conv2d\
            (in_planes, out_planes, kernel_size=1, 
            stride=1, padding=0, bias=False)
        self.bn2 = nn.BatchNorm2d(out_planes)

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = F.relu(self.bn2(self.conv2(out)))
        return out


class MobileNet(nn.Module):
    # (128,2) means conv planes=128, conv stride=2, 
    # by default conv stride=1
    cfg = [64, (128,2), 128, (256,2), 256, (512,2), 
           512, 512, 512, 512, 512, (1024,2), 1024]

    def __init__(self, num_classes=10):
        super(MobileNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, 
        	stride=1, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(32)
        self.layers = self._make_layers(in_planes=32)
        self.linear = nn.Linear(1024, num_classes)

    def _make_layers(self, in_planes):
        layers = []
        for x in self.cfg:
            out_planes = x if isinstance(x, int) else x[0]
            stride = 1 if isinstance(x, int) else x[1]
            layers.append(Block(in_planes, out_planes, stride))
            in_planes = out_planes
        return nn.Sequential(*layers)

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.layers(out)
        out = F.avg_pool2d(out, 2)
        out = out.view(out.size(0), -1)
        out = self.linear(out)
        return out

net = MobileNet()
x = torch.randn(1,3,32,32)
y = net(x)
print(y.size())
> torch.Size([1, 10])

正常情況下這個預訓練模型都會輸出1024個線性節點,然后這里我自己加上了一個1024->10的一個全連接層。

我們來看一下這個網絡結構:

print(net)

輸出結果:

然后代碼中:

關於模型通道數的設置部分:

MobileNet就差不多完事了,下一節課為SENet的PyTorch實現和詳解。

文章來自微信公眾號【機器學習煉丹術】。我是煉丹兄,歡迎加我微信好友交流學習:cyx645016617。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM