轉(zhuǎn)載自:CSDN極客頭條
作者:李理 目前就職于環(huán)信,即時通訊云平臺和全媒體智能客服平臺西篓,在環(huán)信從事智能客服和智能機器人相關(guān)工作难衰,致力于用深度學(xué)習(xí)來提高智能機器人的性能捏卓。
相關(guān)文章:
李理:從Image Caption Generation理解深度學(xué)習(xí)(part I)
李理:從Image Caption Generation理解深度學(xué)習(xí)(part II)
李理:從Image Caption Generation理解深度學(xué)習(xí)(part III)
李理:從Image Caption Generation理解深度學(xué)習(xí) (part IV)
2.2.5 反向傳播算法的推導(dǎo)
前面我們用很簡單的幾十行python代碼基本上完成了一個多層神經(jīng)網(wǎng)絡(luò)榜配。但是還差最重要的部分否纬,那就是計算loss function對參數(shù)的偏導(dǎo)數(shù),也就是反向傳播算法蛋褥。下面我們來仔細的完成公式的推導(dǎo)临燃,以及接下來會講怎么用代碼來實現(xiàn)。這一部分數(shù)學(xué)公式多一些壁拉,可能很多讀者會希望跳過去谬俄,不過我還是建議大家仔細的閱讀柏靶,其實神經(jīng)網(wǎng)絡(luò)用到的數(shù)學(xué)相比svm,bayes network等機器學(xué)習(xí)算法弃理,已經(jīng)非常簡單了。請讀者閱讀的時候最好準備一支筆和幾張白紙屎蜓,每一個公式都能推導(dǎo)一下痘昌。如果堅持下來,你會覺得其實挺簡單的炬转。
(1) feedforward階段的矩陣參數(shù)表示和計算
之前我們討論的是一個神經(jīng)元的計算辆苔,而在代碼里用到的卻是矩陣向量乘法。而且細心的讀者會發(fā)現(xiàn)我們在構(gòu)造參數(shù)矩陣weights的時候扼劈,行數(shù)和列數(shù)分別是后一層的節(jié)點數(shù)和前一層的節(jié)點數(shù)驻啤。這似乎有點不自然,為什么不反過來呢荐吵?看過下面這一部分就會明白了骑冗。
首先我們熟悉一下第L(因為小寫的L和1太像,所以我用大寫的L)層的參數(shù)w_jk
先煎。它表示第L-1層的第k個神經(jīng)元到第L層的第j個神經(jīng)元的權(quán)重贼涩。比如第3層的w_24
,參考上面的圖薯蝎,它表示的是第2層的第4個神經(jīng)元到第3層的第二個神經(jīng)元遥倦。
對bias和激活函數(shù)后的結(jié)果a也采用類似的記號,如下圖所示占锯。
b_32
表示第2層的第3個神經(jīng)元的bias袒哥,而a_13
第3層的第1個神經(jīng)元的激活缩筛。
使用上面的記號,我們就可以計算第L層的第j個神經(jīng)元的輸出a_jl
:
第L層的第j個神經(jīng)元的輸入是L-1層的a_1,a_2,...
堡称;對應(yīng)的權(quán)值是w_j1,w_j2,...
歪脏;bias是b_jL
。所以a_jL
就是上面的公式粮呢,k的范圍是從1到第L-1層的神經(jīng)元的個數(shù)婿失。 為了用矩陣向量乘法來一次計算第L層的所有神經(jīng)元的輸出,我們需要定義第L層的參數(shù)矩陣w_l
啄寡,它的大小是m*n豪硅,其中m是第L層的神經(jīng)元個數(shù);而n則是第L-1層的個數(shù)挺物。它的第i行第j列就是我們上面定義的w_jk
懒浮。此外我們還要定義向量b_l
识藤,它的大小是m(也就是第L層神經(jīng)元的個數(shù))砚著,它的第j個元素就是我們上面定義的b_j
。 最后痴昧,我們定義element-wise的函數(shù)稽穆,比如f(x) = x^2,如果輸入是一個向量赶撰,那么結(jié)果是和輸入一樣大小的向量舌镶,它的每個元素是對輸入向量的每一個元素應(yīng)用這個函數(shù)的結(jié)果。
有了上面的定義豪娜,我們就可以一次計算出第L層的輸出(一個長度為m的向量)
下面是對上面這個公式的詳細證明(說明): 我們需要證明的是向量aL的第j個元素就是前面的a_jL
此外餐胀,為了方便后面的求解,我們把加權(quán)累加和也用一個符號z_l來表示瘤载。
其中否灾,它的第j個元素就是第L層的第j個神經(jīng)元的加權(quán)累加和:
這樣a_l
就可以簡單的對z_l
的每個元素計算激活函數(shù)
現(xiàn)在我們再回顧一下feedforward的代碼就非常直觀了:
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鸣奔,第二層就是第一個隱層墨技,每一層的計算就是非常簡單的參數(shù)矩陣w_l
乘以上一層的激活a_l-1
在加上b_l
,然后用激活函數(shù)計算溃蔫。
初始化的時候w的大小是 (后一層的神經(jīng)元個數(shù)) * (前一層的神經(jīng)元個數(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]私痹,注意隨機的矩陣是(y,x),所以self.weights是兩個矩陣,大小分別是30784
和1030
(2) 關(guān)于損失函數(shù)C的兩個假設(shè) 1. 損失函數(shù)是每個訓(xùn)練數(shù)據(jù)的損失的平均 也就是C是這樣的形式:
對于之前我們使用的MSE損失函數(shù)紊遵,這是滿足的账千。我們使用batch的梯度下降的時候需要求C對參數(shù)w的偏導(dǎo)數(shù),因為損失函數(shù)是每個訓(xùn)練數(shù)據(jù)的損失的平均暗膜,所以我們只需要求每個數(shù)據(jù)的偏導(dǎo)數(shù)匀奏,然后加起來平均就行。這個假設(shè)幾乎所有的損失函數(shù)都是滿足的【我是沒見過損失函數(shù)不滿足這個條件】
損失函數(shù)是最后一層輸出的函數(shù)
這個條件幾乎常見的損失函數(shù)都是這樣的学搜,我們之前時候的MSE就是計算最后一層的輸出aL和正確的y(one-hot)的均方誤差娃善,顯然是滿足的。
(3) Hadamard product 這個名字看起來很復(fù)雜瑞佩,其實很簡單聚磺,就是兩個向量elementwise的乘法【嫱瑁看一個例子就清楚了:
(4) 反向傳播算法(back propagation)的4個公式
回顧一下瘫寝,我們之前說了,梯度下降其實最核心的問題就是求損失函數(shù)對每一個參數(shù)的偏導(dǎo)數(shù)稠炬。那我們就直接一個一個求好了焕阿,為什么又要搞出一個反向傳播算法呢?其實這個算法在不同的領(lǐng)域被不同的人重復(fù)“發(fā)現(xiàn)”過很多次首启,有過很多不同的名字暮屡,最本質(zhì)的應(yīng)該就是逆向求導(dǎo)(reverse-mode differentiation)或者叫做自動求導(dǎo)(automatic differentiation)。自動求導(dǎo)(AD)是非常通用的一種求偏導(dǎo)數(shù)的方法闽坡,很早就在流體力學(xué)和大氣物理等領(lǐng)域使用栽惶,反向傳播算法可以認為是AD在神經(jīng)網(wǎng)絡(luò)中的應(yīng)用愁溜。不過最早發(fā)現(xiàn)這個算法的人(是誰最早好像還有點爭議)并不是先知道AD可以直接用于神經(jīng)網(wǎng)絡(luò)疾嗅,他發(fā)現(xiàn)這個算法是基于錯誤的反向傳播而得到的,所有命名為(錯誤的)反向傳播算法冕象。后面我們會講到AD代承,這是一個強大的算法,任何一個函數(shù)渐扮,你能把它分解成有向無環(huán)圖的計算圖【函數(shù)一般都能分解成一些無依賴的最基礎(chǔ)的變量的復(fù)合函數(shù)论悴,因此肯定可以表示成這樣一個有向無環(huán)圖】,然后每個節(jié)點都表示一個函數(shù)墓律。只要你能求出這個函數(shù)在特定點的梯度【也就是這個函數(shù)對所以自變量的偏導(dǎo)數(shù)】(不需要求解析的偏導(dǎo)數(shù)膀估,當(dāng)然很多情況,這些函數(shù)都是能直接求出解析解耻讽,然后代入這個特定點就行察纯,但理論上我們是可以用其他方法,比如數(shù)值梯度近似來求的),就能自動的計算損失函數(shù)對每一個參數(shù)的偏導(dǎo)數(shù)(也是在這個點的)饼记,而且只要反向根據(jù)拓撲排序遍歷這個圖一次就行香伴,非常高效和簡單。后面我們會詳細的介紹AD具则。這個方法非常通用即纲,TensorFlow的核心就是AD。使用AD的框架就比較靈活博肋,我想“創(chuàng)造”一種新的網(wǎng)絡(luò)結(jié)構(gòu)低斋,我又不想【其實更可能是不會】推導(dǎo)出梯度的公式,那么我只需要把我的網(wǎng)絡(luò)能用這樣一個有向無環(huán)圖表示就行匪凡。當(dāng)然節(jié)點必須要能夠求出梯度來拔稳,一般我們的函數(shù)比如矩陣的運算,卷積等等TensorFlow都封裝好了——它把它叫做一個op锹雏。我們只需要搭積木一樣把這個計算圖定義出來巴比,TensorFlow就自動的能根據(jù)AD計算出損失函數(shù)對所有參數(shù)的梯度來了。當(dāng)然如果你要用到一個TensorFlow沒有的op礁遵,那你就需要根據(jù)它的規(guī)范實現(xiàn)這個op轻绞,一個op最核心的接口就是兩個,一個是輸入x佣耐,求f(x)政勃;另一個就是求f在某個x0點的梯度。
不過這里兼砖,我們還是沿著神經(jīng)網(wǎng)絡(luò)的發(fā)展歷史奸远,從錯誤的反向傳播角度來理解和推導(dǎo)這個算法。
首先讽挟,我們會對每一個神經(jīng)元比如第L層的第j個懒叛,都定義一個錯誤δ_jL
也就是損失函數(shù)對z也就是線性累加和的偏導(dǎo)數(shù)。為什么定義這樣一個東西呢耽梅?我們假設(shè)在第L層的第j個神經(jīng)元上有一個精靈(Daemon)
當(dāng)這個神經(jīng)元得到來自上一次的輸入累加計算出z_jL
的時候薛窥,它會惡作劇的給一點很小的干擾Δz_jL
。原來它應(yīng)該輸出的是σ(z_jL)
眼姐,現(xiàn)在變成了σ(z_jL +Δz_jL)
诅迷。這個微小的變化逐層傳播,最終導(dǎo)致?lián)p失函數(shù)C也發(fā)生如下的變化:
這個其實就是導(dǎo)數(shù)的直覺定義:微小的Δx引起微小的Δy众旗,Δy/Δx約等于導(dǎo)數(shù)罢杉。 不過這個精靈是個好精靈,它想幫助我們減少損失贡歧。 當(dāng)
大于0的時候滩租,它讓Δz_jL
小于0拱镐,反之當(dāng)它小于0的時候它讓Δz_jL
大于0。這樣
總是小于0 因此我們的loss就會變小持际。而其絕對值越大沃琅,我們的損失減少的越多。 當(dāng)然你會說為什么不能讓Δz_jL
非常大蜘欲,這樣我們的損失總是減少很多益眉?可惜這個精靈是個數(shù)學(xué)家,它說如果Δx太大姥份,那么Δy=df/dx Δx就不準確了郭脂。
所以我們可以這樣認為:它就是第L層的第j個神經(jīng)元“引起”的“錯誤”。如果絕對值大澈歉,則它的“責(zé)任”也大展鸡,它就得多做出一些調(diào)整;反之如果它趨近于0埃难,說明它沒有什么“責(zé)任”莹弊,也就不需要做出什么改變。 因此通過上面的啟發(fā)涡尘,我們定義出δ_jL
來忍弛。
接下來我們逐個介紹反向傳播算法的4個公式。
公式1. 第L層(最后一層) 的錯誤
這個公式的第一項考抄,就是損失C對a_jL
的導(dǎo)數(shù)细疚,它越大,說明C受a_jL
的影響也就越大川梅,如果有了錯誤疯兼,第a_jL
的“責(zé)任”也就越大,錯誤也就越大贫途。第二項是a_jL
受z_jL
的影響悉抵。兩者乘起來就是z_jL
對最終損失的影響豆茫,也就是它的“責(zé)任”的大小稳摄。
這個公式很好計算活孩,首先第二項就是把z_jL
的值(這個在feedforward節(jié)點就算出來并存儲下來了)代入σ'(x)
吩坝。如果σ
是sigmoid函數(shù)柠贤,我們前面也推導(dǎo)過它的導(dǎo)數(shù):σ’(x)=σ(x)(1-σ(x))穗熬。第一項當(dāng)然依賴于損失函數(shù)的定義濒翻,一般也很好求中狂。比如我們的MSE損失:
具體的推導(dǎo)我在紙上寫了一下凫碌,雖然很簡單,我們也可以練練手胃榕,尤其是對于求和公式的展開盛险,希望大家能熟悉它瞄摊,以后的推導(dǎo)我可能就不展開求和公式了,你需要知道求和公式里哪些項是和外面的自變量無關(guān)的苦掘。
公式BP1是elementwise的换帜,我們需要變量j來計算每一個δ_jL
。我們也可以把它寫成向量的形式鹤啡,以方便利用線性代數(shù)庫惯驼,它們可以一次計算向量或者矩陣,可以用很多技術(shù)利用硬件特性來優(yōu)化(包括GPU递瑰,SSE等)速度祟牲。
右邊δ'(z_L)
很容易理解,左邊的記號可能有些費解抖部,其實我們把?aC當(dāng)成一個整體就好了说贝,它是一個向量,第一個元素是?C/?a_1L
慎颗,第二個就是?C/?a_2L
乡恕,… 如果算上函數(shù)C是MSE的話,上面的公式就可以簡化成:
公式2. 第l層(非最后一層) 的錯誤
等下我們會證明這個公式俯萎,不過首先我們來熟悉一下公式几颜。如果我們想“背”下這個公式的話,似乎看起來比第一個BP1要復(fù)雜很多 讯屈。我們先檢查一下矩陣和向量的維度蛋哭,假設(shè)l+1
層有m個元素,l層n個涮母。則w_l+1
的大小是mn谆趾,轉(zhuǎn)置之后是nm,δ_l+1
的大小是n1,所以矩陣相乘后是m1叛本,這和δ_l
是一樣的沪蓬,沒有問題。
接下來我們仔細觀察一下BP2這個公式来候,首先第二項σ'(z_l)
和前面的含義一樣跷叉,代表a_l
對于z_l
的變化率。 而第一項復(fù)雜一點营搅,我們知道第l層的第j個神經(jīng)元會影響第l+1層的所有神經(jīng)元云挟,從而也影響最終的損失C。這個公式直接給了一個矩陣向量的形式转质,看起來不清楚园欣,所以我在草稿紙上展開了:
最終第L層的第j個神經(jīng)元的損失就是如下公式:
這下應(yīng)該就比較清楚了,第l層的第j個神經(jīng)元的損失休蟹,就是把l+1層的損失“反向傳播”回來沸枯,當(dāng)然要帶上權(quán)重日矫,權(quán)重越大,“責(zé)任”也就越大绑榴。
如果要“背”出這個公式也沒有那么復(fù)雜了哪轿,先不看σ'(z_l)
,第一項應(yīng)該是矩陣w_l+1
乘以δ_l+1
翔怎。由于矩陣是mn窃诉,而向量δ_l+1
是m1,為了能讓矩陣乘法成立姓惑,那么就只能把w轉(zhuǎn)置一下褐奴,變成n*m,然后就很容易記住這個公式了于毙。
注意敦冬,BP2的計算是從后往前的,首先根據(jù)BP1唯沮,最后一層的δ_L
我們已經(jīng)算出來了,因此可以向前計算L-1層的δ_L-1
介蛉, 有了δ_L-1
就能計算δ_L-2
萌庆,…践险,最終能算出第一個隱層(也就是第2層)δ_1
來。
公式3. 損失函數(shù)對偏置b的梯度
這前面費了大力氣求δ_l
,不要忘了我們的最終目標是求損失函數(shù)對參數(shù)w和b的偏導(dǎo)數(shù),而不是求對中間變量z的偏導(dǎo)數(shù)坯癣。
因此這個公式就是對b的偏導(dǎo)數(shù)。
或者寫成向量的形式:
?C/?b就是δ!
公式4. 損失函數(shù)對w的梯度
或者參考下圖寫成好記的形式:
也就是說對于一條邊w_jkL
酌媒,?C/?w_ij
就是這條邊射出的點的錯誤δ乘以進入點的激活雨席。非常好記缨硝。
我們把這四個公式再總結(jié)一下:
(5) 這四個公式的證明
首先是BP1,請參考下圖:
然后是BP2: 這里用到了chain rule列敲,其實也非常簡單和直觀,就是復(fù)合函數(shù)層層組合泄鹏。最簡單的方法就是用圖畫出來绣版,比如y最終 是x的函數(shù),我們要求?y/?x,如果y是u,v的函數(shù),然后u,v才是x的函數(shù)衔瓮,那么我們把變量x,y,u,v都畫成圖上的點薇宠,y是u漂辐,v的函數(shù),那么我們畫上從u和v到y(tǒng)的邊问畅,同樣,我們畫上從x到u和v的邊柒凉,然后從y到x的每條路徑,我們經(jīng)過的邊都是一個偏導(dǎo)數(shù)狐援,我們把它累加起來就行【這其實就是后面我們會講的AD】爹凹。因此?y/?x=?y/?u * ?u/?x +?y/?v * ?v/?x。
剩下的BP3和BP4也非常類似项秉,我就不證明了。
反向傳播算法 1. a_1
= 輸入向量x 2. Feedforward 根據(jù)公式
和
計算z_l
和a_l
并存儲下來(反向傳播時要用的) 3. 計算最后一層的錯誤
向前計算所有層的錯誤
計算損失對所有參數(shù)的偏導(dǎo)數(shù)
2.2.6 代碼實現(xiàn)反向傳播算法
我們已經(jīng)把公式推導(dǎo)出來了稽犁,那怎么用代碼實現(xiàn)呢?我們先把代碼復(fù)制一下菱农,然后說明部分都是作為代碼的注釋了缭付, 請仔細閱讀。
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是兩個向量的妖,大小分別是30和10 nabla_w = [np.zeros(w.shape) for w in self.weights] # 構(gòu)造和self.weights一樣大小的矩陣,比如前面的例子 sizes=[784,30,10]足陨,則 # nabla_w是兩個矩陣嫂粟,大小分別是30784和1030 for x, y in mini_batch: #對于每個訓(xùn)練樣本x和y delta_nabla_b, delta_nabla_w = self.backprop(x, y) # 用backprop函數(shù)計算損失函數(shù)對每一個參數(shù)的偏導(dǎo)數(shù)。 # backprop函數(shù)下面會詳細講解 nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)] # 把返回的對b偏導(dǎo)數(shù)累加到nabla_b中 nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)] # 把返回的對w的偏導(dǎo)數(shù)累加到nabla_w中 self.weights = [w-(eta/len(mini_batch))nw for w, nw in zip(self.weights, nabla_w)] # 計算完一個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墨缘,返回損失函數(shù)C對每個參數(shù)w和b的偏導(dǎo)數(shù) # 返回的格式是兩個元組星虹,第一個是b的偏導(dǎo)數(shù),第二個是w的镊讼。 nabla_b = [np.zeros(b.shape) for b in self.biases] # 構(gòu)造和self.biases一樣大小的向量宽涌,比如前面的例子 sizes=[784,30,10],則 # nabla_b是兩個向量蝶棋,大小分別是30和10 nabla_w = [np.zeros(w.shape) for w in self.weights] # 構(gòu)造和self.weights一樣大小的矩陣卸亮,比如前面的例子 sizes=[784,30,10],則 # nabla_w是兩個矩陣玩裙,大小分別是30784和1030 # feedforward activation = x activations = [x] # 用一個list保存所有層的激活兼贸,下面backward會有用的 zs = [] # 同樣的用一個list保存所有層的加權(quán)累加和z,下面也會用到吃溅。 #下面這段代碼在feedward也有溶诞,不過那里是用來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. 首先計算最后一層的錯誤delta,根據(jù)公式BP1决侈,它是損失函數(shù)對a_L的梯度乘以σ'(z_L) # sigmoid_prime就是σ'(z_L)螺垢,而?C/?a_L就是函數(shù)cost_derivative,對于MSE的損失函數(shù), # 它就是最后一層的激活activations[-1] - y delta = self.cost_derivative(activations[-1], y) * \ sigmoid_prime(zs[-1]) # 2. 根據(jù)公式BP3,損失對b的偏導(dǎo)數(shù)就是delta nabla_b[-1] = delta # 3. 根據(jù)公式BP4,損失對w的偏導(dǎo)數(shù)時delta_out * activation_in # 注意甩苛,我們的公式BP4是elementwise的蹂楣,我們需要寫成矩陣向量的形式 # 那怎么寫呢?我們只需要關(guān)心矩陣的大小就行了讯蒲。 # 假設(shè)最后一層有m(10)個神經(jīng)元痊土,前一層有n(30)個, # 則delta是101, 倒數(shù)第二層的激活activations[-2]是301 # 我們想求的最后一層的參數(shù)nabla_w[-1]是10*30墨林,那么為了能夠正確的矩陣乘法赁酝, # 只要一種可能就是 delta 乘以 activations[-2]的轉(zhuǎn)置,其實也就是向量delta和activations[-2]的外積 nabla_w[-1] = np.dot(delta, activations[-2].transpose()) # 接下來從倒數(shù)第二層一直往前計算delta旭等,同時也把對w和b的偏導(dǎo)數(shù)求出來酌呆。 # 這里用到一個比較小的trick就是python的下標是支持負數(shù)的,-1表示最后一個元素搔耕,-2是倒數(shù)第二個 # l表示倒數(shù)第l層隙袁,2就表示倒數(shù)第2層,num_layers - 1就表示順數(shù)第2層(也就是第1個隱層) # 比如我們的例子:sizes=[784, 30, 10]弃榨,那么l就是從2到3(不包含3)菩收,l就只能是2,頁就是第1個(也是唯一的一 # 個)隱層 for l in xrange(2, self.num_layers): # 倒數(shù)第l層的z z = zs[-l] # 計算σ'(z_l) sp = sigmoid_prime(z) # 根據(jù)BP2鲸睛,計算delta_l,注意weights[-l+1]表示倒數(shù)第l層的下一層 delta = np.dot(self.weights[-l+1].transpose(), delta) * sp # 同上娜饵,根據(jù)BP3 nabla_b[-l] = delta # BP4,矩陣乘法參考前面的說明 nabla_w[-l] = np.dot(delta, activations[-l-1].transpose()) return (nabla_b, nabla_w)
2.2.7 為什么反向傳播算法是一個高效的算法官辈?
分析完代碼箱舞,我們發(fā)現(xiàn)一次backprop函數(shù)調(diào)用需要feedforward一次,網(wǎng)絡(luò)有多少邊拳亿,就有多少次乘法晴股,有多少個點就有多少次加分和激活函數(shù)計算(不算第一層輸入層)。反向計算也是一樣风瘦,不過是從后往前队魏。也就是說這是時間復(fù)雜度為O(n)的算法。 如果我們不用反向傳播算法万搔,假設(shè)我們用梯度的定義計算數(shù)值梯度胡桨。對于每一個參數(shù)wj,
我們都用公式 limit (f(w1, w2, …, wj+Δ wj, …) - f(w1, w2, …, wj, …)/Δwj
f(w1, w2, wj, …)只需要feedforward一次瞬雹,但是對于每個參數(shù)wj昧谊,都需要feedforward一層來計算f(w1, w2, …, wj+Δ wj, …),它的時間復(fù)雜度是O(n)酗捌,那么對所有的參數(shù)的計算需要O(n^2)的時間復(fù)雜度呢诬。
假設(shè)神經(jīng)網(wǎng)絡(luò)有1百萬個參數(shù)涌哲,那么每次需要1012這個數(shù)量級的運算,而反向傳播算法只需要106尚镰,因此這個方法比反向傳播算法要慢1百萬倍阀圾。