文章目錄
前向傳播
從感知機到神經網絡
深度神經網絡(Deep Neural Networks, 以下簡稱DNN)是深度學習的基礎,而要理解DNN,首先我們要理解DNN模型,下面我們就對DNN的模型與前向傳播算法做一個總結。
輸出和輸入之間學習到一個線性關係,得到中間輸出結果:
接着是一個神經元激活函數:
從而得到我們想要的輸出結果1或者-1。
這個模型只能用於二元分類,且無法學習比較複雜的非線性模型,因此在工業界無法使用。
而神經網絡則在感知機的模型上做了擴展,總結下主要有三點:
1)加入了隱藏層,隱藏層可以有多層,增強模型的表達能力,如下圖實例,當然增加了這麼多隱藏層模型的複雜度也增加了好多。
2)輸出層的神經元也可以不止一個輸出,可以有多個輸出,這樣模型可以靈活的應用於分類迴歸,以及其他的機器學習領域比如降維和聚類等。多個神經元輸出的輸出層對應的一個實例如下圖,輸出層現在有4個神經元了。
3) 對激活函數做擴展,感知機的激活函數是sign(z),雖然簡單但是處理能力有限,因此神經網絡中一般使用的其他的激活函數,比如我們在邏輯迴歸裏面使用過的Sigmoid函數,即:
DNN的基本結構
上一節我們瞭解了神經網絡基於感知機的擴展,而DNN可以理解爲有很多隱藏層的神經網絡。這個很多其實也沒有什麼度量標準, 多層神經網絡和深度神經網絡DNN其實也是指的一個東西,當然,DNN有時也叫做多層感知機(Multi-Layer perceptron,MLP), 名字實在是多。後面我們講到的神經網絡都默認爲DNN。
從DNN按不同層的位置劃分,DNN內部的神經網絡層可以分爲三類,輸入層,隱藏層和輸出層,如下圖示例,一般來說第一層是輸入層,最後一層是輸出層,而中間的層數都是隱藏層。
層與層之間是全連接的,也就是說,第i層的任意一個神經元一定與第i+1層的任意一個神經元相連。雖然DNN看起來很複雜,但是從小的局部模型來說,還是和感知機一樣,即一個線性關係加上一個激活函數σ(z)。
由於DNN層數多,則我們的線性關係係數w和偏倚b的數量也就是很多了。具體的參數在DNN是如何定義的呢?
首先我們來看看線性關係係數w的定義。以下圖一個三層的DNN爲例,第二層的第4個神經元到第三層的第2個神經元的線性係數定義爲。上標3代表線性係數w所在的層數,而下標對應的是輸出的第三層索引2和輸入的第二層索引4。你也許會問,爲什麼不是, 而是呢?這主要是爲了便於模型用於矩陣表示運算,如果是而每次進行矩陣運算是wTx+b,需要進行轉置。將輸出的索引放在前面的話,則線性運算不用轉置,即直接爲wx+b。總結下,第l−1層的第k個神經元到第l層的第j個神經元的線性係數定義爲。注意,輸入層是沒有w參數的。
再來看看偏倚b的定義。還是以這個三層的DNN爲例,第二層的第三個神經元對應的偏倚定義爲。其中,上標2代表所在的層數,下標3代表偏倚所在的神經元的索引。同樣的道理,第三個的第一個神經元的偏倚應該表示爲。同樣的,輸入層是沒有偏倚參數b的。
DNN前向傳播算法數學原理
在上一節,我們已經介紹了DNN各層線性關係係數w,偏倚b的定義。假設我們選擇的激活函數是σ(z),隱藏層和輸出層的輸出值爲a,則對於下圖的三層DNN,利用和感知機一樣的思路,我們可以利用上一層的輸出計算下一層的輸出,也就是所謂的DNN前向傳播算法。
對於第二層的的輸出,我們有:
對於第三層的的輸出,我們有:
將上面的例子一般化,假設第l−1層共有m個神經元,則對於第l層的第j個神經元的輸出,我們有
其中,如果l=2,則對於的即爲輸入層的xk。
從上面可以看出,使用代數法一個個的表示輸出比較複雜,而如果使用矩陣法則比較的簡潔。假設第l−1層共有m個神經元,而第l層共有n個神經元,則第l層的線性係數w組成了一個n×m的矩陣, 第l層的偏倚b組成了一個n×1的向量 , 第l−1層的的輸出a組成了一個m×1的向量,第l層的的未激活前線性輸出z組成了一個n×1的向量, 第l層的的輸出a組成了一個n×1的向量。則用矩陣法表示,第l層的輸出爲:
DNN前向傳播算法
有了上一節的數學推導,DNN的前向傳播算法也就不難了。所謂的DNN的前向傳播算法也就是利用我們的若干個權重係數矩陣W,偏倚向量b來和輸入值向量x進行一系列線性運算和激活運算,從輸入層開始,一層層的向後計算,一直到運算到輸出層,得到輸出結果爲值。
輸入: 總層數L,所有隱藏層和輸出層對應的矩陣W,偏倚向量b,輸入值向量x
輸出:輸出層的輸出aL
1) 初始化
2) for l=2 to L, 計算:
最後的結果即爲輸出aL。
反向傳播算法(BP)
DNN反向傳播算法要解決的問題
在瞭解DNN的反向傳播算法前,我們先要知道DNN反向傳播算法要解決的問題,也就是說,什麼時候我們需要這個反向傳播算法?
回到我們監督學習的一般問題,假設我們有m個訓練樣本:{(x1,y1),(x2,y2),…,(xm,ym)},其中x爲輸入向量,特徵維度爲n_in,而y爲輸出向量,特徵維度爲n_out。我們需要利用這m個樣本訓練出一個模型,當有一個新的測試樣本(xtest,?)來到時, 我們可以預測ytest向量的輸出。
如果我們採用DNN的模型,即我們使輸入層有n_in個神經元,而輸出層有n_out個神經元。再加上一些含有若干神經元的隱藏層。此時我們需要找到合適的所有隱藏層和輸出層對應的線性係數矩陣W,偏倚向量b,讓所有的訓練樣本輸入計算出的輸出儘可能的等於或很接近樣本輸出。怎麼找到合適的參數呢?
如果大家對傳統的機器學習的算法優化過程熟悉的話,這裏就很容易聯想到我們可以用一個合適的損失函數來度量訓練樣本的輸出損失,接着對這個損失函數進行優化求最小化的極值,對應的一系列線性係數矩陣W,偏倚向量b即爲我們的最終結果。在DNN中,損失函數優化極值求解的過程最常見的一般是通過梯度下降法來一步步迭代完成的,當然也可以是其他的迭代方法比如牛頓法與擬牛頓法。如果大家對梯度下降法不熟悉,建議先閱讀我之前寫的梯度下降(Gradient Descent)小結。
對DNN的損失函數用梯度下降法進行迭代優化求極小值的過程即爲我們的反向傳播算法。
DNN反向傳播算法的基本思路
在進行DNN反向傳播算法前,我們需要選擇一個損失函數,來度量訓練樣本計算出的輸出和真實的訓練樣本輸出之間的損失。你也許會問:訓練樣本計算出的輸出是怎麼得來的?這 個輸出是隨機選擇一系列W,b,用我們上一節的前向傳播算法計算出來的。即通過一系列的計算:。計算到輸出層第L層對應的aL即爲前向傳播算法計算出來的輸出。
回到損失函數,DNN可選擇的損失函數有不少,爲了專注算法,這裏我們使用最常見的均方差來度量損失。即對於每個樣本,我們期望最小化下式:
其中,aL和y爲特徵維度爲n_out的向量,而||S||2爲S的L2範數。
損失函數有了,現在我們開始用梯度下降法迭代求解每一層的W,b。
首先是輸出層第L層。注意到輸出層的W,b滿足下式:
這樣對於輸出層的參數,我們的損失函數變爲:
這樣求解W,b的梯度就簡單了
注意上式中有一個符號⊙,它代表Hadamard積,對於兩個維度相同的向量和,則
我們注意到在求解輸出層的W,b的時候,有中間依賴部分,因此我們可以把公共的部分即對zL先算出來,記爲:
現在我們終於把輸出層的梯度算出來了,那麼如何計算上一層L−1層的梯度,上上層L−2層的梯度呢?這裏我們需要一步步的遞推,注意到對於第l層的未激活輸出zl,它的梯度可以表示爲:
如果我們可以依次計算出第l層的δl,則該層的Wl,bl很容易計算?爲什麼呢?注意到根據前向傳播算法,我們有:
所以根據上式我們可以很方便的計算出第l層的Wl,bl的梯度如下:
其中,第一個式子的推導可以參考機器學習中的矩陣向量求導(四) 矩陣向量求導鏈式法則中第三節的最後一個公式。
那麼現在問題的關鍵就是要求出δl了。這裏我們用數學歸納法,第L層的δL上面我們已經求出, 假設第l+1層的δl+1已經求出來了,那麼我們如何求出第l層的δl呢?我們注意到:
可見,用歸納法遞推和的關鍵在於求解
而和的關係其實很容易找出:
這樣很容易求出:
將上式帶入上面和關係式我們得到:
DNN反向傳播算法過程
現在我們總結下DNN反向傳播算法的過程。由於梯度下降法有批量(Batch),小批量(mini-Batch),隨機三個變種,爲了簡化描述,這裏我們以最基本的批量梯度下降法爲例來描述反向傳播算法。實際上在業界使用最多的是mini-Batch的梯度下降法。不過區別僅僅在於迭代時訓練樣本的選擇而已。
輸入: 總層數L,以及各隱藏層與輸出層的神經元個數,激活函數,損失函數,迭代步長α,最大迭代次數MAX與停止迭代閾值ϵ,輸入的m個訓練樣本
輸出:各隱藏層與輸出層的線性關係係數矩陣W和偏倚向量b
1) 初始化各隱藏層與輸出層的線性關係係數矩陣W和偏倚向量b的值爲一個隨機值。
2)for iter to 1 to MAX:
2-1) for i =1 to m:
a) 將DNN輸入a1設置爲xi
b) for l=2 to L,進行前向傳播算法計算
c) 通過損失函數計算輸出層的δi,L
d) for l= L-1 to 2, 進行反向傳播算法計算
2-2) for l = 2 to L,更新第l層的Wl,bl:
2-3) 如果所有W,b的變化值都小於停止迭代閾值ϵ,則跳出迭代循環到步驟3。
3) 輸出各隱藏層與輸出層的線性關係係數矩陣W和偏倚向量b。
損失函數和激活函數的選擇
均方差損失函數+Sigmoid激活函數的問題
在講反向傳播算法時,我們用均方差損失函數和Sigmoid激活函數做了實例,首先我們就來看看均方差+Sigmoid的組合有什麼問題。
首先我們回顧下Sigmoid激活函數的表達式爲:
σ(z)的函數圖像如下:
從圖上可以看出,對於Sigmoid,當z的取值越來越大後,函數曲線變得越來越平緩,意味着此時的導數σ′(z)也越來越小。同樣的,當z的取值越來越小時,也有這個問題。僅僅在z取值爲0附近時,導數σ′(z)的取值較大。
在上篇講的均方差+Sigmoid的反向傳播算法中,每一層向前遞推都要乘以σ′(z),得到梯度變化值。Sigmoid的這個曲線意味着在大多數時候,我們的梯度變化值很小,導致我們的W,b更新到極值的速度較慢,也就是我們的算法收斂速度較慢。那麼有什麼什麼辦法可以改進呢?
使用交叉熵損失函數+Sigmoid激活函數改進DNN算法收斂速度
上一節我們講到Sigmoid的函數特性導致反向傳播算法收斂速度慢的問題,那麼如何改進呢?換掉Sigmoid?這當然是一種選擇。另一種常見的選擇是用交叉熵損失函數來代替均方差損失函數。
我們來看看二分類時每個樣本的交叉熵損失函數的形式:
這個形式其實很熟悉,在邏輯迴歸原理小結中其實我們就用到了類似的形式,只是當時我們是用最大似然估計推導出來的,而這個損失函數的學名叫交叉熵。
使用了交叉熵損失函數,就能解決Sigmoid函數導數變化大多數時候反向傳播算法慢的問題嗎?我們來看看當使用交叉熵時,我們輸出層δL的梯度情況。
可見此時我們的δl梯度表達式裏面已經沒有了σ′(z),作爲一個特例,回顧一下我們上一節均方差損失函數時在δL梯度,
對比兩者在第L層的δL梯度表達式,就可以看出,使用交叉熵,得到的的δl梯度表達式沒有了σ′(z),梯度爲預測值和真實值的差距,這樣求得的的地圖也不包含σ′(z),因此避免了反向傳播收斂速度慢的問題。
通常情況下,如果我們使用了sigmoid激活函數,交叉熵損失函數肯定比均方差損失函數好用。
使用對數似然損失函數和softmax激活函數進行DNN分類輸出
在前面我們講的所有DNN相關知識中,我們都假設輸出是連續可導的值。但是如果是分類問題,那麼輸出是一個個的類別,那我們怎麼用DNN來解決這個問題呢?
比如假設我們有一個三個類別的分類問題,這樣我們的DNN輸出層應該有三個神經元,假設第一個神經元對應類別一,第二個對應類別二,第三個對應類別三,這樣我們期望的輸出應該是(1,0,0),(0,1,0)和(0,0,1)這三種。即樣本真實類別對應的神經元輸出應該無限接近或者等於1,而非改樣本真實輸出對應的神經元的輸出應該無限接近或者等於0。或者說,我們希望輸出層的神經元對應的輸出是若干個概率值,這若干個概率值即我們DNN模型對於輸入值對於各類別的輸出預測,同時爲滿足概率模型,這若干個概率值之和應該等於1。
DNN分類模型要求是輸出層神經元輸出的值在0到1之間,同時所有輸出值之和爲1。很明顯,現有的普通DNN是無法滿足這個要求的。但是我們只需要對現有的全連接DNN稍作改良,即可用於解決分類問題。在現有的DNN模型中,我們可以將輸出層第i個神經元的激活函數定義爲如下形式:
其中,nL是輸出層第L層的神經元個數,或者說我們的分類問題的類別數。
很容易看出,所有的aLi都是在(0,1) 之間的數字,而作爲歸一化因子保證了所有的aLi之和爲1。
這個方法很簡潔漂亮,僅僅只需要將輸出層的激活函數從Sigmoid之類的函數轉變爲上式的激活函數即可。上式這個激活函數就是我們的softmax激活函數。它在分類問題中有廣泛的應用。將DNN用於分類問題,在輸出層用softmax激活函數也是最常見的了。
下面這個例子清晰的描述了softmax激活函數在前向傳播算法時的使用。假設我們的輸出層爲三個神經元,而未激活的輸出爲3,1和-3,我們求出各自的指數表達式爲:20,2.7和0.05,我們的歸一化因子即爲22.75,這樣我們就求出了三個類別的概率輸出分佈爲0.88,0.12和0。
從上面可以看出,將softmax用於前向傳播算法是也很簡單的。那麼在反向傳播算法時還簡單嗎?反向傳播的梯度好計算嗎?答案是Yes!
對於用於分類的softmax激活函數,對應的損失函數一般都是用對數似然函數,即:
其中yk的取值爲0或者1,如果某一訓練樣本的輸出爲第i類。則yi=1,其餘的j≠i都有yj=0。由於每個樣本只屬於一個類別,所以這個對數似然函數可以簡化爲:
其中i即爲訓練樣本真實的類別序號。
可見損失函數只和真實類別對應的輸出有關,這樣假設真實類別是第i類,則其他不屬於第i類序號對應的神經元的梯度導數直接爲0。對於真實類別第i類,他對應的第j個w鏈接對應的梯度計算爲:
同樣的可以得到bLi的梯度表達式爲:
可見,梯度計算也很簡潔,也沒有第一節說的訓練速度慢的問題。舉個例子,假如我們對於第2類的訓練樣本,通過前向算法計算的未激活輸出爲(1,5,3),則我們得到softmax激活後的概率輸出爲:(0.015,0.866,0.117)。由於我們的類別是第二類,則反向傳播的梯度應該爲:(0.015,0.866-1,0.117)。
梯度爆炸梯度消失與ReLU激活函數
學習DNN,大家一定聽說過梯度爆炸和梯度消失兩個詞。尤其是梯度消失,是限制DNN與深度學習的一個關鍵障礙,目前也沒有完全攻克。
什麼是梯度爆炸和梯度消失呢?從理論上說都可以寫一篇論文出來。不過簡單理解,就是在反向傳播的算法過程中,由於我們使用了是矩陣求導的鏈式法則,有一大串連乘,如果連乘的數字在每層都是小於1的,則梯度越往前乘越小,導致梯度消失,而如果連乘的數字在每層都是大於1的,則梯度越往前乘越大,導致梯度爆炸。
比如我們在前一篇反向傳播算法裏面講到了δ的計算,可以表示爲:
如果不巧我們的樣本導致每一層的都小於1,則隨着反向傳播算法的進行,我們的δl會隨着層數越來越小,甚至接近越0,導致梯度幾乎消失,進而導致前面的隱藏層的W,b參數隨着迭代的進行,幾乎沒有大的改變,更談不上收斂了。這個問題目前沒有完美的解決辦法。
而對於梯度爆炸,則一般可以通過調整我們DNN模型中的初始化參數得以解決。
對於無法完美解決的梯度消失問題,目前有很多研究,一個可能部分解決梯度消失問題的辦法是使用ReLU(Rectified Linear Unit)激活函數,ReLU在卷積神經網絡CNN中得到了廣泛的應用,在CNN中梯度消失似乎不再是問題。那麼它是什麼樣子呢?其實很簡單,比我們前面提到的所有激活函數都簡單,表達式爲:
也就是說大於等於0則不變,小於0則激活後爲0。就這麼一玩意就可以解決梯度消失?至少部分是的。具體的原因現在其實也沒有從理論上得以證明。這裏我也就不多說了。
DNN其他激活函數
除了上面提到了激活函數,DNN常用的激活函數還有:
1) tanh:這個是sigmoid的變種,表達式爲:
tanh激活函數和sigmoid激活函數的關係爲:
tanh和sigmoid對比主要的特點是它的輸出落在了[-1,1],這樣輸出可以進行標準化。同時tanh的曲線在較大時變得平坦的幅度沒有sigmoid那麼大,這樣求梯度變化值有一些優勢。當然,要說tanh一定比sigmoid好倒不一定,還是要具體問題具體分析。
2) softplus:這個其實就是sigmoid函數的原函數,表達式爲:
它的導數就是sigmoid函數。softplus的函數圖像和ReLU有些類似。它出現的比ReLU早,可以視爲ReLU的鼻祖。
3)PReLU:從名字就可以看出它是ReLU的變種,特點是如果未激活值小於0,不是簡單粗暴的直接變爲0,而是進行一定幅度的縮小。如下圖。當然,由於ReLU的成功,有很多的跟風者,有其他各種變種ReLU,這裏就不多提了。
DNN損失函數和激活函數小結
上面我們對DNN損失函數和激活函數做了詳細的討論,重要的點有:1)如果使用sigmoid激活函數,則交叉熵損失函數一般肯定比均方差損失函數好。2)如果是DNN用於分類,則一般在輸出層使用softmax激活函數和對數似然損失函數。3)ReLU激活函數對梯度消失問題有一定程度的解決,尤其是在CNN模型中.
正則化
DNN的L1&L2正則化
想到正則化,我們首先想到的就是L1正則化和L2正則化。L1正則化和L2正則化原理類似,這裏重點講述DNN的L2正則化。
而DNN的L2正則化通常的做法是隻針對與線性係數矩陣W,而不針對偏倚係數b。利用我們之前的機器學習的知識,我們很容易可以寫出DNN的L2正則化的損失函數。
假如我們的每個樣本的損失函數是均方差損失函數,則所有的m個樣本的損失函數爲:
則加上了L2正則化後的損失函數是:
其中,λ即我們的正則化超參數,實際使用時需要調參。而w爲所有權重矩陣W的所有列向量。
如果使用上式的損失函數,進行反向傳播算法時,流程和沒有正則化的反向傳播算法完全一樣,區別僅僅在於進行梯度下降法時,W的更新公式。
回想我們在深度神經網絡(DNN)反向傳播算法(BP)中,W的梯度下降更新公式爲:
則加入L2正則化以後,迭代更新公式變成:
注意到上式中的梯度計算中1m我忽略了,因爲α是常數,而除以m也是常數,所以等同於用了新常數α來代替αm。進而簡化表達式,但是不影響損失算法。
類似的L2正則化方法可以用於交叉熵損失函數或者其他的DNN損失函數,這裏就不累述了。
DNN通過集成學習的思路正則化
除了常見的L1&L2正則化,DNN還可以通過集成學習的思路正則化。在集成學習原理小結中,我們講到集成學習有Boosting和Bagging兩種思路。而DNN可以用Bagging的思路來正則化。常用的機器學習Bagging算法中,隨機森林是最流行的。它 通過隨機採樣構建若干個相互獨立的弱決策樹學習器,最後採用加權平均法或者投票法決定集成的輸出。在DNN中,我們一樣使用Bagging的思路。不過和隨機森林不同的是,我們這裏不是若干個決策樹,而是若干個DNN的網絡。
首先我們要對原始的m個訓練樣本進行有放回隨機採樣,構建N組m個樣本的數據集,然後分別用這N組數據集去訓練我們的DNN。即採用我們的前向傳播算法和反向傳播算法得到N個DNN模型的W,b參數組合,最後對N個DNN模型的輸出用加權平均法或者投票法決定最終輸出。
不過用集成學習Bagging的方法有一個問題,就是我們的DNN模型本來就比較複雜,參數很多。現在又變成了N個DNN模型,這樣參數又增加了N倍,從而導致訓練這樣的網絡要花更加多的時間和空間。因此一般N的個數不能太多,比如5-10個就可以了。
DNN通過dropout 正則化
這裏我們再講一種和Bagging類似但是又不同的正則化方法:Dropout。
所謂的Dropout指的是在用前向傳播算法和反向傳播算法訓練DNN模型時,一批數據迭代時,隨機的從全連接DNN網絡中去掉一部分隱藏層的神經元。
比如我們本來的DNN模型對應的結構是這樣的:
在對訓練集中的一批數據進行訓練時,我們隨機去掉一部分隱藏層的神經元,並用去掉隱藏層的神經元的網絡來擬合我們的一批訓練數據。如下圖,去掉了一半的隱藏層神經元:
然後用這個去掉隱藏層的神經元的網絡來進行一輪迭代,更新所有的W,b。這就是所謂的dropout。
當然,dropout並不意味着這些神經元永遠的消失了。在下一批數據迭代前,我們會把DNN模型恢復成最初的全連接模型,然後再用隨機的方法去掉部分隱藏層的神經元,接着去迭代更新W,b。當然,這次用隨機的方法去掉部分隱藏層後的殘缺DNN網絡和上次的殘缺DNN網絡並不相同。
總結下dropout的方法: 每輪梯度下降迭代時,它需要將訓練數據分成若干批,然後分批進行迭代,每批數據迭代時,需要將原始的DNN模型隨機去掉部分隱藏層的神經元,用殘缺的DNN模型來迭代更新W,b。每批數據迭代更新完畢後,要將殘缺的DNN模型恢復成原始的DNN模型。
從上面的描述可以看出dropout和Bagging的正則化思路還是很不相同的。dropout模型中的W,b是一套,共享的。所有的殘缺DNN迭代時,更新的是同一組W,b;而Bagging正則化時每個DNN模型有自己獨有的一套W,b參數,相互之間是獨立的。當然他們每次使用基於原始數據集得到的分批的數據集來訓練模型,這點是類似的。
使用基於dropout的正則化比基於bagging的正則化簡單,這顯而易見,當然天下沒有免費的午餐,由於dropout會將原始數據分批迭代,因此原始數據集最好較大,否則模型可能會欠擬合。
DNN通過增強數據集正則化
增強模型泛化能力最好的辦法是有更多更多的訓練數據,但是在實際應用中,更多的訓練數據往往很難得到。有時候我們不得不去自己想辦法能無中生有,來增加訓練數據集,進而得到讓模型泛化能力更強的目的。
對於我們傳統的機器學習分類迴歸方法,增強數據集還是很難的。你無中生有出一組特徵輸入,卻很難知道對應的特徵輸出是什麼。但是對於DNN擅長的領域,比如圖像識別,語音識別等則是有辦法的。以圖像識別領域爲例,對於原始的數據集中的圖像,我們可以將原始圖像稍微的平移或者旋轉一點點,則得到了一個新的圖像。雖然這是一個新的圖像,即樣本的特徵是新的,但是我們知道對應的特徵輸出和之前未平移旋轉的圖像是一樣的。
其他DNN正則化方法
DNN的正則化的方法是很多的,還是持續的研究中。在Deep Learning這本書中,正則化是洋洋灑灑的一大章。裏面提到的其他正則化方法有:Noise Robustness, Adversarial Training,Early Stopping等