Python神經網絡代碼實現流程(三):反向傳播與梯度下降

前向傳播爲輸入的矩陣經過計算到輸出層,而反向傳播與梯度下降則是訓練神經網絡的核心步驟.
梯度下降算法的代碼如下:

def SGD(self, training_data, epochs, mini_batch_size, eta, test_data = None):  
    if test_data: n_test = len(test_data)  
    n = len(training_data)  
    for j in range(epochs):   #自動定義的循環次數,也就是訓練神經網絡的循環次數
        random.shuffle(training_data)  #shuffle爲將訓練接隨機打亂重排,保證訓練的數據的隨機性
        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 {0}: {1} / {2}". format(j, self.evaluate(test_data), n_test))  #使用了測試集的數據,進行打印顯示(evaluate函數後面詳述)
        else:  
            print ("Epoch {0} 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:  #mini_batch爲784行,mini_batch_size列的矩陣
        delta_nabla_b, delta_nabla_w = self.backprop(x, y)  #這裏是反向傳播的函數,是神經網絡的核心.以下的代碼就是對權重和偏置的更新
        #由下面的bp代碼可以得到神經網絡的所有參數的偏導數矩陣
        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):  #這裏的(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      #activation爲輸入層的數據
    activations = [x]     #actications爲經過激活函數處理後的輸出矩陣
    zs = []     #zs爲每個層未經過激活函數處理的矩陣
    for b, w in zip(self.biases, self.weights):  #for循環後,得到了兩個矩陣,其中zs矩陣是未經過激活的矩陣,列數和神經網絡的層數相同(不包括輸入層),activations爲經過激活後的矩陣,也就是每層的神經網絡的輸出(包括輸入層),其中第一列爲輸入層,最後一列爲神經網絡的輸出.
        z = np.dot(w, activation)+b  
        zs.append(z)  
        activation = sigmoid(z)  
        activations.append(activation)    
    delta = self.cost_derivative(activations[-1], y) * sigmoid_prime(zs[-1]) 
    #由於求導的公式推出的偏導數公式,每一層偏置的偏導數等於(a L − y) ⊙ σ ′ (z L ),因此得到的delta就是輸出層偏置的偏導數
    nabla_b[-1] = delta  #這是偏置偏導數矩陣的賦值,在上面已經進行了矩陣的初始化
    nabla_w[-1] = np.dot(delta, activations[-2].transpose())   
    for l in range(2, self.num_layers):  #以上是最後一層權重和偏置的偏導數賦值,下面的for循環會依次將每一層的權重和偏置的偏導數進行計算得到.
        z = zs[-l]  
        sp = sigmoid_prime(z)  
        delta = np.dot(self.weights[-l+1].transpose(), delta) * sp 
        #因爲只有最後一層有輸出y,所以前面的層數不能夠再利用輸出求偏導數,利用下一層的偏導數求上一層的偏導數: δ l = ((w l+1 ) T δ l+1 ) ⊙ σ ′ (z l ),這裏,用到了下一層權重,和下一層已經求出來的偏導數.
        nabla_b[-l] = delta  
        nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())  
    return (nabla_b, nabla_w)  #當循環結束,每一次的權重和偏置的偏導數矩陣都已經得到.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章