Deep Learning - 2 反向傳播

深度神經網絡的學習基於兩個關鍵技術:python

  • Stochastic Gradient Descent
  • Backpropagation

利用 SGD 算法學習 Weights 和 Biases,利用 Backpropagation 算法來快速計算 Cost Function 的 Gradient 。算法

反向傳播是一種快速的學習算法,可以讓咱們深刻地瞭解改變 Weights 和 Biases 的值,是如何改變整個網絡的行爲的。網絡

Weights

Weights

  • $W_{jk}^{l}$表示從第 $l-1$ 層的第 k 個神經元,到第 $l$ 層的第 $j$ 個神經元的鏈接的權重。

Biases and Activations

Biases and Activations

  • $b_j^l$ 表示第 $l$ 層第 $j$ 個神經元的 Biases
  • $a_j^l$ 表示第 $l$ 層第 $j$ 個神經元的 Activations(激活值)

神經元的視角

每一個神經元的激活值能夠這樣表示:
$$
a_j^l = \sigma \left(\sum_k w_{jk}^{l} a_{k}^{l-1}+b_{j}^{l}\right)
$$app

層視角

經過使用矩陣:dom

  • 每一層 $l$ 定義一個權重矩陣 $w^l$ ,$w^l$ 中的第 $j$ 行第 $k$ 列的元素就是 $w_{jk}^{l}$ 。
  • $b^l$ 表明第 $l$ 層的 Biases 向量。
  • 將 $\sigma$ 函數向量化,即對向量 $v$ 中的每一項,都單獨地應用 $\sigma$ 函數,記爲 $\sigma(v)$ 。
  • $a^l$表明第 $l$ 層的神經元的激活值向量。

每層的激活值能夠這樣表示:
$$
a^l = \sigma ( w^l a^{l-1} + b^l )
$$函數

  1. 將權值矩陣做用於上一層的激活值
  2. 而後加上偏置向量
  3. 最後用 $\sigma$ 函數做用於這個結果
  4. 就獲得了本層的激活值

Weighted Input

$z^l \equiv w^l a^{l-1} + b^l$ ,$z^l$ 成爲對第 $l$ 層神經元激活函數的加權輸入。
$$
a^l = \sigma (z^l)
$$學習

Cost Function 的兩個假設

MSE代價函數:
$$
C = \frac{1}{2n} \sum_x ||y(x)-a^L(x)||^2
$$ui

  • n是訓練樣本數量
  • $\sum_x$是對每一個獨立訓練樣本 $x$ 求和
  • $y=y(x)$ 是每一個獨立訓練樣本 $x$ 的預期輸出結果
  • $L$ 是神經網絡的層數
  • $a^L = a^L (x)$是輸入爲 $x$ 時網絡的激活函數的輸出向量

爲了可以使用反向傳播,咱們須要對代價函數C進行兩個假設。lua

假設一

假設代價函數可以寫成這樣的形式
$$
C = \frac{1}{n} \sum_x C_x
$$code

  • $C_x$ 是每一個獨立訓練樣本 $x$ 的代價函數。

當代價函數是MSE時,$C_x = \frac{1}{2} ||y-a||^2$ 。

假設二

假設代價函數能夠寫成關於神經網絡輸出結果的函數

MSE代價函數知足這個要求,由於單一訓練樣本x的二次代價能夠表示爲:
$$
C = \frac{1}{2} ||y-a^L||^2 = \frac{1}{2} \sum_j ( y_j - a_j^L )^2
$$
由於輸入的訓練樣本 x 是固定的,因此指望的輸出 y 也是固定的。x 和 y 不是神經網絡所學習的東西,咱們不能經過改變 Weights 和 Biases 來修改它。

因此這裏能夠把 C 視爲是隻關於輸出 $a^L$ 的函數。

Hadamard Product

反向傳播會用到 Hadamard Product ,假設 s 和 t 兩個向量有相同的維數
$$
( s \odot t )_j = s_j t_j
$$
其中,$s \odot t$ 表示兩個向量的對應元素相乘

反向傳播背後的四個基本等式

$$
\delta^L = \nabla a C \odot \sigma'(z^L) \
\delta^l = ((w^{l+1})^T \delta^{l+1}) \odot \sigma' (z^l) \
\frac{\partial C}{\partial b_j^l} = \delta_j^l \
\frac{\partial C}{\partial w_{jk}^l} = a_{k}^{l-1} \delta_j^l
$$

反向傳播算法

輸入一組訓練數據

對於訓練數據中的每一個樣本 x

計算輸入層的激活函數值 $a^{x,1}$,並執行下面的步驟:

Feedforward(正向傳播)

計算樣本x在每一層的激活函數值 $a^{x,l}$
$$
l=2,3,\dots ,L \
z^{x,l} = w^l a^{x,l-1} + b^l \
a^{x,l} = \sigma ( z^{x,l} )
$$

輸出層的偏差

計算樣本x在輸出層的偏差向量
$$
\delta^{x,L} = \nabla_a C_x \odot \sigma' ( z^{x,L} )
$$

將偏差反向傳播

使用輸出層的偏差,計算樣本x在以前每一層的偏差
$$
l = L-1,L-2,\dots ,2 \
\delta^{x,l} = (( w^{l+1} )^T \delta^{x,l+1} ) \odot \sigma'( z^{x,l} )
$$

Gradient Descent

使用樣本x在每一層的偏差,更新 Weights 和 Biases
$$
l = L,L-1,\dots,2 \
w^l \rightarrow w^l - \frac{\eta}{m} \sum_x \delta^{x,l} (a^{x,l-1})^T\
b^l \rightarrow b^l - \frac{\eta}{m} \sum_x \delta^{x,l}
$$

反向傳播算法的實現

初始化網絡

self.num_layers = len(sizes)
self.sizes = sizes
self.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:])]

對於5層的神經網絡,初始化後 Weights 和 Biases 的結構以下

size = [748, 40, 30, 20, 10]

biases  [(40, 1), (30, 1), (20, 1), (10, 1)]
weights [(40, 784), (30, 40), (20, 30), (10, 20)]

隨機梯度降低

隨機

def SGD(self, training_data, epochs, mini_batch_size, eta,
            test_data=None):
    for j in range(epochs):
        # 隨機打散
        random.shuffle(training_data)
        # 分批
        mini_batches = [
            training_data[k:k+mini_batch_size]
            for k in range(0, n, mini_batch_size)]
        for mini_batch in mini_batches:
            # 使用小批樣本快速學習
            self.update_mini_batch(mini_batch, eta)
            if test_data:
                print("Epoch {} : {} / {}"
                    .format(j,self.evaluate(test_data),n_test));
            else:
                print("Epoch {} complete".format(j))

梯度降低

def update_mini_batch(self, mini_batch, eta):
    # 每一層每一個神經元的偏置和權值
    nabla_b = [np.zeros(b.shape) for b in self.biases]
    nabla_w = [np.zeros(w.shape) for w in self.weights]
    
    for x, y in mini_batch:
        # 對於每個樣本x,反向傳播,計算每一層每一個神經元的梯度
        delta_nabla_b, delta_nabla_w = self.backprop(x, y)
        # 將樣本x的偏差梯度彙總到批次梯度上
        nabla_b = [nb+dnb for nb, dnb 
                   in zip(nabla_b, delta_nabla_b)]
        nabla_w = [nw+dnw for nw, dnw 
                   in zip(nabla_w, delta_nabla_w)]
        
    # 使用批次梯度更新權值和偏置
    self.weights = [w-(eta/len(mini_batch))*nw
                        for w, nw in zip(self.weights, nabla_w)]
    self.biases = [b-(eta/len(mini_batch))*nb
                       for b, nb in zip(self.biases, nabla_b)]

反向傳播

def backprop(self, x, y):
    # 每一層的偏置向量和權值矩陣
    nabla_b = [np.zeros(b.shape) for b in self.biases]
    nabla_w = [np.zeros(w.shape) for w in self.weights]
    
    # 正向傳播
    activation = x
    activations = [x] # 樣本x在每一層的激活值向量
    zs = [] # 樣本x在每一層的加權輸入向量
    
    # 逐層計算樣本x在加權輸入向量和激活值向量
    for b, w in zip(self.biases, self.weights):
        z = np.dot(w, activation)+b
        zs.append(z)
        activation = sigmoid(z)
        activations.append(activation)
        
    # 反向傳播
    
    # 計算樣本x在輸出層的偏差和梯度
    delta = self.cost_derivative(activations[-1], y) * \
    sigmoid_prime(zs[-1])
    nabla_b[-1] = delta
    nabla_w[-1] = np.dot(delta, activations[-2].transpose())

    # 使用輸出層的偏差和梯度,逐層向前計算樣本x在每一層的偏差梯度和權值梯度
    for l in range(2, self.num_layers):
        z = zs[-l]
        sp = sigmoid_prime(z)
        delta = np.dot(self.weights[-l+1].transpose(), delta) * sp
        nabla_b[-l] = delta
        nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())
    return (nabla_b, nabla_w)

反向傳播算法爲何更高效

待更新

反向傳播總體描述

咱們對 $w_{jk}^{l}$ 做出一個小的改變$\triangle w_{jk}^l$

這個改變量會致使與它相連的神經元的輸出激活值改變

而後,這個激活着會影響下一層的全部激活值

這樣一層一層地,最終引發代價函數的改變,而且這個改變咱們能夠算出。

相關文章
相關標籤/搜索