吳恩達深度學習——超參數調優

引言

本文是吳恩達深度學習第二課:改善深層網絡的筆記。這次內容包括深度學習的實用技巧、提高算法運行效率、超參數調優。

第二課有以下三個部分,本文是第二部分。

  1. 深度學習的實用指南
  2. 提高算法運行效率
  3. 超參數調優

參數調優處理

神經網絡中有很多超參數,那要如何找到一套好點的參數設置呢。

在這裏插入圖片描述
比如上面的這些參數,如果要按重要程度來排序的話,紅框裏面的學習率是最重要的;其次是橙框框出的那些參數;再其次是紫框框出的那些;最後是沒有框出的那些。

那如果想調試一些參數值,要怎麼做呢。

在這裏插入圖片描述
之前常見的做法是在網格中取樣本點,比如上面有兩個參數,每個樣本點代表兩個參數的值。然後嘗試這25個點,看哪個選擇效果最好。

而在深度學習中,我們常用下面的做法。就是隨機取點,比如也取25個點。
在這裏插入圖片描述
然後從中選出效果最好的點,這樣做的原因是對於你要解決的問題而言,通常都不知道哪個超參數最重要。就像上面看到的一樣,一些參數比其他參數更重要。

舉個例子,假設超參數一是學習率,超參數二是Adam算法中的ε\varepsilon

這種情況下,學習率的取值很重要,而ε\varepsilon則相對無關緊要。

如果在網絡中取點,
在這裏插入圖片描述
用規整網格的方法試了5個學習率取值,你會發現無論ε\varepsilon取什麼,結果基本上都是一樣的(在同一個學習率α\alpha下)。

因爲有25個樣本值,但是隻嘗試了5個α\alpha取值。

而隨機取值,可以嘗試25個獨立的α\alpha值。

在這裏插入圖片描述

上面只是兩個超參數,如果有三個的話,就會是一個立方體。

在這裏插入圖片描述

如果有更多的參數就無法畫出來了。
當給超參數取值時,一個慣例是採用由粗到細的策略。
在這裏插入圖片描述
假設有兩個參數,並且你發現紅線畫出的那個取值效果較好,它附近的幾個點也不錯。接下來要做的是,放到這塊小的區域,然後在裏面更加密集地取值。
在這裏插入圖片描述
也就是在上面藍色方格中取更多的點,再搜索哪個點效果最好。

這種從粗到細的搜索經常使用。

但超參數的搜索內容不止這些,下面我來介紹如何爲超參數選擇合適的範圍。

爲超參數選擇合適的範圍

上面我們已經知道了可以隨機取值提升搜索效率,但是這裏的隨機取值並不是在有效值範圍內的隨機均勻取值,而是選擇合適的範圍,用來探究這些超參數哪個重要。

在這裏插入圖片描述
假設想知道網絡的單元數多少合適,通常要預先設定一個範圍,比如50到100,此時可以看到上面從50到100的數軸,我們可以隨機在上面取點(這裏要取整數),這是一個搜索特定超參數的很直觀的方式。

或者我們要選擇神經網絡的層數LL,或許可以定位2到4中的某個值。

在這裏插入圖片描述
上面都是取整數的例子,那如果取一些小數呢,比如學習率α\alpha的取值。

假設我們將學習率最小值設爲0.0001,最大值爲1。

在這裏插入圖片描述
如果還是用類似前面的方法的話,即從0.0001到1中隨機取值,可能90%的結果落到了0.1到1之間。(相當於按0.1將1分成了10份,取到0~0.1的概率只有10%)。

這樣看上去不對,此時考慮標尺搜索超參數的方式更合理。
在這裏插入圖片描述
即依次取0.0001、0.001、0.01、0.1、1。然後以冪次-4到0之間隨機取值(要有小數),取到的值作爲1010的冪次,就可以得到我們想要的隨機值。

在python中可以這麼做:

在這裏插入圖片描述
上面σ\sigma取值[-4,0]之間的隨機數,然後取10σ10^\sigma就可以得到我們想要的α\alpha

>>> r = -4 * np.random.rand()
>>> r
-2.8480000821641887
>>> 10 ** r
0.001419057253217574

最後另一個棘手的例子是給指數加權平均值的β\beta取值。

假設我們認爲β\beta的取值是0.9到0.999之間的某個值。

在這裏插入圖片描述

解決這個問題的最好方法是改成考慮1β1 -\beta的取值,也就是0.1到0.001之間。這樣就和我們上面考慮α\alpha的問題一樣。

在這裏插入圖片描述
一旦我們得到了一個比較不錯的值,我們還可以應用由粗到細的方法,在附近更加密集的隨機取值。

超參數訓練的實踐:Pandas VS Caviar

你也許已經找到了一組好的超參數設置,並繼續發展算法。但在幾個月的過程中,你可以觀察到你的數據逐漸發生改變,或許只是數據中心更新了服務器。

正因爲有這些原因,可能原來的超參數設定不再好用,所以建議每隔幾個月至少重新評估一次設定的超參數。

關於如何搜索超參數的問題,有兩種主要的流派。
一種是你像照顧嬰兒一樣照顧一個模型

在這裏插入圖片描述
可能第一天收斂效果不錯,然後第二天你增大了學習率,然後過了幾天,你發現學習率太大了,又把學習率改爲之前的設置。可以說每天都花時間照顧此模型。

這種情況通常是你機器的資源不夠好,計算能力不強,不能再同一時間試驗大量模型時才採取的辦法。

而另一種方法則顯得財大氣粗了一點,就是同時試驗多種模型。

在這裏插入圖片描述
一般設定了一些超參數,然後讓它自己運行,可能經過幾周後得到這樣的曲線;同時你可能有不同的模型,第二個模型可能會生成紫色曲線:

在這裏插入圖片描述
顯然紫色曲線對應的模型更好一點,甚至同時試驗了多種模型:
在這裏插入圖片描述
用這種方式可以同時試驗許多不同的參數設定,到最後只要選擇效果最好的那個即可。

一般第一種方法叫做熊貓方式,因爲熊貓的孩子比較少,一次通常只有一個,會花費很多精力撫養熊貓寶寶。

而第二種方式就像魚子醬一樣,一次會產生上億個魚卵。
在這裏插入圖片描述

所以這兩種方法取決於你的計算機資源。

歸一化網絡的激活函數

在深度學習興起後,最重要的一個思想之一就是批歸一化(Batch Normalization),批歸一化會使參數搜索問題變得很容易,使神經網絡對超參數的選擇更加穩定,超參數的範圍也可以更龐大,工作效果也很好。重要的是會使我們很容易的訓練深層網絡。

當訓練一個模型,比如邏輯迴歸時,我們知道歸一化輸入特徵可以加速學習過程。

在這裏插入圖片描述

那更深層的網絡呢,不僅有輸入特徵,每層還有激活值。

在這裏插入圖片描述
如果你想訓練這些參數,比如W[3],b[3]W^{[3]},b^{[3]},那若能歸一化a[2]a^{[2]}豈不是美滋滋。

在邏輯迴歸中,我們看到如果歸一化輸入特徵x1,x2,x3x_1,x_2,x_3,會幫助我們更有效的訓練w,bw,b

現在的問題是,我們能否歸一化每層的輸出值aa。嚴格來說是歸一化zz值。

下面看如何實現批歸一化。

假設你有一些隱藏單元值,從z(1)z^{(1)}z(m)z^{(m)},這些來自隱藏層ll,精確的寫法應該是z[l](i)z^{[l](i)},這裏爲了方便簡化了ll符號。

已知這些值,先要計算平均值,然後計算方差。
在這裏插入圖片描述
接着歸一化每個z(i)z^{(i)}值。

在這裏插入圖片描述
同樣防止分母爲零,加上了一個很小的值。

這樣就把這些zz值歸一化爲均值爲0方差爲1的值,但是我們不想讓隱藏單元總是含有均值0方差1分佈的值,也許有不同的分佈更有意義。

因此要計算z(i)\overset{\sim}{z}^{(i)}

z(i)=γznorm(i)+β \overset{\sim}{z}^{(i)} = \gamma z^{(i)}_{norm}+ \beta

γ\gammaβ\beta是模型的學習參數,不是超參數。

這裏γ\gammaβ\beta的作用是可以隨意設置z\overset{\sim}{z}的平均值。

如果γ=σ2+ε\gamma = \sqrt{\sigma^2 + \varepsilon},而β=μ\beta = \mu

那麼z(i)=z(i)\overset{\sim}{z}^{(i)} = z^{(i)}

通過給γ\gammaβ\beta賦其他值,就可以使你構造含其他平均值和方差的隱藏單元值。

所以現在用z(i)\overset{\sim}{z}^{(i)}取代z(i)z^{(i)}來參與神經網絡的後續計算。

我們從這小節學到的是,批歸一化的作用是它適用的歸一化過程不只是輸入層,同樣適用於神經網絡中的深層隱藏神經元。

不過訓練輸入和這些隱藏單元值的一個區別是,我們不想隱藏單元值必須是均值0方差1的標準正態分佈,以便利用激活函數的非線性部分。

所以批歸一化的真正作用是使隱藏單元值的均值和方差歸一化,使它們有固定的均值和方差。

將Batch Norm擬合進神經網絡

在這裏插入圖片描述
假設我們有一個這樣的神經網絡,對於上面這些記號應該很熟悉了吧。那我們要如何加入Batch Norm(簡稱BN)呢,

在這裏插入圖片描述
如果沒有Batch Norm,下一步就是代入激活函數得到激活值了,但是我們今天要在這一步加入Batch Norm。

在這裏插入圖片描述
如上節所說的,我們加入BN,通過參數β[1],γ[1]\beta^{[1]},\gamma^{[1]} 計算得到z[1]\overset{\sim}{z}^{[1]}。然後再代入激活函數,得到a[1]=g[1](z[1])a^{[1]} = g^{[1]}(\overset{\sim}{z}^{[1]})。這樣就計算完第一層的結果。

在這裏插入圖片描述
BN發生在ZZaa的計算過程之間。接下來通過這個a[1]a^{[1]}來計算z[2]z^{[2]},和第一層一樣,我們對z[2]z^{[2]}進行BN。

在這裏插入圖片描述
這裏要強調的是BN發生在計算zzaa之間的。
這裏我們(每個隱藏層)引入了兩個參數γ,β\gamma,\beta,所有現在網絡的參數是:

在這裏插入圖片描述
這些參數都可以通過模型自己學習的,

在這裏插入圖片描述
更新的公式和參數W,bW,b一樣,同時也可以應用Adam或RMSprop。

在實踐中,BN通常和mini-batch一起使用。

在這裏插入圖片描述
這裏要指出的是
在這裏插入圖片描述
在應用BN時,我們要先將z[l]z^{[l]}歸一化,結果爲均值0方差1的分佈,然後再通過β\betaγ\gamma進行縮放。 這意味着,無論b[l]b^{[l]}的值是多少,都會將均值設成0的過程中被減掉,因此在這裏增加的任何常數的數值都不會發生改變,因爲它們會被均值減法所抵消。 也就是使用BN,可以消除b[l]b^{[l]}這個參數,或者說將它設爲零即可。

在這裏插入圖片描述
z[l]z^{[l]}的式子中可以直接去掉bb
在這裏插入圖片描述

變成由β[l]\beta^{[l]}控制轉移或偏置條件。

在這裏插入圖片描述
下面來看下這些參數的維度,z[l]z^{[l]}的維度是(n[l],1)(n^{[l]},1)β[l]\beta^{[l]}γ[l]\gamma^{[l]}的維度也是(n[l],1)(n^{[l]},1)

因爲這是隱藏單元的數量,要與z[l]z^{[l]}的維度匹配才能對其進行轉換。

下面總結一下如何用BN來應用梯度下降法。

在這裏插入圖片描述

Batch Norm爲什麼奏效

我們知道對輸入特徵進行歸一化可以加速學習,而BN做的和輸入特徵歸一化類似的事情。

BN有用的第二個原因是它可以使權重比你的網絡更加深。

下面以一個例子說明。

在這裏插入圖片描述
比如我們有一個網絡,假設已經在所有黑貓的圖像上訓練了數據集,如果現在要把這個網絡應用於其他顏色的貓
在這裏插入圖片描述
此時可能你的模型適用的不會很好。
在這裏插入圖片描述
如果黑貓圖像中的訓練集是上圖左邊那樣分佈的,而其他顏色貓訓練集分佈是右邊那樣的,那麼無法期待在左邊訓練好的模型能同樣在右邊也表現的很好。

訓練集的數據分佈和預測集的數據分佈不一致的問題就叫做“covariate shift”問題(或者可以想成輸入值改變的問題就是covariate shift)。

如果你已經學習了xxyy的映射,此時若xx的分佈改變了,那麼你可能需要重新訓練你的模型。

還是以一個例子說明下,考慮下面這個神經網絡:
在這裏插入圖片描述
我們從第三個隱藏來看看學習過程,假設已經學習了參數W[3],b[3]W^{[3]},b^{[3]}。從該層來看的話,它從上一層得到一些輸入值,接下來它要做些事情希望使輸出值y^\hat y更接近於真實值yy

在這裏插入圖片描述

我們先遮住左邊部分,從該層來看,它得到了4個輸入值,用a[2]a^{[2]}來表示,但這些值也可能是輸入特徵值。

該層的工作是找到一種方式,使這些值映射到y^\hat y。也許現在做得不錯。

在這裏插入圖片描述
現在把遮罩打開,這個網絡還有參數W[2],b[2]W^{[2]},b^{[2]}W[1],b[1]W^{[1]},b^{[1]}。如果這些參數發生改變,那麼第三層得到的輸入也會發生改變。

因此也就有了covariate shift問題,所以BN的作用是減少這些輸入值改變的程度。
如果繪製出來的話(這裏取兩個輸入來繪製),BN說的是,z1[2],z2[2]z^{[2]}_1,z^{[2]}_2的值可以改變,
在這裏插入圖片描述
但是無論怎麼變化,它們的均值和方差是一樣的,由γ[2]\gamma^{[2]}β[2]\beta^{[2]}決定均值和方差具體是多少。

這在一定程度上限制了上一層的參數更新能影響數據分佈的程度,因此說BN減少了輸入值改變的問題。

也可以這樣想,BN減弱了前層參數的作用與後層參數的作用之間的聯繫,它使得網絡每層可以自己學習,稍微獨立於其他層,這有助於加速整個網絡的學習。

BN還有一個作用是有一點正則化效果。

  • 每個小批次都被該小批次上計算的均值和方差所縮放(假設每個小批次大小爲像64或128這種相對少的大小)。
  • 因爲沒有在整個數據集上計算均值和方差,因此均值和方差會有一點噪音。類似dropout,爲每層激活值增加了噪音。
  • 類似dropout,BN有一點正則化效果。因爲給隱藏單元增加了噪音,迫使後面的隱藏單元不會過分依賴於任何一個隱藏單元。

因爲正則化效果比較小,所以還是可以和dropout一起使用。

因爲BN一次只能處理一個mini-batch數據,它在mini-batch上計算均值和方差。因爲測試時沒有mini-batch樣本,所以需要做一些不同的東西以確保預測有意義。

測試時的Batch Norm

BN將你的數據以mini-batch的形式逐一處理,但在測試時,你可能需要對每個樣本逐一處理。

在這裏插入圖片描述
上面是在訓練時用到的BN式子,在訓練是都是應用於mini-batch的。但在測試時需要用其他方式來得到μ,σ2\mu,\sigma^2

典型的做法是用一個指數加權平均來估算μ,σ2\mu,\sigma^2,這個指數加權平均涵蓋了所有mini-batch。

在這裏插入圖片描述
假設在ll層得到很多小批次的均值,對這些均值做指數加權平均就得到了這一隱藏層的μ\mu估計,同樣地也可以對小批次的σ2\sigma^2進行估計。
在這裏插入圖片描述
最後在測試時,對應於下面這個等式

在這裏插入圖片描述
只要用估計的μ,σ2\mu,\sigma^2來計算即可,zz值是計算出來的,γ,β\gamma,\beta直接用訓練時學到的。

多分類問題-Softmax 迴歸

我們之前的例子都是二分類問題,今天我們來了解下多分類問題。

有一種邏輯迴歸的一般形式叫Softmax迴歸,能預測多個類別的概率。

假設現在不是要識別是否爲貓,而是要識別貓(類1)、狗(類2)和雞(類3),如果不屬於任何一類,則分爲其他(類0)。

在這裏插入圖片描述

我們用CC表示類別的總數,這裏有4種。

在這裏插入圖片描述

因此我們可以構建一個神經網絡,它的輸出層有4個單元。我們想要輸出單元的值告訴我們屬於這4種類型中每一種的概率有多大。

這裏的y^\hat y會是一個(4,1)(4,1)維度的向量,並且因爲輸出的是概率,這4個概率加起來應該等於1。

要做到這一點通常要使用Softmax層。

在這裏插入圖片描述
在計算出最後一層的Z[L]Z^{[L]}後,要應用Softmax激活函數。

它的做法是這樣的,首先要計算臨時變量t=ez[L]t =e ^{z^{[L]}},它的維度這個例子中也是(4,1)(4,1)

然後計算a[L]a^{[L]}爲向量tt的歸一化,使得a[L]a^{[L]}中元素和爲1,它的維度這個例子中也是(4,1)(4,1)

在這裏插入圖片描述
這裏取指數ee的目的是使得所有的值都爲正數,滿足了概率爲正的定義,同時讓每個值除以總和,這樣所得的值加起來就爲11。然後可以根據值的大小得出屬於哪個類別的概率最大。

下面以一個例子說明,
在這裏插入圖片描述
這裏是類別0的值最大,也就是屬於類別0的概率最大。最後用紫線框出來的四個值就是y^\hat y的輸出。

在這裏插入圖片描述
這裏的激活函數是Softmax函數。

在這裏插入圖片描述
上面是Softmax的三個例子,這裏輸入爲x1,x2x_1,x_2,把它們直接接入Softmax層,這裏這裏有3個類別,就會得到3個輸出。上面相當於是一個沒有隱藏層的神經網絡。

從決策邊界可以直覺的感受到,這些決策邊界都是線性的。

當然,深度網絡會有更多的層和神經元,因爲用的激活函數都是非線性的,我們就可以學習到更復雜的非線性決策邊界。

訓練一個Softmax分類器

本小節我們來看下如何訓練一個使用了Softmax層的模型。
在這裏插入圖片描述
先來回顧下上小節的內容,上面是Softmax函數的計算過程,最後由輸入值5變成了0.842。這裏的softmax說的是和hardmax對應的, hardmax是
在這裏插入圖片描述
hardmax會把最大元素的位置上放1,其他放0。這種比較適用於手寫數字識別的one-hot向量。

接下來我們看下如何訓練帶有softmax輸出層的神經網絡。

先來看下損失函數的定義,下面是真實標籤yy和輸出標籤y^\hat y

在這裏插入圖片描述
從上面的例子看到,這個結果不太好。因爲這實際上是一隻貓,但是隻給了貓20%的概率。

那麼如果用損失函數來表示這種差別呢,對應了兩個向量的差別第一個想到的應該就是交叉熵了吧。損失函數的定義如下:

在這裏插入圖片描述
這個例子中只有y2=1y_2 =1,其他都是00,因此上面的項可以簡化爲:
在這裏插入圖片描述
因此
在這裏插入圖片描述
要最小化損失函數,就變成要最小化logy^2-\log \hat y_2,即最大化y^2\hat y_2,由softmax的公式可以值,最大也不會超過11

上面是單個樣本的損失函數,那整個訓練集的成本函數JJ要如何計算呢

在這裏插入圖片描述
就是對每個樣本的損失函數值取個平均即可。

最後說一下代碼實現細節,因爲C=4C=4yyy^\hat y都是一個(4,1)(4,1)向量

所以向量化實現的話,對於mm個樣本,YY寫成:

在這裏插入圖片描述
Y^\hat Y也這樣表示:
在這裏插入圖片描述
最後看一下有softmax輸出層時如何實現梯度下降法。

在這裏插入圖片描述
我們上面講了前向傳播,那反向傳播呢
在這裏插入圖片描述
關鍵是dzdz的表達式。

在這裏插入圖片描述
dzdz表達式可參考:吳恩達深度學習——神經網絡基礎

下面介紹一下深度學習框架,從這次課程開始就可以不用自己實現深度神經網絡了。

深度學習框架


如今又這麼的深度學習框架(教程錄製的時候可能還沒有PyTorch和TensorFlow2)

這裏分享一下選擇框架的標準:

  • 便於編程(包括開發和發佈)
  • 運行高效
  • 完全開放(不僅需要開源,還需要良好的管理)

TensorFlow

(本小節介紹的是TensorFlow1,這還是博主第一次學習TensorFlow1)。

假設現在有一個簡單損失函數JJ需要最小化,

在這裏插入圖片描述
我們來看怎樣利用TensorFlow將其最小化。

# 定義參數w
w = tf.Variable(0,dtype=tf.float32)
# 定義損失函數
cost = tf.add(tf.add(w**2,tf.multiply(-10.,w)),25)# w^2  - 10w + 25
# 學習算法 用0.01的學習率來最小化損失函數 
train = tf.train.GradientDescentOptimizer(0.01).minimize(cost)


init = tf.global_variables_initializer()
session = tf.Session()
session.run(init)
print(session.run(w)) # 初始爲0

session.run(train)
print(session.run(w)) # 運行一次梯度下降後,得到的w是0.099999994

在這裏插入圖片描述
下面我們運行梯度下降1000次迭代。

for i in range(1000):
    session.run(train)
print(session.run(w))

在這裏插入圖片描述
運行1000次梯度下降後,得到的w變成了4.9999,此時已經很接近最優值了。

基本上用tf只要實現前向傳播,它能弄明白如何做反向傳播和梯度計算。

#cost = tf.add(tf.add(w**2,tf.multiply(-10.,w)),25)# w^2  - 10w + 25
cost = w**2 - 10*w +25

tf還重載了運行符,這樣上面的代碼可以簡寫。

上面我們最小化的是固定參數的損失函數。

如果你想要最小化的函數是訓練集函數又該怎麼辦呢,如果把訓練數據加入TensorFlow程序呢

# 定義參數w
w = tf.Variable(0,dtype=tf.float32)
# 把x定義成一個3x1的矩陣
x = tf.placeholder(tf.float32,[3,1])
# 定義損失函數
#cost = tf.add(tf.add(w**2,tf.multiply(-10.,w)),25)# w^2  - 10w + 25
# cost = w**2 - 10*w +25
cost = x[0][0]*w**2 + x[1][0]*w + x[2][0]

現在x變成了控制這個二次函數係數的數據,這個placeholder說的是後面會爲x提供值。

# 模擬x的數據
cofficients = np.array([[1.],[-10.],[25.]])


# 定義參數w
w = tf.Variable(0,dtype=tf.float32)
# 把x定義成一個3x1的矩陣
x = tf.placeholder(tf.float32,[3,1])
# 定義損失函數
#cost = tf.add(tf.add(w**2,tf.multiply(-10.,w)),25)# w^2  - 10w + 25
# cost = w**2 - 10*w +25
cost = x[0][0]*w**2 + x[1][0]*w + x[2][0]

# 學習算法 用0.01的學習率來最小化損失函數 
train = tf.train.GradientDescentOptimizer(0.01).minimize(cost)


init = tf.global_variables_initializer()
session = tf.Session()
session.run(init)
print(session.run(w)) # 初始爲0

session.run(train,feed_dict={x:cofficients})
print(session.run(w)) 

for i in range(1000):
    session.run(train,feed_dict={x:cofficients})
print(session.run(w))

在這裏插入圖片描述

session = tf.Session()
session.run(init)
print(session.run(w)) # 初始爲0

這三行代碼在tf裏面是符合表達習慣的,

with tf.Session() as sessioin:
    session.run(init)
    print(session.run(w)) # 初始爲0

有些程序員習慣上面這麼寫。

這段代碼cost = x[0][0]*w**2 + x[1][0]*w + x[2][0]的作用是讓tf建立計算圖
在這裏插入圖片描述

參考

  1. 吳恩達深度學習 專項課程
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章