李理:從Image Caption Generation理解深度學(xué)習(xí)(part III)

本系列文章面向程序員惊暴,希望通過(guò)Image Caption Generation穷劈,一個(gè)有意思的具體任務(wù),深入淺出地介紹深度學(xué)習(xí)的知識(shí)柑晒,涉及到很多深度學(xué)習(xí)流行的模型欧瘪,如CNN,RNN/LSTM匙赞,Attention等佛掖。本文為第三篇。

作者:李理涌庭,目前就職于環(huán)信芥被,即時(shí)通訊云平臺(tái)和全媒體智能客服平臺(tái),在環(huán)信從事智能客服和智能機(jī)器人相關(guān)工作坐榆,致力于用深度學(xué)習(xí)來(lái)提高智能機(jī)器人的性能拴魄。

相關(guān)文章:

從Image Caption Generation理解深度學(xué)習(xí)(part I)

從Image Caption Generation理解深度學(xué)習(xí)(part II)

2.2.5 反向傳播算法的推導(dǎo)

前面我們用很簡(jiǎn)單的幾十行python代碼基本上完成了一個(gè)多層神經(jīng)網(wǎng)絡(luò)。但是還差最重要的部分席镀,那就是計(jì)算loss function對(duì)參數(shù)的偏導(dǎo)數(shù)匹中,也就是反向傳播算法。下面我們來(lái)仔細(xì)的完成公式的推導(dǎo)豪诲,以及接下來(lái)會(huì)講怎么用代碼來(lái)實(shí)現(xiàn)顶捷。這一部分?jǐn)?shù)學(xué)公式多一些,可能很多讀者會(huì)希望跳過(guò)去屎篱,不過(guò)我還是建議大家仔細(xì)的閱讀服赎,其實(shí)神經(jīng)網(wǎng)絡(luò)用到的數(shù)學(xué)相比svm,bayes network等機(jī)器學(xué)習(xí)算法葵蒂,已經(jīng)非常簡(jiǎn)單了。請(qǐng)讀者閱讀的時(shí)候最好準(zhǔn)備一支筆和幾張白紙重虑,每一個(gè)公式都能推導(dǎo)一下践付。如果堅(jiān)持下來(lái),你會(huì)覺(jué)得其實(shí)挺簡(jiǎn)單的缺厉。

(1) feedforward階段的矩陣參數(shù)表示和計(jì)算

之前我們討論的是一個(gè)神經(jīng)元的計(jì)算永高,而在代碼里用到的卻是矩陣向量乘法。而且細(xì)心的讀者會(huì)發(fā)現(xiàn)我們?cè)跇?gòu)造參數(shù)矩陣weights的時(shí)候芽死,行數(shù)和列數(shù)分別是后一層的節(jié)點(diǎn)數(shù)和前一層的節(jié)點(diǎn)數(shù)乏梁。這似乎有點(diǎn)不自然,為什么不反過(guò)來(lái)呢关贵?看過(guò)下面這一部分就會(huì)明白了。

首先我們熟悉一下第L(因?yàn)樾懙腖和1太像卖毁,所以我用大寫的L)層的參數(shù)w_jk揖曾。它表示第L-1層的第k個(gè)神經(jīng)元到第L層的第j個(gè)神經(jīng)元的權(quán)重。比如第3層的w_24亥啦,參考上面的圖炭剪,它表示的是第2層的第4個(gè)神經(jīng)元到第3層的第二個(gè)神經(jīng)元。

對(duì)bias和激活函數(shù)后的結(jié)果a也采用類似的記號(hào)翔脱,如下圖所示奴拦。

b_32表示第2層的第3個(gè)神經(jīng)元的bias,而a_13第3層的第1個(gè)神經(jīng)元的激活届吁。

使用上面的記號(hào)错妖,我們就可以計(jì)算第L層的第j個(gè)神經(jīng)元的輸出a_jl:

第L層的第j個(gè)神經(jīng)元的輸入是L-1層的a_1,a_2,...;對(duì)應(yīng)的權(quán)值是w_j1,w_j2,...疚沐;bias是b_jL暂氯。所以a_jL就是上面的公式,k的范圍是從1到第L-1層的神經(jīng)元的個(gè)數(shù)亮蛔。

為了用矩陣向量乘法來(lái)一次計(jì)算第L層的所有神經(jīng)元的輸出痴施,我們需要定義第L層的參數(shù)矩陣w_l,它的大小是m*n究流,其中m是第L層的神經(jīng)元個(gè)數(shù)辣吃;而n則是第L-1層的個(gè)數(shù)。它的第i行第j列就是我們上面定義的w_jk芬探。此外我們還要定義向量b_l神得,它的大小是m(也就是第L層神經(jīng)元的個(gè)數(shù)),它的第j個(gè)元素就是我們上面定義的b_j灯节。

最后循头,我們定義element-wise的函數(shù)绵估,比如f(x) = x^2,如果輸入是一個(gè)向量卡骂,那么結(jié)果是和輸入一樣大小的向量国裳,它的每個(gè)元素是對(duì)輸入向量的每一個(gè)元素應(yīng)用這個(gè)函數(shù)的結(jié)果。

有了上面的定義全跨,我們就可以一次計(jì)算出第L層的輸出(一個(gè)長(zhǎng)度為m的向量)

下面是對(duì)上面這個(gè)公式的詳細(xì)證明(說(shuō)明):

我們需要證明的是向量aL的第j個(gè)元素就是前面的a_jL

此外缝左,為了方便后面的求解,我們把加權(quán)累加和也用一個(gè)符號(hào)z_l來(lái)表示浓若。

其中渺杉,它的第j個(gè)元素就是第L層的第j個(gè)神經(jīng)元的加權(quán)累加和:

這樣a_l就可以簡(jiǎn)單的對(duì)z_l的每個(gè)元素計(jì)算激活函數(shù)

現(xiàn)在我們?cè)倩仡櫼幌耭eedforward的代碼就非常直觀了:

def feedforward(self, a):

"""Return the output of the network if a is input."""

for b, w in zip(self.biases, self.weights):

a = sigmoid(np.dot(w, a)+b)

return a

傳給函數(shù)feedforward的參數(shù)a就是輸入向量x,第一層就是x挪钓,第二層就是第一個(gè)隱層是越,每一層的計(jì)算就是非常簡(jiǎn)單的參數(shù)矩陣w_l乘以上一層的激活a_l-1在加上b_l,然后用激活函數(shù)計(jì)算碌上。

初始化的時(shí)候w的大小是 (后一層的神經(jīng)元個(gè)數(shù)) * (前一層的神經(jīng)元個(gè)數(shù))倚评,再回顧一下初始化參數(shù)的代碼:

# sizes =[784, 30, 10]def __init__(self, sizes):self.num_layers = len(sizes)self.sizes = sizesself.biases =[np.random.randn(y, 1) for y in sizes[1:]]self.weights =[np.random.randn(y, x)for x, y in zip(sizes[:-1], sizes[1:])]

x, y in zip(sizes[:-1], sizes[1:]) x是第一層到最后倒數(shù)第二層,y是第二層到最后一層馏予,比如上面的sizes=[784, 30, 10]

x是[784, 30], y是[30, 10]天梧,注意隨機(jī)的矩陣是(y,x),所以self.weights是兩個(gè)矩陣霞丧,大小分別是30*784和10*30

(2) 關(guān)于損失函數(shù)C的兩個(gè)假設(shè)

1. 損失函數(shù)是每個(gè)訓(xùn)練數(shù)據(jù)的損失的平均

也就是C是這樣的形式:

對(duì)于之前我們使用的MSE損失函數(shù)呢岗,這是滿足的。我們使用batch的梯度下降的時(shí)候需要求C對(duì)參數(shù)w的偏導(dǎo)數(shù)蛹尝,因?yàn)閾p失函數(shù)是每個(gè)訓(xùn)練數(shù)據(jù)的損失的平均后豫,所以我們只需要求每個(gè)數(shù)據(jù)的偏導(dǎo)數(shù),然后加起來(lái)平均就行箩言。這個(gè)假設(shè)幾乎所有的損失函數(shù)都是滿足的【我是沒(méi)見(jiàn)過(guò)損失函數(shù)不滿足這個(gè)條件】

損失函數(shù)是最后一層輸出的函數(shù)

這個(gè)條件幾乎常見(jiàn)的損失函數(shù)都是這樣的硬贯,我們之前時(shí)候的MSE就是計(jì)算最后一層的輸出aL和正確的y(one-hot)的均方誤差,顯然是滿足的陨收。

(3) Hadamard product

這個(gè)名字看起來(lái)很復(fù)雜饭豹,其實(shí)很簡(jiǎn)單,就是兩個(gè)向量elementwise的乘法务漩≈羲ィ看一個(gè)例子就清楚了:

(4) 反向傳播算法(back propagation)的4個(gè)公式

回顧一下,我們之前說(shuō)了饵骨,梯度下降其實(shí)最核心的問(wèn)題就是求損失函數(shù)對(duì)每一個(gè)參數(shù)的偏導(dǎo)數(shù)翘悉。那我們就直接一個(gè)一個(gè)求好了,為什么又要搞出一個(gè)反向傳播算法呢居触?其實(shí)這個(gè)算法在不同的領(lǐng)域被不同的人重復(fù)“發(fā)現(xiàn)”過(guò)很多次妖混,有過(guò)很多不同的名字老赤,最本質(zhì)的應(yīng)該就是逆向求導(dǎo)(reverse-mode differentiation)或者叫做自動(dòng)求導(dǎo)(automatic differentiation)。自動(dòng)求導(dǎo)(AD)是非常通用的一種求偏導(dǎo)數(shù)的方法制市,很早就在流體力學(xué)和大氣物理等領(lǐng)域使用抬旺,反向傳播算法可以認(rèn)為是AD在神經(jīng)網(wǎng)絡(luò)中的應(yīng)用。不過(guò)最早發(fā)現(xiàn)這個(gè)算法的人(是誰(shuí)最早好像還有點(diǎn)爭(zhēng)議)并不是先知道AD可以直接用于神經(jīng)網(wǎng)絡(luò)祥楣,他發(fā)現(xiàn)這個(gè)算法是基于錯(cuò)誤的反向傳播而得到的开财,所有命名為(錯(cuò)誤的)反向傳播算法。后面我們會(huì)講到AD误褪,這是一個(gè)強(qiáng)大的算法责鳍,任何一個(gè)函數(shù),你能把它分解成有向無(wú)環(huán)圖的計(jì)算圖【函數(shù)一般都能分解成一些無(wú)依賴的最基礎(chǔ)的變量的復(fù)合函數(shù)兽间,因此肯定可以表示成這樣一個(gè)有向無(wú)環(huán)圖】历葛,然后每個(gè)節(jié)點(diǎn)都表示一個(gè)函數(shù)。只要你能求出這個(gè)函數(shù)在特定點(diǎn)的梯度【也就是這個(gè)函數(shù)對(duì)所以自變量的偏導(dǎo)數(shù)】(不需要求解析的偏導(dǎo)數(shù)渡八,當(dāng)然很多情況啃洋,這些函數(shù)都是能直接求出解析解,然后代入這個(gè)特定點(diǎn)就行屎鳍,但理論上我們是可以用其他方法,比如數(shù)值梯度近似來(lái)求的)问裕,就能自動(dòng)的計(jì)算損失函數(shù)對(duì)每一個(gè)參數(shù)的偏導(dǎo)數(shù)(也是在這個(gè)點(diǎn)的)逮壁,而且只要反向根據(jù)拓?fù)渑判虮闅v這個(gè)圖一次就行,非常高效和簡(jiǎn)單粮宛。后面我們會(huì)詳細(xì)的介紹AD窥淆。這個(gè)方法非常通用,TensorFlow的核心就是AD巍杈。使用AD的框架就比較靈活忧饭,我想“創(chuàng)造”一種新的網(wǎng)絡(luò)結(jié)構(gòu),我又不想【其實(shí)更可能是不會(huì)】推導(dǎo)出梯度的公式筷畦,那么我只需要把我的網(wǎng)絡(luò)能用這樣一個(gè)有向無(wú)環(huán)圖表示就行词裤。當(dāng)然節(jié)點(diǎn)必須要能夠求出梯度來(lái),一般我們的函數(shù)比如矩陣的運(yùn)算鳖宾,卷積等等TensorFlow都封裝好了——它把它叫做一個(gè)op吼砂。我們只需要搭積木一樣把這個(gè)計(jì)算圖定義出來(lái),TensorFlow就自動(dòng)的能根據(jù)AD計(jì)算出損失函數(shù)對(duì)所有參數(shù)的梯度來(lái)了鼎文。當(dāng)然如果你要用到一個(gè)TensorFlow沒(méi)有的op渔肩,那你就需要根據(jù)它的規(guī)范實(shí)現(xiàn)這個(gè)op,一個(gè)op最核心的接口就是兩個(gè)拇惋,一個(gè)是輸入x周偎,求f(x)抹剩;另一個(gè)就是求f在某個(gè)x0點(diǎn)的梯度。

不過(guò)這里蓉坎,我們還是沿著神經(jīng)網(wǎng)絡(luò)的發(fā)展歷史澳眷,從錯(cuò)誤的反向傳播角度來(lái)理解和推導(dǎo)這個(gè)算法。

首先袍嬉,我們會(huì)對(duì)每一個(gè)神經(jīng)元比如第L層的第j個(gè)境蔼,都定義一個(gè)錯(cuò)誤δ_jL

也就是損失函數(shù)對(duì)z也就是線性累加和的偏導(dǎo)數(shù)。為什么定義這樣一個(gè)東西呢伺通?我們假設(shè)在第L層的第j個(gè)神經(jīng)元上有一個(gè)精靈(Daemon)

當(dāng)這個(gè)神經(jīng)元得到來(lái)自上一次的輸入累加計(jì)算出z_jL的時(shí)候箍土,它會(huì)惡作劇的給一點(diǎn)很小的干擾Δz_jL。原來(lái)它應(yīng)該輸出的是σ(z_jL)罐监,現(xiàn)在變成了σ(z_jL +Δz_jL)吴藻。這個(gè)微小的變化逐層傳播,最終導(dǎo)致?lián)p失函數(shù)C也發(fā)生如下的變化:

這個(gè)其實(shí)就是導(dǎo)數(shù)的直覺(jué)定義:微小的Δx引起微小的Δy弓柱,Δy/Δx約等于導(dǎo)數(shù)沟堡。

不過(guò)這個(gè)精靈是個(gè)好精靈,它想幫助我們減少損失矢空。 當(dāng)

大于0的時(shí)候航罗,它讓?duì)_jL小于0,反之當(dāng)它小于0的時(shí)候它讓?duì)_jL大于0屁药。這樣

總是小于0

因此我們的loss就會(huì)變小粥血。而其絕對(duì)值越大,我們的損失減少的越多酿箭。

當(dāng)然你會(huì)說(shuō)為什么不能讓?duì)_jL非常大复亏,這樣我們的損失總是減少很多?可惜這個(gè)精靈是個(gè)數(shù)學(xué)家缭嫡,它說(shuō)如果Δx太大缔御,那么Δy=df/dx *Δx就不準(zhǔn)確了。

所以我們可以這樣認(rèn)為:它就是第L層的第j個(gè)神經(jīng)元“引起”的“錯(cuò)誤”妇蛀。如果絕對(duì)值大耕突,則它的“責(zé)任”也大,它就得多做出一些調(diào)整讥耗;反之如果它趨近于0有勾,說(shuō)明它沒(méi)有什么“責(zé)任”,也就不需要做出什么改變古程。

因此通過(guò)上面的啟發(fā)蔼卡,我們定義出δ_jL來(lái)。

接下來(lái)我們逐個(gè)介紹反向傳播算法的4個(gè)公式。

公式1. 第L層(最后一層) 的錯(cuò)誤

這個(gè)公式的第一項(xiàng)雇逞,就是損失C對(duì)a_jL的導(dǎo)數(shù)荤懂,它越大,說(shuō)明C受a_jL的影響也就越大塘砸,如果有了錯(cuò)誤节仿,第a_jL的“責(zé)任”也就越大,錯(cuò)誤也就越大掉蔬。第二項(xiàng)是a_jL受z_jL的影響廊宪。兩者乘起來(lái)就是z_jL對(duì)最終損失的影響,也就是它的“責(zé)任”的大小女轿。

這個(gè)公式很好計(jì)算箭启,首先第二項(xiàng)就是把z_jL的值(這個(gè)在feedforward節(jié)點(diǎn)就算出來(lái)并存儲(chǔ)下來(lái)了)代入σ'(x)。如果σ是sigmoid函數(shù)蛉迹,我們前面也推導(dǎo)過(guò)它的導(dǎo)數(shù):σ’(x)=σ(x)*(1-σ(x))傅寡。第一項(xiàng)當(dāng)然依賴于損失函數(shù)的定義,一般也很好求北救。比如我們的MSE損失:

具體的推導(dǎo)我在紙上寫了一下荐操,雖然很簡(jiǎn)單,我們也可以練練手珍策,尤其是對(duì)于求和公式的展開(kāi)托启,希望大家能熟悉它,以后的推導(dǎo)我可能就不展開(kāi)求和公式了攘宙,你需要知道求和公式里哪些項(xiàng)是和外面的自變量無(wú)關(guān)的驾中。

公式BP1是elementwise的,我們需要變量j來(lái)計(jì)算每一個(gè)δ_jL模聋。我們也可以把它寫成向量的形式,以方便利用線性代數(shù)庫(kù)唠亚,它們可以一次計(jì)算向量或者矩陣链方,可以用很多技術(shù)利用硬件特性來(lái)優(yōu)化(包括GPU,SSE等)速度灶搜。

右邊δ'(z_L)很容易理解祟蚀,左邊的記號(hào)可能有些費(fèi)解,其實(shí)我們把?aC當(dāng)成一個(gè)整體就好了割卖,它是一個(gè)向量前酿,第一個(gè)元素是?C/?a_1L,第二個(gè)就是?C/?a_2L鹏溯,…

如果算上函數(shù)C是MSE的話罢维,上面的公式就可以簡(jiǎn)化成:

公式2. 第l層(非最后一層) 的錯(cuò)誤

等下我們會(huì)證明這個(gè)公式,不過(guò)首先我們來(lái)熟悉一下公式丙挽。如果我們想“背”下這個(gè)公式的話肺孵,似乎看起來(lái)比第一個(gè)BP1要復(fù)雜很多 匀借。我們先檢查一下矩陣和向量的維度,假設(shè)l+1層有m個(gè)元素平窘,l層n個(gè)吓肋。則w_l+1的大小是m*n,轉(zhuǎn)置之后是n*m,δ_l+1的大小是n*1瑰艘,所以矩陣相乘后是m*1是鬼,這和δ_l是一樣的,沒(méi)有問(wèn)題紫新。

接下來(lái)我們仔細(xì)觀察一下BP2這個(gè)公式均蜜,首先第二項(xiàng)σ'(z_l)和前面的含義一樣,代表a_l對(duì)于z_l的變化率弊琴。

而第一項(xiàng)復(fù)雜一點(diǎn)兆龙,我們知道第l層的第j個(gè)神經(jīng)元會(huì)影響第l+1層的所有神經(jīng)元,從而也影響最終的損失C敲董。這個(gè)公式直接給了一個(gè)矩陣向量的形式紫皇,看起來(lái)不清楚,所以我在草稿紙上展開(kāi)了:

最終第L層的第j個(gè)神經(jīng)元的損失就是如下公式:

這下應(yīng)該就比較清楚了腋寨,第l層的第j個(gè)神經(jīng)元的損失聪铺,就是把l+1層的損失“反向傳播”回來(lái),當(dāng)然要帶上權(quán)重萄窜,權(quán)重越大铃剔,“責(zé)任”也就越大。

如果要“背”出這個(gè)公式也沒(méi)有那么復(fù)雜了查刻,先不看σ'(z_l)键兜,第一項(xiàng)應(yīng)該是矩陣w_l+1乘以δ_l+1。由于矩陣是m*n穗泵,而

向量δ_l+1是m*1普气,為了能讓矩陣乘法成立,那么就只能把w轉(zhuǎn)置一下佃延,變成n*m现诀,然后就很容易記住這個(gè)公式了。

注意履肃,BP2的計(jì)算是從后往前的仔沿,首先根據(jù)BP1,最后一層的δ_L我們已經(jīng)算出來(lái)了尺棋,因此可以向前計(jì)算L-1層的δ_L-1封锉,

有了δ_L-1就能計(jì)算δ_L-2,…,最終能算出第一個(gè)隱層(也就是第2層)δ_1來(lái)烘浦。

公式3. 損失函數(shù)對(duì)偏置b的梯度

這前面費(fèi)了大力氣求δ_l抖坪,不要忘了我們的最終目標(biāo)是求損失函數(shù)對(duì)參數(shù)w和b的偏導(dǎo)數(shù),而不是求對(duì)中間變量z的偏導(dǎo)數(shù)闷叉。

因此這個(gè)公式就是對(duì)b的偏導(dǎo)數(shù)擦俐。

或者寫成向量的形式:

?C/?b就是δ!

公式4. 損失函數(shù)對(duì)w的梯度

或者參考下圖寫成好記的形式:

也就是說(shuō)對(duì)于一條邊w_jkL握侧,?C/?w_ij就是這條邊射出的點(diǎn)的錯(cuò)誤δ乘以進(jìn)入點(diǎn)的激活蚯瞧。非常好記。

我們把這四個(gè)公式再總結(jié)一下:

(5) 這四個(gè)公式的證明

首先是BP1品擎,請(qǐng)參考下圖:

剩下的BP3和BP4也非常類似埋合,我就不證明了。

反向傳播算法

1.?a_1?= 輸入向量x

2. Feedforward 根據(jù)公式

計(jì)算z_l和a_l并存儲(chǔ)下來(lái)(反向傳播時(shí)要用的)

3. 計(jì)算最后一層的錯(cuò)誤

計(jì)算損失對(duì)所有參數(shù)的偏導(dǎo)數(shù)

2.2.6 代碼實(shí)現(xiàn)反向傳播算法

我們已經(jīng)把公式推導(dǎo)出來(lái)了萄传,那怎么用代碼實(shí)現(xiàn)呢甚颂?我們先把代碼復(fù)制一下,然后說(shuō)明部分都是作為代碼的注釋了秀菱,

請(qǐng)仔細(xì)閱讀振诬。

class Network(object):? def update_mini_batch(self, mini_batch, eta):? ? # mini_batch是batch大小,eta是learning rate? ? nabla_b =[np.zeros(b.shape) for b in self.biases]? ? # 構(gòu)造和self.biases一樣大小的向量衍菱,比如前面的例子 sizes=[784,30,10]赶么,則? ? # nabla_b是兩個(gè)向量,大小分別是30和10? ? nabla_w =[np.zeros(w.shape) for w in self.weights]? ? # 構(gòu)造和self.weights一樣大小的矩陣脊串,比如前面的例子 sizes=[784,30,10]辫呻,則? ? # nabla_w是兩個(gè)矩陣,大小分別是30*784和10*30? ? for x, y in mini_batch: #對(duì)于每個(gè)訓(xùn)練樣本x和y delta_nabla_b, delta_nabla_w = self.backprop(x, y) # 用backprop函數(shù)計(jì)算損失函數(shù)對(duì)每一個(gè)參數(shù)的偏導(dǎo)數(shù)琼锋。 # backprop函數(shù)下面會(huì)詳細(xì)講解 nabla_b =[nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)] # 把返回的對(duì)b偏導(dǎo)數(shù)累加到nabla_b中 nabla_w =[nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)] # 把返回的對(duì)w的偏導(dǎo)數(shù)累加到nabla_w中? ? self.weights =[w-(eta/len(mini_batch))*nw? for w, nw in zip(self.weights, nabla_w)]? ? # 計(jì)算完一個(gè)batch后更新參數(shù)w? ? self.biases =[b-(eta/len(mini_batch))*nb for b, nb in zip(self.biases, nabla_b)]? ? # 更新b... def backprop(self, x, y):? ? # 輸入是x和y放闺,返回?fù)p失函數(shù)C對(duì)每個(gè)參數(shù)w和b的偏導(dǎo)數(shù)? ? # 返回的格式是兩個(gè)元組,第一個(gè)是b的偏導(dǎo)數(shù)缕坎,第二個(gè)是w的雄人。? ? nabla_b =[np.zeros(b.shape) for b in self.biases]? ? # 構(gòu)造和self.biases一樣大小的向量,比如前面的例子 sizes=[784,30,10],則? ? # nabla_b是兩個(gè)向量,大小分別是30和10? ? nabla_w =[np.zeros(w.shape) for w in self.weights]? ? # 構(gòu)造和self.weights一樣大小的矩陣毙替,比如前面的例子 sizes=[784,30,10]陡蝇,則? ? # nabla_w是兩個(gè)矩陣,大小分別是30*784和10*30? ? # feedforward? ? activation = x? ? activations =[x] # 用一個(gè)list保存所有層的激活踩萎,下面backward會(huì)有用的? ? zs =? # 同樣的用一個(gè)list保存所有層的加權(quán)累加和z停局,下面也會(huì)用到。? ? #下面這段代碼在feedward也有,不過(guò)那里是用來(lái)predict用的不需要保存zs和activations? ? for b, w in zip(self.biases, self.weights): z = np.dot(w, activation)+b zs.append(z) activation = sigmoid(z) activations.append(activation)? # backward pass? #1. 首先計(jì)算最后一層的錯(cuò)誤delta董栽,根據(jù)公式BP1码倦,它是損失函數(shù)對(duì)a_L的梯度乘以σ'(z_L)? ? #? sigmoid_prime就是σ'(z_L),而?C/?a_L就是函數(shù)cost_derivative锭碳,對(duì)于MSE的損失函數(shù)袁稽,? ? #? 它就是最后一層的激活activations[-1] - y? ? delta = self.cost_derivative(activations[-1], y) * \ sigmoid_prime(zs[-1])? ? # 2. 根據(jù)公式BP3,損失對(duì)b的偏導(dǎo)數(shù)就是delta? ? nabla_b[-1] = delta? ? # 3. 根據(jù)公式BP4,損失對(duì)w的偏導(dǎo)數(shù)時(shí)delta_out * activation_in? ? #? 注意,我們的公式BP4是elementwise的擒抛,我們需要寫成矩陣向量的形式? ? #? 那怎么寫呢推汽?我們只需要關(guān)心矩陣的大小就行了。? ? #? 假設(shè)最后一層有m(10)個(gè)神經(jīng)元歧沪,前一層有n(30)個(gè)歹撒,? ? #? 則delta是10*1, 倒數(shù)第二層的激活activations[-2]是30*1? ? #? 我們想求的最后一層的參數(shù)nabla_w[-1]是10*30,那么為了能夠正確的矩陣乘法诊胞,? ? #? 只要一種可能就是 delta 乘以 activations[-2]的轉(zhuǎn)置暖夭,其實(shí)也就是向量delta和activations[-2]的外積? ? nabla_w[-1] = np.dot(delta, activations[-2].transpose())? ? # 接下來(lái)從倒數(shù)第二層一直往前計(jì)算delta,同時(shí)也把對(duì)w和b的偏導(dǎo)數(shù)求出來(lái)撵孤。? ? # 這里用到一個(gè)比較小的trick就是python的下標(biāo)是支持負(fù)數(shù)的迈着,-1表示最后一個(gè)元素,-2是倒數(shù)第二個(gè)? ? # l表示倒數(shù)第l層早直,2就表示倒數(shù)第2層寥假,num_layers - 1就表示順數(shù)第2層(也就是第1個(gè)隱層)? ? # 比如我們的例子:sizes=[784, 30, 10],那么l就是從2到3(不包含3)霞扬,l就只能是2糕韧,頁(yè)就是第1個(gè)(也是唯一的一? ? # 個(gè))隱層? ? ? for l in xrange(2, self.num_layers): # 倒數(shù)第l層的z z = zs[-l] # 計(jì)算σ'(z_l) sp = sigmoid_prime(z) # 根據(jù)BP2,計(jì)算delta_l,注意weights[-l+1]表示倒數(shù)第l層的下一層 delta = np.dot(self.weights[-l+1].transpose(), delta) * sp # 同上喻圃,根據(jù)BP3 nabla_b[-l] = delta # BP4萤彩,矩陣乘法參考前面的說(shuō)明 nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())? ? return (nabla_b, nabla_w)

2.2.7 為什么反向傳播算法是一個(gè)高效的算法?

分析完代碼斧拍,我們發(fā)現(xiàn)一次backprop函數(shù)調(diào)用需要feedforward一次雀扶,網(wǎng)絡(luò)有多少邊,就有多少次乘法肆汹,有多少個(gè)點(diǎn)就有多少次加分和激活函數(shù)計(jì)算(不算第一層輸入層)愚墓。反向計(jì)算也是一樣,不過(guò)是從后往前昂勉。也就是說(shuō)這是時(shí)間復(fù)雜度為O(n)的算法浪册。

如果我們不用反向傳播算法,假設(shè)我們用梯度的定義計(jì)算數(shù)值梯度岗照。對(duì)于每一個(gè)參數(shù)wj村象,

我們都用公式 limit (f(w1, w2, …, wj+Δ wj, …) - f(w1, w2, …, wj, …)/Δwj

f(w1, w2, wj, …)只需要feedforward一次笆环,但是對(duì)于每個(gè)參數(shù)wj,都需要feedforward一層來(lái)計(jì)算f(w1, w2, …, wj+Δ wj, …)厚者,它的時(shí)間復(fù)雜度是O(n)躁劣,那么對(duì)所有的參數(shù)的計(jì)算需要O(n^2)的時(shí)間復(fù)雜度。

假設(shè)神經(jīng)網(wǎng)絡(luò)有1百萬(wàn)個(gè)參數(shù)库菲,那么每次需要10^12這個(gè)數(shù)量級(jí)的運(yùn)算账忘,而反向傳播算法只需要10^6,因此這個(gè)方法比反向傳播算法要慢1百萬(wàn)倍蝙昙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末闪萄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子奇颠,更是在濱河造成了極大的恐慌败去,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烈拒,死亡現(xiàn)場(chǎng)離奇詭異圆裕,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)荆几,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門吓妆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人吨铸,你說(shuō)我怎么就攤上這事行拢。” “怎么了诞吱?”我有些...
    開(kāi)封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵舟奠,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我房维,道長(zhǎng)沼瘫,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任咙俩,我火速辦了婚禮耿戚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘阿趁。我一直安慰自己膜蛔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布脖阵。 她就那樣靜靜地躺著飞几,像睡著了一般。 火紅的嫁衣襯著肌膚如雪独撇。 梳的紋絲不亂的頭發(fā)上屑墨,一...
    開(kāi)封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音纷铣,去河邊找鬼卵史。 笑死,一個(gè)胖子當(dāng)著我的面吹牛搜立,可吹牛的內(nèi)容都是我干的以躯。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼啄踊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼忧设!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起颠通,我...
    開(kāi)封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤址晕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后顿锰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體谨垃,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年硼控,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了刘陶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡牢撼,死狀恐怖匙隔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情熏版,我是刑警寧澤纷责,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站纳决,受9級(jí)特大地震影響碰逸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜阔加,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一饵史、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧胜榔,春花似錦胳喷、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至尊惰,卻和暖如春讲竿,著一層夾襖步出監(jiān)牢的瞬間泥兰,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工题禀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鞋诗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓迈嘹,卻偏偏與公主長(zhǎng)得像削彬,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秀仲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容