全連接神經(jīng)網(wǎng)絡(luò)之反向傳播算法原理推導(dǎo)

簡(jiǎn)介

在上一篇文章《手寫(xiě)一個(gè)全連接神經(jīng)網(wǎng)絡(luò)用于MNIST數(shù)據(jù)集》中,我們使用少于100行代碼實(shí)現(xiàn)了一個(gè)3層的全連接網(wǎng)絡(luò),并且在MNIST數(shù)據(jù)集上取得了95%以上的準(zhǔn)確率。相信讀過(guò)這篇文章的讀者對(duì)全連接網(wǎng)絡(luò)如何使用梯度下降算法來(lái)學(xué)習(xí)自身的權(quán)值和偏置的原理已經(jīng)有所了解(如果你沒(méi)讀過(guò),建議先看一下再閱讀本文)。但是上篇文章留下了一個(gè)問(wèn)題,就是我們沒(méi)有討論如何計(jì)算損失函數(shù)的梯度,在本文中,我會(huì)詳細(xì)解釋如何計(jì)算這些梯度。

本文會(huì)涉及到較多的數(shù)學(xué)公式,要求讀者了解微積分的基本知識(shí),尤其是鏈?zhǔn)椒▌t。


反向傳播概覽

先引用一下維基百科中對(duì)于反向傳播的定義:

反向傳播(英語(yǔ):Backpropagation,縮寫(xiě)為BP)是“誤差反向傳播”的簡(jiǎn)稱(chēng),是一種與最優(yōu)化方法(如梯度下降法)結(jié)合使用的,用來(lái)訓(xùn)練人工神經(jīng)網(wǎng)絡(luò)的常見(jiàn)方法。該方法對(duì)網(wǎng)絡(luò)中所有權(quán)重計(jì)算損失函數(shù)的梯度。這個(gè)梯度會(huì)反饋給最優(yōu)化方法,用來(lái)更新權(quán)值以最小化損失函數(shù)。

反向傳播的核心是對(duì)損失函數(shù)C關(guān)于任何權(quán)重w(或偏置b)的偏導(dǎo)數(shù)\partial C/\partial w(或\partial C / \partial b)的表達(dá)式。這個(gè)表達(dá)式告訴我們?cè)诟淖儥?quán)重和偏置時(shí),損失函數(shù)變化的快慢。由于C是一個(gè)疊加了多種運(yùn)算的多元函數(shù),所以對(duì)網(wǎng)絡(luò)的某一層的某一個(gè)權(quán)重w_i的偏導(dǎo)數(shù)可能會(huì)變得很復(fù)雜,不過(guò)這也讓我們直觀(guān)的看到了某一個(gè)權(quán)重w_i的變化究竟會(huì)如何改變網(wǎng)絡(luò)的行為。


神經(jīng)網(wǎng)絡(luò)參數(shù)的表示

通過(guò)上一篇文章,我們知道神經(jīng)網(wǎng)絡(luò)是由多個(gè)S型神經(jīng)元構(gòu)成的,每個(gè)S型神經(jīng)元有輸入x,權(quán)重w,偏置b,以及輸出z。那么當(dāng)它們組合在一起形成復(fù)雜的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)的時(shí)候,參數(shù)會(huì)變得異常的多。這個(gè)時(shí)候我們需要先約定一下如何來(lái)表示這些參數(shù),以便數(shù)學(xué)描述和基于矩陣的運(yùn)算。先以一個(gè)簡(jiǎn)單的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)為例:


可以看到上述網(wǎng)絡(luò)一共有3層,第一層是輸入層,第二層是隱層,第三層是輸出層。接下來(lái)我會(huì)在這張圖上標(biāo)注權(quán)重w,偏置b,激活值a等參數(shù),并且會(huì)引入若干個(gè)符號(hào)來(lái)表示這些參數(shù)。

w_{jk}^l表示從(l-1)^{th}層的k^{th}個(gè)神經(jīng)元到l^{th}層的j^{th}個(gè)神經(jīng)元上的權(quán)重。這個(gè)表示看起來(lái)有點(diǎn)奇怪,也不容易理解,因?yàn)檫@個(gè)表達(dá)式里面雖然帶有一個(gè)l,但是實(shí)際上它表示的是第l-1層到第l層的之間的權(quán)重關(guān)系。以上圖的藍(lán)色箭頭所指的線(xiàn)段為例,w_{24}^3代表的含義是,第2層的第4個(gè)神經(jīng)元到第3層的第2個(gè)神經(jīng)元之間的權(quán)重。

其實(shí)如果你仔細(xì)看過(guò)上一篇文章中的代碼,你會(huì)發(fā)現(xiàn)權(quán)重向量w和偏置向量b只是第二層和第三層才會(huì)有,而輸入層是沒(méi)有這些參數(shù)的。在上圖中,我們是用帶箭頭的線(xiàn)段來(lái)表示的權(quán)重,故w^l代表的并不是第l層指出去的線(xiàn)段,而指的是第l-1層指向它的線(xiàn)段,這個(gè)有點(diǎn)違反直覺(jué),但是接下來(lái)你會(huì)看到這么做的好處。

b_j^l表示在第l^{th}層的第j^{th}個(gè)神經(jīng)元的偏置,如上圖b^2_{3}表示第2層的第3個(gè)神經(jīng)元上的偏置。
a_j^l表示在第l^{th}層的第j^{th}個(gè)神經(jīng)元的激活值,如上圖a^2_{4}代表第2層的第4個(gè)神經(jīng)元上的激活值。

有了這些表示,l^{th}層的第j^{th}個(gè)神經(jīng)元的激活值a^l_{j}就和(l-1)^{th}層的激活值a^{l-1}通過(guò)方程關(guān)聯(lián)起來(lái)了:

其中求和是在(l-1)^{th}層的全部k個(gè)神經(jīng)元之間進(jìn)行的。為了用矩陣的形式重寫(xiě)這個(gè)表達(dá)式,我們對(duì)每一層l都定義了一個(gè)權(quán)重矩陣w^l。權(quán)重矩陣w^l的元素是連接到第l層的神經(jīng)元的權(quán)重(即指向第l層神經(jīng)元的全部箭頭)。同理,對(duì)每一層定義一個(gè)偏置向量b^l,向量中的每一個(gè)元素就是b^l_k。最后定義每一層的激活向量a^l,向量中的每個(gè)元素是a^l_j。
也許你還不明白這個(gè)怎么計(jì)算的,讓我以上圖的a^2_4來(lái)描述一下它的計(jì)算過(guò)程。我用橙色的線(xiàn)段來(lái)代表所有指向第2層第4個(gè)神經(jīng)元的權(quán)重,可以看到一共有3條線(xiàn)段指向了它,其代表的權(quán)重分別是w^2_{41},w^2_{42}w^2_{43},由于這是第一層,故它的激活向量就是輸入向量x,b_4^2代表的是這個(gè)神經(jīng)元的偏置值,那么完整的計(jì)算過(guò)程描述如下:
a^2_4 = \sigma (w^2_{41}*a^1_1 + w^2_{42}*a^1_2 + w^2_{43}*a^1_3 + b_4^2)

上面的公式(1)還是太麻煩了,我們注意到(1)式中的參數(shù)w、ab其實(shí)都是向量,故我們可以將其改寫(xiě)成矩陣形式,如下:

這個(gè)式子看著更加簡(jiǎn)潔,它描述了第l層的激活值a^l與第l-1層的激活值a^{l-1}之間的關(guān)系。我們只需要將本層的權(quán)重矩陣作用在上一層的激活向量上,然后加上本層的偏置向量,最后通過(guò)\sigma函數(shù),便得到了本層的激活向量。
如果將(2)式寫(xiě)的更詳細(xì)一點(diǎn),其實(shí)我們是先得到了中間量z^l = w^l a^{l-1} + b^l,然后通過(guò)\sigma函數(shù)。我們稱(chēng)z^l為第l層神經(jīng)元的帶權(quán)輸入。故為了本文后面描述方便,我們也會(huì)將(2)式寫(xiě)成以下形式:

注意z^l是一個(gè)向量,代表了第l層的帶權(quán)輸入。它的每一個(gè)元素是z^l_j = \sum_k w_{jk}^l a_k^{l-1} + b_j^l,其中z^l_j就是第l層的第j個(gè)神經(jīng)元的激活函數(shù)的帶權(quán)輸入。

注意,在本文中,只帶了上標(biāo)而沒(méi)有帶下標(biāo)的表達(dá)式,都是指的向量。


損失函數(shù)

還記得我們?cè)谏弦黄恼轮?,使用了均方差損失函數(shù),定義如下:

其中n是訓(xùn)練樣本的總數(shù),求和運(yùn)算遍歷了每個(gè)訓(xùn)練樣本x,y(x)是每個(gè)樣本對(duì)應(yīng)的標(biāo)簽,L代表網(wǎng)絡(luò)的層數(shù),a^L代表的是輸入為x時(shí)網(wǎng)絡(luò)輸出的激活向量(在MNIST任務(wù)中,輸出是一個(gè)10維的向量)。
在上式中,對(duì)于一個(gè)特定的輸入樣本集x,ny(x)都是固定的,所以我們可以將C看做是a^L的函數(shù)。
本文將會(huì)繼續(xù)使用此損失函數(shù)來(lái)描述如何進(jìn)行反向傳播算法的應(yīng)用。


Hadamard乘積

反向傳播算法基于常規(guī)的線(xiàn)性代數(shù)運(yùn)算 —— 諸如向量加法,向量矩陣乘法等。但是有一個(gè)運(yùn)算不大常?。特別地,假設(shè)st是兩個(gè)同樣維度的向量。那么我們使用s ⊙ t來(lái)表示按元素的乘積。所以s ⊙ t的元素就是(s ⊙ t)_j = s_jt_j。舉個(gè)例子如下:

這種類(lèi)型的按元素乘法有時(shí)候被稱(chēng)為Hadamard乘積,具體定義可以參考百度百科。


反向傳播

定義神經(jīng)元誤差

反向傳播其實(shí)是對(duì)權(quán)重和偏置變化影響損失函數(shù)過(guò)程的理解,最終的目的就是計(jì)算偏導(dǎo)數(shù)\partial C/ \partial w^l_{jk }\partial C / \partial b^l_j。為了計(jì)算這些值,我們首先引入一個(gè)中間量\delta ^l_j,我們稱(chēng)之為在第l^{th}層的第j^{th}個(gè)神經(jīng)元上的誤差。

為了理解誤差是如何定義的,假設(shè)在神經(jīng)網(wǎng)絡(luò)上有一個(gè)調(diào)皮?:


這個(gè)調(diào)皮鬼在第l層的第j^{th}個(gè)神經(jīng)元上。當(dāng)輸入進(jìn)來(lái)的時(shí)候,這個(gè)調(diào)皮鬼對(duì)這個(gè)輸入增加了很小的變化\Delta z^l_j,使得神經(jīng)元輸出由原本的\sigma (z^l_j)變成了\sigma (z^l_j + \Delta z^l_j)。這個(gè)變化會(huì)依次向網(wǎng)絡(luò)后面的層進(jìn)行傳播,最終導(dǎo)致整個(gè)損失函數(shù)產(chǎn)生\frac {\partial C }{\partial z^l_j} \Delta z^l_j的變化(具體可以參考全微分的定義)。
現(xiàn)在加入這個(gè)調(diào)皮鬼改邪歸正了,并且試著幫你優(yōu)化損失函數(shù),它試著找到可以讓損失函數(shù)更小的\Delta z^l_j。假設(shè)\frac {\partial C}{\partial z^l_j}有一個(gè)很大的值,或正或負(fù)。那么這個(gè)調(diào)皮鬼可以通過(guò)選擇適當(dāng)?shù)?img class="math-inline" src="https://math.jianshu.com/math?formula=%5CDelta%20z%5El_j" alt="\Delta z^l_j" mathimg="1">來(lái)降低損失函數(shù)的值。相反,如果\frac {\partial C}{\partial z^l_j}接近0,那么無(wú)論怎么調(diào)整\Delta z^l_j都不能改善太多損失函數(shù)的值。因此,在調(diào)皮鬼看來(lái),這時(shí)神經(jīng)元已經(jīng)接近最優(yōu)了。所以這里有一種啟發(fā)式的認(rèn)識(shí),\frac {\partial C}{\partial z^l_j}可以認(rèn)為是神經(jīng)元誤差的度量。

按照上面的描述,我們定義第l層的第j^{th}個(gè)神經(jīng)元上的誤差\delta ^l_j為:
\delta ^l_j = \frac {\partial C}{\partial z^l_j} \tag 5

反向傳播的四個(gè)方程

反向傳播基于4個(gè)基本方程,這些方程指明了計(jì)算誤差\delta ^l和損失函數(shù)梯度的方法。先列舉出來(lái):

1. 輸出層誤差的方程

結(jié)合(3)式,我們簡(jiǎn)單證明一下第一個(gè)方程:

因?yàn)樯鲜街械那蠛褪窃谳敵鰧拥乃?img class="math-inline" src="https://math.jianshu.com/math?formula=k" alt="k" mathimg="1">個(gè)神經(jīng)元上運(yùn)行的,由于這里是求損失函數(shù)C對(duì)第L層的第j個(gè)神經(jīng)元的輸出激活值求導(dǎo),故當(dāng)k \ne j時(shí), \partial a^L_k / \partial z^L_j都為0。

上式右邊第一個(gè)項(xiàng)\partial C / \partial a^L_j表示損失函數(shù)C隨著j^{th}輸出激活值的變化而變化的速度。假如C不依賴(lài)特定的神經(jīng)元j,那么\delta ^L_j就會(huì)比較小,這也是我們期望的效果。右邊第二項(xiàng)\delta ' (z^L_j)表達(dá)的是在激活函數(shù)\sigmaz^L_j處的變化速度。
對(duì)于第一項(xiàng)\partial C / \partial a^L_j,它依賴(lài)特定的損失函數(shù)C的形式,如果我們使用均方差損失函數(shù),那么其實(shí)很容易就可以算出來(lái),如下:

如果我們令\nabla _aC代表?yè)p失函數(shù)C對(duì)激活值向量a的偏導(dǎo)數(shù)向量,那么(6)式可以被寫(xiě)成矩陣的形式:

(7)式就是反向傳播4個(gè)方程中的第一個(gè)方程。對(duì)于均方差損失函數(shù),\nabla _aC = (a^L - y),所以(7)式可以寫(xiě)成如下形式:

寫(xiě)成上述向量的形式是為了方便使用numpy之類(lèi)的庫(kù)進(jìn)行矩陣計(jì)算。

2. 使用下一層的誤差\delta ^{l+1}來(lái)更新當(dāng)前層的誤差\delta ^{l}

第一個(gè)方程描述了輸出層的誤差\delta ^L,那么如何求得前一層的誤差\delta ^{L-1}呢?我們還是可以從(5)式出發(fā),對(duì)其運(yùn)用鏈?zhǔn)椒▌t,如下:

又根據(jù)上文,我們知道z^l_j = \sum_k w_{jk}^l a_k^{l-1} + b_j^l,a^l= \sigma(z^l),因此可以得到:

上式對(duì)z^l_j微分的結(jié)果如下:

將式(11)帶入式子(9)得:

將式(12)寫(xiě)成向量的形式,得到第二個(gè)方程:

其中(w^{l+1})^T代表第(l+1)^{th}層的權(quán)重矩陣的轉(zhuǎn)置。這個(gè)公式看起來(lái)挺復(fù)雜,但是每一項(xiàng)都有具體的意義。假如第(l+1)^{th}層的誤差是\delta ^{l+1},當(dāng)我們使用同一層的轉(zhuǎn)置權(quán)重矩陣(w^{l+1})^T去乘以它時(shí),直觀(guān)感覺(jué)可以認(rèn)為它是沿著網(wǎng)絡(luò)反向地移動(dòng)誤差,這給了我們度量在第l^{th}層輸出的誤差方法(還記得上文,我們使用帶箭頭的線(xiàn)段來(lái)表明權(quán)重么,這里這么做,相當(dāng)于把第l層第j個(gè)神經(jīng)元指向第l+1層的所有神經(jīng)元的箭頭線(xiàn)段全部逆向了)。
接著我們進(jìn)行Hadamard乘積運(yùn)算,這會(huì)讓誤差通過(guò)第l層的激活函數(shù)反向傳遞回來(lái),并給出在第l層的帶權(quán)輸入誤差\delta

我估計(jì)你看到這里會(huì)很懵逼,我當(dāng)時(shí)學(xué)習(xí)的時(shí)候也是非常迷惑,感覺(jué)腦子一團(tuán)糟,不過(guò)我將會(huì)以第二層第4個(gè)神經(jīng)元的誤差\delta ^2_4為例,來(lái)展示誤差的反向傳播過(guò)程,根據(jù)式(12),可以得出以下計(jì)算過(guò)程:

下面用圖例展示了誤差是如何從第三層的神經(jīng)元反向傳播到第二層的:

其實(shí)這個(gè)也很符合直覺(jué),因?yàn)榈?層第4個(gè)神經(jīng)元連接到了第3層的全部神經(jīng)元,故誤差反向傳播的時(shí)候,應(yīng)該是與其連接的所有神經(jīng)元的誤差之和傳遞給此神經(jīng)元。

有了前兩個(gè)方程之后,我們就可以計(jì)算任何層的誤差\delta ^L。首先使用方程(7)計(jì)算當(dāng)前層誤差\delta ^L,然后使用式(13)來(lái)計(jì)算得到\delta ^{L-1},以此類(lèi)推,直到反向傳播完整個(gè)網(wǎng)絡(luò)。

3. 損失函數(shù)關(guān)于任意偏置b^l_j的變化率

由上面內(nèi)容可知,z^l_j = \sum_k w_{jk}^l a_k^{l-1} + b_j^l,z^l_j對(duì)b^l_j求偏導(dǎo)得\frac {\partial z^l_j}{\partial b^l_j} = 1。則有:

故可知,誤差\delta ^l_j和偏導(dǎo)數(shù)\partial C / \partial b^l_j完全一致。
寫(xiě)成向量的形式如下:

其中\delta和偏置b都是針對(duì)同一個(gè)神經(jīng)元。

4. 損失函數(shù)對(duì)于任意一個(gè)權(quán)重的變化率

由上面內(nèi)容可知,z^l_j = \sum_k w_{jk}^l a_k^{l-1} + b_j^l,z^l_j對(duì)w^l_{jk}求偏導(dǎo)得\frac {\partial z^l_j}{\partial w^l_{jk}} = a^{l-1}_k。又:

上式可以簡(jiǎn)化成更少下標(biāo)的表示:

其中a_{in}是上一層的激活輸出向量,\delta _{out}是當(dāng)前層的誤差,可以使用下圖來(lái)描述:


上圖說(shuō)明,當(dāng)上一層的激活值a_{in}很小的時(shí)候,梯度\partial C / \partial w也會(huì)很小,這意味著在梯度下降算法過(guò)程中,這個(gè)權(quán)重不會(huì)改變很多。這樣導(dǎo)致的問(wèn)題就是來(lái)自較低激活值的神經(jīng)元的權(quán)重學(xué)習(xí)會(huì)非常緩慢。
另外觀(guān)察一下前兩個(gè)方程,可以看到它們的表達(dá)式中都包含\sigma ' (z^l_k),即要計(jì)算\sigma函數(shù)的導(dǎo)數(shù)?;貞浺幌律弦黄恼轮蠸igmoid函數(shù)的圖像,可以看到,當(dāng)輸入非常大或者非常小的時(shí)候,\sigma函數(shù)變得非常平坦,即導(dǎo)數(shù)趨近于0,這會(huì)導(dǎo)致梯度消失的問(wèn)題,后面會(huì)專(zhuān)門(mén)寫(xiě)文章討論。
總結(jié)一下,如果輸入神經(jīng)元激活值很低,或者神經(jīng)元輸出已經(jīng)飽和了,那么權(quán)重學(xué)習(xí)的過(guò)程會(huì)很慢。

反向傳播的算法流程

反向傳播的4個(gè)方程給出了一種計(jì)算損失函數(shù)梯度的方法,下面用算法描述出來(lái):

  1. 輸入x,為輸入層設(shè)置對(duì)應(yīng)的激活值a^1。
  2. 前向傳播: 對(duì)每一層l=2,3,4,...,L,計(jì)算相應(yīng)的權(quán)重輸入z^L = w^la^l + b^la^l = \sigma (z^l)。
  3. 輸出層的誤差\delta ^L: 計(jì)算向量\delta ^L = \nabla C \ ⊙ \ \sigma ' (z^L)
  4. 誤差反向傳播:對(duì)于每一層l=L-1,L-2,...,2,計(jì)算\delta^l = ((w^{l+1})^T \delta ^{l+1}) ⊙ \sigma ' (z^L)
  5. 輸出: 損失函數(shù)的梯度分別由\frac {\partial C}{\partial w^l_{ji}} = a^{l-1}_k \delta ^L_j\frac {\partial C}{\partial b^l_j} = \delta ^L_j得出。

很多人在一開(kāi)始學(xué)習(xí)的時(shí)候,分不清梯度下降和反向傳播之間的關(guān)系,我最開(kāi)始也是。不過(guò)從上面的流程中應(yīng)該可以了解一點(diǎn)。梯度下降法是一種優(yōu)化算法,中心思想是沿著目標(biāo)函數(shù)梯度的方向更新參數(shù)值以希望達(dá)到目標(biāo)函數(shù)最小(或最大)。而這個(gè)算法中需要計(jì)算目標(biāo)函數(shù)的梯度,那么反向傳播就是在深度學(xué)習(xí)中計(jì)算梯度的一種方式。

代碼解析

這里只列舉了代碼中反向傳播部分,若需完整代碼,請(qǐng)?jiān)?a target="_blank">https://github.com/HeartbreakSurvivor/FCN下載。
我對(duì)代碼中關(guān)鍵步驟,都添加了詳細(xì)的注釋?zhuān)x者再對(duì)著文章內(nèi)容,應(yīng)該能夠看明白。

def update_mini_batch(self, mini_batch, eta):
    """
    通過(guò)小批量隨機(jī)梯度下降以及反向傳播來(lái)更新神經(jīng)網(wǎng)絡(luò)的權(quán)重和偏置向量
    :param mini_batch: 隨機(jī)選擇的小批量
    :param eta: 學(xué)習(xí)率
    """
    nabla_b = [np.zeros(b.shape) for b in self._biases]
    nabla_w = [np.zeros(w.shape) for w in self._weights]
    for x, y in mini_batch:
        # 反向傳播算法,運(yùn)用鏈?zhǔn)椒▌t求得對(duì)b和w的偏導(dǎo)
        delta_nabla_b, delta_nabla_w = self.backprop(x, y)
        # 對(duì)小批量訓(xùn)練數(shù)據(jù)集中的每一個(gè)求得的偏導(dǎo)數(shù)進(jìn)行累加
        nabla_b = [nb + dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
        nabla_w = [nw + dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]

    # 使用梯度下降得出的規(guī)則來(lái)更新權(quán)重和偏置向量
    self._weights = [w - (eta / len(mini_batch)) * nw
                     for w, nw in zip(self._weights, nabla_w)]
    self._biases = [b - (eta / len(mini_batch)) * nb
                    for b, nb in zip(self._biases, nabla_b)]

def backprop(self, x, y):
    """
    反向傳播算法,計(jì)算損失對(duì)w和b的梯度
    :param x: 訓(xùn)練數(shù)據(jù)x
    :param y: 訓(xùn)練數(shù)據(jù)x對(duì)應(yīng)的標(biāo)簽
    :return: Return a tuple ``(nabla_b, nabla_w)`` representing the
            gradient for the cost function C_x.  ``nabla_b`` and
            ``nabla_w`` are layer-by-layer lists of numpy arrays, similar
            to ``self.biases`` and ``self.weights``.
    """
    nabla_b = [np.zeros(b.shape) for b in self._biases]
    nabla_w = [np.zeros(w.shape) for w in self._weights]
    # 前向傳播,計(jì)算網(wǎng)絡(luò)的輸出
    activation = x
    # 一層一層存儲(chǔ)全部激活值的列表
    activations = [x]
    # 一層一層第存儲(chǔ)全部的z向量,即帶權(quán)輸入
    zs = []
    for b, w in zip(self._biases, self._weights):
        # 利用 z = wt*x+b 依次計(jì)算網(wǎng)絡(luò)的輸出
        z = np.dot(w, activation) + b
        zs.append(z)
        # 將每個(gè)神經(jīng)元的輸出z通過(guò)激活函數(shù)sigmoid
        activation = sigmoid(z)
        # 將激活值放入列表中暫存
        activations.append(activation)
    # 反向傳播過(guò)程

    # 首先計(jì)算輸出層的誤差delta L
    delta = self.cost_derivative(activations[-1], y) * sigmoid_prime(zs[-1])
    # 反向存儲(chǔ) 損失函數(shù)C對(duì)b的偏導(dǎo)數(shù)
    nabla_b[-1] = delta
    # 反向存儲(chǔ) 損失函數(shù)C對(duì)w的偏導(dǎo)數(shù)
    nabla_w[-1] = np.dot(delta, activations[-2].transpose())
    # 從第二層開(kāi)始,依次計(jì)算每一層的神經(jīng)元的偏導(dǎo)數(shù) 
    for l in range(2, self._num_layers):
        z = zs[-l]
        sp = sigmoid_prime(z)
        # 更新得到前一層的誤差delta
        delta = np.dot(self._weights[-l + 1].transpose(), delta) * sp
        # 保存損失喊出C對(duì)b的偏導(dǎo)數(shù),它就等于誤差delta
        nabla_b[-l] = delta
        # 根據(jù)第4個(gè)方程,計(jì)算損失函數(shù)C對(duì)w的偏導(dǎo)數(shù)
        nabla_w[-l] = np.dot(delta, activations[-l - 1].transpose())
    # 返回每一層神經(jīng)元的對(duì)b和w的偏導(dǎo)數(shù) 
    return (nabla_b, nabla_w)

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容