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訓練後的模型參數在其官方網站上開源了,可用來在特定的圖像分類任務上進行再訓練(至關於提供了很是好的初始化權重),所以被用在了不少地方。
網絡
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網絡的結構(VGG16和VGG19都在):spa
VGG網絡的結構很是一致,從頭至尾所有使用的是3x3的卷積和2x2的max pooling設計
假設這個小圖是咱們的輸入圖像,尺寸是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已經足夠大了,因此後面的層就再也不翻倍了。不管如何,每一步都進行翻倍,或者說在每一組卷積層進行過濾器翻倍操做,正是設計此種網絡結構的另外一個簡單原則。這種相對一致的網絡結構對研究者頗有吸引力,而它的主要缺點是須要訓練的特徵數量很是巨大。
VGGNet的結構很是簡潔,整個網絡都使用了一樣大小的卷積核尺寸(3x3)和最大池化尺寸(2x2)。
幾個小濾波器(3x3)卷積層的組合比一個大濾波器(5x5或7x7)卷積層好:
驗證了經過不斷加深網絡結構能夠提高性能。
VGG耗費更多計算資源,而且使用了更多的參數(這裏不是3x3卷積的鍋),致使更多的內存佔用(140M)。其中絕大多數的參數都是來自於第一個全鏈接層。VGG但是有3個全鏈接層啊!
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
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