TensorFlow筆記(五)卷積神經網絡

第五章 卷積神經網絡

本節主要內容:講解卷積神經網絡,利用基礎CNN、LeNet、AlexNet、VGGNet、InceptionNet和ResNet實現圖像識別。

1 全連接網絡回顧

全連接NN特點:每個神經元與前後相鄰層的每一個神經元都有連接關係。(可以實現分類和預測)

全連接網絡的參數個數爲:image-20220422190704884

如圖5-1所示,針對一張分辨率僅爲28 * 28的黑白圖像(像素值個數爲28 * 28 * 1 = 784),全連接網絡的參數總量就有將近40萬個。

image-20220422190717932

圖5-1 全連接網絡的參數量

在實際應用中,圖像的分辨率遠高於此,且大多數是彩色圖像,如圖5-2所示。雖然全連接網絡一般被認爲是分類預測的最佳網絡,但待優化的參數過多,容易導致模型過擬合。

image-20220422190812138

圖5-2 灰度圖與彩色圖

爲了解決參數量過大而導致模型過擬合的問題,一般不會將原始圖像直接輸入,而是先對圖像進行特徵提取,再將提取到的特徵輸入全連接網絡,如圖5-3所示,就是將汽車圖片經過多次特徵提取後再喂入全連接網絡。

image-20220422190910876

圖5-3 全連接網絡的改進

2 卷積神經網絡

卷積的概念:卷積可以認爲是一種有效提取圖像特徵的方法。一般會用一個正方形的卷積核,按指定步長,在輸入特徵圖上滑動,遍歷輸入特徵圖中的每個像素點。

2.1 卷積計算過程

每一個步長,卷積核會與輸入特徵圖出現重合區域,重合區域對應元素相乘、求和再加上偏置項得到輸出特徵的一個像素點,如圖5-4所示,利用大小爲3×3×1的卷積覈對5×5×1的單通道圖像做卷積計算得到相應結果。

image-20220422191004953

圖5-4 神經網絡中的卷積計算

對於彩色圖像(多通道)來說,卷積核通道數與輸入特徵一致,套接後在對應位置上進行乘加和操作,如圖5-5所示,利用三通道卷積覈對三通道的彩色特徵圖做卷積計算。

image-20220422191036040

圖5-5 三通道彩色圖像的卷積計算

用多個卷積核可實現對同一層輸入特徵的多次特徵提取,卷積核的個數決定輸出層的通道(channels)數,即輸出特徵圖的深度。

image-20220422191414732

2.2 感受野

感受野(Receptive Field)的概念:卷積神經網絡各輸出層每個像素點在原始圖像上的映射區域大小,如圖5-7所示。

image-20220422191123245

圖5-6 感受野示意圖

當我們採用尺寸不同的卷積核時,最大的區別就是感受野的大小不同,所以經常會採用多層小卷積核來替換一層大卷積核,在保持感受野相同的情況下減少參數量和計算量,例如十分常見的用2層3 * 3卷積核來替換1層5 * 5卷積核的方法,如圖5-7所示。

image-20220422191715164

圖5-7 兩層3 * 3卷積核與一層5 * 5卷積核的對比

這裏給出詳細推導:不妨設輸入特徵圖的寬、高均爲x,卷積計算的步長爲1,顯然,兩個3 * 3卷積核的參數量爲9 + 9 = 18,小於5 * 5卷積核的25,前者的參數量更少。

在計算量上,根據圖5-8所示的輸出特徵尺寸計算公式,對於5 * 5卷積核來說,輸出特徵圖共有\((x–5+1)^2\)個像素點,每個像素點需要進行\(5 * 5 = 25\)次乘加運算,則總計算量爲\(25 * (x–5+1)^2 = 25x^2 – 200x + 40\);對於兩

個3 * 3卷積核來說,第一個3 * 3卷積核輸出特徵圖共有\((x–3+1)^2\)個像素點,每個像素點需要進行\(3 * 3 = 9\)次乘加運算,第二個3 * 3卷積核輸出特徵圖共有\((x–3+1–3+1)^2\)個像素點,每個像素點同樣需要進行9次乘加運算,則總計算量爲\(9 * (x – 3 + 1)^2 + 9 * (x – 3 + 1 – 3 + 1)^2 = 18 x^2 – 108x + 180\)

對二者的總計算量(乘加運算的次數)進行對比,\(18 x^2 – 200x + 400 < 25x^2 – 200x + 400\),經過簡單數學運算可得x < 22/7 or x > 10,x作爲特徵圖的邊長,在大多數情況下顯然會是一個大於10的值(非常簡單的MNIST數據集的尺寸也達到了28 * 28),所以兩層3 * 3卷積核的參數量和計算量,在通常情況下都優於一層5 * 5卷積核,尤其是當特徵圖尺寸比較大的情況下,兩層3 * 3卷積核在計算量上的優勢會更加明顯。

輸出特徵尺寸計算:在瞭解神經網絡中卷積計算的整個過程後,就可以對輸出特徵圖的尺寸進行計算,如圖5-8所示,5×5的圖像經過3×3大小的卷積核做卷積計算後輸出特徵尺寸爲3×3。

Sobel Conv Gif

圖5-8 輸出特徵尺寸計算

輸出圖片邊長 = (輸入圖片邊長 - 卷積核長 + 1)/步長

此圖:(5-3+1)/ 1 = 3

2.3 全零填充

全零填充(padding):爲了保持輸出圖像尺寸與輸入圖像一致,經常會在輸入圖像周圍進行全零填充,如圖5-9所示,在5×5的輸入圖像周圍填0,則輸出特徵尺寸同爲5×5。

image-20220422192606084

在Tensorflow框架中,用參數padding = ‘SAME’或padding = ‘VALID’表示是否進行全零填充,其對輸出特徵尺寸大小的影響如下:

image-20220422192626711

上下兩行分別代表對輸入圖像進行全零填充或不進行填充,對於5×5×1的圖像來說,當padding = ‘SAME’時,輸出圖像邊長爲5;當padding = ‘VALID’時,輸出圖像邊長爲3。
具備以上知識後,就可以在Tensorflow框架下利用Keras來構建CNN中的卷積層,使用的是tf.keras.layers.Conv2D函數,具體的使用方法如下:

tf.keras.layers.Conv2D(
input_shape = (高, 寬, 通道數), #僅在第一層有
filters = 卷積核個數,
kernel_size = 卷積核尺寸,
strides = 卷積步長,
padding = ‘SAME’ or ‘VALID’,
activation = ‘relu’ or ‘sigmoid’ or ‘tanh’ or ‘softmax’等#如有BN則此處不用寫
)

使用此函數構建卷積層時,需要給出的信息有:
A)輸入圖像的信息,即寬高和通道數;
B)卷積核的個數以及尺寸,如filters = 16, kernel_size = (3, 3)代表採用16個大小爲3×3的卷積核;
C)卷積步長,即卷積核在輸入圖像上滑動的步長,縱向步長與橫向步長通常是相同的,默認值爲1;
D)是否進行全零填充,全零填充的具體作用上文有描述;
E)採用哪種激活函數,例如relu、softmax等,各種函數的具體效果在前面章節中有詳細描述;
這裏需要注意的是,在利用Tensorflow框架構建卷積網絡時,一般會利用BatchNormalization函數來構建BN層,進行批歸一化操作,所以在Conv2D函數中經常不寫BN。BN操作的具體含義和作用見下文。

2.4 批標準化

Batch Normalization(批標準化):對一小批數據在網絡各層的輸出做標準化處理,其具體實現方式如圖5-10所示。(標準化:使數據符合0均值,1爲標準差的分佈。)

image-20220422192950065

圖5-10 Batch Normalization的實現

Batch Normalization將神經網絡每層的輸入都調整到均值爲0,方差爲1的標準正態分佈,其目的是解決神經網絡中梯度消失的問題,如圖5-11所示。

image-20220422193022266

圖5-11 Batch Normalization的作用(以Sigmoid激活函數爲例)

BN操作的另一個重要步驟是縮放和偏移,值得注意的是,縮放因子γ以及偏移因子β都是可訓練參數,其作用如圖5-12所示。

image-20220422193056646

圖5-12 BN中的縮放與平移

BN操作通常位於卷積層之後,激活層之前,在Tensorflow框架中,通常使用Keras中的tf.keras.layers.BatchNormalization函數來構建BN層。

在調用此函數時,需要注意的一個參數是training,此參數只在調用時指定,在模型進行前向推理時產生作用,當training = True時,BN操作採用當前batch的均值和標準差;當training = False時,BN操作採用滑動平均(running)的均值和標準差。在Tensorflow中,通常會指定training = False,可以更好地反映模型在測試集上的真實效果。

滑動平均(running)的解釋:滑動平均,即通過一個個batch歷史的疊加,最終趨向數據集整體分佈的過程,在測試集上進行推理時,滑動平均的參數也就是最終保存的參數。

此外,Tensorflow中的BN函數其實還有很多參數,其中比較常用的是momentum,即動量參數,與sgd優化器中的動量參數含義類似但略有區別,具體作用爲滑動平均running = momentum * running + (1 – momentum) * batch,一般設置一個比較大的值,在Tensorflow框架中默認爲0.99。

2.5 池化

池化(pooling):池化的作用是減少特徵數量(降維)。最大值池化可提取圖片紋理,均值池化可保留背景特徵,如圖5-13所示。

image-20220422193553926

圖5-13 最大值池化與均值池化

在Tensorflow框架下,可以利用Keras來構建池化層,使用的是tf.keras.layers.MaxPool2D函數和tf.keras.layers.AveragePooling2D函數,具體的使用方法如下:

tf.keras.layers.MaxPool2D(
pool_size = 池化核尺寸,
strides = 池化步長,
padding = ‘SAME’ or ‘VALID’
)
tf.keras.layers.AveragePooling2D(
pool_size = 池化核尺寸,
strides = 池化步長,
padding = ‘SAME’ or ‘VALID’
)

2.6 捨棄

捨棄(Dropout):在神經網絡的訓練過程中,將一部分神經元按照一定概率從神經網絡中暫時捨棄,使用時被捨棄的神經元恢復鏈接,如圖5-14所示。

image-20220422193705002

在Tensorflow框架下,利用tf.keras.layers.Dropout函數構建Dropout層,參數爲捨棄的概率(大於0小於1)。

爲了應對神經網絡很容易過擬合的問題,2014年 Hinton 提出了一個神器, **Dropout: A Simple Way to Prevent Neural Networks from Overfitting ** (original paper: http://jmlr.org/papers/v15/srivastava14a.html)

2.7 卷積神經網絡總結

利用上述知識,就可以構建出基本的卷積神經網絡(CNN)了,其核心思路爲在CNN中利用卷積核(kernel)提取特徵後,送入全連接網絡。

CNN模型的主要模塊:一般包括上述的卷積層、BN層、激活函數、池化層以及全連接層,如圖5-15所示。

image-20220422193733167

在此基礎上,可以總結出在Tensorflow框架下,利用Keras來搭建神經網絡的“八股”套路,在主幹的基礎上,還可以添加其他內容,來完善神經網絡的功能,如利用自己的圖片和標籤文件來自制數據集;通過旋轉、縮放、平移等操作對數據集進行數據增強;保存模型文件進行斷點續訓;提取訓練後得到的模型參數以及準確率曲線,實現可視化等。

構建神經網絡的“八股”套路:
A)import引入tensorflow及keras、numpy等所需模塊。

B)讀取數據集,課程中所利用的MNIST、cifar10等數據集比較基礎,可以直接從sklearn等模塊中引入,但是在實際應用中,大多需要從圖片和標籤文件中讀取所需的數據集。

C)搭建所需的網絡結構,當網絡結構比較簡單時,可以利用keras模塊中的tf.keras.Sequential來搭建順序網絡模型;但是當網絡不再是簡單的順序結構,而是有其它特殊結構出現時(例如ResNet中的跳連結構),便需要利用class來定義自己的網絡結構。前者使用起來更加方便,但實際應用中往往需要利用後者來搭建網絡。

D)對搭建好的網絡進行編譯(compile),通常在這一步指定所採用的優化器(如Adam、sgd、RMSdrop等)以及損失函數(如交叉熵函數、均方差函數等),選擇哪種優化器和損失函數往往對訓練的速度和效果有很大的影響,至於具體如何進行選擇,前面的章節中有比較詳細的介紹。

E)將數據輸入編譯好的網絡來進行訓練(model.fit),在這一步中指定訓練輪數epochs以及batch_size等信息,由於神經網絡的參數量和計算量一般都比較大,訓練所需的時間也會比較長,尤其是在硬件條件受限的情況下,所以在這一步中通常會加入斷點續訓以及模型參數保存等功能,使訓練更加方便,同時防止程序意外停止導致數據丟失的情況發生。

F)將神經網絡模型的具體信息打印出來(model.summary),包括網絡結構、網絡各層的參數等,便於對網絡進行瀏覽和檢查。

3 cifar10數據集

該數據集共有60000張彩色圖像,每張尺寸爲32 * 32,分爲10類,每類6000張。訓練集50000張,分爲5個訓練批,每批10000張;從每一類隨機取1000張構成測試集,共10000張,剩下的隨機排列組成訓練集,如圖5-16所示。

image-20220422201747110

圖5-16 cifar10數據集

cifar10數據集的讀取:

數據集下載:

cifar10 = tf.keras.datasets.cifar10

導入訓練集和測試集:

(x_train, y_train), (x_test, y_test) = cifar10.load_data()

打印訓練集與測試集的數據維度,打印結果爲:

image-20220422201856662

顯然,cifar10是一個用於圖像分類的數據集,共分10類,相較於mnist數據集會更復雜一些,訓練難度也更大,但是圖像尺寸較小,僅爲32 * 32,仍然屬於比較基礎的數據集,利用一些CNN經典網絡結構(如VGGNet、ResNet等,下一小節會具體介紹)進行訓練的話準確率很容易就能超過90%,很適合初學者用來練習。目前學術界對於cifar10數據集的分類準確率已經達到了相當高的水準,圖5-17中爲Github網站上cifar10數據集分類準確率的排行榜。
參考網址:http://rodrigob.github.io/are_we_there_yet/build/classification_datasets_results.html

image-20220422202015267

掌握了利用tf.keras來搭建神經網絡的八股之後,就可以搭建自己的神經網絡來對數據集進行訓練了,這裏提供一個實例,利用一個結構簡單的基礎卷積神經網絡(CNN)來對cifar10數據集進行訓練,網絡結構如圖5-18所示。

image-20220422202039957

圖5-18 網絡結構

利用tf.keras.Sequential模型以及class定義兩種方式都可以構建出圖5-18中的基礎CNN網絡,在此例中二者的效果是完全相同的,前者看起來會更簡潔一些,但後者在實際應用中更加常用,因爲這僅僅是一個非常基礎的網絡,而一些複雜的網絡經常會有Sequential模型無法表達的結構或設計,所以在這裏採用後者,如圖5-19所示。

image-20220422202113017

圖5-19 卷積神經網絡搭建示例

CNN訓練cifar10數據集的baseline源碼:p27_cifar10_baseline.py

import tensorflow as tf
import os
import numpy as np
from matplotlib import pyplot as plt
from tensorflow.keras.layers import Conv2D, BatchNormalization, Activation, MaxPool2D, Dropout, Flatten, Dense
from tensorflow.keras import Model

np.set_printoptions(threshold=np.inf)

cifar10 = tf.keras.datasets.cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0


class Baseline(Model):
    def __init__(self):
        super(Baseline, self).__init__()
        self.c1 = Conv2D(filters=6, kernel_size=(5, 5), padding='same')  # 卷積層
        self.b1 = BatchNormalization()  # BN層
        self.a1 = Activation('relu')  # 激活層
        self.p1 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')  # 池化層
        self.d1 = Dropout(0.2)  # dropout層

        self.flatten = Flatten()
        self.f1 = Dense(128, activation='relu')
        self.d2 = Dropout(0.2)
        self.f2 = Dense(10, activation='softmax')

    def call(self, x):
        x = self.c1(x)
        x = self.b1(x)
        x = self.a1(x)
        x = self.p1(x)
        x = self.d1(x)
        x = self.flatten(x)
        x = self.f1(x)
        x = self.d2(x)
        y = self.f2(x)
        return y

model = Baseline()

model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['sparse_categorical_accuracy'])

checkpoint_save_path = "./checkpoint/Baseline.ckpt"
if os.path.exists(checkpoint_save_path + '.index'):
    print('-------------load the model-----------------')
    model.load_weights(checkpoint_save_path)

cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
                                                 save_weights_only=True,
                                                 save_best_only=True)

history = model.fit(x_train, y_train, batch_size=32, epochs=10, validation_data=(x_test, y_test), validation_freq=1,
                    callbacks=[cp_callback])
model.summary()

# print(model.trainable_variables)
file = open('./weights.txt', 'w')
for v in model.trainable_variables:
    file.write(str(v.name) + '\n')
    file.write(str(v.shape) + '\n')
    file.write(str(v.numpy()) + '\n')
file.close()

###############################################    show   ###############################################

# 顯示訓練集和驗證集的acc和loss曲線
acc = history.history['sparse_categorical_accuracy']
val_acc = history.history['val_sparse_categorical_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']

plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()

上述源碼中,除了CNN網絡的搭建以外,還包含cifar10數據集讀取,模型參數保存與讀取,loss及準確率曲線繪製等功能,在之後的代碼中也會經常用到。

4 CNN經典網絡

在卷積神經網絡的發展歷程中,出現過許多經典的網絡結構,這些CNN經典網絡的提出都曾極大地促進了領域的發展,這裏對5個經典的CNN網絡結構做一個介紹,從1998年由Yann LeCun提出的LeNet直至2015年由何愷明提出的ResNet,如圖5-20所示。

值得一提的是,除了卷積網絡的“開篇之作”LeNet以外,AlexNet、VGGNet、InceptionNet以及ResNet這四種經典網絡全部是在當年的ImageNet競賽中問世的,它們作爲深度學習的經典代表,使得ImageNet數據集上的錯誤率逐年降低。下面將會對這五種經典網絡逐一進行介紹。

image-20220422203428996

附:CNN經典網絡論文出處
LeNet-5:
Yann Lecun, Leon Bottou, Y. Bengio, Patrick Haffner. Gradient-Based Learning Applied to Document Recognition. Proceedings of the IEEE, 1998.
AlexNet:
Alex Krizhevsky, Ilya Sutskever, Geoffrey E. Hinton. ImageNet Classification with Deep Convolutional Neural Networks. In NIPS, 2012.
VGG16:
K. Simonyan, A. Zisserman. Very Deep Convolutional Networks for Large-Scale Image Recognition.In ICLR, 2015.
Inception-v1:
Szegedy C, Liu W, Jia Y, et al. Going Deeper with Convolutions. In CVPR, 2015.
ResNet:
Kaiming He, Xiangyu Zhang, Shaoqing Ren. Deep Residual Learning for Image Recognition. In CPVR, 2016.

4.1 LeNet

借鑑點:

  • 共享卷積核,減少網絡參數

LeNet即LeNet5,由Yann LeCun在1998年提出,做爲最早的卷積神經網絡之一,是許多神經網絡架構的起點,其網絡結構如圖5-21所示。

image-20220422203620099

圖5-21 LeNet5網絡結構

根據以上信息,就可以根據上一節所總結出來的方法,在Tensorflow框架下利用tf.Keras來構建LeNet5模型,如圖5-22所示。

image-20220422203727059

圖中紫色部分爲卷積層,紅色部分爲全連接層,模型圖與代碼一一對應,模型搭建具體流程如下(各步驟的實現函數在5.2節中均有介紹):
A)輸入圖像大小爲32 * 32 * 3,三通道彩色圖像輸入;
B)進行卷積,卷積核大小爲5 * 5,個數爲6,步長爲1,不進行全零填充;
C)將卷積結果輸入sigmoid激活函數(非線性函數)進行激活;
D)進行最大池化,池化核大小爲2 * 2,步長爲2;

image-20220422203808344

E)進行卷積,卷積核大小爲5 * 5,個數爲16,步長爲1,不進行全零填充;
F)將卷積結果輸入sigmoid激活函數進行激活;
G)進行最大池化,池化核大小爲2 * 2,步長爲2;

image-20220422203822509

H)輸入三層全連接網絡進行10分類。

image-20220422203841052

與最初的LeNet5網絡結構相比,這裏做了一點微調,輸入圖像尺寸爲32 * 32 * 3,以適應cifar10數據集(此數據集在5.2節中也有具體介紹)。模型中採用的激活函數有sigmoid和softmax,池化層均採用最大池化,以保留邊緣特徵。

總體上看,誕生於1998年的LeNet5與如今一些主流的CNN網絡相比,其結構可以說是相當簡單,不過它成功地利用“卷積提取特徵→全連接分類”的經典思路解決了手寫數字識別的問題,對神經網絡研究的發展有着很重要的意義。

4.2 AlexNet

模型實現代碼:p34_cifar10_alexnet8.py

借鑑點:

  • 激活函數使用Relu,提升訓練速度
  • Dropout防止過擬合

AlexNet網絡誕生於2012年,其ImageNet Top5錯誤率爲16.4 %,可以說AlexNet的出現使得已經沉寂多年的深度學習領域開啓了黃金時代。

AlexNet的總體結構和LeNet5有相似之處,但是有一些很重要的改進:
A)由五層卷積、三層全連接組成,輸入圖像尺寸爲224 * 224 * 3,網絡規模遠大於LeNet5;
B)使用了Relu激活函數;
C)進行了捨棄(Dropout)操作,以防止模型過擬合,提升魯棒性;
D)增加了一些訓練上的技巧,包括數據增強、學習率衰減、權重衰減(L2正則化)等。

AlexNet的網絡結構如圖5-23所示。

Tensorflow筆記5

圖5-23 AlexNet網絡結構

可以看到,圖5-20所示的網絡結構將模型分成了兩部分,這是由於當時用於訓練AlexNet的顯卡爲GTX 580(顯存爲3GB),單塊顯卡運算資源不足的原因。

在Tensorflow框架下利用Keras來搭建AlexNet模型,這裏做了一些調整,將輸入圖像尺寸改爲32 * 32 * 3以適應cifar10數據集,並且將原始的AlexNet模型中的11 * 11、7 * 7、5 * 5等大尺寸卷積核均替換成了3 * 3的小卷積核,如圖5-24所示。

image-20220422204328606

圖5-24 Keras實現AlexNet模型

圖中紫色塊代表卷積部分,可以看到卷積操作共進行了5次:
A)第1次卷積:共有96個3 * 3的卷積核,不進行全零填充,進行BN操作,激活函數爲Relu,進行最大池化,池化核尺寸爲3 * 3,步長爲2;

image-20220422204355468

B)第2次卷積:與第1次卷積類似,除卷積核個數由96增加到256之外幾乎相同;

image-20220422204405403

C)第3次卷積:共有384個3 * 3的卷積核,進行全零填充,激活函數爲Relu,不進行BN操作以及最大池化;

image-20220422204417472

D)第4次卷積:與第3次卷積幾乎完全相同;

image-20220422204426535

E)第5次卷積:共有96個3 * 3的卷積核,進行全零填充,激活函數爲Relu,不進行BN操作,進行最大池化,池化核尺寸爲3 * 3,步長爲2。

image-20220422204435434

圖中紅色塊代表全連接部分,共有三層:

A)第一層共2048個神經元,激活函數爲Relu,進行0.5的dropout;

image-20220422204456248

B)第二層與第一層幾乎完全相同;

image-20220422204513669

C)第三層共10個神經元,進行10分類。

image-20220422204521611

可以看到,與結構類似的LeNet5相比,AlexNet模型的參數量有了非常明顯的提升,卷積運算的層數也更多了,這有利於更好地提取特徵;Relu激活函數的使用加快了模型的訓練速度;Dropout的使用提升了模型的魯棒性,這些優勢使得AlexNet的性能大大提升。

4.3 VGGNet

借鑑點:

  • 小卷積覈減少參數的同時,提高識別準確率
  • 網絡結構規整,適合並行加速。

在AlexNet之後,另一個性能提升較大的網絡是誕生於2014年的VGGNet,其ImageNet Top5錯誤率減小到了7.3 %。
VGGNet網絡的最大改進是在網絡的深度上,由AlexNet的8層增加到了16層和19層,更深的網絡意味着更強的表達能力,這得益於強大的運算能力支持。VGGNet的另一個顯著特點是僅使用了單一尺寸的3 * 3卷積核,事實上,3 * 3的小卷積核在很多卷積網絡中都被大量使用,這是由於在感受野相同的情況下,小卷積核堆積的效果要優於大卷積核,同時參數量也更少。VGGNet就使用了3 * 3的卷積核替代了AlexNet中的大卷積核(11 * 11、7 * 7、5 * 5),取得了較好的效果(事實上課程中利用Keras實現AlexNet時已經採取了這種方式),VGGNet16的網絡結構如圖5-25所示。

VGGNet16和VGGNet19並沒有本質上的區別,只是網絡深度不同,前者16層(13層卷積、3層全連接),後者19層(16層卷積、3層全連接)。

image-20220422204925438

圖5-25 VGGNet16網絡結構圖

在Tensorflow框架下利用Keras來實現VGG16網絡,爲適應cifar10數據集,將輸入圖像尺寸由224 * 244 * 3調整爲32 * 32 * 3,如圖5-26所示。

image-20220422205000014

圖5-26 Keras實現VGG16模型

根據特徵圖尺寸的變化,可以將VGG16模型分爲六個部分(在VGG16中,每進行一次池化操作,特徵圖的邊長縮小爲1/2,其餘操作均未影響特徵圖尺寸):
A)第一部分:兩次卷積(64個3 * 3卷積核、BN、Relu激活)→最大池化→Dropout

image-20220422205022305

B)第二部分:兩次卷積(128個3 * 3卷積核、BN、Relu激活)→最大池化→Dropout

image-20220422205035270

C)第三部分:三次卷積(256個3 * 3卷積核、BN、Relu激活)→最大池化→Dropout

image-20220422205043687

D)第四部分:三次卷積(512個3 * 3卷積核、BN、Relu激活)→最大池化→Dropout

image-20220422205050932

E)第五部分:三次卷積(512個3 * 3卷積核、BN、Relu激活)→最大池化→Dropout

image-20220422205251051

F)第六部分:全連接(512個神經元)→Dropout→全連接(512個神經元)→Dropout→全連接(10個神經元)

image-20220422205301808

總體來看,VGGNet的結構是相當規整的,它繼承了AlexNet中的Relu激活函數、Dropout操作等有效的方法,同時採用了單一尺寸的3 * 3小卷積核,形成了規整的C(Convolution,卷積)、B(Batch normalization)、A(Activation,激活)、P(Pooling,池化)、D(Dropout)結構,這一典型結構在卷積神經網絡中的應用是非常廣的。

4.4 InceptionNet

模型實現代碼:p40_cifar10_inception26.py
借鑑點:一層內使用不同尺寸的卷積核,提升感知力(通過padding實現輸出特徵面積一致);使用1 * 1卷積核,改變輸出特徵channel數(減少網絡參數)。
InceptionNet即GoogLeNet,誕生於2015年,旨在通過增加網絡的寬度來提升網絡的能力,與VGGNet通過卷積層堆疊的方式(縱向)相比,是一個不同的方向(橫向)。
顯然,InceptionNet模型的構建與VGGNet及之前的網絡會有所區別,不再是簡單的縱向堆疊,要理解InceptionNet的結構,首先要理解它的基本單元,如圖5-27所示。

image-20220422213903262

圖5-27 InceptionNet基本單元

可以看到,InceptionNet的基本單元中,卷積部分是比較統一的C、B、A典型結構,即卷積→BN→激活,激活均採用Relu激活函數,同時包含最大池化操作。

在Tensorflow框架下利用Keras構建InceptionNet模型時,可以將C、B、A結構封裝在一起,定義成一個新的ConvBNRelu類,以減少代碼量,同時更便於閱讀。

image-20220422213941136

參數ch代表特徵圖的通道數,也即卷積核個數;kernelsz代表卷積核尺寸;strides代表卷積步長;padding代表是否進行全零填充。

完成了這一步後,就可以開始構建InceptionNet的基本單元了,同樣利用class定義的方式,定義一個新的InceptionBlk類,如5-28所示。

image-20220422214002206

圖5-28 Inception基本單元的實現

參數ch仍代表通道數,strides代表卷積步長,與ConvBNRelu類中一致;tf.concat函數將四個輸出連接在一起,x1、x2_2、x3_2、x4_2分別代表圖5-27中的四列輸出,結合結構圖和代碼很容易看出二者的對應關係。
可以看到,InceptionNet的一個顯著特點是大量使用了1 * 1的卷積核,事實上,最原始的InceptionNet的結構是不包含1 * 1卷積的,如圖5-29所示。

image-20220422214021310

圖5-29 InceptionNet最原始的基本單元

由圖5-29可以更清楚地看出InceptionNet最初的設計思想,即通過不同尺寸卷積層和池化層的橫向組合(卷積、池化後的尺寸相同,通道可以相加)來拓寬網絡深度,可以增加網絡對尺寸的適應性。但是這樣也帶來一個問題,所有的卷積核都會在上一層的輸出上直接做卷積運算,會導致參數量和計算量過大(尤其是對於5 * 5的卷積核來說)。因此,InceptionNet在3 * 3、5 * 5的卷積運算前、最大池化後均加入了1 * 1的卷積層,形成了圖5-24中的結構,這樣可以降低特徵的厚度,一定程度上避免參數量過大的問題。

那麼1 * 1的卷積運算是如何降低特徵厚度的呢?下面以5 * 5的卷積運算爲例說明這個問題。假設網絡上一層的輸出爲100 * 100 * 128(H *W * C),通過32 * 5 * 5(32個大小爲5 * 5的卷積核)的卷積層(步長爲1、全零填充)後,輸出爲100 * 100 * 32,卷積層的參數量爲32 * 5 * 5 * 128 = 102400;如果先通過32 * 1 * 1的卷積層(輸出爲100 * 100 * 32),再通過32 * 5 * 5的卷積層,輸出仍爲100 * 100 * 32,但卷積層的參數量變爲32 * 1 * 1 * 128 + 32 * 5 * 5 * 32 = 29696,僅爲原參數量的30 %左右,這就是小卷積核的降維作用。

InceptionNet網絡的主體就是由其基本單元構成的,其模型結構如圖5-30所示。

image-20220422214114408

圖5-30 InceptionNet v1模型結構圖

圖中橙色框內即爲InceptionNet的基本單元,利用之前定義好的InceptionBlk類堆疊而成,模型的實現代碼如下。

image-20220422214133097

參數num_layers代表InceptionNet的Block數,每個Block由兩個基本單元構成,每經過一個Block,特徵圖尺寸變爲1/2,通道數變爲2倍;num_classes代表分類數,對於cifar10數據集來說即爲10;init_ch代表初始通道數,也即InceptionNet基本單元的初始卷積核個數。

InceptionNet網絡不再像VGGNet一樣有三層全連接層(全連接層的參數量佔VGGNet總參數量的90 %),而是採用“全局平均池化+全連接層”的方式,這減少了大量的參數。

這裏介紹一下全局平均池化,在tf.keras中用GlobalAveragePooling2D函數實現,相比於平均池化(在特徵圖上以窗口的形式滑動,取窗口內的平均值爲採樣值),全局平均池化不再以窗口滑動的形式取均值,而是直接針對特徵圖取平均值,即每個特徵圖輸出一個值。通過這種方式,每個特徵圖都與分類概率直接聯繫起來,這替代了全連接層的功能,並且不產生額外的訓練參數,減小了過擬合的可能,但需要注意的是,使用全局平均池化會導致網絡收斂的速度變慢。

總體來看,InceptionNet採取了多尺寸卷積再聚合的方式拓寬網絡結構,並通過1 * 1的卷積運算來減小參數量,取得了比較好的效果,與同年誕生的VGGNet相比,提供了卷積神經網絡構建的另一種思路。但InceptionNet的問題是,當網絡深度不斷增加時,訓練會十分困難,甚至無法收斂(這一點被ResNet很好地解決了)。

4.5 ResNet

借鑑點:

  • 層間殘差跳連,引入前方信息,減少梯度消失,使神經網絡層數變身成爲可能。

ResNet即深度殘差網絡,由何愷明及其團隊提出,是深度學習領域又一具有開創性的工作,通過對殘差結構的運用,ResNet使得訓練數百層的網絡成爲了可能,從而具有非常強大的表徵能力,其網絡結構如圖5-31所示。

Tensorflow筆記5

ResNet的核心是殘差結構,如圖5-32所示。在殘差結構中,ResNet不再讓下一層直接擬合我們想得到的底層映射,而是令其對一種殘差映射進行擬合。若期望得到的底層映射爲H(x),我們令堆疊的非線性層擬合另一個映射F(x) := H(x) – x,則原有映射變爲F(x) + x。對這種新的殘差映射進行優化時,要比優化原有的非相關映射更爲容易。不妨考慮極限情況,如果一個恆等映射是最優的,那麼將殘差向零逼近顯然會比利用大量非線性層直接進行擬合更容易。

值得一提的是,這裏的相加與InceptionNet中的相加是有本質區別的,Inception中的相加是沿深度方向疊加,像“千層蛋糕”一樣,對層數進行疊加;ResNet中的相加則是特徵圖對應元素的數值相加,類似於python語法中基本的矩陣相加。

image-20220422214544069

圖5-32 ResNet中的殘差結構

ResNet引入殘差結構最主要的目的是解決網絡層數不斷加深時導致的梯度消失問題,從之前介紹的4種CNN經典網絡結構我們也可以看出,網絡層數的發展趨勢是不斷加深的。這是由於深度網絡本身集成了低層/中層/高層特徵和分類器,以多層首尾相連的方式存在,所以可以通過增加堆疊的層數(深度)來豐富特徵的層次,以取得更好的效果。

image-20220422214602194

但如果只是簡單地堆疊更多層數,就會導致梯度消失(爆炸)問題,它從根源上導致了函數無法收斂。然而,通過標準初始化(normalized initialization)以及中間標準化層(intermediate normalization layer),已經可以較好地解決這個問題了,這使得深度爲數十層的網絡在反向傳播過程中,可以通過隨機梯度下降(SGD)的方式開始收斂。

但是,當深度更深的網絡也可以開始收斂時,網絡退化的問題就顯露了出來:隨着網絡深度的增加,準確率先是達到瓶頸(這是很常見的),然後便開始迅速下降。需要注意的是,這種退化並不是由過擬合引起的。對於一個深度比較合適的網絡來說,繼續增加層數反而會導致訓練錯誤率的提升,圖5-33就是一個例子。

image-20220422214621742

圖5-33 cifar10數據集訓練集錯誤率(左)及測試集錯誤率(右)

ResNet解決的正是這個問題,其核心思路爲:對一個準確率達到飽和的淺層網絡,在它後面加幾個恆等映射層(即y = x,輸出等於輸入),增加網絡深度的同時不增加誤差。這使得神經網絡的層數可以超越之前的約束,提高準確率。圖5-34展示了ResNet中殘差結構的具體用法。

image-20220422214645281

圖5-34 ResNet中的殘差結構

上圖中的實線和虛線均表示恆等映射,實線表示通道相同,計算方式爲H(x) = F(x) + x;虛線表示通道不同,計算方式爲H(x) = F(x) + Wx,其中W爲卷積操作,目的是調整x的維度(通道數)。

我們同樣可以藉助tf.keras來實現這種殘差結構,定義一個新的ResnetBlock類。

image-20220422214710159

卷積操作仍然採用典型的C、B、A結構,激活採用Relu函數;爲了保證F(x)和x可以順利相加,二者的維度必須相同,這裏利用的是1 * 1卷積來實現(1 * 1卷積改變輸出維度的作用在InceptionNet中有具體介紹)。
利用這種結構,就可以利用tf.keras來構建出ResNet模型,如圖5-35所示。

image-20220422214730085

圖5-35 ResNet18模型的代碼實現

參數block_list表示ResNet中block的數量;initial_filters表示初始的卷積核數量。可以看到該模型同樣使用了全局平均池化的方式來替代全連接層(關於全局平均池化的作用InceptionNet中有介紹)。
對於ResNet的殘差單元來說,除了這裏採用的兩層結構外,還有一種三層結構,如圖5-36所示。

image-20220422214753041

圖5-36 兩層/三層殘差單元

兩層殘差單元多用於層數較少的網絡,三層殘差單元多用於層數較多的網絡,以減少計算的參數量。

總體上看,ResNet取得的成果還是相當巨大的,它將網絡深度提升到了152層,於2015年將ImageNet圖像識別Top5錯誤率降至3.57 %。

4.6 總結

image-20220422214810148

對上述的5種CNN經典結構進行總結,如圖5-37所示。

image-20220422214834518

圖5-37 5種經典網絡結構的借鑑點

對於這五種網絡(加上最基本的baseline共6種),課堂上均給出了代碼實現,其測試集準確率曲線及Loss曲線如下。

Baseline3:

image-20220422205457063

LeNet5:

image-20220422205521588

AlexNet8:

image-20220422205535525

VGGNet16:

image-20220422205550579

Inception10:

image-20220422205610246

ResNet18:

image-20220422205621820

可以看到,隨着網絡複雜程度的提高,以及Relu、Dropout、BN等操作的使用,利用各個網絡訓練cifar10數據集的準確率基本上是逐步上升的。五個網絡當中,InceptionNet的訓練效果是最不理想的,首先其本身的設計理念是採用不同尺寸的卷積核,提供不同的感受野,但cifar10只是一個單一的分類任務,二者的契合度並不高,另外,由於本身結構的原因,InceptionNet的參數量和計算量都比較大,訓練需要耗費的資源比較多,所以課堂上僅僅搭建了一個深度爲10的精簡版本(完整的InceptionNet v1,即GoogLeNet有22層,訓練難度很大),主要目的是詮釋InceptionNet的思路,並非單單追求cifar10數據集的準確率。
另外,需要指出的是,在利用這些網絡訓練cifar10數據集時,課程給出的源碼並未包含其它的一些訓練技巧,例如數據增強(對訓練集圖像進行旋轉、偏移、翻轉等多種操作,目的是增強訓練集的隨機性)、學習率策略(一般的策略是在訓練過程中逐步減小學習率)、Batch size的大小設置(每個batch包含訓練集圖片的數量)、模型參數初始化的方式等等。然而實際上,一些訓練方法和超參數的設定對模型訓練結果的影響是相當顯著的,以ResNet18爲例,如果採取合適的訓練技巧,cifar10的識別準確率是足以突破90 %的。所以,在神經網絡的訓練中,除了選擇合適的模型以外,如何更好地訓練一個模型也是一個非常值得探究的問題。

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