天天看點

機器學習(10.4)--手寫數字識别的不同算法比較(4)--神經網絡

如果你對神經網絡還不太了解,又想了解這篇文章,希望你能先看

機器學習(1)--神經網絡初探

在那篇代碼中,對神經網絡基本原理講的更細緻一些,

在這篇文章裡,對于weights和biases的設定,我采用了另一種方法,先簡單說一下

1、在那篇中,biases是在每層神經元(除去最後一層)基礎上再加一個補充的一個神經元當BIASES,但這不能算是相對準确的biases

   假如在一個[10 5 2]  那麼,weights就是[[11,6],[6,2]]的結構,

   同時輸入的10個神經元是以一維數組形式存在同時加上b後,即[0,1,2,3,4,5,6,7,8,9,b]

   在使用矩陣乘法時,即np.dot時,就是  

   先注:後面的(...) 是指每個矩陣的shape

   x(11,) * w(11,6) = (6,) 得到第二層

   (6,)  * (6,2)  =  (2,) 得到最後的輸出層,輸出層是一維數組

    請注意:那裡矩陣乘法的順序是np.dot(x,w),同時np.dot後沒有加b,b已經算在了神經元裡了

2、在本篇中,biases而是對應除第一層外的所有神經元,也是以二維數組形式存在,

    假如在一個[10 5 2]這樣的三層神經網絡中, weights就是[[5,10],[2,5]],b 就是[[5,1],[2,1]]

    注意這裡weights每個元素的順序是導的,這寫是和那篇文章主要的不同處,

    根本原因是那篇文章的 輸入量是一維數組,即(10,)

    而這篇文章的輸入量是二維數組,是(10,1)即[[0],[1],[2],[3],[4],[5],[6],[7],[8],[9]],注意後面沒b

    是以在使用矩陣乘法時,即np.dot時,就是  

    w(5,10) * x(10,1)+ b(5,1)=(5,1)  得到第二層

    (2,5) * (5,1)+b(2,1) = (2,1)  得到最後輸出層,輸出層也是二維數組

    請注意,這裡矩陣乘法的順序是np.dot(w,x)

3、 激活函數使用不同,關于激活函數,可參考

    機器學習09--神經網絡的激活函數(Activation Function)及python代碼實作

因為對weights和biases設定的差異,導緻算法的微小差異,但神經網絡的基本流程還是一樣的

1、讀取資料,調整資料格式以适配算法,設定基本參數

2、建立初始化的weights和biases

3、正向計算(從1層至N層):通過訓練集随機抽取一定數量,計算出最後結果,

4、反向計算(從N層至1層):和正确結果進行比較,梯度下降調整weights和biases

5、循環第3步

6、正向計算測試集:計算出結果

7、和正确結果比較,統計出正确率

一些補充說明

1、關于上述第2點,建立初始化的weights和biases,這裡我用了固定值做為初始化,

   因為在這個例子中是手寫數字,輸入的單條記錄為784元素,資料量比較大,

   初始化在使用随機分布時,計算差異有差異

   是以第一輪初始值使用固定值(這樣實測後是極不合理的做法),所第二輪開始時,我會先調整為随機值

   最後選擇固定初始化的值,我選了0.1,至于為什麼和學習率也就是梯度下降有關,這裡不在詳述,可以參考我别的文章

    tensorflow執行個體(6)--機器學習中學習率的實驗 

    機器學習(7)--梯度下降法(GradientDescent)的簡單實作 

2、關于後面的代碼,共有三段,其實都是一個内容,隻是我做了一些調整

   2.1 我盡量不使用大段的函數,讓代碼以順序流的方式向下走,這樣諸位在測試時比較簡單點不會跳來跳去的找變量什麼

        為了讓諸位如果想自己嘗試寫代碼時,能看資料結果和我寫的是否相同,使得有些地方設計并不是太合理,如

        2.1.1 如前面提到的第一輪使用固定值做初始值

        2.1.2 會加入一些輔助代碼段,我會說明哪些是測試代碼段,具體用處,主便諸位如果想自己嘗試寫代碼時能看資料,

              但這樣代碼變得有些淩亂

        2.1.3 隻提取了十分之一資料,這樣測度調試會快點,但相應的準确率不是那麼高,在這裡最終正确率隻能到83%左右,如果 有全資料,可以到93%左右

   2.2 就是2.1中的代碼去除了大段備注、測試代碼段及以些相對沒用的print語句後的結果

   2.3 将其中一些過程提取成函數形式,

關于使用的資料集,可參考

機器學習(10.1)--手寫數字識别的不同算法比較(1)--mnist資料集不同版本解析及平均灰階實踐

代碼段一:

# -*- coding:utf-8 -*-
import pickle  
import gzip  
import numpy as np  
import random
#激活函數
def sigmoid(z):  
    return 1.0 / (1.0 + np.exp(-z))  
  
def sigmoid_deriv(z):  
    return sigmoid(z) * (1 - sigmoid(z)) 

#讀取資料
with gzip.open(r'mnist.pkl.gz', 'rb')  as f:
    training_data, validation_data, test_data = pickle.load(f,encoding='bytes') 

#整理資料,臨時變量training_label,作用,是将結果值,如5,轉為[0,0,0,0,0,1,0,0,0,0]
#因為我們這是神經網絡,如果不進行轉化,在訓練結束進行測試時,測試的結果不會正好是5,無法與結果進行判斷正确性
#但把結果轉化為[0,0,0,0,0,1,0,0,0,0],在測試時結果可能是[0.0001,0.00001,0.002...]這樣的結果,我們取最大的那個位數,就行了
training_label = np.zeros([training_data[1].shape[0],10,1])
for index,val in enumerate(training_data[1]): training_label[index][val] = 1
training_data = list(zip(training_data[0].reshape(-1,784,1),training_label))
test_data = list(zip(test_data[0].reshape(-1,784,1),test_data[1]))

#以下三行為輔助代碼段,在寫代碼入調試過程,用全部集合實在有點慢,
trainingNumTmp = 5000
training_data = training_data[0:trainingNumTmp]
test_data = test_data[0:int(trainingNumTmp / 5)] 

#1、讀取資料,調整資料格式以适配算法,設定基本參數
layers = [training_data[0][0].shape[0],20,15,training_data[0][1].shape[0]] 
trainingNum = len(training_data)
epochs = 6 #全部訓練集的資料,訓練幾輪
batchNum = 10 #每循環一次時,對全部資料進行分割,每一小組為幾個,如這裡設為10,50000條資料就是這樣[[10條],[10條]..全部共5000組..[10條],[10條]]
learningRate = 3 #學習率

print('-' * 60)
print("一些基本參數資訊")
print('共取得%s條訓練集,%s條測試集。' % (trainingNum,len(test_data)))
print('神經網絡層數為:' + str(layers) + ",在這裡定義為4層,頭尾為輸入輸出層,其實3層效果就可以了,隻是為了後面資料推演更清楚,是以我設了4層")
print('每一條圖像的次元為%s,結果的次元為%s,注意,一定要是(784, 1)和(10, 1)的二維數組.' % (training_data[0][0].shape,training_data[0][1].shape))
print('-' * 60)


#2、建立初始化的weights和biases
weights = [np.ones((layers[x + 1],layers[x])) / 10 for x in range(len(layers) - 1)]
biases = [np.ones((layers[x + 1],1)) / 10 for x in range(len(layers) - 1)]

strW = ""
strB = ""
for x in weights:
    strW+= ('' if strW == '' else ' , ') + str(x.shape)
for x in biases:
    strB+= ('' if strB == '' else ' , ') + str(x.shape)
print("所有weight次元為:" + str(strW))   #(20, 784) , (15, 20) , (10, 15)
print("所有biases次元為:" + str(strB))   #(20, 1) , (15, 1) , (10, 1)
print('-' * 60)


tmp = 0 #輔助代碼,因為在第一輪的batch運作過程,我中間加了一些顯示的資料,
#開始訓練
for j in range(epochs):
    #以下的if為輔助代碼,當第一次運作時,不随機重排列訓練集,這樣第一次運作時的資料會固定下來,在相應的print,我會列印出一些資料,
    #如果你嘗試使用相同的邏輯來試寫時,這些值應該是一樣的
    if j == 0:
        print("第一次運作,訓練集中第一條的第[153]資料為:" + str(np.array(training_data[0][0][153])))      #[0.0703125]
    else:
        random.shuffle(training_data)  
    if j == 1 :
        #這也是輔助代碼,前面初始化weigths和biases用的是固定值,這樣做正确率實在太低,
        #設定固定值的目的是友善保證,第一輪檢視資料計算結果一緻,到第二輪時,我會先調整為随機值
        weights = [np.random.randn(layers[x + 1],layers[x])  for x in range(len(layers) - 1)]
        biases = [np.random.randn(layers[x + 1],1)  for x in range(len(layers) - 1)]

    batchGroup = [training_data[x:x + batchNum] for x in range(0,trainingNum,batchNum)]

    for batch in batchGroup:
        batch_w = [np.zeros(b.shape) for b in weights]   #建立每組batch相關的初始為0的weight 和 biase,當batch下每條記錄全部計算完後,加總求平均并修正weights和biases
        batch_b = [np.zeros(b.shape) for b in biases]  

        for img,label in batch:
            tmp+=1

            #每一條記錄正逆一次計算出一次誤差
            zs = []
            acts = [img]
            #3、正向計算(從1層至N層):通過訓練集随機抽取一定數量,計算出最後結果,
            for w,b in zip(weights,biases):
                z = np.dot(w,acts[-1]) + b
                zs.append(z)
                acts.append(sigmoid(z))


            #4、反向計算(從N層至1層):和正确結果進行比較,梯度下降調整weights和biases
            item_w = [np.zeros(b.shape) for b in weights]   #建立每條記錄相關的初始為0的weight 和 biase
            item_b = [np.zeros(b.shape) for b in biases]  

            for index in range(-1,-1 * len(layers),-1):
                if index == -1:
                    item_b[index] = acts[index] - label #使用delta一個過程變量目的:在反向運算時,後一層計算
                else:
                    item_b[index] = np.dot(weights[index + 1].T,item_b[index + 1])
                item_b[index] = item_b[index] * sigmoid_deriv(zs[index])  
            
                item_w[index] = np.dot(item_b[index],acts[index - 1].T)
                if tmp == 10 and index == -1: print("第一輪運作,第10條記錄結束時,item的最後一個biases值:" + str(item_b[index][-1])) #[0.25077581]

            #當batch下每條記錄計算完後加總
            for index in range(0,len(batch_w)):
                batch_w[index] = batch_w[index] + item_w[index]
                batch_b[index] = batch_b[index] + item_b[index]
            if tmp == 2 : 
                print("第一輪運作,第2條記錄結束時,batch的最後一個biases值:" + str(batch_b[-1][1])) #[0.12539041]

        #一組batch計算結束後,求平均并修正weights和biases
        for index in range(0,len(batch_w)):
            batch_w[index] = batch_w[index] / batchNum
            weights[index] = weights[index] - learningRate * batch_w[index]
            batch_b[index] = batch_b[index] / batchNum
            biases[index] = biases[index] - learningRate * batch_b[index]

    if tmp == 5000: #輔助代碼,第一輪運作結束時
        print("有四層神經網絡,第一輪運作結束時,中間有三個組的weights,即上面的(20, 784) , (15, 20) , (10, 15),\n每組所有的weights的加總值應為:" + str([str(np.sum(x)) for x in weights]))
        print("有四層神經網絡,第一輪運作結束時,中間有三個組的biases,即上面的(20, 1) , (15, 1) , (10, 1),\n每組所有的biases的加總值應為:" + str([str(np.sum(x)) for x in biases]))
    #每組所有的weights的加總值應為:['1567.8833232869501', '-5.362204505945536', '-44.4216909099882']
    #每組所有的biases的加總值應為:['1.9975427310316856', '-0.27341581024693457', '-4.277655163275536']                         



    #6、正向計算測試集:計算出結果
    #7、和正确結果比較,統計出正确率
    correctNum=0
    for testImg,testLabel in test_data:
        for w,b in  zip( weights,biases):
            testImg= sigmoid(np.dot(w, testImg)+b)  
        if np.argmax(testImg)==testLabel : correctNum+=1

    print("共 %d 輪訓練,第 %d 輪訓練結束,測試集數量為 %d 條,測試正确 %d 條。"%(epochs,j+1,len(test_data),correctNum))

print("程式運作結束!")           

代碼段二:

# -*- coding:utf-8 -*-
import pickle  
import gzip  
import numpy as np  
import random
#激活函數
def sigmoid(z):  
    return 1.0 / (1.0 + np.exp(-z))  
  
def sigmoid_deriv(z):  
    return sigmoid(z) * (1 - sigmoid(z)) 

#讀取資料
with gzip.open(r'mnist.pkl.gz', 'rb')  as f:
    training_data, validation_data, test_data = pickle.load(f,encoding='bytes') 

#整理資料,臨時變量training_label,作用,是将結果值,如5,轉為[0,0,0,0,0,1,0,0,0,0]
training_label = np.zeros([training_data[1].shape[0],10,1])
for index,val in enumerate(training_data[1]): training_label[index][val] = 1
training_data = list(zip(training_data[0].reshape(-1,784,1),training_label))
test_data = list(zip(test_data[0].reshape(-1,784,1),test_data[1]))

#1、讀取資料,調整資料格式以适配算法,設定基本參數
layers = [training_data[0][0].shape[0],20,15,training_data[0][1].shape[0]] 
trainingNum = len(training_data)
epochs = 6 
batchNum = 10 
learningRate = 3 

#2、建立初始化的weights和biases
weights = [np.random.randn(layers[x + 1],layers[x])  for x in range(len(layers) - 1)]
biases = [np.random.randn(layers[x + 1],1)  for x in range(len(layers) - 1)]

for j in range(epochs):
    random.shuffle(training_data)  
    batchGroup = [training_data[x:x + batchNum] for x in range(0,trainingNum,batchNum)]
    for batch in batchGroup:
        batch_w = [np.zeros(b.shape) for b in weights]  
        batch_b = [np.zeros(b.shape) for b in biases]  
        for img,label in batch:
            #每一條記錄正逆一次計算出一次誤差
            zs = []
            acts = [img]
            #3、正向計算(從1層至N層):通過訓練集随機抽取一定數量,計算出最後結果,
            for w,b in zip(weights,biases):
                z = np.dot(w,acts[-1]) + b
                zs.append(z)
                acts.append(sigmoid(z))

            #4、反向計算(從N層至1層):和正确結果進行比較,梯度下降調整weights和biases
            item_w = [np.zeros(b.shape) for b in weights]   #建立每條記錄相關的初始為0的weight 和 biase
            item_b = [np.zeros(b.shape) for b in biases]  
            for index in range(-1,-1 * len(layers),-1):
                if index == -1:
                    item_b[index] = acts[index] - label #使用delta一個過程變量目的:在反向運算時,後一層計算
                else:
                    item_b[index] = np.dot(weights[index + 1].T,item_b[index + 1])
                item_b[index] = item_b[index] * sigmoid_deriv(zs[index])  
                item_w[index] = np.dot(item_b[index],acts[index - 1].T)

            #當batch下每條記錄計算完後加總
            for index in range(0,len(batch_w)):
                batch_w[index] = batch_w[index] + item_w[index]
                batch_b[index] = batch_b[index] + item_b[index]

        #一組batch計算結束後,求平均并修正weights和biases
        for index in range(0,len(batch_w)):
            batch_w[index] = batch_w[index] / batchNum
            weights[index] = weights[index] - learningRate * batch_w[index]
            batch_b[index] = batch_b[index] / batchNum
            biases[index] = biases[index] - learningRate * batch_b[index]

    #6、正向計算測試集:計算出結果
    #7、和正确結果比較,統計出正确率
    correctNum=0
    for testImg,testLabel in test_data:
        for w,b in  zip( weights,biases):
            testImg= sigmoid(np.dot(w, testImg)+b)  
        if np.argmax(testImg)==testLabel : correctNum+=1
    print("共 %d 輪訓練,第 %d 輪訓練結束,測試集數量為 %d 條,測試正确 %d 條。"%(epochs,j+1,len(test_data),correctNum))

print("程式運作結束!")           

代碼段三

# -*- coding:utf-8 -*-
import pickle  
import gzip  
import numpy as np  
import random

#激活函數
def sigmoid(z):  
    return 1.0 / (1.0 + np.exp(-z))  
  
def sigmoid_deriv(z):  
    return sigmoid(z) * (1 - sigmoid(z)) 

#讀取資料
def loadData(trainingNum = None):
    with gzip.open(r'mnist.pkl.gz', 'rb')  as f:
        training_data, validation_data, test_data = pickle.load(f,encoding='bytes') 
    training_label = np.zeros([training_data[1].shape[0],10,1])
    for index,val in enumerate(training_data[1]): training_label[index][val] = 1
    training_data = list(zip(training_data[0].reshape(-1,784,1),training_label))
    test_data = list(zip(test_data[0].reshape(-1,784,1),test_data[1]))
    if trainingNum !=None:
        training_data = training_data[0:trainingNum]
        test_data = test_data[0:int(trainingNum / 5)] 
    return training_data,test_data

def batchData(batch,layers,weights,biases):
    batch_w = [np.zeros(b.shape) for b in weights]  
    batch_b = [np.zeros(b.shape) for b in biases]  
    for item in batch:
        item_w,item_b=itemData(item,layers,weights,biases)
        #當batch下每條記錄計算完後加總
        for index in range(0,len(batch_w)):
            batch_w[index] = batch_w[index] + item_w[index]
            batch_b[index] = batch_b[index] + item_b[index]
    return batch_w,batch_b

def itemData(item,layers,weights,biases):
    zs = []
    acts = [item[0]]
    for w,b in zip(weights,biases):
        z = np.dot(w,acts[-1]) + b
        zs.append(z)
        acts.append(sigmoid(z))
    item_w = [np.zeros(b.shape) for b in weights]   
    item_b = [np.zeros(b.shape) for b in biases]  
    for index in range(-1,-1 * len(layers),-1):
        if index == -1:
            item_b[index] = acts[index] - item[1] 
        else:
            item_b[index] = np.dot(weights[index + 1].T,item_b[index + 1])
        item_b[index] = item_b[index] * sigmoid_deriv(zs[index])  
        item_w[index] = np.dot(item_b[index],acts[index - 1].T)
    return item_w,item_b

def predict(test_data,weights,biases):
    #6、正向計算測試集:計算出結果
    #7、和正确結果比較,統計出正确率
    correctNum=0
    for testImg,testLabel in test_data:
        for w,b in  zip( weights,biases):
            testImg= sigmoid(np.dot(w, testImg)+b)  
        if np.argmax(testImg)==testLabel : correctNum+=1
    return correctNum

def mnistNN(trainingNum = None,midLayes=[20,15],epochs=6,batchNum=10,learningRate=3):
    training_data,test_data=loadData(trainingNum)
    #1、讀取資料,調整資料格式以适配算法,設定基本參數
    layers = [training_data[0][0].shape[0]]+midLayes+[training_data[0][1].shape[0]] 
    trainingNum = len(training_data)

    #2、建立初始化的weights和biases
    weights = [np.random.randn(layers[x + 1],layers[x])  for x in range(len(layers) - 1)]
    biases = [np.random.randn(layers[x + 1],1)  for x in range(len(layers) - 1)]

    for j in range(epochs):
        random.shuffle(training_data)  
        batchGroup = [training_data[x:x + batchNum] for x in range(0,trainingNum,batchNum)]
        for batch in batchGroup:
            batch_w,batch_b=batchData(batch,layers,weights,biases)
            #一組batch計算結束後,求平均并修正weights和biases
            for index in range(0,len(batch_w)):
                batch_w[index] = batch_w[index] / batchNum
                weights[index] = weights[index] - learningRate * batch_w[index]
                batch_b[index] = batch_b[index] / batchNum
                biases[index] = biases[index] - learningRate * batch_b[index]
        print("共 %d 輪訓練,第 %d 輪訓練結束,測試集數量為 %d 條,測試正确 %d 條。"%(epochs,j+1,len(test_data),predict(test_data,weights,biases)))

mnistNN(trainingNum=5000)