天天看點

神經網絡反向傳播算法推導

反向傳播是多數神經網絡進行參數更新的基本方法,它的本質是巧妙利用了高數中的鍊式法則,下面對這一方法進行推導:

(1)符号說明

神經網絡反向傳播算法推導

:神經元的激活函數

神經網絡反向傳播算法推導

:神經網絡的權重向量

神經網絡反向傳播算法推導

:神經網絡的偏置向量

神經網絡反向傳播算法推導

:某層的輸入向量

神經網絡反向傳播算法推導

:某層的輸出向量

(2)損失函數

假設神經網絡的損失函數為

神經網絡反向傳播算法推導

,那麼定義其損失函數為:

神經網絡反向傳播算法推導

其中,

神經網絡反向傳播算法推導

為期望的輸出值,

神經網絡反向傳播算法推導

是神經網絡的預測輸出值。

方向傳播算法是通過改變網絡中的權重參數

神經網絡反向傳播算法推導

和偏置

神經網絡反向傳播算法推導

來改變損失函數的方法。

(3)4個基本方程推導

1. 輸出層誤差

首先定義第

神經網絡反向傳播算法推導

層的第

神經網絡反向傳播算法推導

個神經元的誤差為:

神經網絡反向傳播算法推導

在這裡可能很多人會覺得不能了解,具體可以參考這篇文章 http://neuralnetworksanddeeplearning.com/chap2.html

神經網絡反向傳播算法推導
神經網絡反向傳播算法推導

根據定義,對損失函數求導可得:

神經網絡反向傳播算法推導

其中

神經網絡反向傳播算法推導

度量了損失函數在神經元輸出的函數的變化程度,

神經網絡反向傳播算法推導

則度量了激活函數在神經元權重輸入處的變化程度。

對輸出層誤差向量化,得到輸出層的誤差公式BP1,如下:

神經網絡反向傳播算法推導

2. 第

神經網絡反向傳播算法推導

層誤差

非輸出層的誤差依賴于下一層的誤差,也就是網絡第

神經網絡反向傳播算法推導

層的誤差依賴于第

神經網絡反向傳播算法推導

層的誤差,則有:

神經網絡反向傳播算法推導

上式利用了鍊式法則,前一次的輸出可以作用于後一層的輸入,又因為:

神經網絡反向傳播算法推導

是以有:

神經網絡反向傳播算法推導

帶入之前的算是可以得到網絡第

神經網絡反向傳播算法推導

層的第

神經網絡反向傳播算法推導

個神經元誤差為:

神經網絡反向傳播算法推導

向量化後可以得到網絡中第1層的誤差公式BP2:

神經網絡反向傳播算法推導

公式BP2充分展現了誤差反向傳播的特點,隻要知道

神經網絡反向傳播算法推導

層的誤差就可以推導出

神經網絡反向傳播算法推導

層的誤差,以此類推,最後能夠從網絡的輸出層倒推直到輸入層的誤差,這就是誤差反向傳播的含義,另外從誤差傳播的公式中我們可以注意到含有激活函數的導師,一般我們采用的sigmoid的激活函數的導數最大為0.25,每反向傳播一次梯度最多儲存25%,是以當神經網絡隐藏層增多後會産生梯度消失問題。

3. 損失函數關于偏置

神經網絡反向傳播算法推導

的偏導

同理,根據鍊式求導法則可得:

神經網絡反向傳播算法推導

又因為:

神經網絡反向傳播算法推導

帶入得到:

神經網絡反向傳播算法推導

最終損失函數在網絡中任意偏置等于該神經元上的誤差,可以通過誤差直接得到偏置的倒數。

4. 損失函數關于權值

神經網絡反向傳播算法推導

的偏導

神經網絡反向傳播算法推導
神經網絡反向傳播算法推導
神經網絡反向傳播算法推導

從公式中可以知道損失函數關于權重的偏導,連接配接了第1層第j個神經元的誤差以及上一層第i個神經元的輸出。

至此反向傳播的4個基本公式都已經推導完畢。

(4)反向傳播算法的實作代碼示例

import numpy as np
import pprint
pp = pprint.PrettyPrinter(indent=4)

# 定義神經網絡的模型架構 [input, hidden, output]
network_sizes = [3,4,2]

# 初始化該神經網絡的參數
sizes = network_sizes
num_layers = len(sizes)
biases = [np.random.randn(h, 1) for h in sizes[1:]]
weights = [np.random.randn(y, x) for x, y in zip(sizes[:-1], sizes[1:])]

def loss_der(network_y, real_y):
    """
    傳回損失函數的偏導,損失函數使用 MSE
    L = 1/2(network_y-real_y)^2
    delta_L = network_y-real_y
    """
    return (network_y - real_y)


def sigmoid(z):
    """激活函數使用 sigmoid."""
    return 1.0 / (1.0 + np.exp(-z))


def sigmoid_der(z):
    """sigmoid函數的導數 derivative of sigmoid."""
    return sigmoid(z) * (1 - sigmoid(z))


def backprop(x, y):
    """
    根據損失函數 C通過反向傳播算法傳回
    """
    """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"."""
    
    # 初始化網絡參數的導數 權重w的偏導和偏置b的偏導
    delta_w = [np.zeros(w.shape) for w in weights]
    delta_b = [np.zeros(b.shape) for b in biases]
    
    # 向前傳播 feed forward
    activation = x     # 把輸入的資料作為第一次激活值
    activations = [x]  # 存儲網絡的激活值
    zs = []            # 存儲網絡的權重輸入值 (z=wx+b)
    
    for w, b in zip(weights, biases):
        z = np.dot(w, activation) + b
        activation = sigmoid(z)
        
        activations.append(activation)
        zs.append(z)
        
    # 反向傳播 back propagation 
    # BP1 計算輸出層誤差
    delta_L = loss_der(activations[-1], y) * sigmoid_der(zs[-1])
    # BP3 損失函數在輸出層關于偏置的偏導
    delta_b[-1] = delta_L
    # BP4 損失函數在輸出層關于權值的偏導
    delta_w[-1] = np.dot(delta_L, activations[-2].transpose())

    delta_l = delta_L
    for l in range(2, num_layers):
        # BP2 計算第l層誤差
        z = zs[-l] 
        sp = sigmoid_der(z)
        delta_l = np.dot(weights[-l + 1].transpose(), delta_l) * sp
        # BP3 損失函數在l層關于偏置的偏導
        delta_b[-l] = delta_l
        # BP4 損失函數在l層關于權值的偏導
        delta_w[-l] = np.dot(delta_l, activations[-l - 1].transpose())

    return (delta_w, delta_b)


training_x = np.random.rand(3).reshape(3,1)
training_y = np.array([0, 1]).reshape(2,1)
print("training data x:\n{},\n training data y:\n{}".format(training_x, training_y))
backprop(training_x, training_y)
           

輸出的結果為:

training data x:
[[0.69316998]
 [0.16969983]
 [0.51637141]],
 training data y:
[[0]
 [1]]

([array([[-0.00122639, -0.00030024, -0.00091359],
         [-0.0252308 , -0.00617693, -0.01879548],
         [-0.0039671 , -0.00097121, -0.00295526],
         [ 0.01921057,  0.00470307,  0.01431076]]),
  array([[ 0.05610549,  0.02035511,  0.06627431,  0.01132567],
         [-0.11372293, -0.04125875, -0.13433461, -0.02295656]])],
 [array([[-0.00176925],
         [-0.03639915],
         [-0.00572312],
         [ 0.02771408]]), array([[ 0.07169137],
         [-0.14531471]])])
           

 參考:

《深度學習原理與實踐》第2章

繼續閱讀