ShuffleNetV1 V2模型總結及代碼理解


1. ShuffleNet V1 理解

ShuffleNet可以看成是group convolution和depth wise separable convolution的結合。ShuffleNet的創新的點主要有:

  • 利用分組卷積降低了普通卷積的計算量
  • 利用channel shuffle增加了不同通道間的交互能力
    在這里插入圖片描述
2. group convolution的參數量與計算量

group convolution與普通卷積的參數量,計算量對比:
假設卷積的輸入 ( H , W , c 1 ) (H,W,c_1) (H,W,c1),卷積核大小 ( h 1 , w 1 ) (h_1,w_1) (h1,w1),輸出 ( H , W , c 2 ) (H,W,c_2) (H,W,c2),那么對於普通卷積:
參數量: h 1 ⋅ w 1 ⋅ c 1 ⋅ c 2 h_1 \cdot w_1 \cdot c_1 \cdot c_2 h1w1c1c2
計算量: H ⋅ W ⋅ c 2 ⋅ h 1 ⋅ w 1 ⋅ c 1 H \cdot W \cdot c_2 \cdot h_1 \cdot w_1 \cdot c_1 HWc2h1w1c1
conv
同樣的輸入,對於分組卷積,假設分成g組,那么整個過程的:
參數量: h 1 ⋅ w 1 ⋅ c 1 / g ⋅ c 2 / g ⋅ g h_1 \cdot w_1 \cdot c_1/g \cdot c_2/g \cdot g h1w1c1/gc2/gg = h 1 ⋅ w 1 ⋅ c 1 ⋅ c 2 / g h_1 \cdot w_1 \cdot c_1 \cdot c_2/g h1w1c1c2/g
計算量: H ⋅ W ⋅ c 2 / g ⋅ h 1 ⋅ w 1 ⋅ c 1 / g ⋅ g H \cdot W \cdot c_2/g \cdot h_1 \cdot w_1 \cdot c_1/g \cdot g HWc2/gh1w1c1/gg = H ⋅ W ⋅ c 2 ⋅ h 1 ⋅ w 1 ⋅ c 1 / g H \cdot W \cdot c_2 \cdot h_1 \cdot w_1 \cdot c_1/g HWc2h1w1c1/g
由此可見分組卷積的參數量與計算量均是普通卷積的 1 g \frac{1}{g} g1,所以利用分組卷積代替普通卷積可以降低原始卷積的參數量和計算量。
group conv

3. 分組卷積的問題與channel shuffle

但是分組卷積存在的問題在於,輸出的 c 2 / g c_2/g c2/g個通道中只與對應的通道 c 1 / g c_1/g c1/g有信息上的流動,與相鄰的group之間缺少交互,所以作者針對上一組組卷積的結果采用了一種channel shuffle的操作,讓相鄰的group conv有一定的交互能力。
在這里插入圖片描述

4. ShuffleNet V1代碼理解

以下代碼參考自https://github.com/megvii-model/ShuffleNet-Series/blob/master/ShuffleNetV1/blocks.py

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

class ShuffleV1Block(nn.Module):
    def __init__(self, inp, oup, *, group, first_group, mid_channels, ksize, stride):
        super(ShuffleV1Block, self).__init__()
        self.stride = stride
        assert stride in [1, 2]

        self.mid_channels = mid_channels
        self.ksize = ksize
        pad = ksize // 2
        self.pad = pad
        self.inp = inp
        self.group = group

        if stride == 2:
            outputs = oup - inp
        else:
            outputs = oup
		branch_main_1 = [
			# 將pw和dw與分組卷積相結合
            # pw, point wise convolutiuon
            nn.Conv2d(inp, mid_channels, 1, 1, 0, groups=1 if first_group else group, bias=False),
            nn.BatchNorm2d(mid_channels),
            nn.ReLU(inplace=True),
            # dw,depth wise convolution
            nn.Conv2d(mid_channels, mid_channels, ksize, stride, pad, groups=mid_channels, bias=False),
            nn.BatchNorm2d(mid_channels),
        ]
        branch_main_2 = [
            # pw-linear
            nn.Conv2d(mid_channels, outputs, 1, 1, 0, groups=group, bias=False),
            nn.BatchNorm2d(outputs),
        ]
        self.branch_main_1 = nn.Sequential(*branch_main_1)
        self.branch_main_2 = nn.Sequential(*branch_main_2)

        if stride == 2:
            self.branch_proj = nn.AvgPool2d(kernel_size=3, stride=2, padding=1)

channel shuffle 過程:

def channel_shuffle(self, x):
        batchsize, num_channels, height, width = x.data.size()
        assert num_channels % self.group == 0 # 需要提前判斷是否能夠被整除
        group_channels = num_channels # self.group
        # 分成 self.group組,每一組group_channels個通道
        x = x.reshape(batchsize, group_channels, self.group, height, width)
        x = x.permute(0, 2, 1, 3, 4) # 交換不同組的信息
        x = x.reshape(batchsize, num_channels, height, width)
        return x

前向傳遞過程:

def forward(self, old_x):
        x = old_x
        x_proj = old_x
        x = self.branch_main_1(x)
        if self.group > 1:
            x = self.channel_shuffle(x)
        x = self.branch_main_2(x)
        if self.stride == 1:
            return F.relu(x + x_proj)
        elif self.stride == 2:
            return torch.cat((self.branch_proj(x_proj), F.relu(x)), 1)
5. shuffleNet V2 理解

shuffleNet V2 的一個Motivation是:在設計網絡結構時,除了考了計算量Flops,還應該考慮內存的訪問代價(MAC),並行化對應的時間,以及不同的部署環境ARM或者GPU

shuffleNetV2主要實驗性的提出了一些網絡設計方面的trick:

  1. 卷積的輸入的通道數與輸出的通道數應該盡量相同( M A C = h w ( c 1 + c 2 ) + c 1 c 2 MAC=hw(c_1+c_2)+c_1c_2 MAC=hw(c1+c2)+c1c2
  2. 過多的組卷積只會增加內存訪問時間
  3. 網絡碎片化會降低並行度
  4. Element wise的運算增加內存訪問時間

在上面的基礎上,shuffleNet V1 需要組卷積(與2點違背)和 linear bottleneck (與1點違背);mobileNet V2需要殘差結構(與4點違背)和expansion/projection layer(與1點違背)

shuffleNet V2 改進點:

  • 在輸入層采用channel split,代替group convolution的作用
  • 在輸出結果時采用concat 替代add操作
  • 移除bottle neck部分的channel shuffle 操作

圖©為shuffleNet V2 原始版本,圖(d)為V2 的下采樣版本
shuffleNet V2

參考資料

  • shufflenet系列 pytorch 代碼: https://github.com/megvii-model/ShuffleNet-Series
  • group convolution: https://blog.yani.ai/filter-group-tutorial/
  • group convolution 計算量理解:https://zhuanlan.zhihu.com/p/65377955


免責聲明!

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



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