文章目錄
引言
本文是吳恩達深度學習第二課:改善深層網絡的筆記。這次內容包括深度學習的實用技巧、提高算法運行效率、超參數調優。
第二課有以下三個部分,本文是第二部分。
參數調優處理
神經網絡中有很多超參數,那要如何找到一套好點的參數設置呢。
比如上面的這些參數,如果要按重要程度來排序的話,紅框裏面的學習率是最重要的;其次是橙框框出的那些參數;再其次是紫框框出的那些;最後是沒有框出的那些。
那如果想調試一些參數值,要怎麼做呢。
之前常見的做法是在網格中取樣本點,比如上面有兩個參數,每個樣本點代表兩個參數的值。然後嘗試這25個點,看哪個選擇效果最好。
而在深度學習中,我們常用下面的做法。就是隨機取點,比如也取25個點。
然後從中選出效果最好的點,這樣做的原因是對於你要解決的問題而言,通常都不知道哪個超參數最重要。就像上面看到的一樣,一些參數比其他參數更重要。
舉個例子,假設超參數一是學習率,超參數二是Adam算法中的。
這種情況下,學習率的取值很重要,而則相對無關緊要。
如果在網絡中取點,
用規整網格的方法試了5個學習率取值,你會發現無論取什麼,結果基本上都是一樣的(在同一個學習率下)。
因爲有25個樣本值,但是隻嘗試了5個取值。
而隨機取值,可以嘗試25個獨立的值。
上面只是兩個超參數,如果有三個的話,就會是一個立方體。
如果有更多的參數就無法畫出來了。
當給超參數取值時,一個慣例是採用由粗到細的策略。
假設有兩個參數,並且你發現紅線畫出的那個取值效果較好,它附近的幾個點也不錯。接下來要做的是,放到這塊小的區域,然後在裏面更加密集地取值。
也就是在上面藍色方格中取更多的點,再搜索哪個點效果最好。
這種從粗到細的搜索經常使用。
但超參數的搜索內容不止這些,下面我來介紹如何爲超參數選擇合適的範圍。
爲超參數選擇合適的範圍
上面我們已經知道了可以隨機取值提升搜索效率,但是這裏的隨機取值並不是在有效值範圍內的隨機均勻取值,而是選擇合適的範圍,用來探究這些超參數哪個重要。
假設想知道網絡的單元數多少合適,通常要預先設定一個範圍,比如50到100,此時可以看到上面從50到100的數軸,我們可以隨機在上面取點(這裏要取整數),這是一個搜索特定超參數的很直觀的方式。
或者我們要選擇神經網絡的層數,或許可以定位2到4中的某個值。
上面都是取整數的例子,那如果取一些小數呢,比如學習率的取值。
假設我們將學習率最小值設爲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之間隨機取值(要有小數),取到的值作爲的冪次,就可以得到我們想要的隨機值。
在python中可以這麼做:
上面取值[-4,0]之間的隨機數,然後取就可以得到我們想要的。
>>> r = -4 * np.random.rand()
>>> r
-2.8480000821641887
>>> 10 ** r
0.001419057253217574
最後另一個棘手的例子是給指數加權平均值的取值。
假設我們認爲的取值是0.9到0.999之間的某個值。
解決這個問題的最好方法是改成考慮的取值,也就是0.1到0.001之間。這樣就和我們上面考慮的問題一樣。
一旦我們得到了一個比較不錯的值,我們還可以應用由粗到細的方法,在附近更加密集的隨機取值。
超參數訓練的實踐:Pandas VS Caviar
你也許已經找到了一組好的超參數設置,並繼續發展算法。但在幾個月的過程中,你可以觀察到你的數據逐漸發生改變,或許只是數據中心更新了服務器。
正因爲有這些原因,可能原來的超參數設定不再好用,所以建議每隔幾個月至少重新評估一次設定的超參數。
關於如何搜索超參數的問題,有兩種主要的流派。
一種是你像照顧嬰兒一樣照顧一個模型
可能第一天收斂效果不錯,然後第二天你增大了學習率,然後過了幾天,你發現學習率太大了,又把學習率改爲之前的設置。可以說每天都花時間照顧此模型。
這種情況通常是你機器的資源不夠好,計算能力不強,不能再同一時間試驗大量模型時才採取的辦法。
而另一種方法則顯得財大氣粗了一點,就是同時試驗多種模型。
一般設定了一些超參數,然後讓它自己運行,可能經過幾周後得到這樣的曲線;同時你可能有不同的模型,第二個模型可能會生成紫色曲線:
顯然紫色曲線對應的模型更好一點,甚至同時試驗了多種模型:
用這種方式可以同時試驗許多不同的參數設定,到最後只要選擇效果最好的那個即可。
一般第一種方法叫做熊貓方式,因爲熊貓的孩子比較少,一次通常只有一個,會花費很多精力撫養熊貓寶寶。
而第二種方式就像魚子醬一樣,一次會產生上億個魚卵。
所以這兩種方法取決於你的計算機資源。
歸一化網絡的激活函數
在深度學習興起後,最重要的一個思想之一就是批歸一化(Batch Normalization),批歸一化會使參數搜索問題變得很容易,使神經網絡對超參數的選擇更加穩定,超參數的範圍也可以更龐大,工作效果也很好。重要的是會使我們很容易的訓練深層網絡。
當訓練一個模型,比如邏輯迴歸時,我們知道歸一化輸入特徵可以加速學習過程。
那更深層的網絡呢,不僅有輸入特徵,每層還有激活值。
如果你想訓練這些參數,比如,那若能歸一化豈不是美滋滋。
在邏輯迴歸中,我們看到如果歸一化輸入特徵,會幫助我們更有效的訓練。
現在的問題是,我們能否歸一化每層的輸出值。嚴格來說是歸一化值。
下面看如何實現批歸一化。
假設你有一些隱藏單元值,從到,這些來自隱藏層,精確的寫法應該是,這裏爲了方便簡化了符號。
已知這些值,先要計算平均值,然後計算方差。
接着歸一化每個值。
同樣防止分母爲零,加上了一個很小的值。
這樣就把這些值歸一化爲均值爲0方差爲1的值,但是我們不想讓隱藏單元總是含有均值0方差1分佈的值,也許有不同的分佈更有意義。
因此要計算
這和是模型的學習參數,不是超參數。
這裏和的作用是可以隨意設置的平均值。
如果,而,
那麼
通過給和賦其他值,就可以使你構造含其他平均值和方差的隱藏單元值。
所以現在用取代來參與神經網絡的後續計算。
我們從這小節學到的是,批歸一化的作用是它適用的歸一化過程不只是輸入層,同樣適用於神經網絡中的深層隱藏神經元。
不過訓練輸入和這些隱藏單元值的一個區別是,我們不想隱藏單元值必須是均值0方差1的標準正態分佈,以便利用激活函數的非線性部分。
所以批歸一化的真正作用是使隱藏單元值的均值和方差歸一化,使它們有固定的均值和方差。
將Batch Norm擬合進神經網絡
假設我們有一個這樣的神經網絡,對於上面這些記號應該很熟悉了吧。那我們要如何加入Batch Norm(簡稱BN)呢,
如果沒有Batch Norm,下一步就是代入激活函數得到激活值了,但是我們今天要在這一步加入Batch Norm。
如上節所說的,我們加入BN,通過參數 計算得到。然後再代入激活函數,得到。這樣就計算完第一層的結果。
BN發生在和的計算過程之間。接下來通過這個來計算,和第一層一樣,我們對進行BN。
這裏要強調的是BN發生在計算和之間的。
這裏我們(每個隱藏層)引入了兩個參數,所有現在網絡的參數是:
這些參數都可以通過模型自己學習的,
更新的公式和參數一樣,同時也可以應用Adam或RMSprop。
在實踐中,BN通常和mini-batch一起使用。
這裏要指出的是
在應用BN時,我們要先將歸一化,結果爲均值0方差1的分佈,然後再通過和進行縮放。 這意味着,無論的值是多少,都會將均值設成0的過程中被減掉,因此在這裏增加的任何常數的數值都不會發生改變,因爲它們會被均值減法所抵消。 也就是使用BN,可以消除這個參數,或者說將它設爲零即可。
的式子中可以直接去掉:
變成由控制轉移或偏置條件。
下面來看下這些參數的維度,的維度是,和的維度也是。
因爲這是隱藏單元的數量,要與的維度匹配才能對其進行轉換。
下面總結一下如何用BN來應用梯度下降法。
Batch Norm爲什麼奏效
我們知道對輸入特徵進行歸一化可以加速學習,而BN做的和輸入特徵歸一化類似的事情。
BN有用的第二個原因是它可以使權重比你的網絡更加深。
下面以一個例子說明。
比如我們有一個網絡,假設已經在所有黑貓的圖像上訓練了數據集,如果現在要把這個網絡應用於其他顏色的貓
此時可能你的模型適用的不會很好。
如果黑貓圖像中的訓練集是上圖左邊那樣分佈的,而其他顏色貓訓練集分佈是右邊那樣的,那麼無法期待在左邊訓練好的模型能同樣在右邊也表現的很好。
訓練集的數據分佈和預測集的數據分佈不一致的問題就叫做“covariate shift”問題(或者可以想成輸入值改變的問題就是covariate shift)。
如果你已經學習了到的映射,此時若的分佈改變了,那麼你可能需要重新訓練你的模型。
還是以一個例子說明下,考慮下面這個神經網絡:
我們從第三個隱藏來看看學習過程,假設已經學習了參數。從該層來看的話,它從上一層得到一些輸入值,接下來它要做些事情希望使輸出值更接近於真實值。
我們先遮住左邊部分,從該層來看,它得到了4個輸入值,用來表示,但這些值也可能是輸入特徵值。
該層的工作是找到一種方式,使這些值映射到。也許現在做得不錯。
現在把遮罩打開,這個網絡還有參數和。如果這些參數發生改變,那麼第三層得到的輸入也會發生改變。
因此也就有了covariate shift問題,所以BN的作用是減少這些輸入值改變的程度。
如果繪製出來的話(這裏取兩個輸入來繪製),BN說的是,的值可以改變,
但是無論怎麼變化,它們的均值和方差是一樣的,由和決定均值和方差具體是多少。
這在一定程度上限制了上一層的參數更新能影響數據分佈的程度,因此說BN減少了輸入值改變的問題。
也可以這樣想,BN減弱了前層參數的作用與後層參數的作用之間的聯繫,它使得網絡每層可以自己學習,稍微獨立於其他層,這有助於加速整個網絡的學習。
BN還有一個作用是有一點正則化效果。
- 每個小批次都被該小批次上計算的均值和方差所縮放(假設每個小批次大小爲像64或128這種相對少的大小)。
- 因爲沒有在整個數據集上計算均值和方差,因此均值和方差會有一點噪音。類似dropout,爲每層激活值增加了噪音。
- 類似dropout,BN有一點正則化效果。因爲給隱藏單元增加了噪音,迫使後面的隱藏單元不會過分依賴於任何一個隱藏單元。
因爲正則化效果比較小,所以還是可以和dropout一起使用。
因爲BN一次只能處理一個mini-batch數據,它在mini-batch上計算均值和方差。因爲測試時沒有mini-batch樣本,所以需要做一些不同的東西以確保預測有意義。
測試時的Batch Norm
BN將你的數據以mini-batch的形式逐一處理,但在測試時,你可能需要對每個樣本逐一處理。
上面是在訓練時用到的BN式子,在訓練是都是應用於mini-batch的。但在測試時需要用其他方式來得到。
典型的做法是用一個指數加權平均來估算,這個指數加權平均涵蓋了所有mini-batch。
假設在層得到很多小批次的均值,對這些均值做指數加權平均就得到了這一隱藏層的估計,同樣地也可以對小批次的進行估計。
最後在測試時,對應於下面這個等式
只要用估計的來計算即可,值是計算出來的,直接用訓練時學到的。
多分類問題-Softmax 迴歸
我們之前的例子都是二分類問題,今天我們來了解下多分類問題。
有一種邏輯迴歸的一般形式叫Softmax迴歸,能預測多個類別的概率。
假設現在不是要識別是否爲貓,而是要識別貓(類1)、狗(類2)和雞(類3),如果不屬於任何一類,則分爲其他(類0)。
我們用表示類別的總數,這裏有4種。
因此我們可以構建一個神經網絡,它的輸出層有4個單元。我們想要輸出單元的值告訴我們屬於這4種類型中每一種的概率有多大。
這裏的會是一個維度的向量,並且因爲輸出的是概率,這4個概率加起來應該等於1。
要做到這一點通常要使用Softmax層。
在計算出最後一層的後,要應用Softmax激活函數。
它的做法是這樣的,首先要計算臨時變量,它的維度這個例子中也是。
然後計算爲向量的歸一化,使得中元素和爲1,它的維度這個例子中也是。
這裏取指數的目的是使得所有的值都爲正數,滿足了概率爲正的定義,同時讓每個值除以總和,這樣所得的值加起來就爲。然後可以根據值的大小得出屬於哪個類別的概率最大。
下面以一個例子說明,
這裏是類別0的值最大,也就是屬於類別0的概率最大。最後用紫線框出來的四個值就是的輸出。
這裏的激活函數是Softmax函數。
上面是Softmax的三個例子,這裏輸入爲,把它們直接接入Softmax層,這裏這裏有3個類別,就會得到3個輸出。上面相當於是一個沒有隱藏層的神經網絡。
從決策邊界可以直覺的感受到,這些決策邊界都是線性的。
當然,深度網絡會有更多的層和神經元,因爲用的激活函數都是非線性的,我們就可以學習到更復雜的非線性決策邊界。
訓練一個Softmax分類器
本小節我們來看下如何訓練一個使用了Softmax層的模型。
先來回顧下上小節的內容,上面是Softmax函數的計算過程,最後由輸入值5變成了0.842。這裏的softmax說的是和hardmax對應的, hardmax是
hardmax會把最大元素的位置上放1,其他放0。這種比較適用於手寫數字識別的one-hot向量。
接下來我們看下如何訓練帶有softmax輸出層的神經網絡。
先來看下損失函數的定義,下面是真實標籤和輸出標籤。
從上面的例子看到,這個結果不太好。因爲這實際上是一隻貓,但是隻給了貓20%的概率。
那麼如果用損失函數來表示這種差別呢,對應了兩個向量的差別第一個想到的應該就是交叉熵了吧。損失函數的定義如下:
這個例子中只有,其他都是,因此上面的項可以簡化爲:
因此
要最小化損失函數,就變成要最小化,即最大化,由softmax的公式可以值,最大也不會超過。
上面是單個樣本的損失函數,那整個訓練集的成本函數要如何計算呢
就是對每個樣本的損失函數值取個平均即可。
最後說一下代碼實現細節,因爲,和都是一個向量
所以向量化實現的話,對於個樣本,寫成:
也這樣表示:
最後看一下有softmax輸出層時如何實現梯度下降法。
我們上面講了前向傳播,那反向傳播呢
關鍵是的表達式。
求表達式可參考:吳恩達深度學習——神經網絡基礎
下面介紹一下深度學習框架,從這次課程開始就可以不用自己實現深度神經網絡了。
深度學習框架
如今又這麼的深度學習框架(教程錄製的時候可能還沒有PyTorch和TensorFlow2)
這裏分享一下選擇框架的標準:
- 便於編程(包括開發和發佈)
- 運行高效
- 完全開放(不僅需要開源,還需要良好的管理)
TensorFlow
(本小節介紹的是TensorFlow1,這還是博主第一次學習TensorFlow1)。
假設現在有一個簡單損失函數需要最小化,
我們來看怎樣利用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建立計算圖