接口實現可參考:keras框架實現手寫數字識別
思路:
我們的代碼要導出三個接口,分別完成以下功能:
- 初始化initialisation,設置輸入層,中間層,和輸出層的節點數。
- 訓練train:根據訓練數據不斷的更新權重值
- 查詢query,把新的數據輸入給神經網絡,網絡計算后輸出答案。(推理)
keras框架初始化解析:
[1]
'''
我們先給出如下代碼框架:
'''
class NeuralNetWork:def __init__(self):#初始化網絡,設置輸入層,中間層,和輸出層節點數passdef train(self):#根據輸入的訓練數據更新節點鏈路權重passdef query(self):#根據輸入數據計算并輸出答案pass[2]
'''
我們先完成初始化函數,我們需要在這里設置輸入層,中間層和輸出層的節點數,這樣就能決定網絡的形狀和大小。
當然我們不能把這些設置都寫死,而是根據輸入參數來動態設置網絡的形態。
由此我們把初始化函數修正如下:
'''
class NeuralNetWork:def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):#初始化網絡,設置輸入層,中間層,和輸出層節點數self.inodes = inputnodesself.hnodes = hiddennodesself.onodes = outputnodes#設置學習率self.lr = learningrate#passdef train(self):#根據輸入的訓練數據更新節點鏈路權重passdef query(self):#根據輸入數據計算并輸出答案pass[3]
'''
此處舉例說明:
如此我們就可以初始化一個3層網絡,輸入層,中間層和輸出層都有3個節點
'''
input_nodes = 3
hidden_nodes = 3
output_nodes = 3learning_rate = 0.3
n = NeuralNetWork(input_nodes, hidden_nodes, output_nodes, learning_rate)[4]
'''
初始化權重矩陣。
由于權重不一定都是正的,它完全可以是負數,因此我們在初始化時,把所有權重初始化為-0.5到0.5之間
'''
def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):#初始化網絡,設置輸入層,中間層,和輸出層節點數self.inodes = inputnodesself.hnodes = hiddennodesself.onodes = outputnodes#設置學習率self.lr = learningrate'''初始化權重矩陣,我們有兩個權重矩陣,一個是wih表示輸入層和中間層節點間鏈路權重形成的矩陣一個是who,表示中間層和輸出層間鏈路權重形成的矩陣'''self.wih = numpy.random.rand(self.hnodes, self.inodes) - 0.5self.who = numpy.random.rand(self.onodes, self.inodes) - 0.5pass[5]
'''
接著我們先看query函數的實現,它接收輸入數據,通過神經網絡的層層計算后,在輸出層輸出最終結果。
輸入數據要依次經過輸入層,中間層,和輸出層,并且在每層的節點中還得執行激活函數以便形成對下一層節點的輸出信號。
我們知道可以通過矩陣運算把這一系列復雜的運算流程給統一起來。
'''
import numpy
def query(self, inputs):#根據輸入數據計算并輸出答案hidden_inputs = numpy.dot(self.wih, inputs)pass[6]
'''
hidden是個一維向量,每個元素對應著中間層某個節點從上一層神經元傳過來后的信號量總和.
于是每個節點就得執行激活函數,得到的結果將作為信號輸出到下一層.
sigmod函數在Python中可以直接調用,我們要做的就是準備好參數。
我們先把這個函數在初始化函數中設定好,
'''
import scipy.specialclass NeuralNetWork:def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):#初始化網絡,設置輸入層,中間層,和輸出層節點數self.inodes = inputnodesself.hnodes = hiddennodesself.onodes = outputnodes#設置學習率self.lr = learningrate'''初始化權重矩陣,我們有兩個權重矩陣,一個是wih表示輸入層和中間層節點間鏈路權重形成的矩陣一個是who,表示中間層和輸出層間鏈路權重形成的矩陣'''self.wih = numpy.random.rand(self.hnodes, self.inodes) - 0.5self.who = numpy.random.rand(self.onodes, self.inodes) - 0.5'''scipy.special.expit對應的是sigmod函數.lambda是Python關鍵字,類似C語言中的宏定義.我們調用self.activation_function(x)時,編譯器會把其轉換為spicy.special_expit(x)。'''self.activation_function = lambda x:scipy.special.expit(x)pass'''
由此我們就可以分別調用激活函數計算中間層的輸出信號,以及輸出層經過激活函數后形成的輸出信號,
'''
def query(self, inputs):#根據輸入數據計算并輸出答案#計算中間層從輸入層接收到的信號量hidden_inputs = numpy.dot(self.wih, inputs)#計算中間層經過激活函數后形成的輸出信號量hidden_outputs = self.activation_function(hidden_inputs)#計算最外層接收到的信號量final_inputs = numpy.dot(self.who, hidden_outputs)#計算最外層神經元經過激活函數后輸出的信號量final_outputs = self.activation_function(final_inputs)print(final_outputs)return final_outputs
keras框架訓練解析:
import numpy
[1]
'''
自我訓練過程分兩步:
第一步是計算輸入訓練數據,給出網絡的計算結果,這點跟我們前面實現的query()功能很像。
第二步是將計算結果與正確結果相比對,獲取誤差,采用誤差反向傳播法更新網絡里的每條鏈路權重。我們先用代碼完成第一步.inputs_list:輸入的訓練數據;
targets_list:訓練數據對應的正確結果。
'''
def train(self, inputs_list, targets_list):#根據輸入的訓練數據更新節點鏈路權重'''把inputs_list, targets_list轉換成numpy支持的二維矩陣.T表示做矩陣的轉置'''inputs = numpy.array(inputs_list, ndmin=2).Ttargets = numpy.array(targets_list, nmin=2).T#計算信號經過輸入層后產生的信號量hidden_inputs = numpy.dot(self.wih, inputs)#中間層神經元對輸入的信號做激活函數后得到輸出信號hidden_outputs = self.activation_function(hidden_inputs)#輸出層接收來自中間層的信號量final_inputs = numpy.dot(self.who, hidden_outputs)#輸出層對信號量進行激活函數后得到最終輸出信號final_outputs = self.activation_function(final_inputs)[2]
'''
上面代碼根據輸入數據計算出結果后,我們先要獲得計算誤差.
誤差就是用正確結果減去網絡的計算結果。
在代碼中對應的就是(targets - final_outputs).
'''
def train(self, inputs_list, targets_list):#根據輸入的訓練數據更新節點鏈路權重'''把inputs_list, targets_list轉換成numpy支持的二維矩陣.T表示做矩陣的轉置'''inputs = numpy.array(inputs_list, ndmin=2).Ttargets = numpy.array(targets_list, nmin=2).T#計算信號經過輸入層后產生的信號量hidden_inputs = numpy.dot(self.wih, inputs)#中間層神經元對輸入的信號做激活函數后得到輸出信號hidden_outputs = self.activation_function(hidden_inputs)#輸出層接收來自中間層的信號量final_inputs = numpy.dot(self.who, hidden_outputs)#輸出層對信號量進行激活函數后得到最終輸出信號final_outputs = self.activation_function(final_inputs)#計算誤差output_errors = targets - final_outputshidden_errors = numpy.dot(self.who.T, output_errors)#根據誤差計算鏈路權重的更新量,然后把更新加到原來鏈路權重上self.who += self.lr * numpy.dot((output_errors * final_outputs *(1 - final_outputs)),numpy.transpose(hidden_outputs))self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1 - hidden_outputs)),numpy.transpose(inputs))pass[3]
'''
使用實際數據來訓練我們的神經網絡
'''
#open函數里的路徑根據數據存儲的路徑來設定
data_file = open("dataset/mnist_test.csv")
data_list = data_file.readlines()
data_file.close()
len(data_list)
data_list[0]
'''
這里我們可以利用畫圖.py將輸入繪制出來
'''[4]
'''
從繪制的結果看,數據代表的確實是一個黑白圖片的手寫數字。
數據讀取完畢后,我們再對數據格式做些調整,以便輸入到神經網絡中進行分析。
我們需要做的是將數據“歸一化”,也就是把所有數值全部轉換到0.01到1.0之間。
由于表示圖片的二維數組中,每個數大小不超過255,由此我們只要把所有數組除以255,就能讓數據全部落入到0和1之間。
有些數值很小,除以255后會變為0,這樣會導致鏈路權重更新出問題。
所以我們需要把除以255后的結果先乘以0.99,然后再加上0.01,這樣所有數據就處于0.01到1之間。
'''
scaled_input = image_array / 255.0 * 0.99 + 0.01
keras框架運行解析:
import numpy
import matplotlib.pyplot as plt[1]
#open函數里的路徑根據數據存儲的路徑來設定
data_file = open("dataset/mnist_test.csv")
data_list = data_file.readlines()
data_file.close()
print(len(data_list))
print(data_list[0])#把數據依靠','區分,并分別讀入
all_values = data_list[0].split(',')
#第一個值對應的是圖片的表示的數字,所以我們讀取圖片數據時要去掉第一個數值
image_array = numpy.asfarray(all_values[1:]).reshape((28, 28))#最外層有10個輸出節點
onodes = 10
targets = numpy.zeros(onodes) + 0.01
targets[int(all_values[0])] = 0.99
print(targets) #targets第8個元素的值是0.99,這表示圖片對應的數字是7(數組是從編號0開始的).[2]
'''
根據上述做法,我們就能把輸入圖片給對應的正確數字建立聯系,這種聯系就可以用于輸入到網絡中,進行訓練。
由于一張圖片總共有28*28 = 784個數值,因此我們需要讓網絡的輸入層具備784個輸入節點。
這里需要注意的是,中間層的節點我們選擇了100個神經元,這個選擇是經驗值。
中間層的節點數沒有專門的辦法去規定,其數量會根據不同的問題而變化。
確定中間層神經元節點數最好的辦法是實驗,不停的選取各種數量,看看那種數量能使得網絡的表現最好。
'''
#初始化網絡
input_nodes = 784
hidden_nodes = 100
output_nodes = 10
learning_rate = 0.3
n = NeuralNetWork(input_nodes, hidden_nodes, output_nodes, learning_rate)
#讀入訓練數據
#open函數里的路徑根據數據存儲的路徑來設定
training_data_file = open("dataset/mnist_train.csv")
trainning_data_list = training_data_file.readlines()
training_data_file.close()
#把數據依靠','區分,并分別讀入
for record in trainning_data_list:all_values = record.split(',')inputs = (numpy.asfarray(all_values[1:]))/255.0 * 0.99 + 0.01#設置圖片與數值的對應關系targets = numpy.zeros(output_nodes) + 0.01targets[int(all_values[0])] = 0.99n.train(inputs, targets)[3]
'''
最后我們把所有測試圖片都輸入網絡,看看它檢測的效果如何
'''
scores = []
for record in test_data_list:all_values = record.split(',')correct_number = int(all_values[0])print("該圖片對應的數字為:",correct_number)#預處理數字圖片inputs = (numpy.asfarray(all_values[1:])) / 255.0 * 0.99 + 0.01#讓網絡判斷圖片對應的數字outputs = n.query(inputs)#找到數值最大的神經元對應的編號label = numpy.argmax(outputs)print("out put reslut is : ", label)#print("網絡認為圖片的數字是:", label)if label == correct_number:scores.append(1)else:scores.append(0)
print(scores)#計算圖片判斷的成功率
scores_array = numpy.asarray(scores)
print("perfermance = ", scores_array.sum() / scores_array.size)[4]
'''
在原來網絡訓練的基礎上再加上一層外循環
但是對于普通電腦而言執行的時間會很長。
epochs 的數值越大,網絡被訓練的就越精準,但如果超過一個閾值,網絡就會引發一個過擬合的問題.
'''
#加入epocs,設定網絡的訓練循環次數
epochs = 10for e in range(epochs):#把數據依靠','區分,并分別讀入for record in trainning_data_list:all_values = record.split(',')inputs = (numpy.asfarray(all_values[1:]))/255.0 * 0.99 + 0.01#設置圖片與數值的對應關系targets = numpy.zeros(output_nodes) + 0.01targets[int(all_values[0])] = 0.99n.train(inputs, targets)
畫圖部分實現:
import numpy
import matplotlib.pyplot as plt
#%matplotlib inline#open函數里的路徑根據數據存儲的路徑來設定
data_file = open("dataset/mnist_test.csv")
data_list = data_file.readlines()
data_file.close()
print(len(data_list))
print(data_list[0])#把數據依靠','區分,并分別讀入
all_values = data_list[0].split(',')
#第一個值對應的是圖片的表示的數字,所以我們讀取圖片數據時要去掉第一個數值
image_array = numpy.asfarray(all_values[1:]).reshape((28, 28))
plt.imshow(image_array, cmap='Greys', interpolation='None')
plt.show()#數據預處理(歸一化)
scaled_input = image_array / 255.0 * 0.99 + 0.01
print(scaled_input)
綜合起來:數字圖像識別代碼:
數據集來自于mnist
可采用from tensorflow.keras.datasets import mnist
方法下載
import numpy
import scipy.specialclass NeuralNetWork:def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):#初始化網絡,設置輸入層,中間層,和輸出層節點數self.inodes = inputnodesself.hnodes = hiddennodesself.onodes = outputnodes#設置學習率self.lr = learningrate'''初始化權重矩陣,我們有兩個權重矩陣,一個是wih表示輸入層和中間層節點間鏈路權重形成的矩陣一個是who,表示中間層和輸出層間鏈路權重形成的矩陣'''#self.wih = numpy.random.rand(self.hnodes, self.inodes) - 0.5#self.who = numpy.random.rand(self.onodes, self.hnodes) - 0.5self.wih = (numpy.random.normal(0.0, pow(self.hnodes,-0.5), (self.hnodes,self.inodes) ) )self.who = (numpy.random.normal(0.0, pow(self.onodes,-0.5), (self.onodes,self.hnodes) ) )'''每個節點執行激活函數,得到的結果將作為信號輸出到下一層,我們用sigmoid作為激活函數'''self.activation_function = lambda x:scipy.special.expit(x)passdef train(self,inputs_list, targets_list):#根據輸入的訓練數據更新節點鏈路權重'''把inputs_list, targets_list轉換成numpy支持的二維矩陣.T表示做矩陣的轉置'''inputs = numpy.array(inputs_list, ndmin=2).Ttargets = numpy.array(targets_list, ndmin=2).T#計算信號經過輸入層后產生的信號量hidden_inputs = numpy.dot(self.wih, inputs)#中間層神經元對輸入的信號做激活函數后得到輸出信號hidden_outputs = self.activation_function(hidden_inputs)#輸出層接收來自中間層的信號量final_inputs = numpy.dot(self.who, hidden_outputs)#輸出層對信號量進行激活函數后得到最終輸出信號final_outputs = self.activation_function(final_inputs)#計算誤差output_errors = targets - final_outputshidden_errors = numpy.dot(self.who.T, output_errors)#根據誤差計算鏈路權重的更新量,然后把更新加到原來鏈路權重上self.who += self.lr * numpy.dot((output_errors * final_outputs *(1 - final_outputs)),numpy.transpose(hidden_outputs))self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1 - hidden_outputs)),numpy.transpose(inputs))passdef query(self,inputs):#根據輸入數據計算并輸出答案#計算中間層從輸入層接收到的信號量hidden_inputs = numpy.dot(self.wih, inputs)#計算中間層經過激活函數后形成的輸出信號量hidden_outputs = self.activation_function(hidden_inputs)#計算最外層接收到的信號量final_inputs = numpy.dot(self.who, hidden_outputs)#計算最外層神經元經過激活函數后輸出的信號量final_outputs = self.activation_function(final_inputs)print(final_outputs)return final_outputs#初始化網絡
'''
由于一張圖片總共有28*28 = 784個數值,因此我們需要讓網絡的輸入層具備784個輸入節點
'''
input_nodes = 784
hidden_nodes = 200
output_nodes = 10
learning_rate = 0.1
n = NeuralNetWork(input_nodes, hidden_nodes, output_nodes, learning_rate)#讀入訓練數據
#open函數里的路徑根據數據存儲的路徑來設定
training_data_file = open("dataset/mnist_train.csv",'r')
training_data_list = training_data_file.readlines()
training_data_file.close()#加入epocs,設定網絡的訓練循環次數
epochs = 5
for e in range(epochs):#把數據依靠','區分,并分別讀入for record in training_data_list:all_values = record.split(',')inputs = (numpy.asfarray(all_values[1:]))/255.0 * 0.99 + 0.01#設置圖片與數值的對應關系targets = numpy.zeros(output_nodes) + 0.01targets[int(all_values[0])] = 0.99n.train(inputs, targets)test_data_file = open("dataset/mnist_test.csv")
test_data_list = test_data_file.readlines()
test_data_file.close()
scores = []
for record in test_data_list:all_values = record.split(',')correct_number = int(all_values[0])print("該圖片對應的數字為:",correct_number)#預處理數字圖片inputs = (numpy.asfarray(all_values[1:])) / 255.0 * 0.99 + 0.01#讓網絡判斷圖片對應的數字outputs = n.query(inputs)#找到數值最大的神經元對應的編號label = numpy.argmax(outputs)print("網絡認為圖片的數字是:", label)if label == correct_number:scores.append(1)else:scores.append(0)
print(scores)#計算圖片判斷的成功率
scores_array = numpy.asarray(scores)
print("perfermance = ", scores_array.sum() / scores_array.size)
實現結果:
可以發現有部分數據預測錯誤有部分數據預測正確,正確率在60%