1. 程式人生 > >『算法學習』輕量級網絡MobileNet_v2

『算法學習』輕量級網絡MobileNet_v2

conv2 降維 caption 映射 net 節點 con float 實現

前文鏈接:『算法學習』深度可分離卷積和MobileNet_v1

一、MobileNet v1 的不足

Relu 和數據坍縮

Moblienet V2文中提出,假設在2維空間有一組由 技術分享圖片 個點組成的螺旋線 技術分享圖片 數據,經隨機矩陣 技術分享圖片 映射到 技術分享圖片 維並進行ReLU運算,即:

技術分享圖片

再通過 技術分享圖片 矩陣的廣義逆矩陣 技術分享圖片技術分享圖片 映射回2維空間:

技術分享圖片

對比 技術分享圖片技術分享圖片 發現,當映射維度 技術分享圖片 時,數據坍塌;當 技術分享圖片 時,數據基本被保存。雖然這不是嚴格的數學證明,但是至少說明:channel少的feature map不應後接ReLU,否則會破壞feature map。

技術分享圖片

簡單說一下上圖:對於一個輸入圖像,首先通過一個隨機矩陣T將數據轉換為n維,然後對這n維數據進行ReLU操作,最後再使用T的逆矩陣轉換回來,實驗發現當n很小的時候,後面接ReLU非線性變換的話會導致很多信息的丟失,而且維度越高還原的圖片和原圖越相似。

ResNet 、Relu 和神經元死亡

在神經網絡訓練中如果節點的值變為0就會“死掉”。因為ReLU對0值的梯度是0,後續無論怎麽叠代這個節點的值都不會恢復了。而通過ResNet結構的特征復用,可以很大程度上緩解這種特征退化問題(這也從一個側面說明ResNet為何好於VGG)。另外,一般情況訓練網絡使用的是float32浮點數;當使用低精度的float16時,這種特征復用可以更加有效的減緩退化。

技術分享圖片

二、Inverted residual block

理解之前的問題後看,其實Mobilenet V2使用的基本卷積單元結構有以下特點:

  • 整體上繼續使用Mobilenet V1的Separable convolution降低卷積運算量
  • 引入了特征復用結構,即采取了ResNet的思想
  • 采用Inverted residual block結構,對Relu的缺陷進行回避

Inverted residuals 可以認為是residual block的拓展,其重點聚焦在殘差網絡各層的層數,進入block後會先將特征維數放大,然後再壓縮回去,呈現梭子的外形,而傳統殘差設計是沙漏形,下面是MobileNetV1、MobileNetV2 和ResNet微結構對比:

技術分享圖片可以看到MobileNetV2 和ResNet基本結構很相似。不過ResNet是先降維(0.25倍)、提特征、再升維。而MobileNetV2 則是先升維(6倍)、提特征、再降維。、

註:模型中使用 ReLU6 作為非線性層,在低精度計算時能壓縮動態範圍,算法更穩健。
ReLU6 定義為:f(x) = min(max(x, 0), 6),詳見 tf.nn.relu6 API

至於Linear Bottlenecks,論文中用很多公式表達這個思想,但是實現上非常簡單,就是在MobileNetV2微結構中第二個PW後無ReLU6,對於低維空間而言,進行線性映射會保存特征,而非線性映射會破壞特征,實際代碼如下:

def _bottleneck(inputs, nb_filters, t): 
    x = Conv2D(filters=nb_filters * t, kernel_size=(1,1), padding=‘same‘)(inputs) 
    x = Activation(relu6)(x) 
    x = DepthwiseConv2D(kernel_size=(3,3), padding=‘same‘)(x) 
    x = Activation(relu6)(x) 
    x = Conv2D(filters=nb_filters, kernel_size=(1,1), padding=‘same‘)(x) 
    # do not use activation function 
    if not K.get_variable_shape(inputs)[3] == nb_filters: 
        inputs = Conv2D(filters=nb_filters, kernel_size=(1,1), padding=‘same‘)(inputs) 
    outputs = add([x, inputs]) 
    return outputs 

相對應的,主結構堆疊上面的block 即可,下面是一個簡單的版本,

def MobileNetV2_relu(input_shape, k): 
    inputs = Input(shape = input_shape) 
    x = Conv2D(filters=32, kernel_size=(3,3), padding=‘same‘)(inputs) 
    x = _bottleneck_relu(x, 8, 6) 
    x = MaxPooling2D((2,2))(x) 
    x = _bottleneck_relu(x, 16, 6)
    x = _bottleneck_relu(x, 16, 6) 
    x = MaxPooling2D((2,2))(x) 
    x = _bottleneck_relu(x, 32, 6) 
    x = GlobalAveragePooling2D()(x) 
    x = Dense(128, activation=‘relu‘)(x) 
    outputs = Dense(k, activation=‘softmax‘)(x) 
    model = Model(inputs, outputs) 
    return model

原文網絡結構如下:

技術分享圖片

『算法學習』輕量級網絡MobileNet_v2