神经网络与深度学习三:编写单隐层神经网络
三:編寫單隱層神經網絡
1 神經網絡概述
這篇文章你會學到如何實現一個神經網絡,在我們深入學習技術細節之前,現在先大概快速的了解一下如何實現神經網絡,如果你對某些內容不甚理解(后面的文章中會深入其中的細節)
上周我們討論了logistic回歸,一起了解了這個模型
和下面這個流程圖的聯系
這里面你需要輸入特征x,參數w和b,用那些計算z,然后用z計算出 a,我們用a表示 y ^ \hat{y} y^?,接下來你就可以計算損失函數L,神經網絡就是這個樣子。之前提到過你可以把很多sigmoid單元堆疊起來構成一個神經網絡
其中的每一個結點都像logistic的計算(對應像z這樣的計算,然后是類似a的計算)
用x表示輸入特征,還有參數W和b這樣你就計算出了 z [ 1 ] z^{[1]} z[1]
用上標[i]表示第i層,使用[]的目的是區分用來表示單個訓練樣本的圓括號, x ( i ) x^{(i)} x(i)表示第i個樣本,上標[1],[2]用于表示不同的層。然后使用類似logistic回歸去計算 z [ 1 ] z^{[1]} z[1]之后,需要用 s i g m o i d ( z [ 1 ] ) sigmoid(z^{[1]}) sigmoid(z[1])計算 a [ 1 ] a^{[1]} a[1],接下來用另一個線性方程計算 z [ 2 ] z^{[2]} z[2],接著計算 a [ 2 ] a^{[2]} a[2], a [ 2 ] a^{[2]} a[2]就是整個神經網絡最終輸出
用 y ^ \hat{y} y^?表示網絡的輸出,我知道其中有很多細節,但關鍵你要學到這種直覺,logistic回歸中得到z后直接計算a,而這個神經網絡中我們要做多次計算,反復計算z和a,最后計算損失函數。應該還記得在logistic回歸中會有這種反向傳播用來計算da,dz。
同樣在神經網絡中也有類似的反向計算,最后會計算 d a [ 2 ] da^{[2]} da[2], d z [ 2 ] dz^{[2]} dz[2]等等,
現在你已經大概了解了神經網絡是什么樣,基本上我們把logsitic回歸重復了兩次。如果你對以上內容沒有理解,別擔心,接下來會慢慢講解這些細節。
2 神經網絡的表示
我們先看一下只有一個隱藏層的神經網絡如下
在一個神經網絡中,當你用監督學習訓練它的時候,訓練集包含了輸入x,目標輸出y,隱藏層的含義是在訓練集中這些中間節點的真正數值我們是不知道的,在訓練集中你看不到它們的數值。現在我們再引入幾個符號
之前我們用向量x表示輸入特征,輸入特征的數值還有另外一種表示方式,我們用 a [ 0 ] a^{[0]} a[0]表示,這個a也表示激活的意思,它意味著網絡中不同層的值會傳遞給后面的值,輸入層將x的值傳遞給隱藏層,我們將輸入層的激活值稱為 a [ 0 ] a^{[0]} a[0],下一次即隱藏層也同樣會產生一些激活值將其記作 a [ 1 ] a^{[1]} a[1]。隱藏層的第一個結點命名為 a 1 [ 1 ] a^{[1]}_1 a1[1]?,依次類推
這里的 a [ 1 ] a^{[1]} a[1]是一個四維向量,寫成python代碼它是一個4×1的矩陣,或者大小為4的列向量
它是四維的因為本例中我們有四個結點,四個單元 四個隱藏層單元,最后的輸出層會產生某個數值 a [ 2 ] a^{[2]} a[2]是個實數, y ^ = a [ 2 ] \hat{y}=a^{[2]} y^?=a[2],所以這就和logistic回歸類似,在logistic回歸中 y ^ = a \hat{y}=a y^?=a我們只有一個輸出層,所以沒有用帶方括號的上標。在約定俗成的符號里上面這個例子就是雙層神經網絡,當我們計算網絡層數時不算入輸入層的原因是因為我們不把輸入層看作一個標準的層。
我們要知道隱藏層和輸出層是帶有參數的,隱藏層的參數w1,b1,之后我們會看到w是一個4×3的矩陣,b是一個4×1的向量,第一個數字4是說有四個節點或者說四個隱藏單元,然后數字3來自這里有三個輸入特征,之后會更加詳細的討論這些矩陣的維數,到那時你可能就明白了。類似的輸出層也有一些和它有關的參數w2,b2從維數來看分別是1×4和1×1,1×4是因為隱藏層有四個單元而輸出層只有一個單元,之后會對這些矩陣和向量的維度做更加深入的解釋
所以現在你已經知道一個雙層神經網絡是怎樣的,它是只有一個隱藏層的神經網絡,接下來我們將更加深入的了解這個神經網絡到底在計算什么,怎么輸入x然后又是怎么一直算下去得到 y ^ \hat{y} y^?
3 計算神經網絡的輸出
我們之前說的logistic回歸,這里的圓圈代表了回歸計算的兩個步驟
神經網絡不過是重復計算這些步驟很多次,
我們先看隱藏的第一個節點,暫時隱去其他節點,左邊看上去和logistic回歸很相似,隱層的這個節點進行兩步計算,第一步可以看作是節點的左邊計算 z = w T x + b z=w^Tx+b z=wTx+b,然后第二步計算a=sigmoid(z)
上標方括號表示層數,下標i則表示層中的第幾個節點
現在看看神經網絡的第二個節點
所以隱藏層的計算就是
但是用for循環來做很低效,所以接下來要做的就是把這四個等式向量化
我們將單獨的z堆疊起來構成一個列向量 Z [ 1 ] Z^{[1]} Z[1]
最后你需要計算的就是a
用sigmoid函數作用于Z的四個元素
用同樣的方法推導下一層
最后就得到了一個實數
蓋住左側,你會發現和logistic回歸類似,不過是參數名變化了, w T ? > W [ 2 ] , b ? > b [ 2 ] w^T->W^{[2]},b->b^{[2]} wT?>W[2],b?>b[2]
現在你知道如何輸入單個特征向量x,你可以用四行代碼計算出這個神經網絡的輸出,和我們處理logistic回歸時的做法類似,我們也想把整個訓練樣本都向量化,我們會發現通過把不同的訓練樣本堆疊起來構成矩陣,只需要稍微修改這些公式,你就可以得到之前logistic回歸的結果,能不只是一次計算出一個樣本的神經網絡輸出,而是能一次性計算你的整個訓練集。接下來就看看怎么實現
4 多個樣本的向量化
你現在有m個樣本
這里的 a [ 2 ] ( i ) a^{[2](i)} a[2](i)表示:圓括號里的i表示訓練樣本i,方括號指第二層
如果你不通過向量化,使用for循環,就是下面這種形式
下面看看如何向量化m個訓練樣本
還記得我們之前定義過矩陣X,就是我們的訓練樣本堆到各列,做成一個 n x × m n_x×m nx?×m維的矩陣
像X一樣,把z堆疊起來
同樣a,把它們以列向量堆疊起來
橫向的話,我們對所有的訓練樣本用指標排序,所以橫向指標就對應了不同的訓練樣本,當你從左到右掃的時候就掃過了整個訓練集,而在豎向,豎向指標就對應了神經網絡里的不同節點,比如下面紫色(A里的第一行第一列)對應第一個訓練樣本第一個隱藏單元的激活函數
同樣的形式也適用于矩陣Z和矩陣X
這樣就得到了向量化的表示
5 向量化實現的解釋
在之前的文章中我們看到了如何將訓練樣本橫向堆疊起來構成矩陣X,你就可以導出一個網絡中正向傳播算法的向量化實現,這里我們講一下更多的理由說明為什么我們寫下的方程向量化在多樣本時的正確實現,我們對幾個樣本手動算算正向傳播。為了形式上簡便,先讓b=0
到目前為止我們一直用的是sigmoid函數,事實證明這不是最好的選擇,在下面我們進一步深入研究如何使用不同種類的激活函數,其實sigmoid函數只是其中的一個可能選擇
6 激活函數
要搭建一個神經網絡你可以選擇的是選擇隱層里用哪一個激活函數,還有神經網絡的輸出單元用什么激活函數,到目前為止我們一直用的是sigmoid函數,但有時其他函數效果要好得多。我們看看一些可供選擇的函數。
在神經網絡的正向傳播步驟中我們用的是
在更一般的情況下我們可以只用不同的函數 g ( Z [ 1 ] ) g(Z^{[1]}) g(Z[1]),其中g可以是非線性函數不一定是sigmoid
比如sigmoid介于0和1之間,有個激活函數幾乎總比sigmoid表現更好,就是tanh函數或者叫雙曲正切函數
介于-1和1之間,數學上這其實就是sigmoid函數平移后的版本。事實證明對于隱藏單元如果你讓函數g(z)等于tanh(z),這幾乎總比sigmoid函數效果更好,因為現在函數輸出介于-1和1之間,激活函數的平均值就更接近0,有時你可能要平移所有的數據讓數據平均值為0,使用tanh函數而不是sigmoid函數也有類似數據中心化的效果使得數據平均值接近0,而不是0.5,這實際上讓下一層的學習更方便一點,在后面會詳細討論這一點,那時也會介紹算法優化。但這里要記住一點我們幾乎不用sigmoid激活函數了,tanh函數幾乎在所有場合都更優越。一個例外是輸出層,因為如果y是0或1那么你希望 y ^ \hat{y} y^?介于0到1之間更合理。我會使用sigmoid函數的一個例外場合是使用二元分類的時候,在這種情況下你可以使用sigmoid激活函數作為輸出層
所以在這個例子中可以在隱層使用tanh函數,輸出層用sigmoid函數,所以不同層的激活函數可以不一樣,有時候為了表示不同層的不同激活函數,我們可能會用方括號上標來表示 g [ 1 ] g^{[1]} g[1]可能和 g [ 2 ] g^{[2]} g[2]不同。現在sigmoid函數和tanh函數都有一個缺點就是如果z非常大或者非常小那么這個導數的梯度可能就很小,所以z很大或很小的時候導數接近0,這樣會拖慢梯度下降法,在機器學習里最受歡迎的一個工具是所謂的修正線性單元(ReLU)
只要z為正導數就是1,當z為負時,導數為0,如果你實際使用這個函數,z剛好為0時導數是沒有定義的,但如果你編程實現那么你得到z剛好等于0 0 0 0 …的概率很低,所以實踐中不用擔心這一點,你可以在z=0時給導數賦值,你可以賦值成1或0那樣也是可以的,盡管事實上這個函數不可微。在選擇激活函數時有一些經驗法則。如果你的輸出值是0和1,如果你在做二元分類那么sigmoid函數很適合作為輸出層的激活函數,然后其他所有單元都用ReLU所謂的修正線性單元,現在已經變成激活函數的默認選擇了,如果你不確定隱層應該用哪個,我就用ReLU作為激活函數,這是今天大多數人都在用的,雖然人們有時也會用tanh激活函數。而ReLU的一個缺點就是當z<0時導數等于0,在實踐中這沒什么問題,但ReLU還有另一個版本 叫做leaky ReLU,當z為負時,函數不再為0它有一個很平緩的斜率,
這通常比ReLU激活函數更好,不過實際中的使用頻率沒那么高.這些你選一個就好了,如果你一定要選一個,我通常只用ReLU,使用ReLU和leaky ReLU的好處是對于很多z空間,激活函數的導數和0差很遠,所以在實際中使用ReLU 你的神經網絡的學習速度通常會快很多,比使用tanh或sigmoid函數快的多,主要原因在于ReLU函數沒有這種函數斜率接近0的時減慢學習效率的效應。我知道對z的一半范圍來說,ReLU的斜率為0但在實踐中有足夠多的隱藏單元令z大于0,所以對大多數訓練樣本來說還是很快的
總結,sigmoid函數除非在二元分類的輸出層,不然絕對不要使用或者幾乎從來不會用,我從來沒用過,因為tanh幾乎在所有的場合都更優越,還有最常用的默認激活函數是ReLU,或者你想用的話也可以試試leaky ReLU
你可能會問為什么leaky ReLU表達式中第一項是0.01z,為什么常數是0.01,你也可以把它設置成學習函數的另一個參數,有人說這樣效果更好,但我很小見到有人這么做所以如果你想要在你的應用中試試自己喜歡就好,你可以看看效果如何 有多好如果很好就一直使用它
我希望這樣你就對如何在你的神經網絡里選擇激活函數有概念,深度學習其中一個特點是在建立神經網絡時經常有很多不同的選擇,比如隱層單元數,激活函數,還有如何初始化權重,這個我們接下來會講,有時真的很難 去定下一個準則來確定什么參數最適合你的問題,所以接下來會介紹行業里見到的熱門選擇或冷門選擇,但是對于你的應用的特質事實上很難預先準確的知道什么參數最有效,所以一個建議是如果你不確定哪種激化函數最有效,你可以先試試在你的保留交叉驗證數據集上跑跑或者在測試集上試試,看看哪個參數效果更好就用那個,我想通過在你的應用中測試這些不同的選擇你可以搭建出具有前瞻性的神經網絡架構,可以對你的問題的特質更有針對性,讓你的算法迭代更流暢,我這里不會告訴你一定要用ReLU激活函數而不用其他的那對你現在或者未來要處理的問題而言可能管用也可能不管用,好這就是激活函數的選擇,你們看到了最熱門的激活函數。還有一個問題經常有人會問為什么你需要激活函數呢為什么不直接去掉,下面我們會談到為什么
7 為什么需要非線性激活函數
事實證明要讓你的神經網絡能夠計算出有趣的函數你必須使用非線性激活函數
這是神經網絡的正向傳播方程,為什么我們不能直接去掉g然后令 a [ 1 ] = z [ 1 ] a^{[1]}=z^{[1]} a[1]=z[1],或者你可以令g(z)=z,有時這叫線性激活函數,更學術一點的名字是恒等激活函數,因為它們就直接把輸入值輸出了。為了說明問題我們看看 a [ 2 ] = z [ 2 ] 會 怎 樣 a^{[2]}=z^{[2]}會怎樣 a[2]=z[2]會怎樣,事實證明,那么這個模型的輸出 y ^ \hat{y} y^?不過是你輸入特征x的線性組合
如果你要用線性激活函數或者叫恒等激活函數 那么神經網絡只是把輸入線性組合再輸出,我們稍后會談到深度神經網絡,有很多層的神經網絡很多隱層層,事實證明如果你使用線性激活函數或者如果沒有激活函數那么無論你的神經網絡有多少層一直在做的只是計算線性激活函數所以不如直接去掉全部隱藏層
如果你在隱層使用線性激活函數,在輸出層使用sigmoid函數,模型復雜度和沒有任何隱層的的標準logistic回歸是一樣的,線性隱層一點用都沒有,因為兩個線性函數的組合本身就是線性函數,所以除非你引入非線性否則無法計算更有趣的函數網絡層數再多也不行。只有一個地方可以使用線性激活函數g(z)=z,如果你要學習的是回歸問題,所以y是一個實數,比如說你想要預測房價,y不是0,1而是一個實數,你知道房價是0美元一直到能多貴就多貴,我也不知道能到多少,也許幾百萬美元,但不管你的數據集里房價是多少,y都是一個實數,那么用線性激活函數也許可行,所以你的輸出y也是一個實數,從負無窮到正無窮,但是隱層層不能用線性激活函數,它們可以用ReLU或leaky ReLU或其他,
所以唯一可以用線性激活函數的地方通常就是輸出層,除了這種情況會在隱層使用線性激活函數的可能除了一些和壓縮有關的非常特殊的情況,在那之外使用線性激活函數非常少見。哦當然實際上預測住房價格就像在第一篇文章中看到的因為房價都是非負,也許可以使用ReLU函數
希望這樣你就知道為什么使用非線性激活函數對神經網絡來說很關鍵,接下來我們將開始討論梯度下降
8激活函數的導數
在開始梯度下降之前,我們先看一下如何計算單個激活函數的導數,當你對你的神經網絡使用反向傳播的時候你真的需要計算激活函數的斜率或者導數
首先是sigmoid函數
在神經網絡中我們有a=g(z),所以公式就化簡成a(1-a),導數用g’(z)表示,g’(z)在微積分中表示函數g()對輸入變量z的導數
下面看一下tanh的導數
g’(z)= 1 ? a 2 1-a^2 1?a2
最后我們看看如何計算ReLU和leaky ReLU激活函數的導數
在z=0處導數沒有定義,如果你要實現這個算法,可能在數學上不是百分之一百正確但實際是可行的,如果z剛好在0,你可以讓導數等于1或者為0,這其實無關緊要,如果你對優化術語很熟悉g’(z)就變成所謂的激活函數g(z)的次梯度,這樣梯度下降法依然有效,但是z精準為0的概率非常非常小,所以你將z=0處的導數設成哪個值實際無關緊要
如果你在訓練自己的網絡時使用leaky ReLU
z精準為0時梯度也是沒有定義的,但你可以寫一段代碼去定義這個梯度,在z=0時g’(z)=0.01或1,用哪個值其實無關緊要。
當我們知道了以上內容就已經準備好在你的神經網絡上實現梯度下降法了
9 神經網絡的梯度下降法
在本節你將看到梯度下降算法的具體實現,如何處理單隱層神經網絡,我會提供給你所需要的方程來實現反向傳播或者說梯度下降法,下一節會介紹更多推導,介紹為什么這特定的幾個方程是精準的方程,可以針對你的神經網絡實現梯度下降的正確方程
你的單隱層神經網絡可能會有以下參數
有 n x 也 常 用 n [ 0 ] 表 示 有 那 么 多 個 輸 入 特 征 , n [ 1 ] 個 隱 藏 單 元 , n [ 2 ] 個 輸 出 單 元 有n_x也常用n^{[0]}表示有那么多個輸入特征, n^{[1]}個隱藏單元,n^{[2]}個輸出單元 有nx?也常用n[0]表示有那么多個輸入特征,n[1]個隱藏單元,n[2]個輸出單元在我們的例子中只介紹過 n [ 2 ] = 1 n^{[2]}=1 n[2]=1的情況,這些參數的維度如下
如果你在做二元分類,那么損失函數如下
所以要訓練參數,你的算法需要做梯度下降,在訓練神經網絡時,隨機初始化參數很重要,而不是初始化為全0,我們稍后會看到為什么會這樣,當你把參數初始化為某些值之后,每個梯度下降循環都會計算預測值,所以你基本上要計算i=1到m的預測值 h a t y hat{y} haty,然后你需要計算導數dw[1],db[1],dw[2],db[2],然后更新參數
這就是梯度下降法的一次迭代循環,然后你重復這些步驟很多次,直到你的參數看起來在收斂。之前我們討論過如何計算預測值,如何計算輸出,也看到了如何用向量化的方式去做,所以關鍵在于如何計算這些偏導項dw[1],db[1],dw[2],db[2],我想做的是給出你需要的公式,求這些導數的公式,而在下一小節,會深入介紹我們是怎么想出這些公式的,所以總結一下,下面是正向傳播的過程
也就是下面
如果你還記得前面講的logistic回歸,你會發現,第二層的計算和logistic回歸幾乎一樣
下面是反向傳播
這里有個細節,np.sum是python的numpy命令,用來對矩陣的一個維度求和,加上keepdims=True就是防止python直接輸出這些古怪的秩為1的數組
所以加上keepdims=True確保python輸出的是矩陣,對于db[2]這個向量輸出的維度是(n,1),準確的說應該是(n[2],1)這里n[2]等于1,db就是一個數字,但以后我們會看到真正需要考慮多維的情況。到目前為止我們所作的和logistic回歸非常相似,但當你繼續計算反向傳播時
這里的g’[1]是你用的隱藏層的激活函數的導數,*是逐個元素相乘。如果你不想使用keepdims可以使用reshape把np.sum的輸出結果寫成矩陣形式。
所以這是正向傳播一共四個式子而反向傳播有六個式子(下一小節會告訴你反向傳播的式子為什么是這樣),不過總之如果你要實現這些算法,你必須能正確的執行正向傳播和反向傳播計算,你必須要能夠計算所有需要的導數,用梯度下降法來學習神經網絡的參數,你也可以直接實現這個算法讓它工作起來而不去了解很多微積分的知識,有很多成功的深度學習從業者是這樣做的,但如果你想要了解,可以看下一小節
10(選修)直觀理解反向傳播
先復習一下之前講的logistic回歸
在計算單隱層時和上面類似,不過是計算了兩次,注意這里只是單個樣本的推導
把a[1]看成x
就和之前的logistic回歸一樣了,接下來繼續向左求導(假設 n [ 0 ] = 3 , n [ 1 ] = 4 , n [ 2 ] = 1 n^{[0]}=3,n^{[1]}=4,n^{[2]}=1 n[0]=3,n[1]=4,n[2]=1)
其中
實現后向傳播算法有個技巧,你必須保證矩陣的維度相互匹配,最后求處dw[1],db[1]
所以反向傳播就可以寫成
首先是正向,忽略b
接下來是反向傳播
這就是單個樣本的反向傳播,但你應該不會意外我們實際不會一個個樣本的計算,我們要把所有的訓練樣本向量化,把z堆疊起來
所以向量化就是下圖右側的過程
希望能讓你們知道這些后向傳播算法是怎么推導的,在所有的機器學習領域中,我認為反向傳播算法的推導實際上是我看過最復雜的數學之一,它需要知道線性代數以及矩陣的導數要用鏈式法則推導出來,如果你不是矩陣微積分的專家,使用這個步驟你可以自己證明求導算法,但我認為實際上有很多的深度學習實踐者看過類似的推導過程,他們已經掌握了這種直覺,并能夠非常有效的實現該算法。最后還有一個細節想給大家分享,在你實現神經網絡之前如何初始化你的神經網絡的權重,事實證明初始化你的參數不要全零而是隨機初始化,對訓練你的神經網絡來說這一點非常重要
11 隨機初始化
對于logistics回歸可以將權重全部初始化為0,但如果將神經網絡的各參數數組全部初始化為0,再使用梯度下降法那會完全無效,let’s see why
假設你有兩個輸入特征
把參數都初始成0,兩個隱層單元在進行相同的計算,無論有多少個隱層單元它們都在計算完全一樣的函數,所以沒有一點用處
這個問題的解決方案就是隨機化所有參數
乘以一個很小的數是為了將權重初始化成很小的隨機數,b是可以初始化為0的。我們通常喜歡把權重矩陣初始化成非常非常小的隨機數,因為如果你用的是sigmoid或tanh激活函數(或者在輸出層使用了sigmoid),那么如果權重太大 當你計算激活函數值時w 很大,這些z會很大或很小,在這種情況下你最后可能落在這些tanh或sigmoid函數比較平緩的部分,梯度下降法會很慢,學習會很慢。如果你的神經網絡中沒有任何sigmoid或tanh激活函數那么問題可能沒這么大,但如果你在做二元分類你的輸出單元是sigmoid函數那么你就不希望初始參數太大,所以用0.01是比較合理的或者任意其他小數字。
所以事實上有時有比0.01更好用的常數,但當你訓練一個單隱層神經網絡時這是一個相對較淺的神經網絡沒有太多的隱藏層,設為0.01應該還可以,但是當你訓練一個很深的神經網絡時你可能要試試0.01以外的常數
出來,如果你不是矩陣微積分的專家,使用這個步驟你可以自己證明求導算法,但我認為實際上有很多的深度學習實踐者看過類似的推導過程,他們已經掌握了這種直覺,并能夠非常有效的實現該算法。最后還有一個細節想給大家分享,在你實現神經網絡之前如何初始化你的神經網絡的權重,事實證明初始化你的參數不要全零而是隨機初始化,對訓練你的神經網絡來說這一點非常重要
總結
以上是生活随笔為你收集整理的神经网络与深度学习三:编写单隐层神经网络的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【学前教育论文】幼儿学前教育中采茶小游戏
- 下一篇: 关于美元中 单位 换算 English