到目前為止,我們實現的每個模型都是根據某個預先制定的分布來初始化模型的參數,有人會認為初始化方案時理所當然的,忽略了如何做出這些選擇的細節,甚至有人可能會覺得,初始化方案的選擇并不是特別重要,實際上,初始化方案的選擇在神經網絡學習中起著舉足輕重的作用,保持數值穩定性至關重要。此外,這些初始化方案的選擇可以與非線性激活函數的選擇有趣的結合在一起。我們選擇哪個函數以及如何初始化參數可以決定優化算法收斂的速度有多快,糟糕的選擇可能會導致我們在訓練時遇到梯度爆炸或者梯度消失,本節將更詳細的探討這些主題,討論一些有用的啟發方式方法。這些啟發方式在整個深度學習中都很有用。
4.8.1 梯度小時和梯度爆炸
考慮一個具有L層,輸入x和輸出o的深層網絡。每一層l由變換f1定義,該變換的參數為權重W(l),隱藏變量是h(l) 令h(0) =x。我們的網絡可以表示為
h(l) = Flh(l-1), 因此 o = FL .... Fl(x)
換而言之,該剃度是L-1 個矩陣M(L) ... M(l + 1) 與梯度向量v(l)的乘積。因此,我們容易受到數值下溢問題的影響,當將過多的概率在一起相承時,這種問題經常會出現。在處理概率時,一個常見的技巧是切換到對數空間,即將數值表示的壓力從尾數轉移到指數。遺憾的是,這會使上面的問題更為嚴重,矩陣M可能是各種各樣的特征值,可能很小,也可能很大,而他們的乘積可能非常大,也可能非常小。
不穩定梯度帶來的風險不僅在于數值表示,也威脅到優化算法的穩定性。我們可能面臨一些問題,要么是梯度爆炸問題,參數更新過大,破壞了模型的穩定收斂,要么是梯度消失問題,參數更新過小,在每次更新時幾乎不會移動。導致模型無法學習。
1 梯度消失
曾經sigmoid 函數很流行,因為它類似于閾值函數,由于早期的人工神經網絡受到生物神經網絡的啟發,神經元要餓完全激活,要么完全不激活的想法很吸引力。然而,卻是導致梯度消失的一個常見原因,我們仔細看著sigmoid函數為什么導致梯度消失
%matplotlib inline
import torch
from d2l import torch as d2l
x = torch.arange(-8.0, 8.0, 0.1, requires_grad = True);
y = torch.sigmoid(x)
y.backward(torch.ones_like(x))
d2l.plot(x.detach().numpy(),[y.detach().numpy(),x.grad.numpy()],legend=['sigmoid','gradient'],figsize=(4.5,2.5))
正如上圖曲線所示,當sigmoid函數的輸入很大或者很小時,梯度就會消失,此外,當反向傳播通過許多層時,除非恰好在sigmoid函數的輸入接近零的位置,否則整個乘積的梯度可能會消失,當網絡有很多層的時候,除非我們很小心,否則在某一層可能會切斷梯度,事實上,這個問題曾經困擾著深度網絡的訓練,,因此,更穩定的RelU系列函數已經成為從業者的默認選擇(雖然從神經科學的角度看起來不會太合理)
- 梯度爆炸
與梯度消失相反的梯度爆炸可能同樣令人煩惱,為了更好地說明這一點,我們生成100個高斯隨機矩陣,將他們與某個初始矩陣相乘,對于我們選擇的尺寸,矩陣的乘積發生了爆炸,當這種情況是由深度網絡的初始化所導致的,我們沒有機會讓梯度下降優化器收斂。
M = torch.normal(0, 1, size = (4,4))
printf('一個矩陣\n');
for i in range(100)
M = torch.mm(M, torch.normal(0, 1, size(4, 4)));
3 打破對成型
神經網絡設計中的另一個問題是其參數化所固有的對稱性。假設我們有一個簡單的多層感知機,有一個隱藏層和兩個隱藏單元。在這種情況下,我們可以對第一層的權重進行重排列,并且同樣對輸出層和權重進行重排列,可以獲得相同的函數,第一個隱藏單元與第二個隱藏單元沒有什么區別,換句話說,每一層的隱藏單元之間具有排列對稱性。
4.8.2 參數初始化
解決上述問題的一種方法是進行參數初始化,優化期間的適當正則化也可以進一步提高穩定性。