1. 程式人生 > >卷積神經網路之NiN(2013)

卷積神經網路之NiN(2013)

Network-in-network(2013)

文章書寫匆忙,有些使用了網上其他朋友的文字以及圖片,但是沒有及時複製對應的連結,在此深表歉意,以及深深的感謝。 如有朋友看到了對應的出處,或者作者發現,可以留言,小弟馬上修改,新增引用。

2013年年尾,Min Lin提出了在卷積後面再跟一個1x1的卷積核對影象進行卷積,這就是Network-in-network的核心思想了。NiN在每次卷積完之後使用,目的是為了在進入下一層的時候合併更多的特徵引數。同樣NiN層也是違背LeNet的設計原則(淺層網路使用大的卷積核),但卻有效地合併卷積特徵,減少網路引數、同樣的記憶體可以儲存更大的網路。

根據Min Lin的NiN論文,他們說這個“網路的網路”(NIN)能夠提高CNN的區域性感知區域

。例如沒有NiN的當前卷積是這樣的:3x3 256 [conv] -> [maxpooling],當增加了NiN之後的卷積是這樣的:3x3 256 [conv] -> 1x1 256 [conv] -> [maxpooling]。

img

MLP多層感知的厲害之處就在於它把卷積特徵結合起來成為一個更復雜的組合,這個思想將會在後面ResNet和Inception中用到。

NIN的第一個N指mlpconv,第二個N指整個深度網路結構,即整個深度網路是由多個mlpconv構成的。

論文概要

我們提出了一種新型的深度網路結構,稱為“Network In Network”(NIN),它可以增強模型在

感受野(receptive field)內對區域性區域(local patches)的辨別能力。傳統的卷積層使用線性濾波器來掃描輸入,後面接一個非線性啟用函式。而我們則構建了一些結構稍複雜的微型神經網路來抽象receptive field內的資料。

我們用多層感知器例項化微型神經網路,這是一種有效的函式逼近器。

特徵圖可以通過微型神經網路在輸入上滑動得到,類似於CNN;接下來特徵圖被傳入下一層。深度NIN可以通過堆疊上述結構實現。通過微型網路增強區域性模型,我們就可以在分類層中利用所有特徵圖的全域性平均池化層(GAP),這樣更容易解釋且比傳統的全連線層更不容易過擬合

mlpconv層更好地模型化了區域性塊,GAP充當了防止全域性過度擬合的結構正則化器。

使用這兩個NIN元件,我們在CIFAR-10、CIFAR-100和Svhn資料集上演示了最新的效能。

通過特徵對映的視覺化,證明了NIN最後一個mlpconv層的特徵對映是類別的置信度對映,這就激發了通過nin進行目標檢測的可能性。

新奇點

MLP Convolution Layers(MLP卷積層)

CNN的卷積濾波器是底層資料塊的廣義線性模型(generalized linear model /GLM),而且我們認為它的抽象程度較低。這裡的抽象較低是指該特徵對同一概念的變體是不變的

用更有效的非線性函式逼近器代替GLM可以增強區域性模型的抽象能力。當樣本的隱含概念(latent concept)線性可分時,GLM可以達到很好的抽象程度,例如:這些概念的變體都在GLM分割平面的同一邊,而傳統的CNN就默認了這個假設——認為隱含概念(latent concept)是線性可分的

傳統的CNN用超完備濾波器來提取潛在特徵:使用大量的濾波器來提取某個特徵,把所有可能的提取出來,這樣就可以把想要提取的特徵也覆蓋到。這種通過增加濾波器數量的辦法會增大網路的計算量和引數量。

使用MLP來增強conv的原因: 然而,同一概念的資料通常是非線性流形的(nonlinear manifold),捕捉這些概念的表達通常都是輸入的高維非線性函式。在NIN中,GLM用“微型網路”結構替代,該結構是一個非線性函式逼近器。

最終結構我們稱為“mlpconv”層,與CNN的比較見圖.

img

線性卷積層包含線性濾波器,而mlpconv層包含的是微型網路(本文選擇多層感知器)。兩種層都將區域性感受野對映到了隱含概念的置信度值.

線性卷積層和mlpconv層都從區域性感受野(receptive field)對映到了輸出特徵向量。mlpconv 層將區域性塊的輸入通過一個**由全連線層和非線性啟用函式組成的多層感知器(MLP)**對映到了輸出的特徵向量, 從圖上可以看到,每個區域性感受野的神經元進行了更復雜的運算。

MLP在所有區域性感受野中共享。特徵圖通過用像CNN一樣的方式在輸入上滑動MLP得到,NIN的總體結構是一系列mplconv層的堆疊。被稱作“Network In Network”(NIN),因為內部含有MLP。

由於沒有關於潛在概念分佈的先驗資訊,使用通用函式逼近器來提取區域性塊的特徵是可取的,因為它能夠逼近潛在概念的更抽象的表示。徑向基網路和多層感知器是兩種著名的通用函式逼近器。

我們在這項工作中選擇多層感知器有兩個原因。

  • 首先,MLP的引數可以使用BP演算法訓練,與CNN高度整合;
  • 第二,MLP可自行深度化(可以增加Hidden layer),符合特徵複用的思想。

MLP在CNN基礎上的進步在於:

  • 將feature map由多通道的線性組合變為非線性組合,提高特徵抽象能力;
  • 通過1x1卷積核及GAP代替fully connected layers實現減小引數。

多個1x1的卷積核級聯就可以實現對多通道的feature map做非線性的組合,再配合啟用函式,就可以實現MLP結構,同時通過1x1的卷積核操作還可以實現卷積核通道數的降維和升維,實現引數的減小化。通過MLP微結構,實現了不同filter得到的不同feature map之間的整合,可以使網路學習到複雜和有用的跨特徵圖特徵。MLP中的每一層相當於一個卷積核為1x1的卷積層。

1*1卷積核,可以在保持feature map尺度不變的(即不損失解析度)的前提下, 對區域性視野下的神經元進行更加複雜的運算, 大幅增加非線性特性(利用後接的非線性啟用函式),把網路做的很深。

由mlpconv層執行的計算如下:

1539939528422

這裡n是多層感知器中的層數。在多層感知器中,採用整流線性單元作為啟用函式。

從跨通道(跨特徵圖–cross feature map)池的角度看,方程2等價於正規卷積層上的級聯跨通道引數池化( cascaded cross channel parametric pooling)

每個池層對輸入特徵對映執行加權線性重組,然後通過整流線性單元。跨通道引數池功能對映在下一層中一次又一次地跨通道引數池。這種級聯的跨通道引數池結構允許複雜且可學習的交叉通道資訊互動。

因為一般卷積操作可以看成特徵的提取操作,而一般卷積一層只相當於一個線性操作(CNN的卷積濾波器是底層資料塊的廣義線性模型(generalized linear model /GLM)),所以其只能提取出線性特徵。

所以該作者在卷積層後也加入一個MLP使得每層卷積操作能夠提取非線性特徵。

其實這裡的MLP,指的是同一層中,不同特徵層之間,同一個位置上的值的MLP

1539950690621

使用1*1卷積核,實現降維和升維的操作其實就是channel間資訊的線性組合變化,3*3,64channels的卷積核後面新增一個1*1,28channels的卷積核,就變成了3*3,28channels的卷積核,原來的64個channels就可以理解為跨通道線性組合變成了28channels,這就是通道間的資訊互動。

注意:只是在channel維度上做線性組合,W和H上是共享權值的sliding window

MLPConv是一組非線性對映的級聯,Maxout則是整體進行了一個非線性對映,跨多個仿射特徵對映執行max池化, yMaxout=max{w1x+b1...wkx+bkyMlpconv=max(wkmax(...max(w1x+b1,0)...)+bk,0) y_{Maxout}=max\left\{\begin{matrix}w_1x+b_1\\ ...\\w_kx+b_k\end{matrix}\right.\\ y_{Mlpconv}=max(w_k*max(...max(w_1x+b_1,0)...)+b_k,0) Mlpconv層與Maxout層的不同之處在於,凸函式逼近器被一個通用函式逼近器所代替,它在建模各種隱性概念分佈方面具有更強的能力。

跨通道引數池層也等價於具有1x1卷積核的卷積層。這一解釋使得理解NiN的結構更為直觀。

為什麼可以這麼理解?

NIN與1×1卷積核的關係: 因為NIN中的MLP層可以用兩層1×1卷積核來代替.

比如當前這一層是54×54×96的影象層,然後過一個1×1×96的卷積核,還是一個54×54×96的卷積層,然後再過一個1×1×96的卷積核,還是一個54×54×96的卷積層。

這樣看最開始那個96個特徵層的影象同一個位置不同層之間的畫素點,相當於過了一個96×96×96的MLP網路.

全域性平均池化(GAP)層輸出作為可信度

我們不採用在CNN中傳統的完全連通層進行分類,而是通過全域性平均池層(global average pooling layer)直接輸出最後一個mlpconv層的特徵對映的空間平均值作為類別的可信度,然後將得到的向量輸入到Softmax層。

在傳統的CNN中,很難解釋如何將來自分類層(objective cost layer)的分類資訊傳遞迴前一個卷積層,因為全連線層像一個黑盒一樣。相反,全域性平均池化更有意義和可解釋,因為它強制特徵對映和類別之間的對應,這是通過使用微型網路進行更強的區域性建模而實現的。

此外,完全連線層容易過度擬合(特徵組合過多),嚴重依賴於Dropout正則化(隨機丟失特徵),而全域性平均池本身就是一個結構正則化(壓縮特徵),這在本質上防止了對整體結構的過度擬合, 以此取代CNN中傳統的全連通層。

其思想是為最後一個mlpconv層中的分類任務的每個對應類別生成一個特徵對映。我們沒有在特徵對映的頂部新增完全連通的層,而是取每個特徵對映的平均值,並將得到的向量直接輸入到Softmax層。

GAP取代完全連通層上的一個優點是,通過增強特徵對映和類別之間的對應關係,它更適合於卷積結構

因此,特徵對映可以很容易地解釋為類別信任對映(categories confidence maps)。

另一個優點是在全域性平均池中沒有優化引數,從而避免了這一層的過度擬合。此外,全域性平均池綜合了空間資訊,從而對輸入的空間平移具有更強的魯棒性。

我們可以看到GAP是一個結構正則化器,它顯式地將特徵對映強制為概念(類別)的信任對映。這是由Mlpconv層實現的,因為它們比GLMs更接近置信度圖(confidence map)。

全域性平均池層與完全連線層相似,因為它們都執行向量化特徵對映的線性轉換。差別在於變換矩陣。對於GAP,變換矩陣是字首的( prefixed), 並且僅在共享相同值的塊對角線元素上是非零的。完全連通的層可以有密集的變換矩陣,並且這些值要經過反向傳播優化。

GAP的出現,也使得輸入影象的大小可以不用受限,因為可以通過固定GAP的層數,但是對於W/H而言,都是1X1,不管輸入多少,它都可以進行調整.不同於之前使用全連線,因為是壓平了(Flatten),導致輸入大小必須固定.

構思細節

經典卷積神經元網路由交替疊加的卷積層和空間匯聚層組成。卷積層通過線性卷積濾波器和非線性啟用函式(整流器(rectifier)、Sigmoid、tanh等)生成特徵對映。以線性整流器(ReLU:Rectified Linear Units)為例,可以按以下方式計算特徵圖:1539935859594

這裡的(i, j)是特徵影象素的索引,xij代表以位置(i, j)為中心的輸入塊,k用來索引特徵圖的通道。

當隱性概念( the latent concepts )的例項是線性可分的時,這種線性卷積就足以進行抽象。然而,實現良好抽象的表示通常是輸入資料的高度非線性函式。 在傳統的CNN中,這可以通過利用一套完整的濾波器來彌補,覆蓋所有隱含概念的變化。也就是說,可以學習獨立的線性濾波器來檢測同一概念的不同變化。然而,對單個概念有太多的過濾器會給下一層帶來額外的負擔,(因為)下一層需要考慮上一層的所有變化組合。

在CNN中, 來自更高層的濾波器會對映到原始輸入的更大區域。它通過結合下面層中的較低級別概念來生成更高級別的概念。因此,我們認為,在將每個本地塊(local patch)合併為更高級別的概念之前,對每個本地快進行更好的抽象是有益的。

在最近的Maxout網路[8]中,特徵對映的數目通過仿射特徵對映上的最大池化來減少(仿射特徵對映是線性卷積不應用啟用函式的直接結果)。線性函式的極大化使分段線性逼近器( piecewise linear approximator)能夠逼近任意凸函式。

因為是對一個神經單元的輸出進行了一種多種可能的權重測試,選擇輸出最大的一種作為最後的輸出,相當於是一種比ReLU更為複雜的多分段啟用函式, 這也使得它有更強大的能力.

與進行線性分離的傳統卷積層相比,最大輸出網路更強大,因為它能夠分離凸集中的概念。 這種改進使maxout網路在幾個基準資料集上有最好的表現。

但是,Maxout網路強制要求潛在概念的例項存在於輸入空間中的凸集中,這並不一定成立。當隱性概念的分佈更加複雜時,需要使用更一般的函式逼近器。我們試圖通過引入一種新穎的“Network In Network”結構來實現這一目標,即在每個卷積層中引入一個微網路來計算區域性塊的更抽象的特徵。

在以前的一些工作中,已經提出了在輸入端滑動一個微網路。例如,結構化多層感知器(SMLP)[9]在輸入影象的不同塊上應用共享多層感知器;在另一項工作中,基於神經網路的濾波器被訓練用於人臉檢測[10]。然而,它們都是針對特定的問題而設計的,而且都只包含一層滑動網路結構。NIN是從更一般的角度提出的,將微網路整合到CNN結構中,對各個層次的特徵進行更好的抽象

訓練細節

  • 正則化

    • 作為正則化器,除最後一個mlpconv層外,所有輸出都應用了Dropout
    • 除非具體說明,實驗部分中使用的所有網路都使用GAP,而不是網路頂部完全連線的層。
    • 另一個應用的正則化方法是Krizhevsky等人使用的權重衰減[4]。
    • 圖2說明了本節中使用的nin網路的總體結構。補充材料中提供了詳細的引數設定。
  • 網路

    • 我們在AlexKrizhevsky[4]開發的超快Cuda-ConvNet程式碼上實現我們的網路。
  • 預處理

    • 資料集的預處理、訓練和驗證集的分割都遵循GoodFelt等人的觀點[8], 即使用區域性對比度歸一化(local contrast normalization)。

      全域性對比度歸一化旨在通過從每個影象中減去其平均值,然後重新縮放其使得其畫素上的標準差等於某個常數s來防止影象具有變化的對比度。 區域性對比度歸一化確保對比度在每個小視窗上被歸一化,而不是作為整體在影象上被歸一化。

  • 引數

    • 我們採用Krizhevsky等人使用的訓練過程[4]。也就是說,我們手動設定適當的初始化權值和學習率。該網路是使用規模為128的小型批次進行訓練的。
    • 訓練過程從初始權重和學習率開始,一直持續到訓練集的準確性停止提高,然後學習率降低10。這個過程被重複一次,(因此)最終的學習率是初始值的百分之一。

訓練結果

  1. CIFAR-10資料集

    • 資料處理

      對於這個資料集,我們應用了古德費羅等人使用的相同的全域性對比規範化和ZCA白化(global contrast normalization and ZCA whitening)。我們使用最後10000張培訓集的影象作為驗證資料。

    • 實驗結果

      通過提高模型的泛化能力,NIN中的MLpconv層之間使用Dropout可以提高網路的效能。

      在mlpconv層間引用dropout層錯誤率減少了20%多。這一結果與Goodfellow等人的一致,所以本文的所有模型mlpconv層間都加了dropout。沒有dropout的模型在CIFAR-10資料集上錯誤率是14.5%,已經超過之前最好的使用正則化的模型(除了maxout)。由於沒有dropout的maxout網路不可靠,所以本文只與有dropout正則器的版本比較。

      為了與以前的工作相一致,我們還對CIFAR-10資料集的平移和水平翻轉增強的方法進行了評估。我們可以達到8.81%的測試誤差,這就達到了新的最先進的效能。

  2. CIFAR-100

    • 調整

      對於CIFAR-100,我們不調優超引數,而是使用與CIFAR-10資料集相同的設定。

      唯一的區別是最後一個mlpconv層輸出100個功能對映。

    • 效果

      CIFAR-100的測試誤差為35.68%,在不增加資料的情況下,其效能優於目前的最佳效能。

  3. MNIST[1]資料集

    • 調整

      對於這個資料集,採用了與CIFAR-10相同的網路結構.

      但是,從每個mlpconv層生成的特徵對映的數量減少了。因為mnist是一個比CIFAR-10更簡單的資料集,所以需要更少的引數。

      我們在這個資料集上測試我們的方法而不增加資料。

    • 效果

      我們得到了0.47%的表現,但是沒有當前最好的0.45%好,因為MNIST的錯誤率已經非常低了。

架構程式碼

NIN文章使用的網路架構如圖(總計4層):3層的mlpconv + 1層global_average_pooling。

1539951642697

#coding:utf-8
import tensorflow as tf

def print_activation(x):
  print(x.op.name, x.get_shape().as_list())

def inference(inputs,
              num_classes=10,
              is_training=True,
              dropout_keep_prob=0.5,
              scope='inference'):

  with tf.variable_scope(scope):
    x = inputs
    print_activation(x)
    with tf.variable_scope('mlpconv1'):
      x = tf.layers.Conv2D(192, [5,5], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)
      print_activation(x)
      x = tf.layers.Conv2D(160, [1,1], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)
      print_activation(x)
      x = tf.layers.Conv2D(96, [1,1], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)
      print_activation(x)
      x = tf.nn.max_pool(x, [1,3,3,1], [1,2,2,1], padding='SAME')
      print_activation(x)
    if is_training:
      with tf.variable_scope('dropout1'):
        x = tf.nn.dropout(x, keep_prob=0.5)
        print_activation(x)
    with tf.variable_scope('mlpconv2'):
      x = tf.layers.Conv2D(192, [5,5], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)
      print_activation(x)
      x = tf.layers.Conv2D(192, [1,1], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)
      print_activation(x)
      x = tf.layers.Conv2D(192, [1,1], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)
      print_activation(x)
      x = tf.nn.max_pool(x, [1,3,3,1], [1,2,2,1], padding='SAME')
      print_activation(x)
    if is_training:
      with tf.variable_scope('dropout2'):
        x = tf.nn.dropout(x, keep_prob=0.5)
        print_activation(x)
    with tf.variable_scope('mlpconv3'):
      x = tf.layers.Conv2D(192, [3,3], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)
      print_activation(x)
      x = tf.layers.Conv2D(192, [1,1], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)
      print_activation(x)
      x = tf.layers.Conv2D(10, [1,1], padding='SAME', activation=tf.nn.relu, kernel_regularizer=l2_regularizer(0.0001))(x)
      print_activation(x)
    with tf.variable_scope('global_average_pool'):
      x = tf.reduce_mean(x, reduction_indices=[1,2])
      print_activation(x)
      x = tf.nn.softmax(x)
      print_activation(x)
  return x

if __name__ == '__main__':
  x = tf.placeholder(tf.float32,[None,32,32,3])
  logits = inference(inputs=x,
                     num_classes=10,
                     is_training=True)