如果你對神經網絡還不太了解,又想了解這篇文章,希望你能先看
機器學習(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)