1. 程式人生 > >VGG網路的學習,基於keras的VGG-16模擬

VGG網路的學習,基於keras的VGG-16模擬

正在學習基於全卷積神經網路的語義分割內容,看到的FCN,SegNet等網路都是是VGG-16網路為基礎實現的,之前對深度卷積網路的研究也甚少,這次對VGG網路進行學習之後,做個簡單的筆記。

VGG的論文來源:[1409.1556] Very Deep Convolutional Networks for Large-Scale Image Recognition;

使得深度卷積網路大放異彩的結構是AlexNet,在此基礎上,許多研究者對其進行改進。包括對卷積核的尺寸,卷積步長,卷積網路的多尺度輸入等方面進行優化。VGG網路則是從網路的深度方面進行了思考和實踐,論文也證明了通過加深網路的深度,能夠有效提升網路的分類效能。

首先上個VGG的配置表:

在這裡插入圖片描述

VGG網路的一個重點是:採用多層卷積核尺寸為333*3卷積網路代替單層的卷積核尺寸為777*7卷積網路,這是由於多層的小卷積核網路能比一個大尺寸的卷積核網路有更好的非線性擬合能力(原因是3層的conv3層能夠使用3次ReLu啟用函式,而單層的conv7僅能夠使用一次ReLu函式),並且所需要的引數也更少,具體計算方式,首先假設輸入和輸出均是CC通道的,對於777*7的卷積層,引數需要72C27^{2}*C^{2},對於3層333*3卷積層,引數需要3(32C2)3*(3^{2}*C^{2})。 再附一個感受野計算的方式:

RF = 1 #待計算的feature map上的感受野大小
for layer in (top layer To down layer):
  RF = ((RF -1)* stride) + fsize

網路訓練: 1、固定尺寸,即縮放影象最小邊S到256或者384,然後裁剪得到輸入資料是224*224,進行訓練。 2、Multi-Scale,即縮放影象最小邊到[256,512]之間,然後裁剪訓練,該方法有效的考慮了影象中目標大小的不一致,有利於訓練。並且考慮到速度原因,本文在單尺度(S=384)的基礎上,進行微調得到多尺度模型。

網路預測: 預測時,網路採用Multi-Scale的方式,將影象scale到不同的尺寸,因此會得到多個輸出,本文對多個輸出取平均,得到最終的結果。

作者的實驗總結: 1> LRN層對結果影響不大 2> 網路的深度對分類結果影響較大,網路越深,效果越好。 3> 111*1的卷積效果不如333*3的卷積效果好,可能是由於333*3的卷積網路能夠學習到更大尺寸的空間資訊。

其實,我也是第一次接觸VGG,實際的網路架構,具體的網路引數設定,需要跑一次程式碼才能夠有更深的體會。同時,為了學習python和keras,決定參考網上的教程,實現一次VGG-16的網路構建。

def vgg_16(input_shape):
    input_tensor=Input(shape=input_shape)
    x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv1')(input_tensor)
    x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv2')(x)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='block1_pool')(x)

    # Block 2
    x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv1')(x)
    x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv2')(x)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='block2_pool')(x)

    # Block 3
    x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv1')(x)
    x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv2')(x)
    x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv3')(x)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='block3_pool')(x)

    # Block 4
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv1')(x)
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv2')(x)
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv3')(x)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='block4_pool')(x)

    # Classification block
    x = Flatten(name='flatten')(x)
    x = Dense(4096, activation='relu', name='fc1')(x)
    x = Dense(4096, activation='relu', name='fc2')(x)
    x = Dropout(0.5)(x)
    x = Dense(10, activation='softmax', name='predictions')(x)
    return Model(inputs=[input_tensor],outputs=[x])

通過keras實現VGG-16函式式網路構建是非常簡潔易懂的。接著我利用該網路,在網上看到有利用VGG-16對fashion-mnist資料集進行分類的實驗,以下是參考網上教程寫的程式碼,第一次跑python和keras,不是很熟悉。

import numpy as np 
import pandas as pd 
from sklearn.model_selection import train_test_split
import keras
from keras.utils import to_categorical
from vgg_16 import vgg_16
from keras.applications.vgg16 import preprocess_input

train_data = pd.read_csv('./data/fashion-mnist_train.csv')
test_data = pd.read_csv('./data/fashion-mnist_test.csv')
train_data.shape #(60,000*785)
test_data.shape #(10000,785)
train_X= np.array(train_data.iloc[:,1:])
test_X= np.array(test_data.iloc[:,1:])
train_Y= np.array (train_data.iloc[:,0]) # (60000,)
test_Y = np.array(test_data.iloc[:,0]) #(10000,)

classes = np.unique(train_Y)
num_classes = len(classes)

train_X=np.dstack([train_X] * 3)
test_X=np.dstack([test_X]*3)
train_X.shape,test_X.shape

train_X = train_X.reshape(-1, 28,28,3)
test_X= test_X.reshape (-1,28,28,3)

from keras.preprocessing.image import img_to_array, array_to_img
train_X = np.asarray([img_to_array(array_to_img(im, scale=False).resize((48,48))) for im in train_X])
test_X = np.asarray([img_to_array(array_to_img(im, scale=False).resize((48,48))) for im in test_X])
#train_x = preprocess_input(x)
train_X = train_X / 255.
test_X = test_X / 255.
train_X = train_X.astype('float32')
test_X = test_X.astype('float32')

train_Y_one_hot = to_categorical(train_Y)
test_Y_one_hot = to_categorical(test_Y)

train_X,valid_X,train_label,valid_label = train_test_split(train_X,
                                                           train_Y_one_hot,
                                                           test_size=0.2,
                                                           random_state=13)

IMG_WIDTH = 48
IMG_HEIGHT = 48
IMG_DEPTH = 3
BATCH_SIZE = 16

# Preprocessing the input 
train_X = preprocess_input(train_X)
valid_X = preprocess_input(valid_X)
test_X  = preprocess_input (test_X)

input_shape = (IMG_HEIGHT, IMG_WIDTH, IMG_DEPTH)
model=vgg_fm(input_shape)
model.summary()

train_features = model.predict(np.array(train_X), batch_size=BATCH_SIZE, verbose=1)
test_features = model.predict(np.array(test_X), batch_size=BATCH_SIZE, verbose=1)
val_features = model.predict(np.array(valid_X), batch_size=BATCH_SIZE, verbose=1)

model.compile(loss=keras.losses.categorical_crossentropy,
              optimizer='adam',
              metrics=['accuracy'])
train_model = model.fit(train_X, train_label,
                  batch_size=100,
                  epochs=100,
                  verbose=1,
                  validation_data=(valid_X, valid_label))

score = model.evaluate(test_X, test_Y_one_hot, verbose=1)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

一個昨天看別人說到的問題,論文中的VGG網路在預測時,為什麼全連結層改成卷積層,網路引數怎麼設定,因為沒具體看論文的程式碼,這個我的理解是原來在第一個全連線層訓練好的775127*7*512引數還是固定不變的,由於卷積和全連線都是一種點乘的形式,對應到程式碼上,可能就是以conv代替FC,然後可以針對不同尺度的輸入,比如384384384*384的輸入,到最後一個卷積層是121251212*12*512的大小,接著還是以訓練好的775127*7*512引數進行卷積,就可以變成6640966*6*4096的形式,第二層如此,之前FC層訓練好的4096個引數,將它作為1140961*1*4096卷積核,對6640966*6*4096的特徵圖進行卷積,最後一層也是如此,就會得到6610006*6*1000的分類結果圖。最後把這些結果平均,就可以了。