相對重量級網(wǎng)絡而言,輕量級網(wǎng)絡的特點是參數(shù)少、計算量小、推理時間短。更適用于存儲空間和功耗受限的場景,例如移動端嵌入式設備等邊緣計算設備。因此輕量級網(wǎng)絡受到了廣泛的關(guān)注,其中MobileNet可謂是其中的佼佼者。MobileNetV3經(jīng)過了V1和V2前兩代的積累,性能和速度都表現(xiàn)優(yōu)異,受到學術(shù)界和工業(yè)界的追捧,無疑是輕量級網(wǎng)絡的“抗把子“。MobileNetV3 參數(shù)是由NAS(network architecture search)搜索獲取的,又繼承的V1和V2的一些實用成果,并引人SE通道注意力機制,可謂集大成者。本文以應用為主,結(jié)合代碼剖析MobileNetV3的網(wǎng)絡結(jié)構(gòu),不會對NAS以及其設計思想做過多解析。
上圖為MobileNetV3的網(wǎng)絡結(jié)構(gòu)圖,large和small的整體結(jié)構(gòu)一致,區(qū)別就是基本單元bneck的個數(shù)以及內(nèi)部參數(shù)上,主要是通道數(shù)目。
small和large版本參數(shù)
上表為具體的參數(shù)設置,其中bneck是網(wǎng)絡的基本結(jié)構(gòu)。SE代表是否使用通道注意力機制。NL代表激活函數(shù)的類型,包括HS(h-swish),RE(ReLU)。NBN 代表沒有BN操作。s 是stride的意思,網(wǎng)絡使用卷積stride操作進行降采樣,沒有使用pooling操作。
通道分離卷積是MobileNet系列的主要特點,也是其發(fā)揮輕量級作用的主要因素。如下圖,通道可分離卷積分為兩個過程:1.channel方向通道可分離卷積;2.正常的1X1卷積輸出指定的channel個數(shù)。
「代碼實現(xiàn)」:
# 首先利用1X1卷積進行通道壓縮,可以進一步壓縮模型大小
self.conv1 = nn.Conv2d(in_size, expand_size, kernel_size=1, stride=1, padding=0, bias=False)
self.bn1 = nn.BatchNorm2d(expand_size)
self.nolinear1 = nolinear # 激活函數(shù),使用H-swish或H-relu
# 注意,通道可分離卷積使用分組卷積操作進行,這里分成和卷積核相同的channel組數(shù)實現(xiàn)。
self.conv2 = nn.Conv2d(expand_size, expand_size, kernel_size=kernel_size, stride=stride,
padding=kernel_size // 2, groups=expand_size, bias=False)
self.bn2 = nn.BatchNorm2d(expand_size)
self.nolinear2 = nolinear
# 利用1X1卷積輸出指定通道個數(shù)的卷積,這一步一定要有,不然無法控制輸出通道個數(shù)。也有聚合分離特征的作用
self.conv3 = nn.Conv2d(expand_size, out_size, kernel_size=1, stride=1, padding=0, bias=False)
self.bn3 = nn.BatchNorm2d(out_size)
SE通道注意力機制,老生常談的話題。這里不進行解析,直接給出代碼。值得注意的是,這里利用1X1卷積實現(xiàn)的FC操作,本質(zhì)上和FC是一樣的。這里利用hsigmoid模擬sigmoid操作。
「代碼實現(xiàn)」:
class SeModule(nn.Module):
def __init__(self, in_size, reduction=4):
super(SeModule, self).__init__()
self.se = nn.Sequential(
nn.AdaptiveAvgPool2d(1),
nn.Conv2d(in_size, in_size // reduction, kernel_size=1, stride=1, padding=0, bias=False),
nn.BatchNorm2d(in_size // reduction),
nn.ReLU(inplace=True),
nn.Conv2d(in_size // reduction, in_size, kernel_size=1, stride=1, padding=0, bias=False),
nn.BatchNorm2d(in_size),
hsigmoid())
def forward(self, x):
return x * self.se(x)
利用近似操作模擬swish和relu,公式如下:
「代碼實現(xiàn)」:
class hswish(nn.Module):
def forward(self, x):
out = x * F.relu6(x + 3, inplace=True) / 6
return out
class hsigmoid(nn.Module):
def forward(self, x):
out = F.relu6(x + 3, inplace=True) / 6
return out
核心模塊,也是網(wǎng)絡的基本模塊。主要實現(xiàn)了通道可分離卷積+SE通道注意力機制+殘差連接。結(jié)構(gòu)圖如下:
「代碼實現(xiàn)」:
def forward(self, x):
out = self.nolinear1(self.bn1(self.conv1(x))) # 降維
out = self.nolinear2(self.bn2(self.conv2(out))) # 通道可分離卷積
out = self.bn3(self.conv3(out)) # 1X1卷積聚合特征
if self.se != None:
out = self.se(out) # 通道注意力機制
out = out + self.shortcut(x) if self.stride == 1 else out # 殘差連接
return out
移除之前的瓶頸層連接,進一步降低網(wǎng)絡參數(shù)??梢杂行Ы档?1%的推理耗時,而性能幾乎沒有損失。修改結(jié)構(gòu)如下:
「代碼實現(xiàn)」:
out = F.avg_pool2d(out, 7)
out = out.view(out.size(0), -1)
out = self.hs3(self.bn3(self.linear3(out)))
out = self.linear4(out)
return out
MobileNetV3結(jié)構(gòu)簡潔,性能強悍。你一定在yolov3、OCR等等任務上看到它的身影。本文從代碼層面解析,希望能帶給你一點收獲!
最后,如果喜歡本篇文章,歡迎轉(zhuǎn)發(fā)、點贊。
聯(lián)系客服