更新時(shí)間:2023-06-06 來源:黑馬程序員 瀏覽量:
VGG可以看成是加深版的AlexNet,整個(gè)網(wǎng)絡(luò)由卷積層和全連接層疊加而成,和AlexNet不同的是,VGG中使用的都是小尺寸的卷積(3×3),其網(wǎng)絡(luò)架構(gòu)如下圖所示:
VGGNet使用的全部都是3x3的小卷積核和2x2的池化核,通過不斷加深網(wǎng)絡(luò)來提升性能。VGG可以通過重復(fù)使用簡單的基礎(chǔ)塊來構(gòu)建深度模型。
在tf.keras中實(shí)現(xiàn)VGG模型,首先來實(shí)現(xiàn)VGG塊,它的組成規(guī)律是:連續(xù)使用多個(gè)相同的填充為1、卷積核大小為3×33×3的卷積層后接上一個(gè)步幅為2、窗口形狀為2×22×2的最大池化層。卷積層保持輸入的高和寬不變,而池化層則對其減半。我們使用vgg_block函數(shù)來實(shí)現(xiàn)這個(gè)基礎(chǔ)的VGG塊,它可以指定卷積層的數(shù)量num_convs和每層的卷積核個(gè)數(shù)num_filters:
# 定義VGG網(wǎng)絡(luò)中的卷積塊:卷積層的個(gè)數(shù),卷積層中卷積核的個(gè)數(shù)def vgg_block(num_convs, num_filters): # 構(gòu)建序列模型 blk = tf.keras.models.Sequential() # 遍歷所有的卷積層 for _ in range(num_convs): # 每個(gè)卷積層:num_filter個(gè)卷積核,卷積核大小為3*3,padding是same,激活函數(shù)是relu blk.add(tf.keras.layers.Conv2D(num_filters,kernel_size=3, padding='same',activation='relu')) # 卷積塊最后是一個(gè)最大池化,窗口大小為2*2,步長為2 blk.add(tf.keras.layers.MaxPool2D(pool_size=2, strides=2)) return blkVGG16網(wǎng)絡(luò)有5個(gè)卷積塊,前2塊使用兩個(gè)卷積層,而后3塊使用三個(gè)卷積層。第一塊的輸出通道是64,之后每次對輸出通道數(shù)翻倍,直到變?yōu)?12。
# 定義5個(gè)卷積塊,指明每個(gè)卷積塊中的卷積層個(gè)數(shù)及相應(yīng)的卷積核個(gè)數(shù) conv_arch = ((2, 64), (2, 128), (3, 256), (3, 512), (3, 512))因?yàn)檫@個(gè)網(wǎng)絡(luò)使用了13個(gè)卷積層和3個(gè)全連接層,所以經(jīng)常被稱為VGG-16,通過制定conv_arch得到模型架構(gòu)后構(gòu)建VGG16:
# 定義VGG網(wǎng)絡(luò)def vgg(conv_arch): # 構(gòu)建序列模型 net = tf.keras.models.Sequential() # 根據(jù)conv_arch生成卷積部分 for (num_convs, num_filters) in conv_arch: net.add(vgg_block(num_convs, num_filters)) # 卷積塊序列后添加全連接層 net.add(tf.keras.models.Sequential([ # 將特征圖展成一維向量 tf.keras.layers.Flatten(), # 全連接層:4096個(gè)神經(jīng)元,激活函數(shù)是relu tf.keras.layers.Dense(4096, activation='relu'), # 隨機(jī)失活 tf.keras.layers.Dropout(0.5), # 全連接層:4096個(gè)神經(jīng)元,激活函數(shù)是relu tf.keras.layers.Dense(4096, activation='relu'), # 隨機(jī)失活 tf.keras.layers.Dropout(0.5), # 全連接層:10個(gè)神經(jīng)元,激活函數(shù)是softmax tf.keras.layers.Dense(10, activation='softmax')])) return net# 網(wǎng)絡(luò)實(shí)例化net = vgg(conv_arch)我們構(gòu)造一個(gè)高和寬均為224的單通道數(shù)據(jù)樣本來看一下模型的架構(gòu):
# 構(gòu)造輸入X,并將其送入到net網(wǎng)絡(luò)中 X = tf.random.uniform((1,224,224,1)) y = net(X) # 通過net.summay()查看網(wǎng)絡(luò)的形狀 net.summay()網(wǎng)絡(luò)架構(gòu)如下:
Model: "sequential_15" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= sequential_16 (Sequential) (1, 112, 112, 64) 37568 _________________________________________________________________ sequential_17 (Sequential) (1, 56, 56, 128) 221440 _________________________________________________________________ sequential_18 (Sequential) (1, 28, 28, 256) 1475328 _________________________________________________________________ sequential_19 (Sequential) (1, 14, 14, 512) 5899776 _________________________________________________________________ sequential_20 (Sequential) (1, 7, 7, 512) 7079424 _________________________________________________________________ sequential_21 (Sequential) (1, 10) 119586826 ================================================================= Total params: 134,300,362 Trainable params: 134,300,362 Non-trainable params: 0 __________________________________________________________________
手寫數(shù)字勢識(shí)別
因?yàn)镮mageNet數(shù)據(jù)集較大訓(xùn)練時(shí)間較長,我們?nèi)杂们懊娴腗NIST數(shù)據(jù)集來演示VGGNet。讀取數(shù)據(jù)的時(shí)將圖像高和寬擴(kuò)大到VggNet使用的圖像高和寬224。這個(gè)通過tf.image.resize_with_pad來實(shí)現(xiàn)。
import numpy as np # 獲取手寫數(shù)字?jǐn)?shù)據(jù)集 (train_images, train_labels), (test_images, test_labels) = mnist.load_data() # 訓(xùn)練集數(shù)據(jù)維度的調(diào)整:N H W C train_images = np.reshape(train_images,(train_images.shape[0],train_images.shape[1],train_images.shape[2],1)) # 測試集數(shù)據(jù)維度的調(diào)整:N H W C test_images = np.reshape(test_images,(test_images.shape[0],test_images.shape[1],test_images.shape[2],1))由于使用全部數(shù)據(jù)訓(xùn)練時(shí)間較長,我們定義兩個(gè)方法獲取部分?jǐn)?shù)據(jù),并將圖像調(diào)整為224*224大小,進(jìn)行模型訓(xùn)練:
# 定義兩個(gè)方法隨機(jī)抽取部分樣本演示# 獲取訓(xùn)練集數(shù)據(jù)def get_train(size): # 隨機(jī)生成要抽樣的樣本的索引 index = np.random.randint(0, np.shape(train_images)[0], size) # 將這些數(shù)據(jù)resize成22*227大小 resized_images = tf.image.resize_with_pad(train_images[index],224,224,) # 返回抽取的 return resized_images.numpy(), train_labels[index]# 獲取測試集數(shù)據(jù) def get_test(size): # 隨機(jī)生成要抽樣的樣本的索引 index = np.random.randint(0, np.shape(test_images)[0], size) # 將這些數(shù)據(jù)resize成224*224大小 resized_images = tf.image.resize_with_pad(test_images[index],224,224,) # 返回抽樣的測試樣本 return resized_images.numpy(), test_labels[index]調(diào)用上述兩個(gè)方法,獲取參與模型訓(xùn)練和測試的數(shù)據(jù)集:
# 獲取訓(xùn)練樣本和測試樣本 train_images,train_labels = get_train(256) test_images,test_labels = get_test(128)為了讓大家更好的理解,我們將數(shù)據(jù)展示出來:
# 數(shù)據(jù)展示:將數(shù)據(jù)集的前九個(gè)數(shù)據(jù)集進(jìn)行展示 for i in range(9): plt.subplot(3,3,i+1) # 以灰度圖顯示,不進(jìn)行插值 plt.imshow(train_images[i].astype(np.int8).squeeze(), cmap='gray', interpolation='none') # 設(shè)置圖片的標(biāo)題:對應(yīng)的類別 plt.title("數(shù)字{}".format(train_labels[i]))結(jié)果為: