【深度學習】深度卷積網絡AlexNet及其MXNet實現

AlexNet概述

2012年,Alex Krizhevsky使用深度卷積網絡在ImageNet 2012圖像識別比賽中以很大的優勢取勝,這一網絡稱之爲AlexNet1

AlexNet和LeNet(可參考筆者另一篇博文【深度學習】LeNet網絡及其MXNet實現)設計理念和結構類似,但使用了更多的卷積層和更大的參數空間。具體來說有如下四個方面的區別2

  1. 相比於LeNet,AlexNet包含了更多的8層變換,其中5層卷積,2層全連接隱藏層和1層全連接輸出層;
  2. AlexNet使用ReLU函數,而非LeNet使用的sigmoid函數;
  3. AlexNet在全連接隱藏層使用丟棄法來控制模型的複雜度,LeNet並沒有;
  4. AlexNet引入了圖像曾廣,如翻轉、裁剪和顏色變化,增大了數據集緩和過擬合。

網絡結構

AlexNet的網絡結構如下圖所示:
在這裏插入圖片描述
卷積層模塊:

  • 第1層卷積窗口爲11*11。這是因爲相比於LeNet,AlexNet針對的數據集爲ImageNet,其中圖像尺寸較大,圖像中的物體佔用更多的像素,所以需要大卷積窗口捕獲物體;
  • 第2層卷積窗口爲55,之後的卷積層採用33卷積窗口;
  • 第1、2、5卷積層後接最大池化層,池化窗口3*3,步幅爲2;
  • 相比於LeNet,AlexNet卷積層通道數增大數十倍;

全連接層模塊:

  • 緊接卷積層模塊的是兩個輸出爲4096的全連接層,參數規模接近1GB;
  • 最後是全連接輸出層,由於ImageNet中包含1000個類別的圖像,所以AlexNet輸出層有1000個單元。

利用MXNet定義模型,並隨機生成一個輸入數據,打印AlexNet每一層輸出數據的形狀。由於本文我們使用FashionMNIST數據集(可參考筆者先前博文【深度學習】Fashion-MNIST數據集簡介)進行測試,所以輸出層僅包含10個單元。

# coding=utf-8
# author: BebDong
# 2019/1/23
# AlexNet:於2012年提出,用於ImageNet比賽,得名於其作者Alex Krizhevsky
# 網絡結構:8層變換,包括5層卷積、2層全連接隱藏層、1層全連接輸出層
# 卷積結構:第1層卷積窗口爲11*11(因爲ImageNet數據集圖像尺寸大),第二層卷積窗口5*5,其後的卷積窗口爲3*3
#         第1、2、5卷積層後都使用最大池化層,池化窗口爲3*3,步幅爲2
#         卷積層通道數巨大
# 全連接層結構:緊接卷積層的是輸出爲4096的全連接層
# AlexNet使用丟棄法控制全連接層的模型複雜度
# 這裏實現一個簡化的AlexNet,通過FashionMNIST數據集測試

from mxnet.gluon import nn, data as gdata, loss as gloss
from mxnet import init, nd
import mxnet as mx

# 通過Sequential定義模型
alexnet = nn.Sequential()
alexnet.add(nn.Conv2D(channels=96, kernel_size=11, strides=4, activation='relu'),  # 步幅4較大程度減小輸出寬和高
            nn.MaxPool2D(pool_size=3, strides=2),
            nn.Conv2D(channels=256, kernel_size=5, padding=2, activation='relu'),  # 填充,使得輸入和輸出尺寸一致
            nn.MaxPool2D(pool_size=3, strides=2),
            nn.Conv2D(channels=384, kernel_size=3, padding=1, activation='relu'),  # 連續3層卷積,通道數進一步增加
            nn.Conv2D(channels=384, kernel_size=3, padding=1, activation='relu'),
            nn.Conv2D(channels=256, kernel_size=3, padding=1, activation='relu'),
            nn.MaxPool2D(pool_size=3, strides=2),
            nn.Dense(units=4096, activation='relu'),  # 使用丟棄法來抑制過擬合
            nn.Dropout(rate=0.5),
            nn.Dense(units=4096, activation='relu'),
            nn.Dropout(rate=0.5),
            nn.Dense(10))  # 輸出層,FashionMNIST數據集爲10個類別,而ImageNet中爲1000
gpu_id = 0  # 將模型初始化在gpu上
alexnet.initialize(init=init.Xavier(), ctx=mx.gpu(gpu_id))

# 查看每一層輸出的數據尺寸
x = nd.random.uniform(shape=(1, 1, 224, 224), ctx=mx.gpu(gpu_id))
for layer in alexnet:
    x = layer(x)
    print(layer.name, x.shape)

可以看到AlexNet每一層的輸出數據形狀,含義爲:(樣本數,通道數,高,寬)。
在這裏插入圖片描述

FashionMNIST數據集實驗

ImageNet數據集包含1000個類別的圖像,很是龐大,訓練其上的模型需要耗費大量的時間,所以這裏進使用FashionMNIST數據集進行模擬實驗。FashionMNIST僅包含10個類別的圖像,相對較小,其詳細信息可參考筆者先前博文【深度學習】Fashion-MNIST數據集簡介

由於ImageNet中圖像尺寸更大,爲了模擬,這裏在模型訓練前,將FashionMNIST數據集圖像進行了擴大,本文中是擴大到224*224像素大小。擴大操作可以使用MXNet提供的Resize進行,具體可以參考MXNet官方文檔

實驗代碼

代碼邏輯和【深度學習】LeNet網絡及其MXNet實現一文LeNet實驗完全一致。

# coding=utf-8
# author: BebDong
# 2019/1/23
# AlexNet:於2012年提出,用於ImageNet比賽,得名於其作者Alex Krizhevsky
# 網絡結構:8層變換,包括5層卷積、2層全連接隱藏層、1層全連接輸出層
# 卷積結構:第1層卷積窗口爲11*11(因爲ImageNet數據集圖像尺寸大),第二層卷積窗口5*5,其後的卷積窗口爲3*3
#         第1、2、5卷積層後都使用最大池化層,池化窗口爲3*3,步幅爲2
#         卷積層通道數相比LeNet增加巨大
# 全連接層結構:緊接卷積層的是輸出爲4096的全連接層
# AlexNet使用丟棄法控制全連接層的模型複雜度
# 這裏實現一個簡化的AlexNet,通過FashionMNIST數據集測試

from mxnet.gluon import nn, data as gdata, loss as gloss
from mxnet import init, nd, autograd, gluon
import mxnet as mx

import sys
import time
from IPython import display

import matplotlib
from matplotlib import pyplot as plt

# 通過Sequential定義模型
alexnet = nn.Sequential()
alexnet.add(nn.Conv2D(channels=96, kernel_size=11, strides=4, activation='relu'),  # 步幅4較大程度減小輸出寬和高
            nn.MaxPool2D(pool_size=3, strides=2),
            nn.Conv2D(channels=256, kernel_size=5, padding=2, activation='relu'),  # 填充,使得輸入和輸出尺寸一致
            nn.MaxPool2D(pool_size=3, strides=2),
            nn.Conv2D(channels=384, kernel_size=3, padding=1, activation='relu'),  # 連續3層卷積,通道數進一步增加
            nn.Conv2D(channels=384, kernel_size=3, padding=1, activation='relu'),
            nn.Conv2D(channels=256, kernel_size=3, padding=1, activation='relu'),
            nn.MaxPool2D(pool_size=3, strides=2),
            nn.Dense(units=4096, activation='relu'),  # 使用丟棄法來抑制過擬合
            nn.Dropout(rate=0.5),
            nn.Dense(units=4096, activation='relu'),
            nn.Dropout(rate=0.5),
            nn.Dense(10))  # 輸出層,FashionMNIST數據集爲10個類別,而ImageNet中爲1000
gpu_id = 0  # 將模型初始化在gpu上
alexnet.initialize(init=init.Xavier(), ctx=mx.gpu(gpu_id))

# 讀取數據,將FashionMNIST圖像寬和高擴大到ImageNet的224,通過MXNet中Resize實現
batch_size = 128  # 批量大小
resize = 224  # resize爲224*224
transforms = [gdata.vision.transforms.Resize(resize), gdata.vision.transforms.ToTensor()]  # 在ToTensor前使用Resize變換
transformer = gdata.vision.transforms.Compose(transforms)  # 通過Compose將兩個變換串聯
mnist_train = gdata.vision.FashionMNIST(train=True)  # 加載數據
mnist_test = gdata.vision.FashionMNIST(train=False)
num_workers = 0 if sys.platform.startswith('win32') else 4  # 非windows系統多線程加速數據讀取
train_iter = gdata.DataLoader(mnist_train.transform_first(transformer), batch_size, shuffle=True,
                              num_workers=num_workers)
test_iter = gdata.DataLoader(mnist_test.transform_first(transformer), batch_size, shuffle=False,
                             num_workers=num_workers)

# 訓練模型,同LeNet
lr = 0.01  # 學習率
epochs = 130  # 訓練次數
trainer = gluon.Trainer(alexnet.collect_params(), optimizer='sgd', optimizer_params={'learning_rate': lr})
loss = gloss.SoftmaxCrossEntropyLoss()
train_acc_array, test_acc_array = [], []  # 記錄訓練過程中的數據,作圖
for epoch in range(epochs):
    train_los_sum, train_acc_sum = 0.0, 0.0  # 每個epoch的損失和準確率
    epoch_start = time.time()  # epoch開始的時間
    for X, y in train_iter:
        X, y = X.as_in_context(mx.gpu(gpu_id)), y.as_in_context(mx.gpu(gpu_id))  # 將數據複製到GPU中
        with autograd.record():
            y_hat = alexnet(X)
            los = loss(y_hat, y)
        los.backward()
        trainer.step(batch_size)
        train_los_sum += los.mean().asscalar()  # 計算訓練的損失
        train_acc_sum += (y_hat.argmax(axis=1) == y.astype('float32')).mean().asscalar()  # 計算訓練的準確率
    test_acc_sum = nd.array([0], ctx=mx.gpu(gpu_id))  # 計算模型此時的測試準確率
    for features, labels in test_iter:
        features, labels = features.as_in_context(mx.gpu(gpu_id)), labels.as_in_context(mx.gpu(gpu_id))
        test_acc_sum += (alexnet(features).argmax(axis=1) == labels.astype('float32')).mean()
    test_acc = test_acc_sum.asscalar() / len(test_iter)
    print('epoch %d, time %.1f sec, loss %.4f, train acc %.4f, test acc %.4f' %
          (epoch + 1, time.time() - epoch_start, train_los_sum / len(train_iter), train_acc_sum / len(train_iter),
           test_acc))
    train_acc_array.append(train_acc_sum / len(train_iter))  # 記錄訓練過程中的數據
    test_acc_array.append(test_acc)

# 作圖
display.set_matplotlib_formats('svg')  # 矢量圖
plt.rcParams['figure.figsize'] = (3.5, 2.5)  # 圖片尺寸
plt.xlabel('epochs')  # 橫座標
plt.ylabel('accuracy')  # 縱座標
plt.semilogy(range(1, epochs + 1), train_acc_array)
plt.semilogy(range(1, epochs + 1), test_acc_array, linestyle=":")
plt.legend(['train accuracy', 'test accuracy'])
plt.show()

結果討論

筆者設置的參數如上代碼所示,得到結果:
在這裏插入圖片描述
我們作出準確率隨着epoch的變化曲線圖:
在這裏插入圖片描述
從上面的結果中可以看到,隨着訓練次數的增加,雖然訓練的準確率持續提高,幾乎接近100%,但是測試準確率在第50個epoch往後基本維持在93%,已無明顯變化。對比【深度學習】LeNet網絡及其MXNet實現一文中對FashionMNIST數據集的實驗結果有所提升。

其實AlexNet模型對於FashionMNIST這樣的數據集來說還是稍顯複雜,非常容易出現過擬合現象,測試準確率反而下降。

參考文獻


  1. Krizhevsky, A., Sutskever, I., & Hinton, G. E. (2012). Imagenet classification with deep convolutional neural networks. In Advances in neural information processing systems (pp. 1097-1105). ↩︎

  2. Aston Zhang, Mu Li et al. Hands on Deep Learning[EB/OL]. http://zh.d2l.ai/chapter_convolutional-neural-networks/alexnet.html ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章