《Improving Deep Neural Networks:Hyperparameter tuning, Regularization and Optimization》課堂筆記

Lesson 2 Improving Deep Neural Networks:Hyperparameter tuning, Regularization and Optimization

這篇文章其實是 Coursera 上吳恩達老師的深度學習專業課程的第二門課程的課程筆記。

參考了其他人的筆記繼續歸納的。

訓練,驗證,測試集 (Train / Dev / Test sets)

在機器學習發展的小數據量時代,常見做法是將所有數據三七分,就是人們常說的 70% 訓練集,30% 測試集。如果明確設置了驗證集,也可以按照 60% 訓練集,20% 驗證集和 20% 測試集來劃分。這是前幾年機器學習領域普遍認可的最好的實踐方法。

如果只有 100 條,1000 條或者 1 萬條數據,那麼上述比例劃分是非常合理的。

但是在大數據時代,我們現在的數據量可能是百萬級別,那麼驗證集和測試集佔數據總量的比例會趨向於變得更小。因爲驗證集的目的就是驗證不同的算法,檢驗哪種算法更有效,因此,驗證集只要足夠大到能評估不同的算法,比如 2 個甚至 10 個不同算法,並迅速判斷出哪種算法更有效。我們可能不需要拿出 20% 的數據作爲驗證集。

比如我們有 100 萬條數據,那麼取 1 萬條數據便足以進行評估,找出其中表現最好的 1-2 種算法。同樣地,根據最終選擇的分類器,測試集的主要目的是正確評估分類器的性能,所以,如果擁有百萬數據,我們只需要 1000 條數據,便足以評估單個分類器,並且準確評估該分類器的性能。假設我們有 100 萬條數據,其中 1 萬條作爲驗證集,1 萬條作爲測試集,100 萬里取 1 萬,比例是 1%,即:訓練集佔 98%,驗證集和測試集各佔 1%。對於數據量過百萬的應用,訓練集可以佔到 99.5%,驗證和測試集各佔 0.25%,或者驗證集佔 0.4%,測試集佔 0.1%。

因爲我們要用驗證集來評估不同的模型,儘可能地優化性能,所以我們儘量要使驗證集和測試集來自同一分佈。

偏差,方差 (Bias/Variance)

假設這就是數據集,如果給這個數據集擬合一條直線,可能得到一個邏輯迴歸擬合,但它並不能很好地擬合該數據,這是高偏差(high bias)的情況,我們稱爲“欠擬合”(underfitting)。

相反的如果我們擬合一個非常複雜的分類器,比如深度神經網絡或含有隱藏單元的神經網絡,可能就非常適用於這個數據集,但是這看起來也不是一種很好的擬合方式分類器方差較高(high variance),數據過度擬合(overfitting)。

在兩者之間,可能還有一些像圖中這樣的,複雜程度適中,數據擬合適度的分類器,這個數據擬合看起來更加合理,我們稱之爲“適度擬合”(just right)是介於過度擬合和欠擬閤中間的一類。

在這樣一個只有 \(x_1\)\(x_2\) 兩個特徵的二維數據集中,我們可以繪製數據,將偏差和方差可視化。在多維空間數據中,繪製數據和可視化分割邊界無法實現,但我們可以通過幾個指標,來研究偏差和方差。

以下就是幾種情況,知道模型有什麼樣的表現,我們就能對應的採取什麼樣的策略去調試。

吳恩達老師在訓練神經網絡時用到的基本方法如下。

初始模型訓練完成後,首先得知道算法的偏差高不高,如果偏差較高,那麼試着評估訓練集或訓練數據的性能。如果偏差的確很高,甚至無法擬合訓練集,那麼我們要做的就是選擇一個新的網絡,比如說含有更多隱藏層或者隱藏單元的網絡,或者花費更多時間來訓練網絡,或者嘗試更先進的優化算法。

在訓練學習算法時,我們需要不斷嘗試這些方法,直到解決偏差問題,這是最低標準。直到可以擬合數據爲止,至少能夠擬合訓練集。

旦偏差降低到可以接受的數值,檢查一下方差有沒有問題,爲了評估方差,我們要查看驗證集性能,我們能從一個性能理想的訓練集推斷出驗證集的性能是否也理想,如果方差高,最好的解決辦法就是採用更多數據,但有時候,我們無法獲得更多數據,所以可以嘗試通過正則化來減少過擬合。系統地說出做法很難,總之就是不斷重複嘗試,直到找到一個低偏差、低方差的框架。

正則化 (Regularization)

正則化是解決深度學習存在高方差問題的方法。

\(L2\) 正則化

L2 正則化的作用原理

以邏輯迴歸爲例。,求代價函數 \(J\) 的最小值。在邏輯迴歸函數中加入正則化,只需添加參數 \(\lambda\),也就是正則化參數。

\(\frac{\lambda}{2m}\) 乘以參數 \(w\) 範數的平方,即 \(\left\| w \right\|_2^2\)\(w\) 的歐幾里得範數的平方,等於 \(w_j\)\(j\) 值從 1 到 \(n_x\))平方的和,也可表示爲 \(w^Tw\),也就是向量參數 \(w\) 的歐幾里得範數(2 範數)的平方。這個方法稱爲 \(L2\) 正則化,因爲這裏用了歐幾里得範數,被稱爲向量參數 \(w\)\(L2\) 範數。
\[ J(w,b)=\frac{1}{m} \sum^m_{i=1}{L(\hat{y}^{(i)},y^{(i)})}+\frac{\lambda}{2m}\left\|w\right\|^2_2 \]
至於爲什麼只正則化參數 \(w\),而不再加上參數 \(b\) 呢?可以,但沒必要。因爲 \(w\) 通常是一個高緯度參數矢量,已經可以表達高偏差問題,\(w\) 可能包含有很多參數,而我們不可能擬合所有參數,\(b\) 只是單個數字,所以 \(w\) 幾乎涵蓋所有參數。如果加了參數 \(b\),其實也沒太大影響。

\(L2\) 正則化是最常見的正則化類型,其實也有 \(L1\) 正則化。它加的不是 \(L2\) 範數,而是正則項 \(\frac{\lambda}{m}\) 乘以 \(\sum^{n_x}_{j=1}\left|w\right|\)。其中,\(\sum^{n_x}_{j=1}\left|w\right|\) 也被稱爲參數 \(w\) 向量的 \(L1\) 範數,無論分母是 \(m\) 還是 \(2m\),它都是一個比例常量。
\[ J(w,b)=\frac{1}{m} \sum^m_{i=1}{L(\hat{y}^{(i)},y^{(i)})}+\frac{\lambda}{m}\sum^{n_x}_{j=1}\left|w\right| \]
如果用的是 \(L1\) 正則化,\(w\) 最終會是稀疏的,也就是說 \(w\) 向量中有很多 0。有人說這樣有利於壓縮模型,因爲集合中的參數均爲 0,存儲模型所佔用的內存更少。實際上,雖然 \(L1\) 正則化使模型變得稀疏,卻沒有降低太多存儲內存,所以吳恩達老師認爲這並不是 \(L1\) 正則化的目的,至少不是爲了壓縮模型。所以人們在訓練網絡時,越來越傾向於使用 \(L2\) 正則化。

在神經網絡中實現 \(L2\) 正則化

神經網絡含有一個代價函數,該函數包含 \(W^{[1]},b^{[1]}\)\(W^{[L]},b^{[L]}\) 所有參數,其中 \(l\) 表示的是神經網絡的層數,\(L\) 爲神經網絡的總層數。那麼,代價函數等於 \(m\) 個訓練樣本損失函數的均值,正則項爲 \(\frac{\lambda}{2m}\sum^L_{l=1}{\left\|W^{[l]}\right\|^2}\)。這個矩陣範數 \(\left\|W^{[l]}\right\|^2\)(即平方範數),被定義爲矩陣中所有元素的平方求和,我們稱之爲“弗羅貝尼烏斯範數 (Frobenius norm)”。
\[ J(W^{[1]},b^{[1]},\dots,W^{[L]},b^{[L]})=\frac{1}{m} \sum^m_{i=1}{L(\hat{y}^{(i)},y^{(i)})}+\frac{\lambda}{2m}\sum^L_{l=1}\left\|W^{[l]}\right\|^2_F\\ \left\|W^{[l]}\right\|^2_F=\sum^{n^{[l-1]}}_{i=1}\sum^{n^{[l]}}_{j=1}(W^{[l]}_{ij})^2 \]
使用弗羅貝尼烏斯範數實現梯度下降

在沒有使用弗羅貝尼烏斯範數時,我們是用反向傳播計算出 \(dW\) 的值,然後使用它來更新參數 \(W\)
\[ dW^{[l]}=(from \ backprop)\\ W^{[l]}:=W^{[l]}-\alpha dW^{[l]} \]
既然已經增加了正則項,那麼我們需要給 \(dW\) 加上一項 \(\frac{\lambda}{m}W^{[l]}\)。然後更新參數。
\[ dW^{[l]}=(from \ backprop)+\frac{\lambda}{m}W^{[l]}\\ W^{[l]}:=W^{[l]}-\alpha dW^{[l]} \]
我們對上面的公式進行一下數學變換。
\[ W^{[l]}:=W^{[l]}-\alpha ((from \ backprop)+\frac{\lambda}{m}W^{[l]})\\ =W^{[l]}-\frac{\alpha \lambda}{m}W^{[l]}-\alpha(from \ backprop)\\ =(1-\frac{\alpha \lambda}{m})W^{[l]}-\alpha(from \ backprop) \]
我們發現 \((1-\frac{\alpha \lambda}{m})\) 這個係數時小於 1 的,所以不論 \(W^{[l]}\) 是什麼,我們都試圖讓它變得更小。所以,\(L2\) 範數正則化也被稱爲“權重衰減”。

爲什麼正則化有利於預防過擬合?

直觀理解就是正則化參數 \(\lambda\) 增加到足夠大,參數 \(W\) 會接近於 0(實際上是不會發生這種情況的)。我們嘗試消除或至少減少許多隱藏單元的影響,最終這個網絡會變得更簡單,這個神經網絡越來越接近邏輯迴歸,也就是從過擬合狀態不斷接近高偏差狀態。但是,\(\lambda\) 會存在一箇中間值,於是會有一個接近“Just Right”的中間狀態。

而從圖形上來看的話,假設我們用的是 tanh 雙曲線激活函數。用 \(g(z)\) 表示 \(tanh(z)\)。如果正則化參數 \(\lambda\) 很大,參數 \(W\) 會相對較小,而由於 \(Z^{[l]}=W^{[l]}a^{[l-1]}+b^{[l]}\),所以 \(z\) 也會很小。特別地,如果 \(z\) 的值最終在紅色區域這個範圍內,都是相對較小的值,\(g(z)\) 大致呈線性。而我們之前說過,如果每層都是線性的,那麼整個網絡就是一個線性網絡,即使是一個非常深的深層網絡,因具有線性激活函數的特徵,最終我們只能計算線性函數,因此,它不適用於非常複雜的決策,以及過度擬合數據集的非線性決策邊界。

Dropout 正則化

除了 \(L2\) 正則化,還有一個非常實用的正則化方法——“Dropout(隨機失活)”。

Dropout 正則化的作用原理

假設我們在訓練下圖這樣的神經網絡,它存在過擬合。

我們複製這個神經網絡,dropout 會遍歷網絡的每一層,並設置消除神經網絡中節點的概率(假設爲 0.5)。於是一些節點會被消除,最後我們得到一個節點更少,規模更小的網絡,然後用反向傳播方法進行訓練。

在神經網絡中實現 Dropout 正則化

實現 dropout 的最常用的方法是 inverted dropout(反向隨機失活)

我們用神經網絡的第三層來舉例說明。

首先要定義向量 \(d\)\(d^{[3]}\) 表示網絡第三層 的 dropout 向量:

d3 = np.random.rand(a3.shape[0],a3.shape[1])

然後看它是否小於某數,我們稱之爲 keep-prob,它是一個具體的數字。在上面的圖裏面,它被設爲 0.5,現在我們把它設爲 0.8。它表示保留某個隱藏單元的概率,即消除任意一個隱藏單元的概率是 0.2。

接下來要做的就是從第三層中獲取激活函數,這裏我們叫它 \(a^{[3]}\)\(a^{[3]}\) 含有要計算的激活函數。這個 \(a^{[3]}\) 就等於沒有 dropout 的 \(a^{[3]}\) 乘以 \(d^{[3]}\)a3 = np.multiply(a3,d3),這裏是元素相乘。它的作用就是讓 \(d^{[3]}\) 中 0 元素與 \(a^{[3]}\) 中相對元素歸零。

順便一提,如果用 python 實現這個算法的話,我們的 \(d^{[3]}\) 其實是一個布爾型數組,值爲 true 和 false,而不是 1 和 0。

由於 \(z^{[4]}=w^{[4]}a^{[3]}+b^{[4]}\),而 \(a^{[3]}\) 被隨機減少了 20%。爲了不影響 \(z^{[4]}\) 的期望值,我們需要用 \(\frac{w^{[4]}a^{[3]}}{0.8}\) 來修正被隨機減少的那 20%。我們在測試階段是不使用 dropout 函數的,它會使我們的輸出結果隨機。

當然,我們可以在網絡的不同層使用不同的 keep-prob 值進行不同程度的 dropout。如下圖所示。注意 keep-prob 的值爲 1 表示的是保留所有單元,不在這一層使用 dropout。

dropout 一大缺點就是代價函數 \(J\) 不再被明確定義,每次迭代,都會隨機移除一些節點,如果再三檢查梯度下降的性能,實際上是很難進行復查的。定義明確的代價函數 \(J\) 每次迭代後都會下降,因爲我們所優化的代價函數 \(J\) 實際上並沒有明確定義,或者說在某種程度上很難計算,所以我們失去了調試工具來繪製迭代曲線。
吳恩達老師的做法是,關閉 dropout 函數,將 keep-prob 設爲 1,運行代碼,確保代價函數單調遞減。然後再打開 dropout 函數。

理解 Dropout

Dropout 隨機刪除網絡中的神經單元,看起來很奇怪的操作卻能實現正則化。

直觀上理解,dropout 使得網絡不依賴於任何一個特徵,因爲該單元的輸入可能隨時被清除,因此該單元通過這種方式傳播下去,併爲單元的四個輸入增加一點權重,通過傳播所有權重,dropout 將產生收縮權重的平方範數的效果。

"Can't rely on any one feature, so have to spread out weights."

其他正則化方法

數據擴增 (Data augmentation)

我們可以通過擴增訓練數據來解決過擬合,但擴增數據代價高,而且有時候是無法擴增數據的。那麼可以通過水平翻轉圖片、隨意裁剪圖片等方法來擴增數據,這雖然不如額外收集一組新圖片那麼好,但這樣節省了很高的數據收集成本。

early stopping

運行梯度下降時,我們可以繪製訓練誤差,或只繪製代價函數 \(J\) 的優化過程,在訓練集上用 0-1 記錄分類誤差次數。

因爲在訓練過程中,我們希望訓練誤差,代價函數 \(J\) 都在下降,通過 early stopping,我們不但可以繪製上面這些內容,還可以繪製驗證集誤差,它可以是驗證集上的分類誤差,或驗證集上的代價函數,邏輯損失和對數損失等。我們發現,驗證集誤差通常會先呈下降趨勢,然後在某個節點處開始上升。而 early stopping 的作用是,在神經網絡已經在迭代過程中表現得很好的時候停止訓練。

當我們還未在神經網絡上運行太多迭代過程的時候,參數 \(w\) 接近 0,因爲隨機初始化 \(w\) 值時,它的值可能都是較小的隨機值,所以在長期訓練神經網絡之前 \(w\) 依然很小,在迭代過程和訓練過程中 \(w\) 的值會變得越來越大,所以 early stopping 要做就是在中間點停止迭代過程,我們得到一個 \(w\) 值中等大小的弗羅貝尼烏斯範數,與\(L2\)正則化相似,選擇參數 \(w\) 範數較小的神經網絡。

early stopping 的主要缺點就是我們不能獨立地處理這兩個問題,因爲提早停止梯度下降,也就是停止了優化代價函數 \(J\),因爲現在不再嘗試降低代價函數 \(J\),所以代價函數 \(J\) 的值可能不夠小,同時我們又希望不出現過擬合,沒有采取不同的方式來解決這兩個問題,而是用一種方法同時解決兩個問題,這樣做的結果是我們要考慮的東西變得更復雜。

early stopping 的優點是,只運行一次梯度下降,就可以找出 \(w\) 的較小值,中間值和較大值,而無需嘗試 \(L2\) 正則化超參數 \(\lambda\) 的很多值。

歸一化輸入 (Normalizing inputs)

訓練神經網絡,其中一個加速訓練的方法就是歸一化輸入。

假設一個訓練集有兩個特徵,輸入特徵爲 2 維,歸一化需要兩個步驟:(1)零均值化;(2)歸一化方差。

(1)零均值化

\(\mu = \frac{1}{m}\sum_{i =1}^{m}x^{(i)}\),它是一個向量,\(x\) 等於每個訓練數據 \(x\) 減去 \(\mu\),意思是移動訓練集,直到它完成零均值化。

(2)歸一化方差

注意特徵 \(x_{1}\) 的方差比特徵 \(x_{2}\) 的方差要大得多,我們要做的是給 \(\sigma\) 賦值,\(\sigma^{2}= \frac{1}{m}\sum_{i =1}^{m}{({x^{(i)})}^{2}}\),這是節點 \(y\) 的平方,\(\sigma^{2}\) 是一個向量,它的每個特徵都有方差,注意,我們已經完成零值均化,\(({x^{(i)})}^{2}\) 元素 \(y^{2}\) 就是方差,我們把所有數據除以向量 \(\sigma^{2}\),最後變成上圖形式。

如果你用它來調整訓練數據,那麼用相同的 \(μ\)\(\sigma^{2}\) 來歸一化測試集。

如果我們使用非歸一化的輸入特徵,代價函數會非常細長狹窄,這樣我們必須使用一個非常小的學習率,進行多次迭代,來找到最小值。而歸一化特徵之後,代價函數會更對稱,是一個更圓的球形輪廓。那麼不論從哪個位置開始,可以使用較大步長,梯度下降法都能夠更直接地找到最小值。

所以如果輸入特徵處於不同範圍內,可能有些特徵值從 0 到 1,有些從 1 到 1000,那麼歸一化特徵值就非常重要了。如果特徵值處於相似範圍內,那麼歸一化就不是很重要了。

梯度消失/梯度爆炸 (Vanishing / Exploding gradients)

訓練神經網絡,尤其是深度神經所面臨的一個問題就是梯度消失或梯度爆炸,也就是訓練神經網絡的時候,導數或坡度有時會變得非常大,或者非常小,甚至於以指數方式變小,這加大了訓練的難度。

對於下圖這個很深的神經網絡(爲了簡便,我們只畫出兩個隱藏單元,實際可以有很多)。

爲了簡單起見,假設我們使用激活函數 \(g(z)=z\),也就是線性激活函數,我們忽略 \(b\),假設 \(b^{[l]}\)=0,如果那樣的話,輸出 \(y=W^{[L]}W^{[L -1]}W^{[L - 2]}\ldots W^{[3]}W^{[2]}W^{[1]}x\)。其實我們得到的輸出結果是 \(\hat{y}\) 而不是 \(y\)

接着,假設每個權重矩陣 \(W^{[l]}=\begin{bmatrix} 1.5 & 0 \\0 & 1.5 \\\end{bmatrix}\),最後一項有不同的維度,也就是 \(y= W^{[L]}\begin{bmatrix} 1.5 & 0 \\ 0 & 1.5 \\\end{bmatrix}^{(L -1)}x\)。但是我們假設所有的矩陣都等於 1.5 倍的單位矩陣,那麼最後的計算結果 \(\hat{y}=1.5^Lx\)。所以,對於一個深度神經網絡來說 \(L\) 值較大,那麼 \(\hat{y}\) 的值也會非常大,實際上它是呈指數級增長的。

而如果權重是 0.5 倍的單位矩陣,即 \(W^{[l]} = \begin{bmatrix} 0.5& 0 \\ 0 & 0.5 \\ \end{bmatrix}\),那麼 \(\hat{y}=0.5^Lx\)。激活函數的值將以指數級下降。

權重初始化

先以單個神經元的情況舉例。

單個神經元可能有 4 個輸入特徵,從 \(x_1\)\(x_4\),經過 \(a=g(z)\) 處理,最終得到 \(\hat{y}\)。這些輸入表示爲 \(a^{[l]}\),暫時我們用 \(x\) 表示。

我們把 \(b\) 設爲 0,那麼 \(z=w_1x_1+w_2x_2+\dots+w_nx_n\)。可以發現,\(n\) 越大,即輸入特徵數越多,\(z\) 的值就越大。爲了預防 \(z\) 值過大或過小,我們希望每項值更小,合理的方法是設置 \(w_i=\frac{1}{n}\)

實際上,我們設置每層權重矩陣如下
\[ w^{[l]}=np.random.randn(shape)*np.sqrt(\frac{1}{n^{[l-1]}}) \]
如果用的是 Relu 激活函數,方差設置爲 \(\frac{2}{n}\),效果會更好。

如果激活函數的輸入特徵被零均值和標準方差化,方差是 1,\(z\) 也會調整到相似範圍,這就沒有解決梯度消失和梯度爆炸問題。但它卻是降低了這些問題,因爲它給權重矩陣 \(w\) 設置了合理值,它不會比 1 大很多,也不會比 1 小很多,所以梯度沒有爆炸或消失過快。

一篇由 Herd 等人撰寫的論文曾介紹過。對於幾個其它變體函數,如 tanh 激活函數,有篇論文提到,常量 1 比常量 2 的效率更高,對於 tanh 函數來說,它是\(\sqrt{\frac{1}{n^{[l-1]}}}\),它適用於 tanh 激活函數,被稱爲Xavier初始化。Yoshua Bengio 和他的同事還提出另一種方法,他們使用的是公式\(\sqrt{\frac{2}{n^{[l-1]} + n^{\left[l\right]}}}\)

優化算法 (Optimization algorithms)

機器學習的應用是一個高度依賴經驗的過程,伴隨着大量的迭代的過程,我們需要訓練諸多模型。而優化算法能夠幫助我們快速訓練模型。

Mini-batch 梯度下降 (Mini-batch gradient descent)

我們之前使用向量化能夠讓我們有效地對所有 \(m\) 個樣本進行計算,只需把所有的訓練樣本放大巨大的矩陣 \(X\) 中去,\(X=[x^{(1)} x^{(2)} \dots x^{(m)}]\)。同理,\(Y\) 也是這樣。但是如果 \(m\) 很大的話,處理速度仍然會很慢。我們必須處理整個訓練集,才能迭代一次梯度下降。

那麼我們可以把訓練集分割爲小一點的子集訓練,這些子集被取名爲 mini-batch。假設每個子集中只有 1000 個樣本,那麼把其中的 \(x^{(1)}\)\(x^{(1000)}\) 取出來,將其稱爲第一個子訓練集。然後接着取 \(x^{(1001)}\)\(x^{(2000)}\),以此類推。我們把它們記爲 \(X^{\{1\}}, X^{\{2\}},\dots\)。如果訓練樣本一共有 500 萬個,每個 mini-batch 都有 1000 個樣本,那麼我們就有 5000 個 mini-batch,最後得到的是 \(X^{\{5000\}}\)。同樣,\(Y\) 也進行這樣的處理。從 \(Y^{\{1\}}\)\(Y^{\{5000\}}\)

Mini-batch 梯度下降與我們之前用的梯度下降算法不一樣的是,我們每次處理的只是單個 mini-batch \(X^{\{t\}}\)\(Y^{\{t\}}\),而不是同時處理全部的 \(X\)\(Y\)。僞代碼如下圖。

需要注意的是,上圖的代碼只是進行了“一代 (1 epoch)”的訓練,也就是隻是一次遍歷了訓練集。

理解 mini-batch 梯度下降法

使用 batch 梯度下降法,每次迭代都需要遍歷整個訓練集,可以預期每次迭代成本都會下降,所以如果代價函數 \(J\) 在某次迭代中增加了,那肯定出 bug 了,可能是由於學習率太大了。

而使用 mini-batch 梯度下降法,我們會發現代價函數 \(J\) 並不是每次迭代都是下降的。那是因爲代價函數 \(J^{\{t\}}\) 只和 \(X^{\{t\}},Y^{\{t\}}\) 有關,也就是說每次迭代我們都在訓練不同的樣本集(不同的 mini-batch)。所以 \(J^{\{t\}}\) 的圖像是總體趨勢朝下,但是有很多噪聲。

對於 mini-batch 梯度下降法來說,我們需要決定的變量之一就是 mini-batch 的大小。

極端情況下,它可以等於訓練集的大小,其實就是 batch 梯度下降法。但是它的弊端在於,樣本數量巨大的時候,單次迭代耗時太長。如果訓練樣本不大, batch 梯度下降法運行地很好。

另一種極端情況下,它可以等於 1,也叫作隨機梯度下降法。每個樣本都是獨立的 mini-batch,每次迭代我們都只處理一個樣本。隨機梯度下降法是有很多噪聲的,平均來看,它最終會靠近最小值,不過有時候也會方向錯誤,因爲隨機梯度下降法永遠不會收斂,而是會一直在最小值附近波動,但它並不會在達到最小值並停留在此。隨機梯度下降法的一大缺點是,我們會失去所有向量化帶給我們的加速,效率會過於低下。

所以 mini-batch 的大小應該取 1 到 \(m\) 之間的值。如果訓練集較小(< 2000),直接使用 batch 梯度下降法,否則使用 mini-batch。一般 mini-batch 大小爲 64 到 512,考慮到電腦內存設置和使用的方式,如果 mini-batch 大小是 2 的 n 次方,代碼會運行地快一些。

指數加權平均 (Exponentially weighted averages)

在統計中也叫指數加權移動平均。以倫敦的氣溫爲例。

散點看起來有些雜亂,如果要計算趨勢的話,也就是溫度的局部平均值,或者說移動平均值。我們要做的是,首先使 \(v_0=0\),每天使用 0.9 倍的之前的加權數加上當日溫度的 0.1 倍。即 \(v_1=0.9v_0+0.1\theta_1\),得到第一天的溫度值。以此類推。

這樣,我們得到圖中紅色的曲線。

把 0.9 這個常數泛化爲變量 \(\beta\),那麼之前的 0.1 則爲 \((1-\beta)\),即 \(v_t=\beta v_{t-1}+(1-\beta)\theta_t\)。在計算時,\(v_t\) 可以看作是 \(\frac{1}{1-\beta}\) 的每日溫度。如果 \(\beta\) 是 0.9,這是十天的平均值。而如果 \(\beta\) 爲 0.98,\(\frac{1}{1-0.98}=50\),那麼可以看作是過去 50 天的溫度,這時,我們就可以得到下圖中綠色的曲線。

指數加權平均公式在溫度變化時,適應地更緩慢一些,所以會出現一定延遲,因爲當 \(\beta=0.98\),相當於給前一天的值加了太多權重,只有 0.02 的權重給了當日的值,所以溫度變化時,溫度上下起伏,當 \(\beta\) 較大時,指數加權平均值適應地更緩慢一些。

我們可以再換一個值試一試,如果 \(\beta\) 是另一個極端值,比如說 0.5,\(\frac{1}{1-0.5}=2\),這是平均了兩天的溫度。作圖運行後得到黃色的曲線。

由於僅平均了兩天的溫度,平均的數據太少,所以得到的曲線有更多的噪聲,有可能出現異常值,但是這個曲線能夠更快適應溫度變化。

理解指數加權平均

其實核心公式就是
\[ v_t=\beta v_{t-1}+(1-\beta)\theta_t \]
現在我們使 \(\beta\) 等於 0.9,把每個公式具體寫出來,倒着寫。
\[ v_{100}=0.9v_{99}+0.1\theta_{100}\\ v_{99}=0.9v_{98}+0.1\theta_{99}\\ v_{98}=0.9v_{97}+0.1\theta_{98}\\ \dots \dots \]
那麼 \(v_{100}\) 其實可以寫作下面這個形式
\[ v_{100} = 0.1\theta_{100} + 0.1 \times 0.9 \theta_{99} + 0.1 \times {(0.9)}^{2}\theta_{98} + 0.1 \times {(0.9)}^{3}\theta_{97} + 0.1 \times {(0.9)}^{4}\theta_{96} + \dots \]
假設我們有一些日期的溫度,所以這是數據,\(t\) 爲 100,99,98 等等,這就是數日的溫度數值。

然後我們構建一個指數衰減函數,從 0.1 開始,到 \(0.1 \times 0.9\),到 \(0.1 \times {(0.9)}^{2}\),以此類推,所以就有了這個指數衰減函數。

計算 \(v_{100}\) 是通過把每日溫度與指數衰減函數相乘,然後求和。

所有的這些係數(\((0.1 \times 0.1) \times (0.9 \times 0.1) \times ({(0.9)}^{2} \times 0.1)) \times ({(0.9)}^{3}\times 0.1) \times \dots\)),相加起來爲 1 或者逼近 1,我們稱之爲偏差修正

我們如果實際運行 \(\beta=0.98\),得到的其實不是下圖中的綠色曲線而是紫色曲線。而紫色曲線起點較低。

計算移動平均數的時候,初始化 \(v_{0} = 0\)\(v_{1} = 0.98v_{0} +0.02\theta_{1}\),但是 \(v_{0} =0\),所以這部分沒有了(\(0.98v_{0}\)),所以 \(v_{1} =0.02\theta_{1}\),所以如果一天溫度是 40 華氏度,那麼 \(v_{1} = 0.02\theta_{1} =0.02 \times 40 = 8\),因此得到的值會小很多,所以第一天溫度的估測不準。同理,第二天也會出現估測不準的情況。

有個辦法可以修改這一估測,讓估測變得更好,更準確,特別是在估測初期,也就是不用 \(v_{t}\),而是用 \(\frac{v_{t}}{1- \beta^{t}}\)

舉個具體例子,當 \(t=2\) 時,\(1 - \beta^{t} = 1 - {0.98}^{2} = 0.0396\),因此對第二天溫度的估測變成了 \(\frac{v_{2}}{0.0396} =\frac{0.0196\theta_{1} + 0.02\theta_{2}}{0.0396}\),也就是 \(\theta_{1}\)\(\theta_{2}\) 的加權平均數,並去除了偏差。隨着 \(t\) 增加,\(\beta^{t}\) 接近於 0,所以當 \(t\) 很大的時候,偏差修正幾乎沒有作用,因此當 \(t\) 較大的時候,紫線基本和綠線重合了。

動量梯度下降法 (Gradient descent with Momentum)

動量梯度下降法運行速度幾乎總是快於標準的梯度下降算法,簡而言之,基本的想法就是計算梯度的指數加權平均數,並利用該梯度更新權重。

假如我們要優化下圖所示的代價函數,紅點表示最小值的位置。而我們從藍色點開始梯度下降,無論是 batch 還是 mini-batch,一步一步迭代纔會慢慢擺動到最小值。這種上下波動減慢了梯度下降法的速度,而如果使用較大的學習率(紫色箭頭),結果可能會偏離函數的範圍,爲了避免擺動過大,我們要用一個較小的學習率。

使用動量梯度下降法,我們可以在縱軸上,學習慢一點,因爲我們不想要這些擺動;而在橫軸上,加快學習,快速移向最小值。

我們需要做的是,在每次迭代即第 \(t\) 次迭代的過程中,計算微分 \(dW,db\) 的移動平均數。也就是
\[ v_{dW}=\beta v_{dW}+(1-\beta)dW\\ v_{db}=\beta v_{db}+(1-\beta)db \]
然後重新賦值權重
\[ W:=W-\alpha v_{dW}\\ b:=b-\alpha v_{db} \]
這樣平均過程中,縱軸上正負數相互抵消,平均值接近於 0。而橫軸上,所有的微分都指向橫軸方向,因此橫軸方向的平均值仍然較大。

我們一般 \(\beta\) 爲 0.9 可以達到不錯的效果,當然也可以嘗試不同的值。實際中,在使用梯度下降法或動量梯度下降法時,人們不會受到偏差修正的困擾。

RMSprop

RMSprop 算法的全稱爲 root mean square prop 算法,它也可以加速梯度下降。

還是上面那個例子。我們假設縱軸代表參數 \(b\),橫軸代表參數 \(W\),可能有 \(W_1,W_2\) 或者其他重要的參數,爲了便於理解,被稱爲 \(b\)\(W\)

在第 \(t\) 次迭代中,該算法會照常計算當下 mini-batch 的微分 \(dW,db\),保留指數加權平均數,但是用新符號 \(S_{dW}\) 而不是 \(v_{dW}\),因此 \(S_{dW}=\beta S_{dW}+(1-\beta)dW^2\),其中平方是對於整個符號進行平方的。同樣地,\(S_{db}=\beta S_{db}+(1-\beta)db^2\)

接着,仍然是更新參數
\[ W:=W-\alpha \frac{dW}{\sqrt{S_{dW}}}\\ b:=b-\alpha \frac{db}{\sqrt{S_{db}}} \]
我們來理解一下其原理。記得在橫軸方向或者在例子中的 \(W\) 方向,我們希望學習速度快,而在垂直方向,也就是例子中的 \(b\) 方向,我們希望減緩縱軸上的擺動,所以有了 \(S_{dW}\)\(S_{db}\),我們希望 \(S_{dW}\) 會相對較小,所以我們要除以一個較小的數,而希望 \(S_{db}\) 又較大,所以這裏我們要除以較大的數字,這樣就可以減緩縱軸上的變化。你看這些微分,垂直方向的要比水平方向的大得多,所以斜率在 \(b\) 方向特別大,所以這些微分中,\(db\) 較大,\(dW\) 較小,因爲函數的傾斜程度,在縱軸上,也就是 b 方向上要大於在橫軸上,也就是 \(W\) 方向上。\(db\) 的平方較大,所以 \(S_{db}\) 也會較大,而相比之下,\(dW\) 會小一些,亦或 \(dW\) 平方會小一些,因此 \(S_{dW}\) 會小一些,結果就是縱軸上的更新要被一個較大的數相除,就能消除擺動,而水平方向的更新則被較小的數相除。

Adam 優化算法

Adam 優化算法基本上就是將 Momentum 和 RMSprop 結合在一起。Adam代表的是Adaptive Moment Estimation

使用 Adam 算法,首先我們初始化各項參數 ,\(v_{dW} = 0\)\(S_{dW} =0\)\(v_{db} = 0\)\(S_{db} =0\),在第 \(t\) 次迭代中,計算微分 \(dW,db\),一般我們會用 mini-batch 梯度下降法。

接下來計算 Momentum 指數加權平均數,這裏我們爲了不混淆兩個算法中的參數,Momentum 中使用 \(\beta_1\),RMSprop 中使用 \(\beta_2\)。 即
\[ v_{dW}= \beta_{1}v_{dW} + ( 1 - \beta_{1})dW\\ v_{db}= \beta_{1}v_{db} + ( 1 - \beta_{1})db \]
然後,使用 RMSprop 進行更新,即
\[ S_{dW}=\beta_2 S_{dW}+(1-\beta_2)(dW)^2\\ S_{db}=\beta_2 S_{db}+(1-\beta_2)(db)^2 \]
一般使用 Adam 算法的時候,要計算偏差修正,即
\[ v_{dW}^{corrected}=\frac{v_{dW}}{1-\beta_1^t}\\ v_{db}^{corrected}=\frac{v_{db}}{1-\beta_1^t}\\ S_{dW}^{corrected}=\frac{S_{dW}}{1-\beta_1^t}\\ S_{db}^{corrected}=\frac{S_{db}}{1-\beta_1^t} \]
最後,更新權重。由於我們要確保我們的算法不會除以 0,而如果 \(S_dW\) 的平方根趨近於 0 的話,就會導致我們的結果非常大。爲了確保數值穩定,在實際操作中,我們要在分母上加上一個很小很小的 \(\epsilon\),一般我們設爲 \(10^{-8}\)。即
\[ W:=W-\frac{\alpha v_{dW}^{corrected}}{\sqrt{S_{dW}^{corrected}}+\epsilon}\\ b:=b-\frac{\alpha v_{db}^{corrected}}{\sqrt{S_{db}^{corrected}}+\epsilon} \]
所以 Adam 算法結合了兩個算法,是一種極其常用的學習算法,被證明能有效適用於不同神經網絡,適用於廣泛的結構。

關於這個算法,它其實有很多超參數。

學習率 \(\alpha\) 很重要,經常需要調試。\(\beta_1\) 常用的缺省值爲 0.9。\(\beta_2\) 的值,Adam 的作者推薦使用 0.999;而 \(\epsilon\) 一般也建議使用 \(10^{-8}\)

學習率衰減 (Learning rate decay)

加快學習算法的一個辦法就是隨時間慢慢減少學習率,我們將之稱爲學習率衰減。

假設我們使用 mini-batch 梯度下降法,mini-batch 的數量不大,在迭代過程中會有噪音(下圖中藍色的線),下降朝向最小值。但是不會精確地收斂,所以算法最後在附近擺動,因爲我們用的 \(\alpha\) 是固定值,不同的 mini-batch 中有噪音。

但是如果我們慢慢減小學習率 \(\alpha\) 的話,在初期的時候,學習率還較大,學習還是相對較快;隨着學習率變小,學習的步伐也會變慢變小,最後曲線會在最小值附近的一小塊區域裏擺動(上圖中綠色的線),而不是在訓練過程中,大幅度在最小值附近擺動。

具體做法可以這樣實現。我們將學習率 \(\alpha\) 設爲 \(\alpha=\frac{1}{1+decay\_rate*epoch\_num}\alpha_0\),其中 decay_rate 爲衰減率,epoch_num 爲迭代代數,\(\alpha_0\) 爲初始學習率。衰減率 decay_rate 其實是一個我們需要調整的超參數。

當然,還有其他人們會用的公式。如下。
\[ \alpha=0.95^{epoch\_num}\alpha_0\\ \alpha=\frac{k}{\sqrt{epoch\_num}}\alpha_0\\ \alpha=\frac{k}{\sqrt{t}}\alpha_0 \]
有時也會用一個離散下降的學習率,也就是某個步驟有某個學習率,一會之後,學習率減少了一半,一會兒減少一半,一會兒又一半,這就是離散下降(discrete stair cease)的意思。還有時候,人們也會手動控制 \(\alpha\),但這隻有模型數量小的時候有用。

Batch 歸一化 (Batch Norm)

Batch 歸一化算法由 Sergey Loffe 和 Christian Szegedy 創造,簡稱爲 BN,它會使參數搜索問題變得很容易,使神經網絡對超參數的選擇更加穩定,超參數的範圍會更加龐大,工作效果也更好。

當訓練一個模型時,我們曾經使用過歸一化輸入特徵加快學習過程。而對於更深的模型,我們不禁有輸入特徵值 \(x\),還有各個層的激活值 \(a^{[1]},a^{[2]}\) 等等。那麼歸一化這些激活值也會使我們的訓練更有效率。嚴格來說,Batch 歸一化的不是激活值 \(a^{[l]}\) 而是 \(z^{[l]}\)

現在我們以第 \(l\) 層的歸一化爲例,在符號中省略上標 \(^{[l]}\)。於是我們有這一層隱藏單元的值,從 \(z^{(1)}\)\(z^{(m)}\),於是我們用下面的公式使每個 \(z^{(i)}\) 值規範化。爲了使數值穩定,通常會在分母中加上 \(\epsilon\),以防 \(\sigma=0\)
\[ \mu=\frac{1}{m}\sum_{i=1}^mz^{(i)}\\ \sigma^2=\frac{1}{m}\sum_{i=1}^m(z^{(i)}-\mu)\\ z^{(i)}_{norm}=\frac{z^{(i)}-\mu}{\sqrt{\sigma^2+\epsilon}} \]
這樣我們就把這些 \(z\) 值標準化,化爲含平均值 0 和標準單位方差。但是我們不想讓隱藏單元總是含有平均值 0 和方差 1,也許隱藏單元有了不同的分佈會有意義,所以我們還要計算一個變量 \({\tilde{z}}^{(i)}= \gamma z_{\text{norm}}^{(i)} +\beta\)。其中 \(\gamma\)\(\beta\) 是我們需要學習的參數(這裏的 \(\beta\) 與 Adam 等優化算法中的參數 \(\beta\) 不是同一個),正如更新權重一樣,使用梯度下降或者優化算法,我們也會更新這兩個參數。

通過賦予 \(\gamma\)\(\beta\) 其他值,我們可以構造含其他平均值和方差的隱藏單元值。如果 \(\gamma=\sqrt{\sigma^2+\epsilon}\)\(\beta=\mu\),那麼\(\tilde{z}^{(i)}=z^{(i)}\)

一般來說,如果使用深度學習編程框架,一般一行代碼就能實現 BN。比如在 TensorFlow 框架中,我們可以用函數 tf.nn.batch_normalization 來實現。

Softmax 迴歸

如果我們要進行多類型的預測的話,我們就需要用到 softmax 迴歸。

以下面這個例子進行說明。

於是爲了分出上圖中的四類,我們將建立一個神經網絡,其輸出層有 4 個或者說 \(C\) 個輸出單元。

我們想要的是,輸出層單元的數字告訴我們這 4 種類型中每個的概率有多大,因此這裏的 \(\hat{y}\) 將是一個 \(4\times1\) 維向量,而且輸出的四個數字加起來應該等於 1。

在神經網絡的最後一層,我們將會像往常一樣計算各層的線性部分,也就是 \(z^{[L]}=W^{[L]}a^{[L-1]}+b^{[L]}\)。然後我們應用 softmax 激活函數。首先,計算一個臨時變量 \(t\),它等於 \(e^{z^{[L]}}\),也就是對所有元素求冪,然後對這個變量 \(t\) 進行歸一化來輸出 \(a^{[L]}\),也就是 \(a^{[L]}=\frac{t}{\sum_{i=1}^Ct_i}\)

以一個具體的例子來說,就是,假如我們算出的 \(z^{[L]}\) 的值如下,那麼計算過程也就如下所示。
\[ z^{[L]}= \begin{bmatrix} 5 \\ 2 \\ - 1 \\ 3 \\ \end{bmatrix}\\ t =e^{z^{[L]}}=\begin{bmatrix} e^{5} \\ e^{2} \\ e^{- 1} \\ e^{3} \\ \end{bmatrix}=\begin{bmatrix} 148.4 \\ 7.4 \\ 0.4 \\ 20.1 \\ \end{bmatrix}\\ a^{[L]}=\frac{t}{\sum_{i=1}^4t_i}=\begin{bmatrix} 0.842 \\ 0.042 \\ 0.002 \\ 0.114 \\ \end{bmatrix} \]
softmax 這個名詞的來源是與所謂 hardmax 對比而來的。hardmax 會把向量 \(z\) 變成 \(\begin{bmatrix} 1 \\ 0 \\ 0 \\ 0 \\ \end{bmatrix}\) 這種形式的向量。

假如說對於某個樣本的目標輸出,真實標籤是 \(\begin{bmatrix} 0 \\ 1 \\ 0 \\ 0 \\ \end{bmatrix}\),但是輸出的 \(\hat{y}=\begin{bmatrix} 0.3 \\ 0.2\\0.1\\0.4\end{bmatrix}\)。說明這個樣本,神經網絡的表現不佳。在 softmax 分類中,一般用到的損失函數是 \(L(\hat{y},y)=-\sum_{j=1}^Cy_j\log{\hat{y}_j}\)。用剛剛說的具體的數據代入的話,如下所示
\[ L(\hat{y},y)=-\sum_{j=1}^4y_j\log{\hat{y}_j}=-y_2\log{\hat{y}_2}=-\log{\hat{y}_2} \]
而我們試圖不斷使這個損失函數變小,也就需要使 \(\hat{y}_2\) 儘可能大。

對於整個訓練集的代價函數 \(J\) 來說,仍然是每個樣本的損失的均值。
\[ J(W^{[1]},b^{[1]},\dots)=\frac{1}{m}\sum_{i=1}^mL(\hat{y}^{(i)},y^{(i)}) \]

References

[1] Coursera深度學習教程中文筆記

[2] 深度學習 500 問

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