VGG

前言  

  VGGNet是牛津大學計算機視覺組(VisualGeometry Group)和GoogleDeepMind公司的研究員一塊兒研發的的深度卷積神經網絡。VGGNet探索了卷積神經網絡的深度與其性能之間的關係,經過反覆堆疊3*3的小型卷積核和2*2的最大池化層,VGGNet成功地構築了16~19層深的卷積神經網絡。VGGNet相比以前state-of-the-art的網絡結構,錯誤率大幅降低,並取得了ILSVRC 2014比賽分類項目的第2名和定位項目的第1名。同時VGGNet的拓展性很強,遷移到其餘圖片數據上的泛化性很是好。VGGNet的結構很是簡潔,整個網絡都使用了一樣大小的卷積核尺寸(3*3)和最大池化尺寸(2*2)。到目前爲止,VGGNet依然常常被用來提取圖像特徵。VGGNet訓練後的模型參數在其官方網站上開源了,可用來在特定的圖像分類任務上進行再訓練(至關於提供了很是好的初始化權重),所以被用在了不少地方。

網絡

VGG原理  

VGG16相比AlexNet的一個改進是採用連續的幾個3x3的卷積核代替AlexNet中的較大卷積核(11x11,7x7,5x5)。對於給定的感覺野(與輸出有關的輸入圖片的局部大小),採用堆積的小卷積核是優於採用大的卷積核,由於多層非線性層能夠增長網絡深度來保證學習更復雜的模式,並且代價還比較小(參數更少)。ide

  簡單來講,在VGG中,使用了3個3x3卷積核來代替7x7卷積核,使用了2個3x3卷積核來代替5*5卷積核,這樣作的主要目的是在保證具備相同感知野的條件下,提高了網絡的深度,在必定程度上提高了神經網絡的效果。函數

  好比,3個步長爲1的3x3卷積核的一層層疊加做用可當作一個大小爲7的感覺野(其實就表示3個3x3連續卷積至關於一個7x7卷積),其參數總量爲 3x(9xC^2) ,若是直接使用7x7卷積核,其參數總量爲 49xC^2 ,這裏 C 指的是輸入和輸出的通道數。很明顯,27xC^2小於49xC^2,即減小了參數;並且3x3卷積核有利於更好地保持圖像性質。性能

這裏解釋一下爲何使用2個3x3卷積核能夠來代替5*5卷積核:學習

  5x5卷積看作一個小的全鏈接網絡在5x5區域滑動,咱們能夠先用一個3x3的卷積濾波器卷積,而後再用一個全鏈接層鏈接這個3x3卷積輸出,這個全鏈接層咱們也能夠看作一個3x3卷積層。這樣咱們就能夠用兩個3x3卷積級聯(疊加)起來代替一個 5x5卷積。

網站

 

VGG網絡結構

下面是VGG網絡的結構(VGG16和VGG19都在):spa

 

 

  • VGG16包含了16個隱藏層(13個卷積層和3個全鏈接層),如上圖中的D列所示
  • VGG19包含了19個隱藏層(16個卷積層和3個全鏈接層),如上圖中的E列所示

 VGG網絡的結構很是一致,從頭至尾所有使用的是3x3的卷積和2x2的max pooling設計

VGG-16

  假設這個小圖是咱們的輸入圖像,尺寸是224×224×3,進行第一個卷積以後獲得224×224×64的特徵圖,接着還有一層224×224×64,獲得這樣2個厚度爲64的卷積層,意味着咱們用64個過濾器進行了兩次卷積。正如我在前面提到的,這裏採用的都是大小爲3×3,步幅爲1的過濾器,而且都是採用same卷積,因此我就再也不把全部的層都畫出來了,只用一串數字表明這些網絡。3d

  接下來建立一個池化層,池化層將輸入圖像進行壓縮,從224×224×64縮小到多少呢?沒錯,減小到112×112×64。而後又是若干個卷積層,使用129個過濾器,以及一些same卷積,咱們看看輸出什麼結果,112×112×128.而後進行池化,能夠推導出池化後的結果是這樣(56×56×128)。接着再用256個相同的過濾器進行三次卷積操做,而後再池化,而後再卷積三次,再池化。如此進行幾輪操做後,將最後獲得的7×7×512的特徵圖進行全鏈接操做,獲得4096個單元,而後進行softmax激活,輸出從1000個對象中識別的結果。code

 

  順便說一下,VGG-16的這個數字16,就是指在這個網絡中包含16個卷積層和全鏈接層。確實是個很大的網絡,總共包含約1.38億個參數,即使以如今的標準來看都算是很是大的網絡。但VGG-16的結構並不複雜,這點很是吸引人,並且這種網絡結構很規整,都是幾個卷積層後面跟着能夠壓縮圖像大小的池化層,池化層縮小圖像的高度和寬度。同時,卷積層的過濾器數量變化存在必定的規律,由64翻倍變成128,再到256和512。做者可能認爲512已經足夠大了,因此後面的層就再也不翻倍了。不管如何,每一步都進行翻倍,或者說在每一組卷積層進行過濾器翻倍操做,正是設計此種網絡結構的另外一個簡單原則。這種相對一致的網絡結構對研究者頗有吸引力,而它的主要缺點是須要訓練的特徵數量很是巨大。

 

 

VGG優缺點

VGG優勢

VGGNet的結構很是簡潔,整個網絡都使用了一樣大小的卷積核尺寸(3x3)和最大池化尺寸(2x2)。

幾個小濾波器(3x3)卷積層的組合比一個大濾波器(5x5或7x7)卷積層好:

驗證了經過不斷加深網絡結構能夠提高性能。

VGG缺點

VGG耗費更多計算資源,而且使用了更多的參數(這裏不是3x3卷積的鍋),致使更多的內存佔用(140M)。其中絕大多數的參數都是來自於第一個全鏈接層。VGG但是有3個全鏈接層啊!

 代碼實現,分別用原生tensorflow和slim實現

tensorflow

 

 1 import tensorflow as tf
 2 
 3 #VGGNet-16包含不少層卷積,咱們先寫一個函數conv_op,用來建立卷積層並把本層的參數存入參數列表。
 4 def conv_op(input_op,name,kh,kw,n_out,dh,dw,p):
 5     n_in = input_op.get_shape()[-1].value
 6     with tf.name_scope(name) as scope:
 7         kernel = tf.get_variable(scope+'w',shape=[kh,kw,n_in,n_out],dtype=tf.float32,initializer=tf.contrib.layers.xavier_initializer_conv2d())
 8         conv = tf.nn.conv2d(input_op,kernel,(1,dh,dw,1),padding='SAME')
 9         bias_init_val = tf.constant(0.0,shape=[n_out],dtype=tf.float32)
10         biases = tf.Variable(bias_init_val,trainable=True,name='b')
11         z = tf.nn.bias_add(conv,biases)
12         activation = tf.nn.relu(z,name=scope)
13         p += [kernel,biases]
14         return activation
15 
16 def fc_op(input_op,name,n_out,p):
17     n_in = input_op.get_shape()[-1].value
18     with tf.name_scope(name) as scope:
19         kernel = tf.get_variable(scope + "w", shape=[n_in, n_out], dtype=tf.float32,initializer=tf.contrib.layers.xavier_initializer())
20         biases = tf.Variable(tf.constant(0.1, shape=[n_out], dtype=tf.float32), name='b')
21         activation = tf.nn.relu_layer(input_op, kernel, biases, name=scope)
22         p += [kernel, biases]
23         return activation
24 
25 def mpool_op(input_op, name, kh, kw, dh, dw):
26     return tf.nn.max_pool(input_op, ksize=[1, kh, kw, 1], strides=[1, dh, dw, 1], padding='SAME', name=name)
27 
28 
29 def inference_op(input_op,keep_prob):
30     p = []
31     conv1_1 = conv_op(input_op,name='conv1_1',kh=3,kw=3,n_out=64,dh=1,dw=1,p=p)
32     conv1_2 = conv_op(conv1_1, name="conv1_2", kh=3, kw=3, n_out=64, dh=1, dw=1, p=p)
33     pool1 = mpool_op(conv1_2, name="pool1", kh=2, kw=2, dw=2, dh=2)
34 
35     conv2_1 = conv_op(pool1, name="conv2_1", kh=3, kw=3, n_out=128, dh=1, dw=1, p=p)
36     conv2_2 = conv_op(conv2_1, name="conv2_2", kh=3, kw=3, n_out=128, dh=1, dw=1, p=p)
37     pool2 = mpool_op(conv2_2, name="pool2", kh=2, kw=2, dw=2, dh=2)
38 
39     conv3_1 = conv_op(pool2, name="conv3_1", kh=3, kw=3, n_out=256, dh=1, dw=1, p=p)
40     conv3_2 = conv_op(conv3_1, name="conv3_2", kh=3, kw=3, n_out=256, dh=1, dw=1, p=p)
41     conv3_3 = conv_op(conv3_2, name="conv3_3", kh=3, kw=3, n_out=256, dh=1, dw=1, p=p)
42     pool3 = mpool_op(conv3_3, name="pool3", kh=2, kw=2, dw=2, dh=2)
43 
44     conv4_1 = conv_op(pool3, name="conv4_1", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
45     conv4_2 = conv_op(conv4_1, name="conv4_2", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
46     conv4_3 = conv_op(conv4_2, name="conv4_3", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
47     pool4 = mpool_op(conv4_3, name="pool4", kh=2, kw=2, dw=2, dh=2)
48 
49     conv5_1 = conv_op(pool4, name="conv5_1", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
50     conv5_2 = conv_op(conv5_1, name="conv5_2", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
51     conv5_3 = conv_op(conv5_2, name="conv5_3", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
52     pool5 = mpool_op(conv5_3, name="pool5", kh=2, kw=2, dw=2, dh=2)
53 
54     shp = pool5.get_shape()
55     flattened_shape = shp[1].value * shp[2].value * shp[3].value
56     resh1 = tf.reshape(pool5, [-1, flattened_shape], name="resh1")
57 
58     fc6 = fc_op(resh1, name="fc6", n_out=4096, p=p)
59     fc6_drop = tf.nn.dropout(fc6, keep_prob, name="fc6_drop")
60 
61     fc7 = fc_op(fc6_drop, name="fc7", n_out=4096, p=p)
62     fc7_drop = tf.nn.dropout(fc7, keep_prob, name="fc7_drop")
63 
64     fc8 = fc_op(fc7_drop, name="fc8", n_out=1000, p=p)
65     softmax = tf.nn.softmax(fc8)
66     predictions = tf.argmax(softmax, 1)
67     return predictions, softmax, fc8, p

 

slim實現

 1 def vgg16(inputs):
 2       with slim.arg_scope([slim.conv2d, slim.fully_connected],
 3                 activation_fn=tf.nn.relu,
 4                 weights_initializer=tf.truncated_normal_initializer(0.0, 0.01),
 5                 weights_regularizer=slim.l2_regularizer(0.0005)):
 6         net = slim.repeat(inputs, 2, slim.conv2d, 64, [3, 3], scope='conv1')
 7         net = slim.max_pool2d(net, [2, 2], scope='pool1')
 8         net = slim.repeat(net, 2, slim.conv2d, 128, [3, 3], scope='conv2')
 9         net = slim.max_pool2d(net, [2, 2], scope='pool2')
10         net = slim.repeat(net, 3, slim.conv2d, 256, [3, 3], scope='conv3')
11         net = slim.max_pool2d(net, [2, 2], scope='pool3')
12         net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3], scope='conv4')
13         net = slim.max_pool2d(net, [2, 2], scope='pool4')
14         net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3], scope='conv5')
15         net = slim.max_pool2d(net, [2, 2], scope='pool5')
16          net = slim.flatten(net)
17         net = slim.fully_connected(net, 4096, scope='fc6')
18         net = slim.dropout(net, 0.5, scope='dropout6')
19         net = slim.fully_connected(net, 4096, scope='fc7')
20         net = slim.dropout(net, 0.5, scope='dropout7')
21         net = slim.fully_connected(net, 1000, activation_fn=None, scope='fc8')
22       return net
相關文章
相關標籤/搜索