(四)卷積神經網路 -- 8 網路中的網路(NiN)

Fiona-Dong發表於2020-10-18

8. 網路中的網路(NiN)

前文所述的LeNet、AlexNet和VGG,在設計上的共同點是:
先以由卷積層構成的模組充分抽取空間特徵,再以由全連線層構成的模組來輸出分類結果。

其中,AlexNet和VGG對LeNet的改進,主要在於:
如何對卷積層模組和全連線層模組,加寬(增加通道數)和加深。

網路中的網路(NiN)提出了另一個思路:
通過串聯多個由卷積層和“全連線”層構成的小網路,來構建一個深層網路。


8.1 NiN塊

卷積層的輸入和輸出通常是四維陣列(樣本,通道,高,寬),而全連線層的輸入和輸出則通常是二維陣列(樣本,特徵)。

如果想在全連線層後再接上卷積層,需要將全連線層的輸出變換為四維。

(四)卷積神經網路 – 3 多輸入通道和多輸出通道 一節裡介紹的1×1卷積層,可以看作全連線層。其中,空間維度(高和寬)上的每個元素相當於樣本,通道相當於特徵。

因此,NiN使用1×1卷積層來替代全連線層,從而使空間資訊能夠自然傳遞到後面的層中去。

下圖對比了NiN同AlexNet和VGG等網路在結構上的主要區別:

(四)卷積神經網路 -- 8 網路中的網路(NiN)

NiN塊是NiN中的基礎塊,它由一個卷積層加兩個充當全連線層的1×1卷積層串聯而成。

其中,第一個卷積層的超引數可以自行設定,而第二和第三個卷積層的超引數一般是固定的。


程式碼實現如下:

import tensorflow as tf
print(tf.__version__)

import numpy as np

from tensorflow.keras import Sequential


for gpu in tf.config.experimental.list_physical_devices('GPU'):
    tf.config.experimental.set_memory_growth(gpu, True)
2.3.0
def nin_block(num_channels, kernel_size, strides, padding):
    blk = Sequential()
    blk.add(Conv2D(num_channels, kernel_size, strides, padding, activation='relu'))
    blk.add(Conv2D(num_channels, kernel_size=1, activation='relu'))
    blk.add(Conv2D(num_channels, kernel_size=1, activation='relu'))
    return blk


8.2 NiN模型

NiN是在AlexNet問世不久後提出的,

兩者相似點:
NiN使用卷積視窗形狀分別為11×11、5×5和3×3的卷積層,相應的輸出通道數也與AlexNet中的一致;
每個NiN塊後接一個步幅為2、視窗形狀為3×3的最大池化層。

兩者不同點:
使用NiN塊;
NiN去掉了AlexNet最後的3個全連線層,替換為輸出通道數等於標籤類別數的NiN塊,然後使用全域性平均池化層對每個通道中所有元素求平均並直接用於分類。

其中,全域性平均池化層,指視窗形狀等於輸入空間維形狀的平均池化層。
NiN的這個設計的好處是,可以顯著減小模型引數尺寸,從而緩解過擬合。
然而,該設計有時會造成獲得有效模型的訓練時間的增加。


程式碼實現如下:

net = Sequential()
net.add(nin_block(96, kernel_size=11, strides=4, padding='valid'))
net.add(MaxPool2D(pool_size=3, strides=2))
net.add(nin_block(256, kernel_size=5, strides=1, padding='same'))
net.add(MaxPool2D(pool_size=3, strides=2))
net.add(nin_block(384, kernel_size=3, strides=1, padding='same'))
net.add(MaxPool2D(pool_size=3, strides=2))
net.add(Dropout(0.5))
net.add(nin_block(10, kernel_size=3, strides=1, padding='same'))
net.add(GlobalAveragePooling2D())
net.add(Flatten())

構造一個高和寬均為224的單通道資料樣本觀察每一層的輸出形狀:

X = tf.random.uniform((1, 224, 224, 1))
for blk in net.layers:
    X = blk(X)
    print(blk.name, "output shape: ", X.shape)
sequential_1 output shape:  (1, 54, 54, 96)
max_pooling2d output shape:  (1, 26, 26, 96)
sequential_2 output shape:  (1, 26, 26, 256)
max_pooling2d_1 output shape:  (1, 12, 12, 256)
sequential_3 output shape:  (1, 12, 12, 384)
max_pooling2d_2 output shape:  (1, 5, 5, 384)
dropout output shape:  (1, 5, 5, 384)
sequential_4 output shape:  (1, 5, 5, 10)
global_average_pooling2d output shape:  (1, 10)
flatten output shape:  (1, 10)


8.3 資料獲取和模型訓練

依然使用Fashion-MNIST資料集來訓練模型。

NiN的訓練與AlexNet和VGG的類似,但這裡使用的學習率更大。


資料獲取

(四)卷積神經網路 – 6 AlexNet 小節:

class DataLoader():
    
    def __init__(self):
                
#         fashion_mnist = tf.keras.datasets.fashion_mnist
#         (self.train_images, self.train_labels), (self.test_images, self.test_labels) = fashion_mnist.load_data()

        # load data from local
        
        with open("../input/fashionmnist/train-labels-idx1-ubyte", 'rb') as f:
            self.train_labels = np.frombuffer(f.read(), np.uint8, offset=8)
    
        with open("../input/fashionmnist/train-images-idx3-ubyte", 'rb') as f:
            self.train_images = np.frombuffer(f.read(), np.uint8, offset=16).reshape(len(self.train_labels), 28, 28)
            
        with open("../input/fashionmnist/t10k-labels-idx1-ubyte", 'rb') as f:
            self.test_labels = np.frombuffer(f.read(), np.uint8, offset=8)
            
        with open("../input/fashionmnist/t10k-images-idx3-ubyte", 'rb') as f:
            self.test_images = np.frombuffer(f.read(), np.uint8, offset=16).reshape(len(self.test_labels), 28, 28)
        
        
        # np.expand_dims(images, axis=-1) -- convert (10000, 28, 28) into (10000, 28, 28, 1)
        
        self.train_images = np.expand_dims(self.train_images.astype(np.float32)/255.0,axis=-1)
        self.test_images = np.expand_dims(self.test_images.astype(np.float32)/255.0,axis=-1)
        
        self.train_labels = self.train_labels.astype(np.int32)
        self.test_labels = self.test_labels.astype(np.int32)
        
        self.num_train, self.num_test = self.train_images.shape[0], self.test_images.shape[0]

        
    def get_batch_train(self, batch_size):
        """
        Examples
        --------
        >>> np.random.randint(0, 10, size=2) 
        array([5, 7])
        
        """
        index = np.random.randint(0, np.shape(self.train_images)[0], batch_size)
        resized_images = tf.image.resize_with_pad(self.train_images[index],224,224)
        return resized_images.numpy(), self.train_labels[index]

    
    def get_batch_test(self, batch_size):
        index = np.random.randint(0, np.shape(self.test_images)[0], batch_size)
        resized_images = tf.image.resize_with_pad(self.test_images[index],224,224)
        return resized_images.numpy(), self.test_labels[index]

    
batch_size = 128
dataLoader = DataLoader()
x_batch, y_batch = dataLoader.get_batch_train(batch_size)
print("x_batch shape:",x_batch.shape,"y_batch shape:", y_batch.shape)
x_batch shape: (128, 224, 224, 1) y_batch shape: (128,)

模型訓練

模型訓練過程與(四)卷積神經網路 – 6 AlexNet 小節類似,且使用更大的學習率:

def train_nin():
    epoch = 5
    num_iter = dataLoader.num_train//batch_size
    for e in range(epoch):
        for n in range(num_iter):
            x_batch, y_batch = dataLoader.get_batch_train(batch_size)
            net.fit(x_batch, y_batch)
            if n%20 == 0:
                net.save_weights("5.8_nin_weights.h5")

optimizer = tf.keras.optimizers.Adam(lr=1e-7)

net.compile(optimizer=optimizer,
            loss='sparse_categorical_crossentropy',
            metrics=['accuracy'])

x_batch, y_batch = dataLoader.get_batch_train(batch_size)
net.fit(x_batch, y_batch)

train_nin()

讀入訓練好的引數,在測試集上評估準確率:

net.load_weights("5.8_nin_weights.h5")

x_test, y_test = dataLoader.get_batch_test(2000)
net.evaluate(x_test, y_test, verbose=2)
63/63 - 1s - loss: 6.9264 - accuracy: 0.0945
[6.926401138305664, 0.09449999779462814]




參考

《動手學深度學習》(TF2.0版)

A. Krizhevsky, I. Sutskever, and G. Hinton. Imagenet classification with deep convolutional neural networks. In NIPS, 2012.

相關文章