批量歸一化(batch normalization)層,它能讓較深的神經網絡的訓練變得更加容易。對圖像處理的輸入數據作了標準化處理:處理後的任意一個特徵在數據集中全部樣本上的均值爲0、標準差爲1。標準化處理輸入數據使各個特徵的分佈相近:這每每更容易訓練出有效的模型。網絡
一般來講,數據標準化預處理對於淺層模型就足夠有效了。隨着模型訓練的進行,當每層中參數更新時,靠近輸出層的輸出較難出現劇烈變化。但對深層神經網絡來講,即便輸入數據已作標準化,訓練中模型參數的更新依然很容易形成靠近輸出層輸出的劇烈變化。這種計算數值的不穩定性一般令咱們難以訓練出有效的深度模型。架構
批量歸一化的提出正是爲了應對深度模型訓練的挑戰。在模型訓練時,批量歸一化利用小批量上的均值和標準差,不斷調整神經網絡中間輸出,從而使整個神經網絡在各層的中間輸出的數值更穩定。批量歸一化和下一節將要介紹的殘差網絡爲訓練和設計深度模型提供了兩類重要思路。ide
對全鏈接層和卷積層作批量歸一化的方法稍有不一樣。函數
對卷積層來講,批量歸一化發生在卷積計算以後、應用激活函數以前。若是卷積計算輸出多個通道,咱們須要對這些通道的輸出分別作批量歸一化,且每一個通道都擁有獨立的拉伸和偏移參數,並均爲標量。設小批量中有m個樣本。在單個通道上,假設卷積計算輸出的高和寬分別爲p和q。須要對該通道中m×p×q個元素同時作批量歸一化。對這些元素作標準化計算時,咱們使用相同的均值和方差,即該通道中m×p×q個元素的均值和方差。優化
使用批量歸一化訓練時,咱們能夠將批量大小設得大一點,從而使批量內樣本的均值和方差的計算都較爲準確。將訓練好的模型用於預測時,咱們但願模型對於任意輸入都有肯定的輸出。所以,單個樣本的輸出不該取決於批量歸一化所須要的隨機小批量中的均值和方差。一種經常使用的方法是經過移動平均估算整個訓練數據集的樣本均值和方差,並在預測時使用它們獲得肯定的輸出。可見,和丟棄層同樣,批量歸一化層在訓練模式和預測模式下的計算結果也是不同的。spa
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)
先思考一個問題:對神經網絡模型添加新的層,充分訓練後的模型是否只可能更有效地下降訓練偏差?理論上,原模型解的空間只是新模型解的空間的子空間。也就是說,若是咱們能將新添加的層訓練成恆等映射f(x)=x,新模型和原模型將一樣有效。因爲新模型可能得出更優的解來擬合訓練數據集,所以添加層彷佛更容易下降訓練偏差。然而在實踐中,添加過多的層後訓練偏差每每不降反升。即便利用批量歸一化帶來的數值穩定性使訓練深層模型更加容易,該問題仍然存在。針對這一問題,何愷明等人提出了殘差網絡(ResNet)。它在2015年的ImageNet圖像識別挑戰賽奪魁,並深入影響了後來的深度神經網絡的設計。設計
聚焦於神經網絡局部。下圖所示,設輸入爲x。假設咱們但願學出的理想映射爲f(x),從而做爲下圖上方激活函數的輸入。左圖虛線框中的部分須要直接擬合出該映射f(x),而右圖虛線框中的部分則須要擬合出有關恆等映射的殘差映射f(x)−x。殘差映射在實際中每每更容易優化。以本節開頭提到的恆等映射做爲咱們但願學出的理想映射f(x)。只需將下圖中右圖虛線框內上方的加權運算(如仿射)的權重和誤差參數學成0,那麼f(x)即爲恆等映射。實際中,當理想映射f(x)極接近於恆等映射時,殘差映射也易於捕捉恆等映射的細微波動。下圖右圖也是ResNet的基礎塊,即殘差塊(residual block)。在殘差塊中,輸入可經過跨層的數據線路更快地向前傳播。code
ResNet沿用了VGG全3×3卷積層的設計。殘差塊裏首先有2個有相同輸出通道數的3×3卷積層。每一個卷積層後接一個批量歸一化層和ReLU激活函數。而後咱們將輸入跳過這兩個卷積運算後直接加在最後的ReLU激活函數前。這樣的設計要求兩個卷積層的輸出與輸入形狀同樣,從而能夠相加。若是想改變通道數,就須要引入一個額外的1×1卷積層來將輸入變換成須要的形狀後再作相加運算。orm
ResNet的前兩層跟以前介紹的GoogLeNet中的同樣:在輸出通道數爲6四、步幅爲2的7×7卷積層後接步幅爲2的3×3的最大池化層。不一樣之處在於ResNet每一個卷積層後增長的批量歸一化層。xml
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的主要區別以下圖所示。
上圖將部分先後相鄰的運算抽象爲模塊A和模塊B。與ResNet的主要區別在於,DenseNet裏模塊B的輸出不是像ResNet那樣和模塊A的輸出相加,而是在通道維上連結。這樣模塊A的輸出能夠直接傳入模塊B後面的層。在這個設計裏,模塊A直接跟模塊B後面的全部層鏈接在了一塊兒。這也是它被稱爲「稠密鏈接」的緣由。
DenseNet的主要構建模塊是稠密塊(dense block)和過渡層(transition layer)。前者定義了輸入和輸出是如何連結的,後者則用來控制通道數,使之不過大。
enseNet使用了ResNet改良版的「批量歸一化、激活和卷積」結構。稠密塊由多個conv_block
組成,每塊使用相同的輸出通道數。但在前向計算時,咱們將每塊的輸入和輸出在通道維上連結。
因爲每一個稠密塊都會帶來通道數的增長,使用過多則會帶來過於複雜的模型。過渡層用來控制模型複雜度。它經過1×1卷積層來減少通道數,並使用步幅爲2的平均池化層減半高和寬,從而進一步下降模型複雜度。
DenseNet首先使用同ResNet同樣的單卷積層和最大池化層。
相似於ResNet接下來使用的4個殘差塊,DenseNet使用的是4個稠密塊。同ResNet同樣,咱們能夠設置每一個稠密塊使用多少個卷積層。這裏咱們設成4,從而與上一節的ResNet-18保持一致。稠密塊裏的卷積層通道數(即增加率)設爲32,因此每一個稠密塊將增長128個通道。ResNet裏經過步幅爲2的殘差塊在每一個模塊之間減少高和寬。這裏咱們則使用過渡層來減半高和寬,並減半通道數。同ResNet同樣,最後接上全局池化層和全鏈接層來輸出。