李宏毅深度學習2021春p5-9:神經網路訓練技巧
兄弟萌也可以到我的博客康康:冬于的博客
訓練遇到的問題
- 引數不斷的更新,training loss一開始下降,然后不會再下降,但距離0還有很遠的gap;
- 一開始model就train不起來,不管怎么update引數,loss一直比較大,

導致上述問題的原因可能有很多,我們先回憶一下梯度下降演算法在現實世界中面臨的挑戰:
- 問題1:區域最優(Stuck at local minima)
- 問題2:等于0(Stuck at saddle point)
- 問題3:趨近于0(Very slow at the plateau)

像這種gradient為0的點,統稱critical point,我們先從問題1和問題2來看看如何“煉丹”,
區域最小值local minima和鞍點saddle point
Critical Point
gradient為0的點,

local minima
**現在所在的位置已經是區域loss最低的點,**往四周走 loss都會比較高,可能沒有路可以走,
saddle point
**saddle point從某個方向還是有可能到達loss更低的位置,**只要逃離saddle point,就有可能讓loss更低,
如何判斷某個位置是local minima還是saddle point?
通過泰勒級數展開估計(Tayler Series Approximation)loss function的形狀,
也就是,雖然無法完整、準確寫出 L ( θ ) L(\theta) L(θ),但如果給定某一組引數 θ ′ \theta' θ′,在 θ ′ \theta' θ′附近的loss function可以通過泰勒級數展開來估計:

- 第一項 L ( θ ′ ) L(\theta') L(θ′):當 θ \theta θ跟 θ ′ \theta' θ′很近的時候, L ( θ ) L(\theta) L(θ)跟 L ( θ ′ ) L(\theta') L(θ′)比較靠近,但還有一些差距;
- 第二項 ( θ ? θ ′ ) T g (\theta-\theta')^Tg (θ?θ′)Tg:是一個向量,這個 g g g是gradient,這個gradient會來彌補 θ ′ \theta' θ′跟 θ \theta θ之間的差距,有時候gradient會寫成 ? L ( θ ′ ) \nabla L(\theta') ?L(θ′),它的第 i i i個component,就是 θ θ θ的第 i i i個component對 L L L的微分,加上這一項之后仍然還有差距;
- 第三項中 ( θ ? θ ′ ) T H ( θ ? θ ′ ) (\theta-\theta')^TH(\theta-\theta') (θ?θ′)TH(θ?θ′):其中 H H H跟Hessian有關,是一個矩陣,第三項會再補足與真正的L(θ)之間的差距, H H H是L的二次微分構成的矩陣,它第 i i i個row,第 j j j個column的值 H i j H_{ij} Hij?,是把 θ θ θ的第 i i i個component,對 L L L作微分,再把 θ θ θ的第 j j j個component,對 L L L作微分,也就是做兩次微分以后的結果 ,
總的來說, L ( θ ) L(\theta) L(θ)跟兩個東西有關,跟gradient有關,跟hessian有關,gradient就是一次微分,hessian是內含二次微分的專案,
如果我們今天走到了一個critical point,意味著上式中 g = 0 g=0 g=0,只剩下 L ( θ ′ ) L(\theta') L(θ′)和紅色的這一項:

于是可以通過紅色這一項判斷 θ ′ \theta' θ′附近的error surface長什么樣,從而判斷現在是在local minima、local max還是saddle point,
通過Hession矩陣判斷 θ ′ \theta' θ′附近的error surface
把 ( θ ? θ ′ ) (\theta-\theta') (θ?θ′)用向量 v v v來表示,根據 v T H v v^THv vTHv的值來判斷:

線性代數中,如果所有的 v v v帶入 v T H v v^THv vTHv的值都大於零,那 H H H叫做positive definite 正定矩陣,所以我們不需要通過窮舉所有的點來判斷 v T H v v^THv vTHv是大于零還是小于零,而是直接利用 H H H是否正定來判斷,而判斷 H H H是否是正定矩陣可以通過求解 H H H的特征值來判斷,如果所有的eigen value特征值都是正的,那么 H H H就是positive definite 正定矩陣,
所以判斷條件就轉化為:

如何逃離saddle point?
** H H H不只可以幫助我們判斷,現在是不是在一個saddle point,還指出了引數可以update的方向,**注意這個時候 g = 0 g=0 g=0,
根據 λ x = A x \lambda x=Ax λx=Ax,可以對式子進行轉化:

于是如果 λ < 0 λ<0 λ<0(eigen value<0),那 λ ‖ u ‖ 2 < 0 λ‖u‖2<0 λ‖u‖2<0,所以eigen value是負的,那這一整項就會是負的,也就是 u T H u u^THu uTHu是負的,也就是紅色整項是負的,于是 L ( θ ) < L ( θ ′ ) L(\theta)<L(\theta') L(θ)<L(θ′),也就是說令 θ ? θ ′ = μ \theta-\theta'=\mu θ?θ′=μ,在 θ ′ θ' θ′的位置加上 μ \mu μ,沿 μ \mu μ的方向做update得到 θ θ θ,就可以讓loss變小,

這個方法也有一點問題: H H H的運算量非常非常的大,還需要算其特征值和特征向量,運算量驚人,實際操作中還有其他方法可以逃離saddle point,在最糟糕的情況下還有這種方法可以逃離,
Saddle Point v.s. Local Minima
**事實上Local Minima沒有那么常見,**一個可能的解釋是:在低維的空間中,低維的一個引數的error surface,好像到處都是local minima,但是在高維空間來看,它可能只是一個saddle point,
如下圖所示,幾乎找不到完全所有eigen value都是正的critical point,下圖這個例子種,minimum ratio代表正的eigen value的數目占總數的比例,最大也在0.5~0.6,代表只有一半的eigen value是正的,還有一半的eigen value是負的,
在這個圖上,越往右代表critical point越像local minima,但是它們都沒有真的,變成local minima,

batch/mini-batch和動量Momentum
batch/mini-batch
Optimization with Batch
每次在 Update 引數的時候,拿一個batch出來,算個 Loss,算個 Gradient,Update 引數,然后再拿另外個batch,再算個 Loss,算gradient,更新引數,以此類推,
mini-batch就是不把所有訓練資料拿出來一起算loss,而是分小塊,
所有的 Batch 訓練過一遍,叫做一個 Epoch,
在生成batch的時候長春會做shuffle,
Shuffle 有很多不同的做法,常見的一個做法是在每一個 Epoch 開始之前,會分一次 Batch,每一個 Epoch 的 Batch 都不一樣,

為什么要batch
直接上對比圖:

之前提到,更大的batch在看完更多的example后才會更新一次引數,但更新方向可能比小batch更準確(powerful,而小batch可能更noisy)如果都是串行的話,看上去更大的batch的引數更新速度更慢,

但實際上,在有并行計算條件下,比較大的 Batch Size算 Loss再進而算 Gradient所需要的時間,不一定比小的 Batch Size 要花的時間長,而更小的batch一個epoch的時間更長,(一個用于MINIST資料集上的實驗如下圖,)

所以看上去big batch的時間劣勢消失了,那是不是big batch更好呢?

答案是否定的,神奇的地方是 Noisy 的 Gradient,反而可以幫助 Training,
同一個model,batchsize過大效果反而更差?
用過大的batch size optimizer可能會有問題,
拿不同的 Batch 來訓練模型,可能會得到下圖的結果:

Batch Size 越大,Validation Acc 上的結果越差,但這個不是 Overfitting,因為如果 Training也是 Batch Size 越大,Training 的結果越差,
現在用的是同一個模型,它們可以表示的 Function 就是一模一樣的,所以這個不是 Model Bias 的問題,這個是 Optimization 的問題,代表當用大的 Batch Size 的時候, Optimization 可能會有問題,小的 Batch Size,Optimization 的結果反而是比較好的,
為什么小的 Batch Size在 Training Set 上會得到比較好的結果?
一個可能的解釋是這樣子的,如下圖所示:
- 假設是 Full Batch,沿著一個 Loss Function 來 Update 引數,走到一個 Local Minima/saddle Point,顯然就停下來了,Gradient 是零,可能就沒辦法更新引數了;
- 假如是 Small Batch 的話,因為**每次挑一個 Batch 出來算它的 Loss,所以每一次 Update 你的引數的時候,用的 Loss Function 都是略有差異的,**選到第一個 Batch 的時候,,是用 L1 來算 Gradient;選到第二個 Batch 的時候,用 L2 來算Gradient,**假設用 L1 算 Gradient 的時候,發現 Gradient 是零,卡住了,但 L2 它的 Function 和 L1 又不一樣,L2 就不一定會卡住,**所以在某一個batch卡住了沒關系,還是有辦法 Training Model,還是有辦法讓 Loss 變小,所以mini-batch這種 Noisy 的 Update 的方式,結果反而對 Training是有幫助的,

另外一個神奇的事:小的batch在testing也有幫助
論文On Large-Batch Training For Deep Learning,Generalization Gap And Sharp Minima中,就做了這樣一個實驗:努力的調big- Batch 的 Learning Rate,然后想辦法把big-Batch的訓練模型,跟small-Batch 訓練得得一樣好,結果發現small-Batch在 Testing 的時候是比較好的,
注意這個時候big-batch在訓練集中表現和small-batch性能相似,但在測驗集表現更差說明的問題才是over fitting,

為什么會這樣呢?文章也給了一個解釋:

假設這個實線是 Training Loss,那在這個 Training Loss 上面,可能有很多個 Local Minima,這些 Local Minima 它們的 Loss 都很低,它們 Loss 可能都趨近于 0,但是 Local Minima還是有好 Minima 跟壞 Minima 之分,
- 好的Local Minima:在一個平原上(左邊的);
- 壞的Local Minima:在一個峽谷里面(右邊的),
為什么這么判定呢?
假設現在 Training 跟 Testing 中間,有一個 Mismatch,Training 的 Loss 跟 Testing 的 Loss,它們的Function 不一樣,導致這種mismatch的原因可能有兩個:
- 可能是本來 Training 跟 Testing 的 Distribution就不一樣;
- 那也有可能是因為 Training 跟 Testing都是從 Sample 的 Data 算出來的,也許 Training 跟 Testing Sample(采樣)到的 Data 不一樣,那它們算出來的 Loss,自然有一點差距,
那我們就假設這個 Training 跟 Testing的差距就是把 Training 的 Loss,這個 Function 往右平移一點(上圖虛線為testing loss),這時候會發現,對左邊這個在一個盆地的 Minima 來說,它的在 Training 跟 Testing 上面的結果,不會差太多;但是對右邊這個在峽谷的 Minima 來說,就差別很大,
它在這個 Training Set 上算出來的 Loss 很低,但是因為 Training 跟 Testing 之間的mismatch,所以 Testing 的時候,這個 Error Surface 變化后,算出來的 Loss 就變得很大,所以猜想這個大的 Batch Size,會讓模型傾向于走到峽谷里面,而小的 Batch Size,傾向于讓模型走到盆地裡面:
- 因為小的 Batch有很多的 Loss,每次 Update 的方向都不太一樣,所以如果這個峽谷非常地窄,可能一個不小心就跳出去了,因為每次 Update 的方向都不太一樣,它的 Update 的方向也就隨機性,所以一個很小的峽谷,沒有辦法困住小的 Batch,如果峽谷很小,它可能動一下就跳出去,之后如果有一個非常寬的盆地,它才會停下來;
- 而對于大的 Batch Size,就是順著規定 Update,它就很有可能,走到一個比較小的峽谷里面,
但這只是一個解釋,實際上這個還是一個尚待研究的問題,
總結batch/mini-batch
總的來說,大的 Batch 跟小的 Batch,它們各自有它們擅長的地方,**Batch Size,變成另外一個需要去調整的 Hyperparameter,**一些論文也討論了如何兼顧魚和熊掌,需要用一些特殊的方法來解決大的Batch Size 可能會帶來的劣勢:

Momentum
Momentum是另外一個有可能可以對抗 Saddle Point,或 Local Minima 的技術,
傳統的梯度下降

Gradient Descent + Momentum
加上 Momentum 以后,每一次在移動引數的時候,不是只往 Gradient 的反方向來移動引數,是 Gradient 的反方向+前一步移動的方向去調整去到引數

具體來看步驟如下:把藍色的虛線加紅色的虛線,前一步指示的方向跟 Gradient 指示的方向,當做引數下一步要移動的方向,


下面有一個例子來展示:
在第三步,Gradient 變得很小,但是沒關系,如果有 Momentum 的話,根據上一步的Momentum 可以繼續往前走;
甚至走到第四步時,Gradient 表示應該要往左走了,但是如果前一步的影響力,比 Gradient 要大的話,還是有可能繼續往右走,甚至翻過一個小丘,可能可以走到更好 Local Minima,

自動調整學習率Adaptive Learning Rate
critical point不一定是訓練程序中最大的阻礙,思考一個問題:
loss不再下降的時候,gradient真的很小嗎?
并不是的,如下圖所示雖然loss不再下降,但是這個gradient的大小并沒有真的變得很小,它可能在error surface山谷的兩個谷壁間,不斷的來回的震蕩,

所以有的時候訓練不下去的原因不是critical point,而是其他的原因,
舉個例子:
在下面這個error surface中,縱軸的引數變化很小就會導致結果變化很大,而橫軸引數變化很大結果變化很小,
如果兩個引數使用同一個學習率,當學習率調的過大,在縱軸方向可能直接震蕩:

如果學習率調的很小,在縱軸可以逐漸找到好的引數,但是在橫軸更新時,由于學習率過小,更新速度太慢,也很難找到最優解,

所以可以考慮為不同的引數和不同的iteration設定不同的learning rate,
上面的案例展示了學習率選擇的兩個大原則:
- 如果在某一個方向上,gradient的值很小,非常的平坦,那learning rate調大一點;
- 如果在某一個方向上非常的陡峭,坡度很大,那其實learning rate可以設得小一點;

learning rate自動調整策略:為不同的引數和不同的iteration設定不同的learning rate
以一個引數 θ i \theta_i θi?為例:
原始策略:
θ
i
t
+
1
←
θ
i
t
?
η
g
i
t
\theta_i^{t+1}\leftarrow \theta_i^{t}-\eta g_i^t
θit+1?←θit??ηgit?
不同的引數和不同的iteration自動調整策略:
θ
i
t
+
1
←
θ
i
t
?
η
σ
t
t
g
i
t
\theta_i^{t+1}\leftarrow \theta_i^{t}-\frac{\eta}{\sigma_t^t} g_i^t
θit+1?←θit??σtt?η?git?
上式中
σ
i
t
\sigma_i^t
σit?的下標
i
i
i代表它是depend on
i
i
i的,也就是不同引數有不同的
σ
\sigma
σ,而上標
t
t
t表示它是iteration dependent的,不同的iteration會有不同的σ,因此
η
σ
t
t
\frac{\eta}{\sigma_t^t}
σtt?η?變成parameter dependent的learning rate,
那么如何計算 σ \sigma σ呢?
常見的計算 σ σ σ可以用gradient的Root Mean Square,這種計算方法也被用于Adagrad這一優化器中,
用于Adagrad中的Root mean square
Root mean square計算 σ σ σ是通過求歷史梯度的均方根得到的,計算步驟如下圖所示:

為什么Root mean square可以做到坡度比較大的時候,learning rate就減小,坡度比較小的時候,learning rate就放大呢?
看看下面這個例子:

現在我們有兩個引數: θ ? 1 θ?1 θ?1和 θ ? 2 θ?2 θ?2 , θ ? 1 θ?1 θ?1坡度小 θ ? 2 θ?2 θ?2坡度大,
θ ? 1 θ?1 θ?1因為坡度小,所以在 θ ? 1 θ?1 θ?1這個引數上面,算出來的gradient值都比較小,那么 σ σ σ也比較小, η σ \frac{\eta}{\sigma} ση?就比較大,
所以有了 σ σ σ這一項以后,就可以隨iteration gradient的不同,每一個引數的gradient的不同,來自動的調整learning rate的大小,
但是,就算是同一個引數,它需要的learning rate,也可能會隨時間而改變,于是還有進階版的策略自適應learning rate,
RMSProp
同一個引數同一個方向也希望可以自適應調整,比如下面這個新月形的error surface,
在水平方向(同一引數同一方向)在綠色箭頭這個地方坡度比較陡峭,需要比較小的learning rate,走到了中間這一段,到了紅色箭頭坡度又變得平滑,需要比較大的learning rate,

RMSProp和Apagrad在計算 σ \sigma σ時,第一步時一樣的,在第二步更新時,通過一個超引數 α \alpha α賦予了歷史梯度和當前梯度不一樣的權重(而Apagrad中是一樣的權重,也就是每一個gradient同樣重要),
計算程序如下圖所示:

α就像learning rate一樣是一個hyperparameter,需要自己調整:
- 如果α設很小趨近于0,就代表我覺得** g ? 1 g?1 g?1相較于之前所算出來的gradient而言,比較重要**;
- α設很大趨近于1,那就代表現在算出來的 g ? 1 g?1 g?1比較不重要,之前算出來的gradient比較重要,
舉個例子看一下RMSProp的效果:
下圖中,一開始梯度很小,學習率較大;
到第三步時,梯度變大,原來的Adagrad反應比較慢,可能還會用比較大的學習率,但如果用RMS Prop,把α設小一點,也就是讓新的gradient影響比較大,可以很快的讓σ的值變大,于是很快的讓學習率變小,

還記得前面提到了momentum這個方法,RMSProp+momentum就得到了一個高級版的策略:Adam,
Adam論文鏈接

tips
這些優化器在pytorch等框架中都寫好了,其中也包括很多超引數,李宏毅老師還提到往往用默認的超引數就夠好了,自己調有時候反而會調到比較差的,煉丹玄學~
learning rate scheduling
現在讓我們回到前面提到的error surface上,加上Adagrad方法后,訓練效果如下圖右下角的子圖所示:

加了Adagrad以后,在橫軸方向learning rate會自動變大,從而能盡快接近最優解,但我們可以看到一個奇怪的現象,走到非常接近終點的位置,梯度突然爆炸了,這是為什么呢?
從Adagrad的 σ \sigma σ計算方式可以回答這個問題,計算 σ σ σ時,把過去所有看到的gradient,都拿來作平均:
-
所以這個縱軸的方向,在初始的時候gradient很大,學習率比較小;
-
可是繼續走了很長一段路以后,gradient算出來都很小,于是y軸的方向就開始積累很小的σ;
-
累積到一個地步以后,學習率就變很大,于是發生爆炸;
-
爆炸后其實也沒關系,因為爆炸后就走到gradient比較大的地方,于是σ又慢慢的變大,引數update學習率又慢慢的變小,
-
但是累計一段時間又會爆炸,然后恢復,反復,
解決這種問題的策略是learning rate scheduling,這里介紹兩種:
- Learning Rate Decay(學習率衰減策略)
- warm up
Learning Rate Decay(學習率衰減策略)
在前面使用的Adagrad中, η \eta η被當作一個固定的值,而learning rate scheduling是指讓 η \eta η和時間有關,常見的策略為:Learning Rate Decay(學習率衰減策略),隨著訓練不斷進行,引數不斷更新, η \eta η越來越小,
于是:一開始距離終點很遠,隨著引數不斷update,距離終點越來越近,把learning rate減小,讓引數更新減速,所以前面的那個問題,加上Learning Rate Decay可以解決,

warm up
Warm Up的方法是讓learning rate,要先變大后變小,**其中變大到多大、變大速度、變小到多小、變小速度都是超引數,**如下圖所示:

但是為什么需要Warm Up,為什么Warm Up有效都是待研究的問題,但在很多文獻中,它就是work了,比如BERT、transformer等等模型中都用到了warm up這個技術,
李宏毅老師提到了一個可能的解釋:
? 當我們在用Adam RMS Prop,或Adagrad的時候,需要計算σ,它是一個統計的結果,σ告訴我們,某一個方向它到底有多陡,或者是多平滑,這個統計的結果,要看得夠多筆資料以后才比較精確,所以一開始我們的統計是不精確的,于是我們一開始不要讓引數走離初始的地方太遠,先讓它在初始的做一些探索,所以一開始learning rate比較小,是讓它探索 收集一些有關error surface的情報,先收集有關σ的統計資料,等σ統計得比較精準以后,在讓learning rate呢慢慢地增大,
自動調整學習率方法總結
我們逐步將原始梯度下降方法進行優化:
- 加入momentum;
- 加入自適應iteration和引數的 σ \sigma σ;
- 使用自適應iteration的 η \eta η;
注意:momentum和 σ σ σ雖然都與過去所有的gradient有關,一個放在分母,一個放在分子,但是momentum考慮了方向,而 σ σ σ只考慮了gradient的大小,

另一個角度-鏟平error surface?
之前考慮的常見都是假設error surface非常崎嶇情況下怎么找到比較好的引數,但其實我們可以從error surface出發,考慮如何把error surface變成一個比較平坦的,好訓練的error surface,如下圖所示:

通過修改損失函式和batch normalization可能可以做到修改error surface,讓模型更好訓練,
損失函式loss function
在講分類模型的時候,李宏毅老師解釋了為什么cross entropy更常用在分類上 ,并且用一個例子展示了loss function對error surface的影響,
我們這里直接看這個例子:
現在我們用下面這個模型做一個3個Class的分類任務,下面兩個坐標系中的圖是當loss function分別設定為Mean Square Error和Cross-entropy的時候,算出來的Error surface( y ? 、 y ? y?、y? y?、y?的變化對loss的影響),在紅色部分(左上角)loss很大,藍紫色部分(右下角)loss比較小,所以我們希望在訓練后,引數走到右下角的地方,

假設我們開始的地方,都是左上角:

- 如果loss function 是Cross-Entropy,那么error surface的左上角這個地方是有斜率的,所以可以通過gradient descent一路往右下的地方走;
- 如果loss function 是Mean square error的話,Mean square error在左上角這種Loss很大的地方,是非常平坦的,也就是說它的gradient是非常小趨近于0的,如果初始的時候在這個地方,離目標非常遠,它gradient又很小,就會沒有辦法用gradient descent順利的走到右下角的地方去(當然用Adam還是有可能可以成功訓練起來的,不過訓練起步比較慢,訓練更困難),
總結loss function
由此例可以發現,Loss function的定義也可能影響error surface從而影響Training,選一個合適的loss function可以改變optimization的難度,
這里補充一個資料,對于一些常見任務應該選擇什么樣的loss function進行了總結,深度學習中常見的激活函式與損失函式的選擇與介紹
batch normalization
Batch Normalization是修改error surface,讓模型更好訓練的其中一個方法,
比如現在有兩個引數,它們對 Loss 的斜率差別非常大,在 w 1 w_1 w1?這個方向上面斜率變化很小,在 w 2 w_2 w2?這個方向上斜率變化很大,如下圖左邊所示,
之前提到用daptive 的 learning rate,比如Adam 等等比較進階的 optimization 的方法去更新引數,其實另一個角度就是**把這種很難訓練的error surface改掉,**改成下圖中右邊所示,
于是我們需要思考:斜率差很多的這種狀況,到底是哪里來的?

假設我們的模型上圖中下半部分的簡單模型:
- 輸入的 x 1 x_1 x1?很小, w 1 w_1 w1?有小小的改變,對輸出 y y y的影響比較小,對 L L L的影響也小;
- 如果 x 2 x_2 x2?取值很大, w 2 w_2 w2?變化很小也會造成 y y y很大的變化,于是對 L L L的影響也大;
- 所以如果 x 1 , x 2 x_1,x_2 x1?,x2?的取值范圍差別過大(每一個 dimension 的值scale 差距很大)會導致不同方向斜率差別很大,
因此,**希望給不同的 dimension,同樣的數值范圍(轉為上圖右邊部分),將原本的error surface變成比較好訓練的error surface,**可以完成這個操作的方法統稱為feature normalization,
feature normalization
feature normalization可以讓Loss 收斂更快一點,讓梯度下降更順利一點,
下面舉一個常用的feature normalization方法:利用均值和方差進行標準化standardization,
假設 x 1 x^1 x1到 x R x^R xR是所有的訓練資料的 feature vector,把所有訓練資料的 feature vector ,統統都集合起來, x 1 1 x_1^1 x11?代表 x 1 x_1 x1?的第一個 element, x 1 2 x_1^2 x12?就代表 x 2 x_2 x2?的第一個 element,以此類推,
把不同樣本即不同 feature vector,同一個 dimension 里面的數值計算均值mean m i m_i mi?,并且計算第 i i i 個 dimension 的標準差standard deviation σ i \sigma_i σi?,
然后用下式進行標準化standardization,然后用標準化后的
x
~
i
r
\tilde x_i^r
x~ir?作為模型的輸入,
x
~
i
r
←
x
i
r
?
m
i
σ
i
\tilde x_i^r \leftarrow \frac{x_i^r-m_i}{\sigma_i}
x~ir?←σi?xir??mi??
示意圖如下:

做完 normalize 以后,這個 dimension 上面的數值平均是 0, variance是 1,所以一排數值的分布就都會在 0 上下,對每一個 dimension都做一樣的 normalization,就會發現所有 feature 不同 dimension 的數值都在 0 上下,那可能就可以構造比較好的 error surface,
feature normalization for deep learning
我們先用feature normalization對最原始的輸入做了處理得到 x ~ \tilde x x~,那么經過第一層引數時,我們的輸入各個維度的scale就是差不多的,但是深度學習的模型都是有很多層的,像下圖這樣**:上一層的輸出是下一層的輸入,那么經過了一層神經網路的特征(輸出 z 1 z^1 z1或經過sigmoid層的輸出 a 1 a^1 a1)各個維度可能又會有不同的scale,**

所以我們可以對 z z z或者 a a a做normalization,這里也有一個tips:
- 一般來說,這個 normalization,要放在 activation function 之前,或之后都是可以的,在實操上,可能沒有太大的差別,
- 不過,如果選擇的是 Sigmoid作為激活函式,那可能比較推薦對 z z z 做 Feature Normalization因為Sigmoid 是一個 s 的形狀,它在 0 附近斜率比較大,所以如果對$ z$ 做 Feature Normalization,把所有的值都挪到 0 附近,算 gradient 的時候值會比較大,
- 因為激活函式不一定是選 sigmoid,所以也不一定要對 z z z做 Feature Normalization,如果是選別的激活函式,也許對 a a a做normalization可能會有好的結果,
我們這邊假設對 z z z做feature normalization,舉個例子:
和之前對 x x x做的操作類似,對 z z z也是計算均值和方差,然后進行歸一化:

這里有個地方需要特別注意:
上圖中的 μ \mu μ跟 σ \sigma σ ,它們其實都是根據 z 1 , z 2 , z 3 z^1,z^2,z^3 z1,z2,z3算出來的,那么:
- 如果沒有做feature normalization,因為 x ~ \tilde x x~是處理后單獨輸入的,修改 z 1 z^1 z1只會影響 z ~ 1 \tilde z^1 z~1和 a 1 a^1 a1;
- 做了feature normalization后,修改 z 1 z^1 z1會影響 μ \mu μ和 σ \sigma σ,進而不僅影響 z ~ 1 \tilde z^1 z~1和 a 1 a^1 a1,還會影響 z ~ 2 \tilde z^2 z~2和 a 2 a^2 a2、 z ~ 3 \tilde z^3 z~3和 a 3 a^3 a3;
- 于是這三個 example,它們變得彼此關聯了,如下圖所示:

因為彼此關聯,于是整個process(包括根據feature算均值方差)就變成了network的一部分,所以現在變成一個比較大的network,如下圖所示,
- 之前的 network,都只輸入一個 input,得到一個 output;
- 現在有一個比較大的 network是輸入一堆 input,用這堆 input 在這個 network 里面,要算出均值和方差,然后輸出一堆 output,

自然而然地產生一個問題,是不能一次性把很大的一個訓練集全部輸入網路中訓練的,因為記憶體有限,
再自然而然地就可以考慮不輸入全部的資料,而是輸入一部分(batch),每次只考慮一個batch里的樣本,所以在實際操作中,是對一個batch做normalization,于是這個方法叫batch normalization,
normalization in each batch - batch normalization
顯然一定要有一個夠大的 batch,才算得出均值和方差,假設batch size=1,那么均值和方差就沒啥好算的,
所以Batch Normalization適用于 batch size 比較大的時候,相當于我們用一個batch size的訓練資料分布估計整個資料集的資料分布,如果 batch size 比較大,也許這個 batch size 里的 data足以表示整個 corpus 的分布,這個時候就可以把對整個 corpus做 Feature Normalization改成只在一個 batch做 Feature Normalization,
進階的batch normalization
在做 Batch Normalization 的時候,往往還會對計算出來的
z
~
\tilde z
z~進行下一步操作,得到最終的
z
^
\hat z
z^:
z
^
i
=
γ
⊙
z
~
i
+
β
\hat z^i=\gamma \odot \tilde z^i+\beta
z^i=γ⊙z~i+β
其中
γ
\gamma
γ和
β
\beta
β是 network 的引數,在訓練程序中被學習到的,如下圖所示:

為什么要加上$\beta 和 和 和\gamma$?
有一種猜測是,做 normalization 以后feature的平均就一定是 0,這可能會給 network 一些限制,也許這個限制會帶來什麼負面的影響,所以在訓練的時候,將$\beta 和 和 和\gamma$作為訓練引數,重新調整其分布,讓它的 hidden layer 的 output平均不是 0 ,
如果是這樣,那可能又會問:上一步做Batch Normalization 就是要讓每一個不同的 dimension的 range 都是一樣的,現在如果做這一步,不是又讓 dimension 的分布不一樣了嗎?
答案是:確實有可能,以我們**在初始的時候,將$\beta 內 的 元 素 都 設 置 為 1 , 內的元素都設定為 1, 內的元素都設置為1,\gamma 內 的 元 素 都 設 置 為 0 , ? ? 于 是 n e t w o r k 在 一 開 始 訓 練 的 時 候 , 每 一 個 d i m e n s i o n 的 分 布 , 是 比 較 接 近 的 , 也 許 訓 練 到 后 來 , 已 經 找 到 一 個 比 較 好 的 e r r o r s u r f a c e , 走 到 一 個 比 較 好 的 地 方 以 后 , 再 慢 慢 調 整 內的元素都設定為0,**于是 network 在一開始訓練的時候,每一個 dimension 的分布,是比較接近的,也許訓練到后來,已經找到一個比較好的 error surface,走到一個比較好的地方以后,再慢慢調整 內的元素都設置為0,??于是network在一開始訓練的時候,每一個dimension的分布,是比較接近的,也許訓練到后來,已經找到一個比較好的errorsurface,走到一個比較好的地方以后,再慢慢調整\beta 和 和 和\gamma$,
batch normalization for testing/inference
之前說的都是training的時候要做什么,在testing(inference)的時候,想用batch normalization會有什么問題呢?
batch normalization需要一個batch的資料計算 μ \mu μ和 σ \sigma σ,在testing的時候,首先遇到的問題是,可能輸入不夠一個batch size,因為在工程上不可能等資料攢到一個batch size才開始計算輸出,

也就是可能根本就不是一個batch的輸入,應該怎么獲得 μ \mu μ和 γ \gamma γ呢?
在實際操作中,如果是PyTorch 的話,Batch Normalization 在 testing 的時候,其使用的 μ \mu μ和 γ \gamma γ是通過:如果training的時候有做 Batch Normalization 的話,在 training 的時候,每一個 batch 計算出來的 μ \mu μ和 σ \sigma σ 都計算moving average,然后得到平均值( μ ˉ \bar \mu μˉ?和 σ ˉ \bar \sigma σˉ),將這兩個值作為testing的 μ \mu μ和 σ \sigma σ,如下圖所示,
moving average的計算程序是:每一次取一個 batch 出來的時候,就會算一個 μ 1 \mu^1 μ1,取第二個 batch 出來的時候算 μ 2 \mu^2 μ2 ,一直到取第 t 個 batch 出來的時候算 μ t \mu^t μt,利用 μ 1 . . . μ t ? 1 \mu^1...\mu^{t-1} μ1...μt?1算一個平均值得到 μ ˉ \bar \mu μˉ?然后結合一個超引數 p p p更新 μ ˉ \bar\mu μˉ?,

看看加了batch normalization的訓練效果:橫軸是訓練程序,縱軸是在驗證集上的精度,
- 訓練速度變快,收斂速度變快;
- 更好訓練(sigmoid比較難訓練,但是加入batch normalization后成功訓練起來了);
- 如果做 Batch Normalization 的話,error surface 會比較平滑 比較容易訓練,所以可以把 learning rate 設大一點,(但是這里有個問題是:learning rate 設 30 倍的時候比 5 倍差,作者也沒有解釋為什么),

internal covariate shift
在原始的 Batch Normalization那篇 paper 作者提出來一個概念,叫做 internal covariate shift,(友情先提醒這個猜測已經被推翻了)
其中covariate shift是本身存在的概念:訓練集和預測集樣本分布不一致的問題就叫做“covariate shift”現象,
internal covariate shift指的是:當我們在計算 B,update 到 B′ 的 gradient 的時候,這個時候前一層的引數是 A (或者說是前一層的 output 是a),那當前一層從 A 變成 A′ 的時候,它的 output 就從 a 變成 a′ ,但是我們計算這個 gradient 的時候,我們是根據這個 a 算出來的,所以這個 update 的方向,也許它適合用在 a 上,但不適合用在 a′ 上面,
所以如果每次都有做 normalization,可能就會讓 a 跟 a′ 的分布比較接近,也許這樣就會對訓練有幫助,
如下圖所示:

但是!
有一篇 paperHow Does Batch Normalization,Help Optimization就推翻了internal covariate shift 的這一個觀點,
作者從各種各樣的角度說明internal covariate shift它不一定是 training network 的時候的一個問題,且Batch Normalization會比較好不一定是因為解決了 internal covariate shift問題,
作者通過比較訓練時a 分布的變化發現:
- 不管有沒有做 Batch Normalization,它的變化都不大;
- 就算是變化很大,對 training 也沒有太大的傷害;
- 不管你是根據 a 算出來的 gradient,還是根據 a′ 算出來的 gradient,方向都差不多,
這篇 How Does Batch Normalization,Help Optimization 論文中從實驗上、也從理論上至少支持了 Batch Normalization,可以改變 error surface,讓 error surface 比較不崎嶇這個觀點,并且說:如果我們要讓 network的error surface 變得比較不崎嶇,不一定要做 Batch Normalization,還有很多其他的方法(作者也試了一些其他的方法,見原文實驗),batch normalization只是一個serendipitous(意料之外的發現),恰好work了而已,
其他normalization的方法
主要包括以下幾種方法:BatchNorm(2015年)、LayerNorm(2016年)、InstanceNorm(2016年)、GroupNorm(2018年),這幾個方法在之前總結Transformer時簡單總結過了,可以參考:Transformer相關——(6)Normalization方式

normalization方法和paper原文如下:
Batch Renormalization
Layer Normalization
Instance Normalization
Group Normalization
Weight Normalization
Spectrum Normalization
參考資料
[李宏毅深度學習2021春季筆記](https://unclestrong.github.io/DeepLearning_LHY21_Notes/Notes_html/05_Batch and Momentum.html)
(強推)李宏毅2021春機器學習課程
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/302424.html
標籤:AI
上一篇:VulnHub-Gigachad
