首頁人工智能技術資訊正文

怎樣利用VGG實現(xiàn)手寫數(shù)字識別?

更新時間:2023-06-06 來源:黑馬程序員 瀏覽量:

VGG可以看成是加深版的AlexNet,整個網(wǎng)絡由卷積層和全連接層疊加而成,和AlexNet不同的是,VGG中使用的都是小尺寸的卷積(3×3),其網(wǎng)絡架構如下圖所示:

1686032296153_71.png

VGGNet使用的全部都是3x3的小卷積核和2x2的池化核,通過不斷加深網(wǎng)絡來提升性能。VGG可以通過重復使用簡單的基礎塊來構建深度模型。

1686032395181_72.png

在tf.keras中實現(xiàn)VGG模型,首先來實現(xiàn)VGG塊,它的組成規(guī)律是:連續(xù)使用多個相同的填充為1、卷積核大小為3×33×3的卷積層后接上一個步幅為2、窗口形狀為2×22×2的最大池化層。卷積層保持輸入的高和寬不變,而池化層則對其減半。我們使用vgg_block函數(shù)來實現(xiàn)這個基礎的VGG塊,它可以指定卷積層的數(shù)量num_convs和每層的卷積核個數(shù)num_filters:

# 定義VGG網(wǎng)絡中的卷積塊:卷積層的個數(shù),卷積層中卷積核的個數(shù)def vgg_block(num_convs, num_filters):
    # 構建序列模型
    blk = tf.keras.models.Sequential()
    # 遍歷所有的卷積層
    for _ in range(num_convs):
        # 每個卷積層:num_filter個卷積核,卷積核大小為3*3,padding是same,激活函數(shù)是relu
        blk.add(tf.keras.layers.Conv2D(num_filters,kernel_size=3,
                                    padding='same',activation='relu'))
    # 卷積塊最后是一個最大池化,窗口大小為2*2,步長為2
    blk.add(tf.keras.layers.MaxPool2D(pool_size=2, strides=2))
    return blk
VGG16網(wǎng)絡有5個卷積塊,前2塊使用兩個卷積層,而后3塊使用三個卷積層。第一塊的輸出通道是64,之后每次對輸出通道數(shù)翻倍,直到變?yōu)?12。
# 定義5個卷積塊,指明每個卷積塊中的卷積層個數(shù)及相應的卷積核個數(shù)
conv_arch = ((2, 64), (2, 128), (3, 256), (3, 512), (3, 512))
因為這個網(wǎng)絡使用了13個卷積層和3個全連接層,所以經(jīng)常被稱為VGG-16,通過制定conv_arch得到模型架構后構建VGG16:
# 定義VGG網(wǎng)絡def vgg(conv_arch):
    # 構建序列模型
    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個神經(jīng)元,激活函數(shù)是relu
        tf.keras.layers.Dense(4096, activation='relu'),
        # 隨機失活
        tf.keras.layers.Dropout(0.5),
        # 全連接層:4096個神經(jīng)元,激活函數(shù)是relu
        tf.keras.layers.Dense(4096, activation='relu'),
        # 隨機失活
        tf.keras.layers.Dropout(0.5),
        # 全連接層:10個神經(jīng)元,激活函數(shù)是softmax
        tf.keras.layers.Dense(10, activation='softmax')]))
    return net# 網(wǎng)絡實例化net = vgg(conv_arch)
我們構造一個高和寬均為224的單通道數(shù)據(jù)樣本來看一下模型的架構:
# 構造輸入X,并將其送入到net網(wǎng)絡中
X = tf.random.uniform((1,224,224,1))
y = net(X)
# 通過net.summay()查看網(wǎng)絡的形狀
net.summay()
網(wǎng)絡架構如下:
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ù)字勢識別

因為ImageNet數(shù)據(jù)集較大訓練時間較長,我們仍用前面的MNIST數(shù)據(jù)集來演示VGGNet。讀取數(shù)據(jù)的時將圖像高和寬擴大到VggNet使用的圖像高和寬224。這個通過tf.image.resize_with_pad來實現(xiàn)。

import numpy as np
# 獲取手寫數(shù)字數(shù)據(jù)集
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
# 訓練集數(shù)據(jù)維度的調整: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ù)維度的調整: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ù)訓練時間較長,我們定義兩個方法獲取部分數(shù)據(jù),并將圖像調整為224*224大小,進行模型訓練:
# 定義兩個方法隨機抽取部分樣本演示# 獲取訓練集數(shù)據(jù)def get_train(size):
    # 隨機生成要抽樣的樣本的索引
    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):
    # 隨機生成要抽樣的樣本的索引
    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]
調用上述兩個方法,獲取參與模型訓練和測試的數(shù)據(jù)集:
# 獲取訓練樣本和測試樣本
train_images,train_labels = get_train(256)
test_images,test_labels = get_test(128)
為了讓大家更好的理解,我們將數(shù)據(jù)展示出來:
# 數(shù)據(jù)展示:將數(shù)據(jù)集的前九個數(shù)據(jù)集進行展示
for i in range(9):
    plt.subplot(3,3,i+1)
    # 以灰度圖顯示,不進行插值
    plt.imshow(train_images[i].astype(np.int8).squeeze(), cmap='gray', interpolation='none')
    # 設置圖片的標題:對應的類別
    plt.title("數(shù)字{}".format(train_labels[i]))
結果為:

1686034679670_73.png

分享到:
在線咨詢 我要報名
和我們在線交談!