轉 googlenet論文解讀

版權聲明:本文爲博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接和本聲明。
本文連接: https://blog.csdn.net/u014061630/article/details/80308245

Inception v1 / GoogLeNetGoing Deeper with Convolutionscss

摘要:咱們提出了一個名爲Inception的深度卷積神經網絡架構,它是ILSVRC 2014的冠軍。Inception的最大特色是:經過精心設計,使得網絡在計算量不變的前提下,深度和寬度獲得了增長,從而提升了深度網絡對於計算資源的利用。Inception的網絡架構基於Hebbian原則和多尺度處理直覺設計的,這優化了Inceotion的架構。ILSVRC 2014中咱們提交的22層深的GooLeNet是本研究的一個特例(參加了當年的分類和探測比賽)。
關鍵點:Inception module,輔助分類器html

文章總結:python

  1. 設計了Inception模塊,用dense結構去近似sparse結構。
  2. Inception內部1x1,3x3,5x5,pooling,一方面增長了網絡寬度,另外一方面加強了模型對尺度的適應性。
  3. 大量使用了1x1卷積,下降了模型的計算量。
  4. 低層仍使用傳統卷積,高層使用Inception
  5. 用輔助分類器解決梯度傳播問題

1.介紹

分類準確率和物體探測準確度的提升,不只僅源於硬件提升、更大數據集和更大模型,並且來自於新思想、算法和網絡架構。GoogLeNet使用的參數量是AlexNet的十二分之一的狀況下,將準確率提升了許多。在探測任務中,性能提高主要來自於深度結構和經典的計算機視覺的結合,好比R-CNN算法。git

另外一個十分重要的因素是移動和嵌入式設備的發展,模型的內存使用量和計算量變得很關鍵。因此Inception架構在追求準確率的同時,保持模型inference時的計算量在1.5 billion次multiply-adds。因此Inceotion的提出不只僅是爲了準確率,同時也是爲了實際應用。web

Inception這個名字的靈感來自http://www.javashuo.com/tag/nin和[we need to go deeper][3]這句話。本文中的deep的含義主要是兩個方面:發明了Inception模塊;增長了網絡的深度。通常來講,Inception模塊能夠被看做12的一個合乎邏輯的實現,這其中靈感和指導思想主要來自於Arora et al的理論工做。算法

2. 相關工做

自從LeNet-5開始,卷積神經網絡(CNN)有了一個典型的標準結構:堆疊卷積層(可選follow contrast normalization和max pool),而後跟一層或多層FC。基礎結構的各類變種在圖像分類任務中很流行,而且在MNIST、CIFAR和ImageNet分類比賽上取得了state of art。當前對於更大的數據集,當前的趨勢是增長模型的深度和寬度,同時使用dropout來克服overfitting問題。安全

儘管注意到max-pooling致使準確的空間信息的丟失,可是AlexNet相似的網絡也成功應用到了定位,物體識別,人的姿式估計任務上。受最近Serre等人在視覺皮層上的神經科學模型的啓發(使用一系列不一樣大小的固定Gabor filter去處理多尺度),咱們設計了Inception模塊。更近一步,Inception層重複不少次,從而產生了一個22層深的模型(GoogLeNet)。markdown

NIN網絡是Lin等人提出的一種增長神經網絡表示能力的一種方法。當NIN應用到卷積層以後,該方法能夠被當作1x1卷積層followed ReLU,這使得它很容易集成到當前的CNN piplines中,在咱們的網絡中,大量使用了1x1卷積。可是,在咱們的網絡中,1x1卷積有兩個目的:最重要的是,它做爲一個降維模塊去,能夠下降計算量。這容許咱們能夠增長深度和寬度,卻不下降網絡的性能。網絡

當前最好的物體探測方法是Regions with Convolutional Neural Networks(R-CNN)。R-CNN分解overall物體探測任務爲兩部分:去首先利用低層的線索(顏色,超像素的一致性)生成物體潛在的區域(不考慮類別),而後使用CNN分類器去識別這些區域的物體的種類。這種兩步方法充分利用了:低層信息進行bounding box分割的準確度,和state of art模型的強大分類能力。在探測任務中,咱們採用了一個類似的pipline,可是在兩個步驟都有了提升,例如:multi-box prediction for higher object bounding box recall, and ensemble approach for better categorization of bounding box proposals。數據結構

3. 動機和更高層的考慮(Motivation and High Level Considerations)

最直接的提升深度神經網絡的性能的方法是增長它的size。這包括增長深度(the number of levels)和寬度(the number of units at each level)。這是一個訓練更高質量模型的容易且安全的方法,尤爲是在有大量帶標籤的訓練數據的狀況下。但這個簡單的解決方案帶來了兩個主要問題。

更大size通常意味着大量的參數,這使得加大後的網絡更容易過擬合,尤爲是帶標籤數據比較少的狀況下。這很難,由於高質量的大數據集的製做須要技巧,而且費用很高。尤爲是當expert human raters須要去分辨細微差異的種類。

均勻增長網絡size(全部層都增大size)的另外一個缺點是計算量的劇增。例如一個兩層的卷積網絡,均勻增長兩層的filters的數量會致使計算量立方級的增加。因此計算量的高效分佈是頗有必要的。

解決這兩個問題的最基本方法是將FC轉爲sparsely connected架構(即便在卷積網絡中)。除了模擬生物系統,這也利用了Arora等人開創性的堅實的理論基礎的優點。他們的結果說明
若果一個數據集的機率分佈可以被一個大的稀疏的深度神經網絡表示,那麼最優網絡的拓撲可以被一層一層的構建by分析最後一層activations的相關性的統計和聚類輸出高度相關的神經元。儘管沒有進行嚴格的數學推導,可是這一描述和著名的Hebbian準測是相似的:激活的神經元鏈接在一塊兒,抑制的神經元連在一塊兒。

退一步(on the downside),今天的計算硬件是很是不高效的,當涉及到非均勻的稀疏數據結構的計算時。當前的視覺方面的機器學習系統在空間域上一用稀疏性by卷積。可是卷積能夠當作一系列的局部FC。因此稀疏矩陣應該被聚類,轉爲密集矩陣。因此,可將相似的方法應用到non-uniform deep architectures的自動構建。

在假設輸出的最優網絡拓撲構建算法中去嘗試用可理解的dense結構來代替稀疏結構的過程當中,第一做者提出了Inception架構。在兩次嘗試後,做者便看到了明顯的性能改善(有點運氣)。在微調學習速率,超參數和改進訓練策略以後,獲得瞭如今的Inception架構。

一個須要注意的地方是:儘管Inception架構在視覺上取得了成功,但它值得思考,性能提高是否來真的自於設計時的指導思想。雖然Inception架構取得了成功,可是自動構建系統仍值得進一步研究。

4.架構信息

Inception架構的理念基於尋找怎麼用可理解、可用的dense架構近似最優局部稀疏結構。1x1卷積用於將稀疏轉爲密集。1x1,3x3,5x5的大小是爲了方便對齊而不是必須。池化在如今的各類模型中很重要,因此加了池化。

下面是Inception模塊的圖:
Inception模塊
上面的架構(至少naive版本)有一個很大的問題:即便5x5卷積的計算量很大(當前層有不少channel時)。這個問題隨着max-pooling的加入,變得更明顯。因此做者在b中加了1x1卷積,減小計算量。

因爲訓練時的內存efficiency,因此只在高層使用Inception模塊,低層仍然使用傳統的卷積配置。

這個架構的 最主要特色是,在計算量不顯著變化的狀況下,它容許增長網絡寬度(這主要歸功於1x1卷積)。另外一個特色是,它將多尺度考慮了進去。

Inception架構對於計算資源的高效利用容許增長網絡尺寸(深度、寬度),同時幾乎不增長計算量。而且Inception的推理速度很快。

5.GoogLeNet

做者在ILSVRC2014提交的模型是GoogLeNet。這個名字是爲了記念LeNet-5。做者也訓練了更深更寬的網絡,但性能稍差,把該模型和最好的進行了集成,以提升準確率。

GoogLeNet的網絡配置:
GoogLeNet配置
GoogLeNet共22層(計算pool層,總計27層)

下面是GoogLeNet的架構圖:
GoogLeNet架構圖
網絡這麼深,梯度怎麼合理的反向傳播,做者給網絡額外增長了兩個分類器(Inception 4a和4d輸出)。訓練時,將額外的兩個分類器的loss乘以0.3加到最後的cost上,inference時,去除額外的分類器。(增長額外的分類器就是爲了解決梯度反向傳播的問題)

兩個額外分類器的配置以下:

  • A average pooling with 5x5 filter size and stride 3. resulting in an 4x4x512 output for the (4a), and 4x4x528 for the (4d) stage.

  • A 1x1 conv with 128 filters and ReLU

  • A FC with 1024 units and ReLU

  • A dropoout layer with 70% ratio of dropped outputs

  • A linear layer with softmax as classifier (predicting the same 1000 classes as the main classifier, but removed at inference time)

6.訓練策略

訓練使用的是TensorFlow的前身DistBelief。硬件使用的是CPU。優化算法使用的是異步梯度降低with 0.9 momentum。學習速率設定每8 epoch降低4%。Polyak averaging被使用去建立inference的模型。

訓練了7個模型,爲了使問題更精細,一些模型在較小的crop上訓練,一些模型則在大的crop上訓練。
使模型訓練較好的因素包括:
1.圖像各類尺寸patch的採樣,這些patch 的size平均分佈在圖像區域的8%到100%間,長寬比例在3/4和4/3間。
2.另外Andrew Howard的photometric distortions對於克服過擬合頗有用。
3.resize時內插方法隨機使用bilinear, area, nearest neighbor and cubic, with equal probability。

測試時

一、訓練7個相同的模型,其中包括一個較寬的版本,而且對結果進行集成。這些模型用相同的初始化和學習率測量,不一樣的是採樣的方法不一樣和輸入圖像的順序不一樣。
二、測試時更暴力的crop。
把圖像resize成4個不一樣的尺度,使的短邊分別是256,288,320和352.從這些resize後的圖像中取左中右的方塊。對每個方塊,取4個角落,中間的224224crops,以及整個方塊的224224的resize,同時對這patch鏡像。這樣每一個圖像提取出的patch數是:436*2=224.
然而,彷佛這種crop不是必須的。
3.softmax機率在多個crops和全部分類器上進行平均,得到最終的預測

下面給出TensorFlow實現的GoogLeNet

首先給出Inception module的代碼:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
#coding:utf-8 #inception_modules.py ''' Inception module ''' import tensorflow as tf relu = tf.nn.relu def inception_naive(inputs, sub_chs, scope='inception_naive'): ''' sub_chs: sub channels ''' [sub_ch1,sub_ch2,sub_ch3] = sub_chs with tf.variable_scope(scope): x = inputs sub1 = tf.layers.Conv2D(sub_ch1, [1,1], padding='SAME', activation=relu)(x) sub2 = tf.layers.Conv2D(sub_ch2, [3,3], padding='SAME', activation=relu)(x) sub3 = tf.layers.Conv2D(sub_ch3, [5,5], padding='SAME', activation=relu)(x) sub4 = tf.layers.MaxPooling2D([3,3], 1, padding='SAME')(x) x = tf.concat([sub1,sub2,sub3,sub4], axis=-1) return x def inception(inputs, sub_chs, scope='inception'): ''' sub_chs: sub channels ''' [sub_ch1, sub_ch2, sub_ch3, sub_ch4] = sub_chs with tf.variable_scope(scope): x = inputs sub1 = tf.layers.Conv2D(sub_ch1, [1,1], padding='SAME', activation=relu)(x) _sub2 = tf.layers.Conv2D(sub_ch2[0], [1,1], padding='SAME', activation=relu)(x) sub2 = tf.layers.Conv2D(sub_ch2[1], [3,3], padding='SAME', activation=relu)(_sub2) _sub3 = tf.layers.Conv2D(sub_ch3[0], [1,1], padding='SAME', activation=relu)(x) sub3 = tf.layers.Conv2D(sub_ch3[1], [5,5], padding='SAME', activation=relu)(_sub3) _sub4 = tf.layers.MaxPooling2D([3,3], 1, padding='SAME')(x) sub4 = tf.layers.Conv2D(sub_ch4, [1,1], padding='SAME', activation=relu)(_sub4) x = tf.concat([sub1,sub2,sub3,sub4], axis=-1) return x if __name__ == '__main__': x = tf.placeholder(tf.float32, [192, 28, 28, 3]) y = inception_naive(x, [64, 128, 32]) assert y.get_shape().as_list()==[192,28,28,227] print('inception_naive is ok') y1 = inception(x, [64, [96,128], [16,32], 32]) assert y1.get_shape().as_list()==[192,28,28,256] print('inception is ok')

下面給出GoogLeNet的實現:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
#coding:utf-8 ''' Inception v1 ''' import tensorflow as tf relu = tf.nn.relu import inception_modules as modules def print_activation(x): print(x.op.name, x.get_shape().as_list()) def inference(inputs, num_classes=10, is_training=True, dropout_rate=0.4): ''' inputs: a tensor of images num_classes: the num of category. is_training: set ture when it used for training dropout_prob: the rate of dropout during training ''' caches = [] x = inputs print_activation(x) # conv1 x = tf.layers.Conv2D(64, [7,7], 2, activation=relu, padding='SAME', name='conv1')(x) print_activation(x) # pool1 x = tf.layers.MaxPooling2D([3,3], 2, padding='SAME', name='pool1')(x) print_activation(x) # lrn1 x = tf.nn.local_response_normalization(x, name='lrn1') print_activation(x) # conv2 x = tf.layers.Conv2D(64, [1,1], 1, activation=relu, padding='SAME', name='conv2')(x) print_activation(x) # conv3 x = tf.layers.Conv2D(192, [3,3], 1, activation=relu, padding='SAME', name='conv3')(x) print_activation(x) # lrn2 x = tf.nn.local_response_normalization(x, name='lrn2') print_activation(x) # pool3 x = tf.layers.MaxPooling2D([3,3], 2, padding='SAME', name='pool3')(x) print_activation(x) with tf.variable_scope('inception3'): # inception_3a x = modules.inception(x, [64, [96,128], [16,32], 32], scope='inception_3a') print_activation(x) # inception_3b x = modules.inception(x, [128, [128,192], [32,96], 64], scope='inception_3b') print_activation(x) # pool4 x = tf.layers.MaxPooling2D([3,3], 2, padding='SAME', name='pool4')(x) print_activation(x) with tf.variable_scope('inception4'): # inception_4a x = modules.inception(x, [192, [96,208], [16,48], 64], scope='inception_4a') caches.append(x) print_activation(x) # inception_4b x = modules.inception(x, [160, [112,224], [24,64], 64], scope='inception_4b') print_activation(x) # inception_4c x = modules.inception(x, [128, [128,256], [24,64], 64], scope='inception_4c') print_activation(x) # inception_4d x = modules.inception(x, [112, [144,288], [32,64], 64], scope='inception_4d') print_activation(x) caches.append(x) # inception_4e x = modules.inception(x, [256, [160,320], [32,128], 128], scope='inception_4e') print_activation(x) # pool5 x = tf.layers.MaxPooling2D([3,3], 2, padding='SAME', name='pool5')(x) print_activation(x) with tf.variable_scope('inception5'): # inception_5a x = modules.inception(x, [256, [160,320], [32,128], 128], scope='inception_5a') print_activation(x) # inception_5b x = modules.inception(x, [384, [192,384], [48,128], 128], scope='inception_5b') print_activation(x) # avg_pool _ksize = x.get_shape().as_list()[1] x = tf.layers.AveragePooling2D([_ksize,_ksize], 1, name='avg_pool')(x) print_activation(x) # dropout x = tf.layers.Dropout(dropout_rate, name='dropout')(x) print_activation(x) # linear+softmax logits = tf.layers.Conv2D(num_classes, [1,1], 1, activation=tf.nn.softmax, name='linear-softmax')(x) print_activation(logits) return logits, caches def build_cost(logits, labels, scope='costs'): with tf.variable_scope(scope): cost = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits( logits=logits, labels=labels), name='xent') return cost def build_sub_cost(cache, labels, scope='sub_costs'): num_classes = labels.get_shape().as_list()[-1] with tf.variable_scope(scope): x = cache x = tf.layers.AveragePooling2D([5,5], 3, name='avg_pool')(x) x = tf.layers.Conv2D(128, [1,1], 1, activation=relu, name='conv')(x) _ksize = x.get_shape().as_list()[1] x = tf.layers.Conv2D(1024, [_ksize,_ksize], 1, activation=relu, name='fc')(x) x = tf.layers.Dropout(0.7)(x) logits = tf.layers.Conv2D(num_classes, [1,1], 1, activation=tf.nn.softmax, name='fc-softmax')(x) cost = build_cost(tf.layers.flatten(logits), labels) return cost def build_train_op(cost, lrn_rate=0.001, scope='train'): with tf.variable_scope(scope): train_op = tf.train.AdamOptimizer(lrn_rate).minimize(cost) return train_op if __name__ == '__main__': mode = 'train' with tf.variable_scope('inputs'): images = tf.placeholder(tf.float32, [None,224,224,3]) labels = tf.placeholder(tf.float32, [None, 1000]) logits, caches = inference(inputs=images, num_classes=1000) logits = tf.layers.flatten(logits) print('inference is ok!') if mode=='train': with tf.variable_scope('costs'): cost = tf.add_n([build_cost(logits, labels), build_sub_cost(caches[0], labels, scope='sub_cost1')*0.3, build_sub_cost(caches[1], labels, scope='sub_cost2')*0.3]) else: cost = build_cost(logits, labels) print('build_cost is ok!') train_op = build_train_op(cost, lrn_rate=0.001) print('build_train_op is ok!') sess = tf.Session() tf.summary.FileWriter('./',sess.graph)

注意:使用本博客的代碼,請添加引用


[3]: We Need to Go Deeper:The world of We Need to go Deeper was heavily inspired by the works of Jules Verne, with 20,000 Leagues Under the Sea in particular being a heavy influence on our game’s universe. What would it be like to be a crew member aboard the Nautilus? To be exploring the deep, facing the wrath of a giant squid one moment, and uncovering the lost city of Atlantis the next?

相關文章
相關標籤/搜索