看到了一個講反向傳播計算圖的很棒的博客,看的過程順手翻譯了一下。
原文在這里:Calculus on Computational Graphs: Backpropagation。
介紹
反向傳播是使訓練深度模型在計算上易于處理的關鍵算法。 對于現代神經網絡而言,相對于樸素貝葉斯的實現,它可以使梯度下降的訓練速度提高千萬倍。 這相當于花一周時間訓練和花費200,000年的時間訓練模型的區別。
除了用于深度學習之外,反向傳播在許多其他領域也是一個強大的計算工具,從天氣預報到分析數值穩定性 - 它只是名稱不同而已。 事實上,該算法在不同領域至少被重復發明了十次(參見Griewank(2010))。 一般來說,與具體應用無關的名稱應該是“反向模式微分”。
從根本上說,這是一種快速求導的技術。 無論是在深度學習中,還是在各種數值計算環境中,這都是一個必不可少的竅門。
計算圖
計算圖是思考數學表達式的好方法。
例如,考慮表達式 : e = (a+b)*(b+1)。
這里有三個操作:兩個加法和一個乘法。 為了幫助我們討論這個問題,我們引入兩個中間變量c和d,以便每個表達式的輸出都有一個變量。 我們現在有:
- c= a+ b
- d= b+ 1
- e = c * d
為了創建計算圖,我們將這些運算連同輸入變量一起放入節點中。 當一個節點的值是另一個節點的輸入時,箭頭從一個節點指向另一個節點。
這些圖形總是出現在計算機科學中,特別是在討論功能性程序時。 它們與依賴圖和調用圖的概念密切相關。它們也是深受歡迎的深度學習框架Theano的核心抽象。
我們可以通過將輸入變量設置為特定值,然后通過圖形自底向上的計算節點值來對表達式求值。 例如,我們設置a = 2和b = 1.
計算結果這個表達式的值是6。
計算圖中的導數
理解計算圖上導數的關鍵是理解邊上的導數。如果a直接影響c,那么我們想知道它是如何影響c的。 如果a改變一點,c怎么改變? 我們稱之為c關于a的偏導數。
為了計算這個圖中的各個偏導數,我們需要借助加法和乘法的求導公式:
下圖標出了每個邊上的導數。
如果我們想了解那些不是直接相連的節點是如何相互影響的呢? 讓我們考慮一下e是如何被a影響的吧。 如果我們以1的速度改變a,c也以1的速度改變。相應地,c以1的速度改變導致e以2的速度改變。因此e相對于a以1 * 2的速率改變。一般規則是:如果存在從節點i到節點j的路徑,將該條路徑經過的每條邊上的導數相乘,得到i通過該條路徑對j產生的影響,將從節點i到節點j的所有可能路徑的影響值相加,得到j關于i的導數。 例如,為了計算e關于b的導數,進行如下計算:
這解釋了b如何通過c影響e以及b如何通過d來影響e。
這種 “路徑求和”的一般規則只是對多元鏈式規則的不同思考方式。
路徑分解
只是簡單的“路徑求和”的問題在于,很容易導致可能的路徑數量的組合爆炸。
在上面的圖中,有三條從X到Y的路徑,還有三條從Y到Z的路徑。如果我們想通過在所有路徑上求和得到導數?Z/?X,我們需要對3 * 3 = 9條路徑求和:
雖然上面的例子只有九條路徑,但據此很容易類推出,隨著圖形變得越來越復雜,路徑的數量會呈指數級增長。
比單純對路徑進行簡單的求和更好的一種做法是,將它們因式分解:
這就是“前向模式微分”和“反向模式微分”的出處,它們是通過因式分解來進行高效的路徑求和的算法。并不是顯示地對所有路徑求和,而是通過在每個節點處把路徑合并到一起來更高效地計算和。 實際上,這兩種算法都訪問每條邊剛好一次!
前向模式微分從圖形的一個輸入開始,向終點移動。在每個節點上,它對所有流入的路徑求和。流入的每條路徑都代表著輸入影響該節點的一種方式。通過將它們相加,我們得到這個節點受輸入影響的總體方式,也就是它的導數。
雖然你可能從來沒有從圖的角度來思考過它,前向模式微分和你在微積分導論課程上隱式地學習的要做的事情非常相似。
與此相反,反向模式微分從圖的輸出開始,向開始點移動。 在每個節點上,它合并所有源自該節點的路徑。
前向模式微分跟蹤一個輸入如何影響每個節點。 反向模式微分跟蹤每個節點如何影響一個輸出。 也就是說,前向模式的微分將運算符?/?X應用于每個節點,而反向模式微分則將運算符?Z/?應用于每個節點。
計算的勝利
在這一點上,你可能會想知道為什么會有人關心反向模式微分。 它看起來像是一個做與前向模式微分同樣事情的奇怪方式。 反向模式有一些它自己的優勢嗎?
再次考慮我們的原始示例:
我們可以從b開始使用向上的前向模式微分。 這給了我們每個節點關于b的導數。
我們已經計算了?e/?b,它是我們的輸出關于其中一個輸入的導數。
如果我們從e向下做反向模式微分會怎么樣? 這給了我們e關于每個節點的導數:
當我說反向模式微分給了我們e對于每個節點的導數時,我確實在說每個節點!我們同時得到了?e/?a和?e/?b,e對于兩個輸入的導數。前向模式微分給我們輸出相對于單個輸入的導數,但反向模式微分給了我們所有輸入的導數。
對于這個圖,這只是兩倍速的一個因式分解,但想象一個具有一百萬個輸入和一個輸出的函數。前向模式微分將要求我們通過這張圖一百萬次才能獲得導數。反向模式微分可以讓他們一舉成功!一百萬倍速的分解是不是很棒呢?
在訓練神經網絡時,我們將cost(一個描述神經網絡表現如何差的值)視為參數(描述網絡行為方式的數字)的函數。我們想要計算cost對于所有參數的導數,以用于梯度下降。現在,神經網絡中通常有數百萬甚至上千萬的參數。所以,反向模式微分,在神經網絡中被稱為反向傳播,給我們帶來了巨大的加速!
(是否有前向模式微分更有意義的情況呢?是的,有!反向模式給出一個輸出相對于所有輸入的導數,前向模式給出了所有輸出關于一個輸入的導數,如果一個函數具有很多輸出,則前向模式微分會快很多)。
這很平凡嗎?
當我第一次明白后向傳播是什么時,我的反應是:“哦,那只是鏈式規則!我們怎么花了那么長時間才弄明白?“我不是唯一一個有這種反應的人。如果你問“在前饋神經網絡中是否有一種聰明的方法來計算導數?”,看起來回答這個答案并不困難。
但我認為這比看起來要困難得多。你看,在反向傳播發明的時候,人們并不十分關注我們研究的前饋神經網絡。導數是訓練它們的正確方式這點也不是很明顯。只有你意識到你可以快速計算導數時,這些才是顯而易見的。這里有一個循環依賴。
更糟糕的是,將零散的循環依賴關系作為不可能實現的偶然想法是很容易的。用導數工具訓練神經網絡?你會想當然的認為,那只會陷入局部最小。顯然,計算所有這些導數會很昂貴。只有當我們知道這種方法有效,我們才不會立即開始列出這么做不可能的原因。
這是后見之明的好處。一旦你確定了這個問題,最困難的工作就已經完成了。
結論
導數的計算比你想象的要便宜。這是這篇文章幫你刪除的主要教訓。事實上,它們直覺上并不便宜,我們這些愚蠢的人類不得不不斷重新發現這一事實。在深度學習中理解這一點非常重要。在其他領域這也是一件非常有用的事情,如果它還不是常識,那就更是如此。
還有其他課程嗎?我認為有。
反向傳播也是理解導數如何流經模型的有用工具。這對推理為什么某些模型難以優化很有幫助。這個問題的經典的例子是循環神經網絡中梯度消失的問題。
最后,我聲稱有一個廣泛的算法教訓可以從這些技術中剝離出來。反向傳播和前向模式微分使用一對強大的技巧(線性化和動態規劃)來超出人們想象地更有效地計算導數。如果你真的了解這些技巧,你可以使用它們來有效地計算其他涉及導數的有趣表達式。我們將在稍后的博客文章中對此進行探討。
這篇文章給出了一個非常抽象的反向傳播處理。我強烈建議你閱讀Michael Nielsen關于它的章節,進行一次精彩的討論,更具體地聚焦于神經網絡。