深度學習(4)--手寫數字mnist實現

       前面兩節,講述了梯度下降和方向傳播的原理,這裏我通過mnist訓練來講述下python的實現方法

  頭文件

        numpy用於矩陣運算,random用於數據集的shuffle,mnist_loader 用於加載數據集

import numpy as np
import random
import mnist_loader

  初始化網絡

         其中參數是sizes 格式是[784,30,10] 代表每層神經元的個數,self.weights 格式[34*784],[10*34],self.biases格式是[34*1]和[10*1] 前者代表每兩層神經元之間連線的權重,後者代表每個神經元上的偏向值。

    def __init__(self,sizes):
        self.layers = len(sizes)
        self.weights = [np.random.randn(x,y) for (x,y) in zip(sizes[1:],sizes[:-1])]
        self.biases = [np.random.randn(x,1) for x in sizes[1:]]

SGD 

     原理部分第二節已經說過了,我們來解讀一下參數

        train_data:我們輸入的訓練集可以抽象成[(x,y),(x1,y1)....(xn,yn)]

        epoches:訓練的輪數由我們自己指定

        mini_size:每次隨機抽樣訓練的訓練樣本數量

        eta(\eta):表示learning rate 學習率,可以理解爲步長

        test_data:格式與train_data相同,用於測試(注意本實例中沒有介紹驗證集) 

        程序中先對數據全集進行shuffle打亂操作,之後按mini_size 進行分堆,下面分別對每個mini_batch 進行訓練,也就是隨機抽樣的過程,這樣做極大的加快了學習效率。 

  def SGD(self,train_data,epoches,mini_size,eta,test_data):
        if test_data: n_test = len(test_data)
        n = len(train_data) #表示 訓練樣本數量
        for epoch in range(epoches):
            random.shuffle(train_data)

            mini_batches = [train_data[k:k+mini_size] for k in xrange(0,n,mini_size)] #xrange 返回的是生成器
            for mini_batch in mini_batches:
                self.update_minibatch(mini_batch,eta)
            if test_data:
                print "epoches {0} acc {1}/{2} ".format(epoch,self.cal_acc(test_data),n_test)
            else:
                print "no test data"

     update_minibatch 主要完成的操作是,求出樣本集中每個實例的偏導數累加求和,在根據下面的公式進行公式對weights和biases進行更新

              

    def update_minibatch(self,mini_batch,eta):

        nabla_b = [np.zeros(b.shape) for b in self.biases] #隨機梯度下降算法,用於存儲我們指定的minibatch中的數據的bias的總和
        nabla_w = [np.zeros(w.shape) for w in self.weights]

        for x,y in mini_batch:
            new_nabla_b ,new_nabla_w = self.back(x,y)

            nabla_w = [nw+w for nw,w in zip(new_nabla_w,nabla_w)]
            nabla_b = [nb+b for nb,b in zip(new_nabla_b,nabla_b)]

        self.weights = [w-((eta/len(mini_batch))*nabla_w) for w,nabla_w in zip(self.weights,nabla_w)]
        self.biases = [b-((eta/len(mini_batch))*nabla_b) for b,nabla_b in zip(self.biases,nabla_b)]

反向傳播

梯度下降和反向傳播的核心就是在於學習參數的偏導數計算,下圖是反向傳播求偏導數的過程。

    1,首先初始化各層的參數爲zero 和Pytorch 中optimizer.zero_grad() 操作大體相似,避免之前梯度的累加,用於存儲各個層偏導數結果

        nabla_b = [np.zeros(b.shape) for b in self.biases] #隨機梯度下降算法,用於存儲我們指定的minibatch中的數據的bias的總和
        nabla_w = [np.zeros(w.shape) for w in self.weights]

    2,z 在原始的公式中相當於 多個wx+b 的累加和,待激活的值, 第一層默認等於原始輸入的圖片數據的tensor值

            z = np.dot(w,activation)+b

   activation=\sigma (wx+b),都是和神經元的個數一致。

                 

      3,進行前向傳播,計算所有的z和activation的值

 for w,b in zip(self.weights,self.biases):
           # print w.shape,b.shape
            z = np.dot(w,activation)+b
            zs.append(z)
            activation = sigmoid(z)
            activations.append(activation)

      4,根據公式計算最後一層的error    

        delta = cost(activations[-1],y)*sigmoid_prime(zs[-1])

      5,更新最後一層的bias和weight   ,其中,bias等於最後一層的error值,weight是最後一層的error值與前一層激活值的轉置乘積

        nabla_b[-1] = delta
        #delta 目前可能是10*1 的向量,而activation[-2]可能是784*1 無法直接點乘 需要轉置後者 最終是10個784維向量 內部相加輸出 10*1
        nabla_w[-1] = np.dot(delta,activations[-2].transpose())

     6,根據鏈式求導法則 ,反向更新各層的weight值與bias值 ,並最終以(nabla_w,nable_w)形式返回給隨機梯度下降算法(update_mini_batch)的進行,全局weights和biases的更新

 for l in range(2,self.layers):
            delta = np.dot(self.weights[-l+1].transpose(),delta)*sigmoid_prime(zs[-l])
            nabla_b[-l] =delta
            nabla_w[-l] = np.dot(delta,activations[-l-1].transpose())
        return (nabla_b,nabla_w)

準確率計算 

    測試集是10000個(x,y),首先進行前向傳播和softmax,獲取最終預測的label值,並計算預測值與真實值的數量來計算準確率。

    def cal_acc(self,test_data):
        test_set = [(self.feedforward(x),y) for x,y in test_data]
        return np.sum(int(np.argmax(x))==y for (x,y) in test_set)
    def feedforward(self,x):
        for w,b in zip(self.weights,self.biases):
            x = np.dot(w,x)+b
            x = sigmoid(x)  #一定不能忘記激活

    下面是工具方法,包含了\sigma的導數:Oj = \sigma z*(1-\sigma z)

def sigmoid_prime(z):
    return sigmoid(z)*(1-sigmoid(z))

     激活函數 \sigma

def sigmoid(z):
    return 1/(1+np.exp(-z))

源代碼請移步     堅持一件事或許很難,但堅持下來一定很酷!^_^

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章