- 深度學(xué)習程序設(shè)計實戰(zhàn)
- 方林 陳海波編著
- 5367字
- 2021-08-12 17:34:29
3.3 三層神經(jīng)網(wǎng)絡(luò)
在研究Exp類時,我們以標量作為數(shù)據(jù)的類型進行考慮,而TF的基本數(shù)據(jù)類型是矩陣。標量和向量分別被認為是0維和1維矩陣。與標量一樣,矩陣的運算主要有算術(shù)運算、關(guān)系運算、邏輯運算、條件運算和函數(shù)等。這一節(jié),我們將首先介紹矩陣乘法運算,然后給出三層神經(jīng)網(wǎng)絡(luò)的定義,最后說明三層神經(jīng)網(wǎng)絡(luò)可以擬合任意函數(shù)。
3.3.1 神經(jīng)元網(wǎng)絡(luò)訓(xùn)練算法
前面章節(jié)中我們已經(jīng)學(xué)會了用迭代法、GD法和BP算法計算平方根,但是這個方法有兩個問題。第一,每計算一個平方根都要進行很多次迭代;第二,由于每一次迭代都是基于上一次迭代的結(jié)果,所以計算無法并行化。有沒有辦法用少量的有限次數(shù)的并行計算解決問題呢?有的,這就是目前深度學(xué)習最常用的方法之一——基于樣本的神經(jīng)元網(wǎng)絡(luò)訓(xùn)練算法(簡稱為樣本訓(xùn)練算法)。
首先,明確幾個概念。依賴關(guān)系圖是一個數(shù)學(xué)概念,計算圖是TF對這個概念的實現(xiàn)。神經(jīng)元網(wǎng)絡(luò)(簡稱為網(wǎng)絡(luò),以下同)就是依賴關(guān)系圖在深度學(xué)習中的等價概念。依賴關(guān)系圖中的結(jié)點或者說函數(shù)就是神經(jīng)元。所以我們有時用圖表示神經(jīng)元網(wǎng)絡(luò),有時用復(fù)合函數(shù)表示神經(jīng)元網(wǎng)絡(luò),因為它們是等價的。下面的算法就是一個例子。
算法3-1 基于樣本的神經(jīng)元網(wǎng)絡(luò)訓(xùn)練算法
1)搭建神經(jīng)元網(wǎng)絡(luò)y=f(x,θ),其中x、y分別是網(wǎng)絡(luò)的輸入集合和輸出集合,θ是要訓(xùn)練的參數(shù)的集合。
2)準備樣本集合S={(xi,yi)|i=1,2,3,…,m},其中m是樣本個數(shù)。
3)構(gòu)建一個以求最小值為目的的目標函數(shù),例如L=∑i(yi-f(xi))2。
4)選擇S的一個子集S′,按照算法2-1求L的最小值,并優(yōu)化θ中的每個參數(shù)。
5)重復(fù)4),直到滿足結(jié)束條件。
樣本訓(xùn)練算法與迭代求解算法(算法2-1)的最大區(qū)別在于訓(xùn)練的目的不同。前者訓(xùn)練的目的是優(yōu)化參數(shù)集合θ。訓(xùn)練完畢之后,使用網(wǎng)絡(luò)時不必再更新θ中的參數(shù),從而達到減少計算量的目的。后者并不存在訓(xùn)練過程,而是直接通過迭代計算確定目標函數(shù)的最優(yōu)解。兩者的聯(lián)系在于,在訓(xùn)練階段,前者利用后者對參數(shù)進行了優(yōu)化。
下面我們還是通過求解平方根這個例子來說明樣本訓(xùn)練算法的使用。
3.3.2 線性變換和激活函數(shù)
根據(jù)算法3-1,我們首先要建立求平方根的網(wǎng)絡(luò)。輸入是非負數(shù)x,輸出是x的平方根y。在x與y之間建立100個結(jié)點,分別與x、y相連,構(gòu)成一個簡單的神經(jīng)元網(wǎng)絡(luò),如圖3-2所示。

圖3-2 簡單的神經(jīng)元網(wǎng)絡(luò)
接著,該如何定義函數(shù)zi(i=1,2,3,…,100)以及zi與y之間的關(guān)系呢?最簡單的辦法就是使用線性變換和激活函數(shù):
zi=xai+bi(i=1,2,3,…,100)
y=relu(zi)ci+d(i=1,2,3,…,100)
其中ai、bi、ci和d都是需要我們利用GD法進行優(yōu)化的參數(shù)。relu()是一個激活函數(shù)(Activation Function),全稱為線性矯正單元(Rectified Linear Unit)。relu激活函數(shù)定義如下:

relu()函數(shù)的圖像如圖3-3所示。根據(jù)定義,我們發(fā)現(xiàn)relu(x)≡max(0,x)。如果不使用激活函數(shù),那么,從x到y不過是兩個線性變換的組合;而任意有限次線性變換的組合仍然是線性變換。由于我們要求的平方根是非線性的,也就是說,不存在m和n使得(mx+n)2=x對任意x都成立,所以有必要使用激活函數(shù)以實現(xiàn)非線性變換。除了relu()之外,還有很多其他激活函數(shù),可參見后面章節(jié)。

圖3-3 relu()函數(shù)圖像
下面是用樣本訓(xùn)練算法求解平方根的主程序代碼:


接下來,我們要實現(xiàn)Tensors。在繪制計算圖時(例如圖3-2),我們只考慮一個樣本的輸入x和輸出y;但在TF和幾乎所有的深度學(xué)習框架中,都按照批次(Batch)來考慮樣本。一批中含有多個樣本,假設(shè)樣本輸入和輸出的形狀分別是[m,n]和[p,q,r],則模型的實際輸入和輸出形狀分別是[s,m,n]和[s,p,q,r],其中第一個維度s表示樣本的個數(shù)。這是因為深度學(xué)習框架幾乎都是基于矩陣運算的,一批多個樣本有利于并行計算,提高訓(xùn)練速度。
既然采用矩陣運算,圖3-2所示的計算圖可以簡化為:
Z=XA+B
Y=relu(Z)C+D
其中X、Y分別是輸入向量和輸出向量,Z是中間變量,A、B、C、D是模型的參數(shù)。所謂訓(xùn)練模型,就是優(yōu)化這些參數(shù)。式中的乘法(包括?)和加法分別是矩陣乘法和矩陣加法。relu()函數(shù)是可廣播的,所以relu(Z)表示對矩陣Z中的每一個元素執(zhí)行relu()后得到的結(jié)果。
綜上所述,我們對Tensors類的實現(xiàn)如下:


代碼3-12第4行tf.placeholder()的第二個參數(shù)是[None],表示這個占位符只有一個維度(也就是說它是一個向量),且這個維度的長度不確定。形狀[None,3,None]表示第一個和第三個維度的長度不確定。TF中絕大部分運算都允許矩陣有不確定的維度,只需在運行時把數(shù)據(jù)的真正維度代入即可。但有一個例外,變量的維度必須都是確定的。這是因為變量需要在內(nèi)存和GPU中占據(jù)空間,所以TF必須準確地知道它到底有多大。
如果張量的某個維度是不確定的,則在運算后對應(yīng)位置處的維度要用-1表示。例如代碼3-12第5行tf.reshape(self.x,[-1,1])的第一個維度是-1,這是因為self.x的形狀是[None],說明它是一個長度不確定的向量。當把它整形為二維矩陣時,如果確定它的第二個維度是1,則它的第一個維度就無法確定,于是就用-1代替。同樣的例子在后面所寫的大部分代碼中都會出現(xiàn)。
在SqrtModel類中,方法train()被用來對模型進行訓(xùn)練,predict()被用來使用模型。它們的實現(xiàn)如下:

運行p03_ 12號程序得到的結(jié)果是:
sqrt(0.0555)=0.2316
sqrt(0.1297)=0.3098
sqrt(0.1994)=0.3836
sqrt(0.4350)=0.6314
sqrt(0.6073)=0.7866
sqrt(0.7146)=0.8607
sqrt(0.9207)=0.9762
sqrt(1.1832)=1.1022
sqrt(1.6513)=1.2870
sqrt(1.6923)=1.3017
sqrt(1.8790)=1.3690
sqrt(1.8974)=1.3756
sqrt(2.0739)=1.4386
sqrt(2.1878)=1.4781
sqrt(2.2408)=1.4957
sqrt(2.3656)=1.5356
sqrt(2.6307)=1.6157
sqrt(2.6467)=1.6205
sqrt(2.8969)=1.6961
sqrt(2.9273)=1.7053
由于樣本僅包含了區(qū)間[0,3)里的數(shù)據(jù),所以測試時,也只能計算這個區(qū)間上數(shù)據(jù)的平方根。當試著求解其他區(qū)間的平方根時,會發(fā)現(xiàn)結(jié)果誤差比較大。出現(xiàn)這個現(xiàn)象的原因我們后面講三層神經(jīng)網(wǎng)絡(luò)時會解釋。
3.3.3 矩陣乘法和全連接
代碼3-12的第6、7、8三行是:

這三行代碼的實質(zhì)含義是實現(xiàn)線性變換Z=XA+B。這種線性變換在深度學(xué)習中被稱為全連接(Full Connection,F(xiàn)C)。由于全連接操作比較常見,TF用一個函數(shù)tf.layers.dense()幫我們實現(xiàn)了全連接。例如上述三行可以用一行代替:
z=tf.layers.dense(x,100)
假設(shè)X的形狀是[n,-1],經(jīng)過上述操作后,Z的形狀是[n,100]。由于n通常表示樣本的個數(shù),所以全連接操作的作用是改變樣本向量的長度。
如果在全連接之后要執(zhí)行激活操作,例如:
z=tf.nn.relu(tf.layers.dense(x,100))
可以簡化為:
z=tf.layers.dense(x,100,tf.nn.relu)
或者
z=tf.layers.dense(x,100,′relu′)
如果不需要考慮偏置B,則把參數(shù)use_ bias置為False即可:
z=tf.nn.relu(tf.layers.dense(x,100,use_ bias=False))
即Z=relu(XA)。
3.3.4 激活函數(shù)
TF中的relu函數(shù)族中一共有6個激活函數(shù),分別是:
1)tf.nn.relu()。這是最傳統(tǒng)的relu激活函數(shù),公式如下:

2)tf.nn.leaky_ relu()。公式如下:
leaky_ relu(x)=a>0是一個超參數(shù)
3)tf.nn.elu()。公式如下:

4)tf.nn.selu()。公式如下:
selu(x)=是一個可以訓(xùn)練的變量
5)tf.nn.relu6。限定最大值為6的relu激活函數(shù),公式如下:

6)tf.nn.crelu()。公式如下:

除了crelu()外,其他5個函數(shù)的圖像如圖3-4所示。elu()、relu()是selu()分別在a=1和a=0的特殊情形,所以凡是使用elu()、relu()的情形一般都可以用selu()代替。crelu()實際上是把輸入的x值按照正負兩種情況并列處理,使得負值也有機會參與前向傳播和梯度下降。所以,relu()、leaky_relu()、relu6()都是crelu()的特殊情形。注意,除了crelu()外,其他5個函數(shù)都不會改變輸入數(shù)據(jù)的形狀,而crelu()會增加一個長度為2的維度。
除了relu函數(shù)族以外,還有兩個激活函數(shù)tf.sigmoid()和tf.tanh(),后者又叫雙曲正切函數(shù)。sigmoid()和tanh()函數(shù)公式如下:

它們的圖像都是S型,如圖3-5所示,不同的是值域。sigmoid把(-∞,+∞)上的數(shù)映射到(0,1)區(qū)間,tanh把(-∞,+∞)上的數(shù)映射到(-1,1)區(qū)間。這兩個函數(shù)的性質(zhì)如下:

圖3-4 relu函數(shù)族的圖像

圖3-5 sigmoid函數(shù)和tanh函數(shù)
1)tanh(x)=2 sigmoid(2x)-1
2)sigmoid(x)=
3)sigmoid(0)=0.5
4)tanh(0)=0
5)sigmoid′(x)=sigmoid(x)(1-sigmoid(x))
6)0<sigmoid′(x)≤,且x=0時取最大值
7)tanh′(x)=1-tanh2(x)
8)0<tanh′(x)≤1,且x=0時取最大值
這些性質(zhì)使得sigmoid()適合把數(shù)據(jù)映射為一個概率,tanh()適合把數(shù)據(jù)映射到(-1,1)區(qū)間從而有利于模型的收斂;但兩者都不太適合做激活函數(shù),尤其是sigmoid()。這是因為sigmoid()的導(dǎo)數(shù)的最大值是0.25,所以有:
▽x≤0.25▽y,其中y=sigmoid(x)
這意味著梯度每經(jīng)過一次sigmoid()就會縮小3/4。經(jīng)過的次數(shù)越多,縮小的程度就越呈指數(shù)增長。這就有可能導(dǎo)致梯度過小而消失,從而無法對模型可訓(xùn)練參數(shù)進行優(yōu)化。tanh()也有類似情況。所以現(xiàn)在一般流行用relu函數(shù)族中的函數(shù)做激活函數(shù)。
3.3.5 全連接和Relu的梯度
根據(jù)relu()函數(shù)的定義,我們可知relu()的導(dǎo)數(shù)為:

上式中,我們約定x=0時的導(dǎo)數(shù)為1。這是因為在GD法中,偏導(dǎo)數(shù)代表了因變量相對于自變量的變化趨勢,GD法根據(jù)變化趨勢決定增加或者減少當前變量的值,并用學(xué)習率限制了步長。這就使得我們完全可以自行定義函數(shù)在不可求導(dǎo)點的導(dǎo)數(shù),從而使GD法可以正常運行下去。
由于relu()的導(dǎo)數(shù)是分段確定的,所以,如果一個神經(jīng)網(wǎng)絡(luò)中含有relu()這樣的導(dǎo)數(shù)分段的函數(shù),則網(wǎng)絡(luò)上relu()所依賴的結(jié)點的梯度也是分段確定的。例如y=x3,z=relu(y),由于relu依賴于x,所以z對x的導(dǎo)數(shù)也是分段的:

把根據(jù)x的不同計算出來的不同的y分別代入上式,就可以計算不同情況下x的梯度。
對于全連接Z=XA+B,有以下公式成立:

設(shè)有:

Z =XA+B
Y =relu(Z)
求:▽X、▽A和▽B。
解:由計算得
,所以
,即正數(shù)對應(yīng)的導(dǎo)數(shù)為1,負數(shù)對應(yīng)的導(dǎo)數(shù)為0。

在GD法中,有了X、A和B的梯度,我們就可以根據(jù)學(xué)習步長分別調(diào)整X、A和B的值。GD法就是根據(jù)這樣的方法優(yōu)化參數(shù)的。
3.3.6 求正弦
如果仔細研究代碼3-12和代碼3-13,會發(fā)現(xiàn)無論Tensors類還是SqrtModel類,它們與平方根這個概念都沒有什么關(guān)系。這意味著什么?意味著Tensors和SqrtModel其實是通用的。基于這個考慮,我們先把Tensors類中的線性變換改為全連接,把中間層的長度從常數(shù)100改為變量middle_ units,再把SqrtModel改名為Model,最后把主程序稍作調(diào)整,即可得到求解正弦的程序:


在上面的程序中,我們還引入了math包和pyplot包。math被用來獲取π的值,pyplot被用來作圖。
得到的結(jié)果如圖3-6a所示。正弦曲線和預(yù)測得到的曲線的重合度“看起來”還是比較高的。但是如果把局部放大,就會看到兩者還是有區(qū)別的,如圖3-6b所示。
增大epoches參數(shù)或者縮小學(xué)習步長lr,可以進一步加大兩者的重合度,但是由于步長的限制,我們不可能無限地加大重合度。另外,過小的學(xué)習步長將導(dǎo)致網(wǎng)絡(luò)收斂緩慢。所以,選取合適的epoches和lr就顯得很重要。讀者可以通過用不同的參數(shù)組合多做練習,以便摸清這些超參的最優(yōu)組合。除此之外,還可以使用其他優(yōu)化器,如AdamOptimizer。

圖3-6 正弦曲線和預(yù)測值對比
3.3.7 BGD、SGD和MBGD
樣本參與訓(xùn)練的方法有3種:
1)BGD(Batch Gradient Descent),批量梯度下降法,即一次訓(xùn)練所有樣本。前面求正弦和平方根的程序就是這樣的。
2)SGD(Stochastic Gradient Descent),隨機梯度下降法,即一次僅訓(xùn)練一個樣本。
3)MBGD(Mini Batch Gradient Descent),小批量梯度下降法。這是介于BGD和SGD之間的方法,一次訓(xùn)練多個樣本。由于在實際項目中樣本數(shù)量眾多,我們很難使用BGD,但一次僅訓(xùn)練一個樣本效率又實在太低,且容易出現(xiàn)過擬合,所以MBGD就成為比較常用的方法。
BGD和MBGD是不是等價于SGD?換句話說,如果BGD或MBGD一次訓(xùn)練10個樣本,那么在同樣的情況下,是不是等價于用SGD訓(xùn)練10次?答案是否定的。BGD或MBGD一次訓(xùn)練10個樣本,模型的參數(shù)就用這10個樣本的梯度的平均值進行優(yōu)化。而用SGD訓(xùn)練10次,即對每個參數(shù)優(yōu)化10次,且每次優(yōu)化都是在上一次優(yōu)化的基礎(chǔ)上進行的,結(jié)果當然不一樣。
3.3.8 三層神經(jīng)網(wǎng)絡(luò)模型
本節(jié)最核心的內(nèi)容是三層神經(jīng)網(wǎng)絡(luò)。前面求平方根和正弦的網(wǎng)絡(luò)都是三層的,包括輸入層、中間層和輸出層。我們還發(fā)現(xiàn),這樣的網(wǎng)絡(luò)既可以用來訓(xùn)練求平方根,也可以用來求正弦。那么這種通用性是不是放之四海皆準呢?更進一步,我們可不可以用一個模型同時求正弦和平方根呢?
三層神經(jīng)網(wǎng)絡(luò)可以表達任意函數(shù),這個定理在1989年就已經(jīng)被G.Cybenko證明(參見http://www.dartmouth.edu/~gvc/Cybenko_MCSS.pdf);但是那是一個非常數(shù)學(xué)化的證明,一般人閱讀起來比較吃力。從本節(jié)開始,我們將給出一個形象的、容易理解的說明(而非證明)。我們會發(fā)現(xiàn),神經(jīng)網(wǎng)絡(luò)的表達能力是有限的,超過極限,增加深度和神經(jīng)元個數(shù)并不能提高網(wǎng)絡(luò)的表達能力。我們還將學(xué)習樣本數(shù)量與神經(jīng)元數(shù)量之間的聯(lián)系,理解過擬合的實質(zhì)以及根本解決辦法,發(fā)現(xiàn)最小樣本區(qū)域的概念,發(fā)現(xiàn)神經(jīng)元網(wǎng)絡(luò)的內(nèi)在不穩(wěn)定性,理解網(wǎng)絡(luò)在樣本空間之外表現(xiàn)不佳的原因。
所謂三層神經(jīng)網(wǎng)絡(luò),就是除了輸入層和輸出層,僅有一個中間層(又稱為隱藏層)的網(wǎng)絡(luò),結(jié)構(gòu)如圖3-7所示。其中各層之間的連接都是全連接,中間層帶一個激活函數(shù)(通常是relu())。這樣的網(wǎng)絡(luò)就可以被用來模擬任意一個一元或者多元函數(shù),甚至可以同時模擬多個多元函數(shù)。

圖3-7 三層神經(jīng)網(wǎng)絡(luò)
但是請注意,第一,這里所說的函數(shù)是連續(xù)函數(shù)。對于非連續(xù)函數(shù),只要它是分段連續(xù)的,我們的證明在每一段上仍然是有效的。第二,函數(shù)的定義域是有界的。也就是說,我們不考慮下界是負無窮,或者上界是正無窮的情況。這個限制對實際應(yīng)用幾乎沒有影響。因為一般來說,我們不太可能在一個上界或者下界完全不受限的范圍內(nèi)使用網(wǎng)絡(luò)。第三,中間層的激活函數(shù)是relu()。如果使用其他激活函數(shù),如leaky_relu()、sigmoid(),結(jié)論是一樣的。其證明是可以類推的,本文不再贅述。
我們將首先從一元函數(shù)講起,然后再擴展到多元函數(shù),最后擴展到多個多元函數(shù)。
- Learning Python Web Penetration Testing
- Testing with JUnit
- C#完全自學(xué)教程
- C語言程序設(shè)計(第2版)
- Java從入門到精通(第4版)
- Learning Informatica PowerCenter 10.x(Second Edition)
- Modern JavaScript Applications
- 蘋果的產(chǎn)品設(shè)計之道:創(chuàng)建優(yōu)秀產(chǎn)品、服務(wù)和用戶體驗的七個原則
- D3.js By Example
- Visual Studio 2015高級編程(第6版)
- SQL Server 入門很輕松(微課超值版)
- 單片機原理及應(yīng)用技術(shù)
- IPython Interactive Computing and Visualization Cookbook
- 深度剖析ApacheDubbo核心技術(shù)內(nèi)幕
- ACE技術(shù)內(nèi)幕:深入解析ACE架構(gòu)設(shè)計與實現(xiàn)原理