一開始看的是cs231n的min-char-rnn.py,無奈怎麼都看不懂反向傳播那一塊的內容。後也是不斷搜索,發現那不是普通的bp,也不是正規的bptt,因此要理解karpathy的代碼我還須要更深地理解反向傳播,從頭開始。php
最先看了這麼一篇 初學者——從NN到RNN,深入理解以softmax爲激活函數的反向傳播,有代碼 ,還頗覺得詳盡明白,可是套到min-char-rnn裏一點也講不通,後來明白了,這篇博文講的是bptt的一部分,甚至還不全,其參考的是 Recurrent Neural Networks Tutorial, Part 2 – Implementing a RNN with Python, Numpy and Theano但卻缺斤少兩,不事後一篇在推導上也是頗有不足,不談了,你們沒必要去看這兩篇,甚至cs231n-optimization2和ufldl的bp也沒必要先看(這個都是亂花眼的公式,雖然其表達的意思很準確,可是過於抽象,不利於咱們從實現代碼的細節角度上理解),通過個人實踐,我認爲咱們應該先從講的最好最清楚的BP開始,從How the backpropagation algorithm works開始,若是你遵從個人建議,你應該會少走不少的彎路。html
so,let's begin.python
How the backpropagation algorithm works 是 Neural Networks and Deep Learning 這本書的第二章,你們要是不習慣英文也能夠去找翻譯版。下面我不徹底按照書裏來,可是我會解釋的很清楚的。git
首先要反向傳播,咱們須要有向前傳播。
向前傳播很簡單,畫個圖,圖上每一個點都須要作個線性計算,一層一層傳遞,這就是前向傳播。github
(圖一)網絡
layer1當成輸入層,layer2是hidden_layer(隱含層),layer3是輸出層,這個要記住了,其實深度網絡有一點很難理解的就是圖的抽象思惟。上面這張圖若是是在rnn中,變成了下面這樣。函數
(圖二)工具
圖中的s看上去是一個點,其實裏面就是上上圖拆解開來的全鏈接,由於咱們是矩陣與向量操做!!因此這點先明白清楚。學習
那麼回到圖一。ui
w是一個權值矩陣,一樣的還須要b偏置向量,再看下圖。
(圖三)
a是輸出層的每一個神經元的輸出。
這就是輸出,l表明的是第幾層,j,k都是來表明第幾個神經元。這裏用的是sigmoid函數,也就是括號外的sigma符號,如今咱們能夠暫時無論這個函數是什麼,由於咱們還能夠用relus,tanh等等,其實sigmoid如今已經不多用了,可是爲了讀者不那麼麻煩去查,我仍是把這個公式貼出來。
好了,這個就是一個簡單的全鏈接的網絡的公式解釋。爲了咱們後面更方便地使用公式,咱們在這裏許澄清一點,以上的都是element-wise也就是單元素操做,以下。
咱們能夠把公式美化成下面這個版本。
在這裏我須要提一點,書中講到一個quirk就是w的角標j,k的設置爲何是這樣的,由於這樣咱們不用把上面這個公式中的w轉置了,但其實現實代碼中仍是轉置了,因此當大家看到現實代碼中有轉置就不用那麼疑惑了。
通常來說,咱們往下走以前還需定義幾個公式。
$$z^l≡w^la^{l−1}+b^l$$
$$z^l_j=\sum{kw^l_{jk}a^{l−1}_k+b^l_j}$$
$$a^l=σ(z^l)$$
以上就是簡單的前向傳播。可是咱們有個最重要的損失函數纔是咱們須要擬合的函數,這個函數就是利用輸出層的輸出做爲損失函數的輸入。下面咱們介紹損失函數。
損失函數是幹什麼用的?咱們就是利用損失函數求∂C/∂w 和 ∂C/∂b進行反向傳播來更新w矩陣和b向量。咱們能夠先定義一個最簡單的損失函數以下(相似於方差)
好清楚明白的,我就不過多廢話了。
如今開始是核心部分,BP的核心。
先介紹一箇中間量,殘差。殘差不等於偏差。
$$δ^l_j$$
表明着第l層第j個神經元的殘差。
如上圖,一個形象可愛的說法是每一個神經元的地方都有一個惡魔,這個惡魔會給輸入帶來一點誤差,
$$Δz^l_j$$
這樣會形成輸出
$$σ(z^l_j+Δz^l_j)$$
總的損失會改變
$$\frac{∂C}{∂z^l_j}Δz^l_j$$
如今呢,這個惡魔成爲了一個好的惡魔,可以幫助咱們減小損失。那麼怎麼去減小損失呢?下面就用數學來講明。
這表明着第l層的第j個神經元所得到的殘差。
咱們的模型比較簡單,咱們須要的bp公式只有四個,我先不討論怎麼證實這四個公式的正確性,咱們先來看看這四個公式,證實留在後面。
bp1在成爲上圖中的bp1以前不長這樣,
這是個十分天然的表達式,左邊的偏導表明第j個神經元的cost的改變速率,這麼來理解,若是很小的話,說明這個點的偏差不大。右邊的表示sigma函數在其輸入z上的改變速率。
爲了方便點,假設咱們的C是
$$C=\frac{1}{2}∑_j(y_j−a^L_j)^2$$
那求導後就很簡單了。
$$\frac{∂C}{∂a^L_j}=(a^L_j−y_j)$$
因爲這個初始的bp1是份量計算的方式,咱們能夠重寫成
$$∇_aC=(a^L−y)$$
這個公式計算的是最後一層反向傳播的公式,由於只有最後一層纔有loss_function,而中間的隱含層的反向傳播利用的公式則是下面的bp2,這個很重要,我一開始都沒理解,最後一層和中間的隱含層是不同的。
理解這個公式如同理解第一個公式同樣,只是這裏利用了前面的殘差做爲輸入。
後兩個就不講了,只要有了這四個公式做爲工具,咱們就能夠輕鬆反向傳播。
從bp1開始,首先咱們先定義什麼是殘差,
根據鏈式法則咱們能獲得
這裏咱們須要結合一下圖來解釋更清楚
好比這張圖,L=3,也就是咱們在輸出層,z是這層的神經元的輸入,因此當j!=k時,偏導是爲0的,所以,咱們能夠簡化公式
下面證實bp2,由於這個是隱藏層的傳播,我在以前已經有提醒過了,因此這裏的神經元的輸出a實際上是下一個鏈接到的神經元的輸入z,不過是層數加1。因此,
以上證實了bp2。
bp3證實其實就是
再乘偏z偏b
偏z偏b等於1,因此證得。bp4證略。
在下一篇[深度學習原理之理解反向傳播[2] --- 簡單全鏈接網絡(BP)]()我會來介紹代碼,不只僅是這個簡單版的代碼,主要講講min-char-rnn.py的代碼,不少的疑問。