用Python實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)(附完整代碼)!

用Python實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)(附完整代碼)!

liziping 2025-03-20 百度 2 次瀏覽 0個(gè)評(píng)論

作者:[美]霍布森·萊恩,科爾·霍華德

在學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)之前,我們需要對(duì)神經(jīng)網(wǎng)絡(luò)底層先做一個(gè)基本的了解。我們將在本節(jié)介紹感知機(jī)、反向傳播算法以及多種梯度下降法以給大家一個(gè)全面的認(rèn)識(shí)。

數(shù)字感知機(jī)的本質(zhì)是從數(shù)據(jù)集中選取一個(gè)樣本(example),并將其展示給算法,然后讓算法判斷“是”或“不是”。一般而言,把單個(gè)特征表示為xi,其中i是整數(shù)。所有特征的集合表示為

,表示一個(gè)向量:

,

類似地,每個(gè)特征的權(quán)重表示為

其中

對(duì)應(yīng)于與該權(quán)重關(guān)聯(lián)的特征

的下標(biāo),所有權(quán)重可統(tǒng)一表示為 一個(gè)向量

:

這里有一個(gè)缺少的部分是是否激活神經(jīng)元的閾值。一旦加權(quán)和超過(guò)某個(gè)閾值,感知機(jī)就輸出1,否則輸出0。我們可以使用一個(gè)簡(jiǎn)單的階躍函數(shù)(在圖5-2中標(biāo)記為“激活函數(shù)”)來(lái)表示這個(gè)閾值。

一般而言我們還需要給上面的閾值表達(dá)式添加一個(gè)偏置項(xiàng)以確保神經(jīng)元對(duì)全0的輸入具有彈性,否則網(wǎng)絡(luò)在輸入全為0的情況下輸出仍然為0。

注:所有神經(jīng)網(wǎng)絡(luò)的基本單位都是神經(jīng)元,基本感知機(jī)是廣義神經(jīng)元的一個(gè)特例,從現(xiàn)在開始,我們將感知機(jī)稱為一個(gè)神經(jīng)元。

2.1 代價(jià)函數(shù)

很多數(shù)據(jù)值之間的關(guān)系不是線性的,也沒(méi)有好的線性回歸或線性方程能夠描述這些關(guān)系。許多數(shù)據(jù)集不能用直線或平面來(lái)線性分割。比如下圖中左圖為線性可分的數(shù)據(jù),而右圖為線性不可分的數(shù)據(jù):

在這個(gè)線性可分?jǐn)?shù)據(jù)集上對(duì)兩類點(diǎn)做切分得到的誤差可以收斂于0,而對(duì)于線性不可分的數(shù)據(jù)點(diǎn)集,我們無(wú)法做出一條直線使得兩類點(diǎn)被完美分開,因此我們?nèi)我庾鲆粭l分割線,可以認(rèn)為在這里誤差不為0,因此我們需要一個(gè)衡量誤差的函數(shù),通常稱之為代價(jià)函數(shù):

而我們訓(xùn)練神經(jīng)網(wǎng)絡(luò)(感知機(jī))的目標(biāo)是最小化所有輸入樣本數(shù)據(jù)的代價(jià)函數(shù)

2.2 反向傳播

權(quán)重

通過(guò)下一層的權(quán)重(

)和(

)來(lái)影響誤差,因此我們需要一種方法來(lái)計(jì)算對(duì)

誤差的貢獻(xiàn),這個(gè)方法就是反向傳播。

下圖中展示的是一個(gè)全連接網(wǎng)絡(luò),圖中沒(méi)有展示出所有的連接,在全連接網(wǎng)絡(luò)中,每個(gè)輸入元素都與下一層的各個(gè)神經(jīng)元相連,每個(gè)連接都有相應(yīng)的權(quán)重。因此,在一個(gè)以四維向量為輸入、有5個(gè)神經(jīng)元的全連接神經(jīng)網(wǎng)絡(luò)中,一共有20個(gè)權(quán)重(5個(gè)神經(jīng)元各連接4個(gè)權(quán)重)。

用Python實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)(附完整代碼)!

感知機(jī)的每個(gè)輸入都有一個(gè)權(quán)重,第二層神經(jīng)元的權(quán)重不是分配給原始輸入的,而是分配給來(lái)自第一層的各個(gè)輸出。從這里我們可以看到計(jì)算第一層權(quán)重對(duì)總體誤差的影響的難度。第一層權(quán)重對(duì)誤差的影響并不是只來(lái)自某個(gè)單獨(dú)權(quán)重,而是通過(guò)下一層中每個(gè)神經(jīng)元的權(quán)重來(lái)產(chǎn)生的。反向傳播的推導(dǎo)過(guò)程較為復(fù)雜,這里僅簡(jiǎn)單展示其結(jié)果:

如果該層是輸出層,借助于可微的激活函數(shù),權(quán)重的更新比較簡(jiǎn)單, 對(duì)于第

個(gè)輸出,誤差的導(dǎo)數(shù)如下

如果要更新隱藏層的權(quán)重,則會(huì)稍微復(fù)雜一點(diǎn)兒:

函數(shù)

表示實(shí)際結(jié)果向量,

表示該向量第

個(gè)位置上的值,

,

是倒數(shù)第二層第

個(gè)節(jié)點(diǎn)和輸出第

個(gè)節(jié)點(diǎn)的輸出,連接這兩個(gè)節(jié)點(diǎn)的權(quán)重為

,誤差代價(jià)函數(shù)對(duì)

求導(dǎo)的結(jié)果相當(dāng)于用

(學(xué)習(xí)率)乘以前一層的輸出再乘以后一層代價(jià)函數(shù)的導(dǎo)數(shù)。公式中

表示

層第

個(gè)節(jié)點(diǎn)上的誤差項(xiàng),前一層第

個(gè)節(jié)點(diǎn)到

層所有的節(jié)點(diǎn)進(jìn)行加權(quán)求和。

2.3 多種梯度下降法

到目前為止,我們一直是把所有訓(xùn)練樣本的誤差聚合起來(lái)然后再做梯度下降,這種訓(xùn)練方法稱為批量學(xué)習(xí)(batch learning)。一批是訓(xùn)練數(shù)據(jù)的一個(gè)子集。但是在批量學(xué)習(xí)中誤差曲面對(duì)于整個(gè)批是靜態(tài)的,如果從一個(gè)隨機(jī)的起始點(diǎn)開始,得到的很可能是某個(gè)局部極小值,從而無(wú)法看到其他的權(quán)重值的更優(yōu)解。這里有兩種方法來(lái)避開這個(gè)陷阱。

第一種方法是隨機(jī)梯度下降法。在隨機(jī)梯度下降中,不用去查看所有的訓(xùn)練樣本,而是在輸入每個(gè)訓(xùn)練樣本后就去更新網(wǎng)絡(luò)權(quán)重。在這個(gè)過(guò)程中,每次都會(huì)重新排列訓(xùn)練樣本的順序,這樣將為每個(gè)樣本重新繪制誤差曲面,由于每個(gè)相異的輸入都可能有不同的預(yù)期答案,因此大多數(shù)樣本的誤差曲面都不一樣。對(duì)每個(gè)樣本來(lái)說(shuō),仍然使用梯度下降法來(lái)調(diào)整權(quán)重。不過(guò)不用像之前那樣在每個(gè)訓(xùn)練周期結(jié)束后聚合所有誤差再做權(quán)重調(diào)整,而是針對(duì)每個(gè)樣本都會(huì)去更新一次權(quán)重。其中的關(guān)鍵點(diǎn)是,每一步都在向假定的極小值前進(jìn)(不是所有路徑都通往假定的極小值)。

使用正確的數(shù)據(jù)和超參數(shù),在向這個(gè)波動(dòng)誤差曲面的各個(gè)最小值前進(jìn)時(shí),可以更容易地得到全局極小值。如果模型沒(méi)有進(jìn)行適當(dāng)?shù)恼{(diào)優(yōu),或者訓(xùn)練數(shù)據(jù)不一致,將導(dǎo)致原地踏步,模型無(wú)法收斂,也學(xué)不會(huì)任何東西。不過(guò)在實(shí)際應(yīng)用中,隨機(jī)梯度下降法在大多數(shù)情況下都能有效地避免局部極小值。這種方法的缺點(diǎn)是計(jì)算速度比較慢。計(jì)算前向傳播和反向傳播,然后針對(duì)每個(gè)樣本進(jìn)行權(quán)重更新,這在本來(lái)已經(jīng)很慢的計(jì)算過(guò)程的基礎(chǔ)上又增加了很多時(shí)間開銷。

第二種方法,也是更常見的方法,是小批量學(xué)習(xí)。在小批量學(xué)習(xí)中,會(huì)傳入訓(xùn)練集的一個(gè)小的子集,并按照批量學(xué)習(xí)中的誤差聚合方法對(duì)這個(gè)子集對(duì)應(yīng)的誤差進(jìn)行聚合。然后對(duì)每個(gè)子集按批將其誤差進(jìn)行反向傳播并更新權(quán)重。下一批會(huì)重復(fù)這個(gè)過(guò)程,直到訓(xùn)練集處理完成為止,這就重新構(gòu)成了一個(gè)訓(xùn)練周期。這是一種折中的辦法,它同時(shí)具有批量學(xué)習(xí)(快速)和隨機(jī)梯度下降(具有彈性)的優(yōu)點(diǎn)。

用原生Python來(lái)編寫神經(jīng)網(wǎng)絡(luò)是一個(gè)非常有趣的嘗試,而且可以幫助大家理解神經(jīng)網(wǎng)絡(luò)中的各種概念,但是Python在計(jì)算速度上有明顯缺陷,即使對(duì)于中等規(guī)模的網(wǎng)絡(luò),計(jì)算量也會(huì)變得非常棘手。不過(guò)有許多Python庫(kù)可以用來(lái)提高運(yùn)算速度,包括PyTorch、Theano、TensorFlow和Lasagne等。本書中的例子使用Keras。

Keras是一個(gè)高級(jí)封裝器,封裝了面向Python的API。API接口可以與3個(gè)不同的后端庫(kù)相兼容:Theano、谷歌的TensorFlow和微軟的CNTK。這幾個(gè)庫(kù)都在底層實(shí)現(xiàn)了基本的神經(jīng)網(wǎng)絡(luò)單元和高度優(yōu)化的線性代數(shù)庫(kù),可以用于處理點(diǎn)積,以支持高效的神經(jīng)網(wǎng)絡(luò)矩陣乘法運(yùn)算。

我們以簡(jiǎn)單的異或問(wèn)題為例,看看如何用Keras來(lái)訓(xùn)練這個(gè)網(wǎng)絡(luò)。

可以看到模型的結(jié)構(gòu)為:

提供了網(wǎng)絡(luò)參數(shù)及各階段權(quán)重?cái)?shù)()的概覽。我們可以快速計(jì)算一下:10個(gè)神經(jīng)元,每個(gè)神經(jīng)元有3個(gè)權(quán)重,其中有兩個(gè)是輸入向量的權(quán)重(輸入向量中的每個(gè)值對(duì)應(yīng)一個(gè)權(quán)重),還有一個(gè)是偏置對(duì)應(yīng)的權(quán)重,所以一共有30個(gè)權(quán)重需要學(xué)習(xí)。輸出層中有10個(gè)權(quán)重,分別與第一層的10個(gè)神經(jīng)元一一對(duì)應(yīng),再加上1個(gè)偏置權(quán)重,所以該層共有11個(gè)權(quán)重。

下面的代碼可能有點(diǎn)兒不容易理解:

SGD是之前導(dǎo)入的隨機(jī)梯度下降優(yōu)化器,模型用它來(lái)最小化誤差或者損失。是學(xué)習(xí)速率,與每個(gè)權(quán)重的誤差的導(dǎo)數(shù)結(jié)合使用,數(shù)值越大模型的學(xué)習(xí)速度越快,但可能會(huì)使模型無(wú)法找到全局極小值,數(shù)值越小越精確,但會(huì)增加訓(xùn)練時(shí)間,并使模型更容易陷入局部極小值。損失函數(shù)本身也定義為一個(gè)參數(shù),在這里用的是。參數(shù)是訓(xùn)練過(guò)程中輸出流的選項(xiàng)列表。用方法進(jìn)行編譯,此時(shí)還未開始訓(xùn)練模型,只對(duì)權(quán)重進(jìn)行了初始化,大家也可以嘗試一下用這個(gè)隨機(jī)初始狀態(tài)來(lái)預(yù)測(cè),當(dāng)然得到的結(jié)果只是隨機(jī)猜測(cè):

方法將給出最后一層的原始輸出,在這個(gè)例子中是由函數(shù)生成的。

之后再?zèng)]什么好寫的了,但是這里還沒(méi)有關(guān)于答案的任何知識(shí),它只是對(duì)輸入使用了隨機(jī)權(quán)重。接下來(lái)可以試著進(jìn)行訓(xùn)練。

提示

在第一次訓(xùn)練時(shí)網(wǎng)絡(luò)可能不會(huì)收斂。第一次編譯可能以隨機(jī)分布的參數(shù)結(jié)束,導(dǎo)致難以或者不能得到全局極小值。如果遇到這種情況,可以用相同的參數(shù)再次調(diào)用,或者添加更多訓(xùn)練周期,看看網(wǎng)絡(luò)能否收斂?;蛘咭部梢杂貌煌碾S機(jī)起始點(diǎn)來(lái)重新初始化網(wǎng)絡(luò),然后再次嘗試。如果使用后面這種方法,請(qǐng)確保沒(méi)有設(shè)置隨機(jī)種子,否則只會(huì)不斷重復(fù)同樣的實(shí)驗(yàn)結(jié)果。

當(dāng)網(wǎng)絡(luò)一遍又一遍地學(xué)習(xí)這個(gè)小數(shù)據(jù)集時(shí),它終于弄明白了這是怎么回事。它從樣本中“學(xué)會(huì)”了什么是異或!這就是神經(jīng)網(wǎng)絡(luò)的神奇之處。

在這個(gè)經(jīng)過(guò)訓(xùn)練的模型上再次調(diào)用(和)會(huì)產(chǎn)生更好的結(jié)果。它在這個(gè)小數(shù)據(jù)集上獲得了 100%的精確度。當(dāng)然,精確率并不是評(píng)估預(yù)測(cè)模型的最佳標(biāo)準(zhǔn),但對(duì)這個(gè)小例子來(lái)說(shuō)完全可以說(shuō)明問(wèn)題。接下來(lái)展示了如何保存這個(gè)異或模型:

同樣也有對(duì)應(yīng)的方法來(lái)重新實(shí)例化模型,這樣做預(yù)測(cè)時(shí)不必再去重新訓(xùn)練模型。雖然運(yùn)行這個(gè)模型只需要幾秒,但是在后面的章節(jié)中,模型的運(yùn)行時(shí)間將會(huì)快速增長(zhǎng)到以分鐘、小時(shí)甚至天為單位,這取決于硬件性能和模型的復(fù)雜度,所以請(qǐng)準(zhǔn)備好!

轉(zhuǎn)載請(qǐng)注明來(lái)自濟(jì)南富森木工刀具制造有限公司 ,本文標(biāo)題:《用Python實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)(附完整代碼)!》

百度分享代碼,如果開啟HTTPS請(qǐng)參考李洋個(gè)人博客
每一天,每一秒,你所做的決定都會(huì)改變你的人生!

發(fā)表評(píng)論

快捷回復(fù):

驗(yàn)證碼

評(píng)論列表 (暫無(wú)評(píng)論,2人圍觀)參與討論

還沒(méi)有評(píng)論,來(lái)說(shuō)兩句吧...

Top