天天看點

《魔改Yolov5》替換MobileNet V3 backbone

作者:MuggleZero 

 《魔改Yolov5》專欄位址:魔改Yolov5

MobileNet V3 論文翻譯位址:

MobileNet V3 論文翻譯

《魔改Yolov5》替換MobileNet V3 backbone

h_sigmoid複現:

common.py

class h_sigmoid(nn.Module):
     def __init__(self, inplace=True):
         super(h_sigmoid, self).__init__()
         self.relu = nn.ReLU6(inplace=inplace)
 
     def forward(self, x):
         return self.relu(x + 3) / 6      

h_swish複現:

common.py

至此已經實作了h_swish公式内容。

class h_swish(nn.Module):
     def __init__(self, inplace=True):
         super(h_swish, self).__init__()
         self.sigmoid = h_sigmoid(inplace=inplace)
 
     def forward(self, x):
         y = self.sigmoid(x)
         return x * y      

網絡結構複現

主體結構:

《魔改Yolov5》替換MobileNet V3 backbone
《魔改Yolov5》替換MobileNet V3 backbone

NL:使用的非線性層

HS:h-swish

RE:ReLU

NBN:沒有批處理規範化

SE: squeeze-and-excite

第一行的意思:使用了h-swish的非線性層的3x3 conv2d,stride=2, out channels=16。複現代碼:

class Conv3BN(nn.Module):
     """
     This equals to
     def conv_3x3_bn(inp, oup, stride):
         return nn.Sequential(
             nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
             nn.BatchNorm2d(oup),
             h_swish()
         )
     """
 
     def __init__(self, inp, oup, stride):
         super(Conv3BN, self).__init__()
         self.conv = nn.Conv2d(inp, oup, 3, stride, 1, bias=False)
         self.bn = nn.BatchNorm2d(oup)
         self.act = h_swish()
 
     def forward(self, x):
         return self.act(self.bn(self.conv(x)))
 
     def fuseforward(self, x):
         return self.act(self.conv(x))      

squeeze-and-excite

MobileNet v3 中 引用的Squeeze-and-Excite是怎麼回事

AdaptiveAvgPool2d
 Linear
 ReLU
 Linear
 h_sigmoid      
《魔改Yolov5》替換MobileNet V3 backbone

對應的操作:

  • Squeeze(Fsq)

    用AdaptiveAvgPool2d将H × W × C H的feature map壓縮為1 × 1 × C

  • Excitation(Fex)

    得到Squeeze的1 × 1 × C 的feature map後,使用FC全連接配接層,對每個通道的重要性進行預測,得到不同channel的重要性大小。有兩個全連接配接,一個降維,一個恢複次元。

  • Scale

    最後将學習到的各個channel的激活值(sigmoid激活,值0~1)乘以之前的feature map的對應channel上。

class SELayer(nn.Module):
     def __init__(self, channel, reduction=4):
         super(SELayer, self).__init__()
         #用1x1的卷積
         self.avg_pool = nn.AdaptiveAvgPool2d(1) #Squeeze,将H × W × C H的feature map壓縮為1 × 1 × C
         self.fc = nn.Sequential(
             nn.Linear(channel, channel // reduction),
             nn.ReLU(inplace=True),
             nn.Linear(channel // reduction, channel),
             h_sigmoid() # h_sigmoid,修改了x
         ) # 使用FC全連接配接層,對每個通道的重要性進行預測,得到不同channel的重要性大小
 
     def forward(self, x):
         # b=1,c=16,_=64,16個資料組成一行,一共64排資料。
         b, c, _, _ = x.size() #傳回x張量行數、列數、深度.
         # Squeeze
         y = self.avg_pool(x).view(b, c)  #池化後的1 × 1 × C feature map重塑為 b*c 的張量
         
         # Excitation 預測并重塑為 b*c*1*1張量
         y = self.fc(y).view(b, c, 1, 1)
         
         # Scale
         return x * y      

濾波器4維張量:

[height,width,input_channels,output_channels]   //濾波器張量
 [卷積核高度,卷積核寬度,圖像通道數,卷積核個數]  //第三維input_channels為input張量的第四維。      

圖像4維張量張量:

[batch_size,height,width, channels]  //圖像張量
 [個數,高度,寬度,通道數]      

InvertedResidual

InvertedResidual(論文中是bneck)是MobileNetv2中提出的,stride=1和stride=2在走向是有差異的。

stride=1的時候:

1、point-wise升維 2、depth-wise提取特征 3、通過Linear的point-wise降維。 4、input與結果 相加(殘差結構)
《魔改Yolov5》替換MobileNet V3 backbone
《魔改Yolov5》替換MobileNet V3 backbone

第一層bneck是支援relu6的3x3卷積,第二個箭頭所指的bneck是支援hswish的5x5卷積。

整體結構如下所示,

《魔改Yolov5》替換MobileNet V3 backbone

即先利用1x1卷積進行升次元,再進行下面的操作,并具有殘差邊:

《魔改Yolov5》替換MobileNet V3 backbone

在輸入1x1卷積進行升次元後,進行3x3深度可分離卷積:

《魔改Yolov5》替換MobileNet V3 backbone

注意力機制調整每個通道的權重:

《魔改Yolov5》替換MobileNet V3 backbone

1x1卷積:

class InvertedResidual(nn.Module):
     def __init__(self, inp, oup, hidden_dim, kernel_size, stride, use_se, use_hs):
         super(InvertedResidual, self).__init__()
         assert stride in [1, 2]
 
         self.identity = stride == 1 and inp == oup
         # 如果輸入通道等于 隐藏層的維數,來一個dw
         if inp == hidden_dim:
             self.conv = nn.Sequential(
                 # dw Depth-wise
                 nn.Conv2d(hidden_dim, hidden_dim, kernel_size, stride, (kernel_size - 1) // 2, groups=hidden_dim,
                           bias=False),
                 nn.BatchNorm2d(hidden_dim),
                 h_swish() if use_hs else nn.ReLU(inplace=True),
                 # Squeeze-and-Excite
                 SELayer(hidden_dim) if use_se else nn.Sequential(),
                 # pw-linear
                 nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
                 nn.BatchNorm2d(oup),
             )
         else:
             self.conv = nn.Sequential(
                 # pw Point-wise 1x1 
                 nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False),
                 nn.BatchNorm2d(hidden_dim),
                 h_swish() if use_hs else nn.ReLU(inplace=True),
                 # dw Depth-wise
                 nn.Conv2d(hidden_dim, hidden_dim, kernel_size, stride, (kernel_size - 1) // 2, groups=hidden_dim,
                           bias=False),
                 nn.BatchNorm2d(hidden_dim),
                 # Squeeze-and-Excite
                 SELayer(hidden_dim) if use_se else nn.Sequential(),
                 h_swish() if use_hs else nn.ReLU(inplace=True),
                 # pw-linear
                 nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
                 nn.BatchNorm2d(oup),
             )
 
     def forward(self, x):
         y = self.conv(x)
         # stride=1時,輸入加輸出
         if self.identity:
             return x + y
         else:
             return y      

yaml定義:

# YOLOv5 backbone
 backbone:
   # MobileNetV3-small
   # [from, number, module, args]
   [[-1, 1, Conv3BN, [16, 2]],                         # 0-p1/2
    [-1, 1, InvertedResidual, [16,  16, 3, 2, 1, 0]],  # 1-p2/4
    [-1, 1, InvertedResidual, [24,  72, 3, 2, 0, 0]],  # 2-p3/8
    [-1, 1, InvertedResidual, [24,  88, 3, 1, 0, 0]],  # 3-p3/8
    [-1, 1, InvertedResidual, [40,  96, 5, 2, 1, 1]],  # 4-p4/16
    [-1, 1, InvertedResidual, [40, 240, 5, 1, 1, 1]],  # 5-p4/16
    [-1, 1, InvertedResidual, [40, 240, 5, 1, 1, 1]],  # 6-p4/16
    [-1, 1, InvertedResidual, [48, 120, 5, 1, 1, 1]],  # 7-p4/16
    [-1, 1, InvertedResidual, [48, 144, 5, 1, 1, 1]],  # 8-p4/16
    [-1, 1, InvertedResidual, [96, 288, 5, 2, 1, 1]],  # 9-p5/32
    [-1, 1, InvertedResidual, [96, 576, 5, 1, 1, 1]],  # 10-p5/32
    [-1, 1, InvertedResidual, [96, 576, 5, 1, 1, 1]],  # 11-p5/32
   ]