批量歸一化
批量歸一化(batch normalization)層,它能讓較深的神經網絡的訓練變得更加容易。對圖像處理的輸入數據做了標准化處理:處理后的任意一個特征在數據集中所有樣本上的均值為0、標准差為1。標准化處理輸入數據使各個特征的分布相近:這往往更容易訓練出有效的模型。
通常來說,數據標准化預處理對於淺層模型就足夠有效了。隨着模型訓練的進行,當每層中參數更新時,靠近輸出層的輸出較難出現劇烈變化。但對深層神經網絡來說,即使輸入數據已做標准化,訓練中模型參數的更新依然很容易造成靠近輸出層輸出的劇烈變化。這種計算數值的不穩定性通常令我們難以訓練出有效的深度模型。
批量歸一化的提出正是為了應對深度模型訓練的挑戰。在模型訓練時,批量歸一化利用小批量上的均值和標准差,不斷調整神經網絡中間輸出,從而使整個神經網絡在各層的中間輸出的數值更穩定。批量歸一化和下一節將要介紹的殘差網絡為訓練和設計深度模型提供了兩類重要思路。
批量歸一化層
對全連接層和卷積層做批量歸一化的方法稍有不同。
對全連接層做批量歸一化
對卷積層做批量歸一化
對卷積層來說,批量歸一化發生在卷積計算之后、應用激活函數之前。如果卷積計算輸出多個通道,我們需要對這些通道的輸出分別做批量歸一化,且每個通道都擁有獨立的拉伸和偏移參數,並均為標量。設小批量中有m個樣本。在單個通道上,假設卷積計算輸出的高和寬分別為p和q。需要對該通道中m×p×q個元素同時做批量歸一化。對這些元素做標准化計算時,我們使用相同的均值和方差,即該通道中m×p×q個元素的均值和方差。
預測時的批量歸一化
使用批量歸一化訓練時,我們可以將批量大小設得大一點,從而使批量內樣本的均值和方差的計算都較為准確。將訓練好的模型用於預測時,我們希望模型對於任意輸入都有確定的輸出。因此,單個樣本的輸出不應取決於批量歸一化所需要的隨機小批量中的均值和方差。一種常用的方法是通過移動平均估算整個訓練數據集的樣本均值和方差,並在預測時使用它們得到確定的輸出。可見,和丟棄層一樣,批量歸一化層在訓練模式和預測模式下的計算結果也是不一樣的。
- 在模型訓練時,批量歸一化利用小批量上的均值和標准差,不斷調整神經網絡的中間輸出,從而使整個神經網絡在各層的中間輸出的數值更穩定。
- 對全連接層和卷積層做批量歸一化的方法稍有不同。
- 批量歸一化層和丟棄層一樣,在訓練模式和預測模式的計算結果是不一樣的。
- Gluon提供的BatchNorm類使用起來簡單、方便。
批量歸一化代碼實現

1 import d2lzh as d2l 2 from mxnet import autograd, gluon, init, nd 3 from mxnet.gluon import nn 4 5 net = nn.Sequential() 6 net.add(nn.Conv2D(6, kernel_size=5), 7 nn.BatchNorm(), 8 nn.Activation('sigmoid'), 9 nn.MaxPool2D(pool_size=2, strides=2), 10 nn.Conv2D(16, kernel_size=5), 11 nn.BatchNorm(), 12 nn.Activation('sigmoid'), 13 nn.MaxPool2D(pool_size=2, strides=2), 14 nn.Dense(120), 15 nn.BatchNorm(), 16 nn.Activation('sigmoid'), 17 nn.Dense(84), 18 nn.BatchNorm(), 19 nn.Activation('sigmoid'), 20 nn.Dense(10)) 21 lr, num_epochs, batch_size, ctx = 1.0, 5, 256, d2l.try_gpu() 22 23 net.initialize(ctx=ctx, init=init.Xavier()) 24 trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': lr}) 25 d2l.train_ch5(net, train_iter, test_iter, batch_size, trainer, ctx, 26 num_epochs)
殘差網絡(ResNet)
先思考一個問題:對神經網絡模型添加新的層,充分訓練后的模型是否只可能更有效地降低訓練誤差?理論上,原模型解的空間只是新模型解的空間的子空間。也就是說,如果我們能將新添加的層訓練成恆等映射f(x)=x,新模型和原模型將同樣有效。由於新模型可能得出更優的解來擬合訓練數據集,因此添加層似乎更容易降低訓練誤差。然而在實踐中,添加過多的層后訓練誤差往往不降反升。即使利用批量歸一化帶來的數值穩定性使訓練深層模型更加容易,該問題仍然存在。針對這一問題,何愷明等人提出了殘差網絡(ResNet)。它在2015年的ImageNet圖像識別挑戰賽奪魁,並深刻影響了后來的深度神經網絡的設計。
殘差塊
聚焦於神經網絡局部。下圖所示,設輸入為x。假設我們希望學出的理想映射為f(x),從而作為下圖上方激活函數的輸入。左圖虛線框中的部分需要直接擬合出該映射f(x),而右圖虛線框中的部分則需要擬合出有關恆等映射的殘差映射f(x)−x。殘差映射在實際中往往更容易優化。以本節開頭提到的恆等映射作為我們希望學出的理想映射f(x)。只需將下圖中右圖虛線框內上方的加權運算(如仿射)的權重和偏差參數學成0,那么f(x)即為恆等映射。實際中,當理想映射f(x)極接近於恆等映射時,殘差映射也易於捕捉恆等映射的細微波動。下圖右圖也是ResNet的基礎塊,即殘差塊(residual block)。在殘差塊中,輸入可通過跨層的數據線路更快地向前傳播。
ResNet沿用了VGG全3×3卷積層的設計。殘差塊里首先有2個有相同輸出通道數的3×3卷積層。每個卷積層后接一個批量歸一化層和ReLU激活函數。然后我們將輸入跳過這兩個卷積運算后直接加在最后的ReLU激活函數前。這樣的設計要求兩個卷積層的輸出與輸入形狀一樣,從而可以相加。如果想改變通道數,就需要引入一個額外的1×1卷積層來將輸入變換成需要的形狀后再做相加運算。
ResNet模型
ResNet的前兩層跟之前介紹的GoogLeNet中的一樣:在輸出通道數為64、步幅為2的7×7卷積層后接步幅為2的3×3的最大池化層。不同之處在於ResNet每個卷積層后增加的批量歸一化層。
GoogLeNet在后面接了4個由Inception塊組成的模塊。ResNet則使用4個由殘差塊組成的模塊,每個模塊使用若干個同樣輸出通道數的殘差塊。第一個模塊的通道數同輸入通道數一致。由於之前已經使用了步幅為2的最大池化層,所以無須減小高和寬。之后的每個模塊在第一個殘差塊里將上一個模塊的通道數翻倍,並將高和寬減半。接着我們為ResNet加入所有殘差塊。這里每個模塊使用兩個殘差塊。最后,與GoogLeNet一樣,加入全局平均池化層后接上全連接層輸出。每個模塊里有4個卷積層(不計算1×1卷積層),加上最開始的卷積層和最后的全連接層,共計18層。這個模型通常也被稱為ResNet-18。通過配置不同的通道數和模塊里的殘差塊數可以得到不同的ResNet模型,例如更深的含152層的ResNet-152。雖然ResNet的主體架構跟GoogLeNet的類似,但ResNet結構更簡單,修改也更方便。這些因素都導致了ResNet迅速被廣泛使用。
- 殘差塊通過跨層的數據通道從而能夠訓練出有效的深度神經網絡。
- ResNet深刻影響了后來的深度神經網絡的設計。
稠密連接網絡(DenseNet)
ResNet中的跨層連接設計引申出了數個后續工作。本節我們介紹其中的一個:稠密連接網絡(DenseNet)。 它與ResNet的主要區別如下圖所示。

上圖將部分前后相鄰的運算抽象為模塊A和模塊B。與ResNet的主要區別在於,DenseNet里模塊B的輸出不是像ResNet那樣和模塊A的輸出相加,而是在通道維上連結。這樣模塊A的輸出可以直接傳入模塊B后面的層。在這個設計里,模塊A直接跟模塊B后面的所有層連接在了一起。這也是它被稱為“稠密連接”的原因。
DenseNet的主要構建模塊是稠密塊(dense block)和過渡層(transition layer)。前者定義了輸入和輸出是如何連結的,后者則用來控制通道數,使之不過大。
稠密塊
enseNet使用了ResNet改良版的“批量歸一化、激活和卷積”結構。稠密塊由多個conv_block
組成,每塊使用相同的輸出通道數。但在前向計算時,我們將每塊的輸入和輸出在通道維上連結。
過渡層
由於每個稠密塊都會帶來通道數的增加,使用過多則會帶來過於復雜的模型。過渡層用來控制模型復雜度。它通過1×1卷積層來減小通道數,並使用步幅為2的平均池化層減半高和寬,從而進一步降低模型復雜度。
DenseNet模型
DenseNet首先使用同ResNet一樣的單卷積層和最大池化層。
類似於ResNet接下來使用的4個殘差塊,DenseNet使用的是4個稠密塊。同ResNet一樣,我們可以設置每個稠密塊使用多少個卷積層。這里我們設成4,從而與上一節的ResNet-18保持一致。稠密塊里的卷積層通道數(即增長率)設為32,所以每個稠密塊將增加128個通道。ResNet里通過步幅為2的殘差塊在每個模塊之間減小高和寬。這里我們則使用過渡層來減半高和寬,並減半通道數。同ResNet一樣,最后接上全局池化層和全連接層來輸出。
- 在跨層連接上,不同於ResNet中將輸入與輸出相加,DenseNet在通道維上連結輸入與輸出。
- DenseNet的主要構建模塊是稠密塊和過渡層。