neural network神經網絡識別手寫字體

(持續更新中…)

第一部分:神經網絡

目的:實現字體識別,比如下圖

在這裏插入圖片描述
需要讓電腦識別出是數字7.

想要知道上面如何操作,先簡單講解一下神經網絡
在這裏插入圖片描述
上圖展示了神經網絡的框架,有一個輸入層(input layer),一個隱藏層(hidden layer)和一個輸出層(output layer),每個層都有兩個節點(nodes). 數據X\mathbf{X}經過輸入層之後,與連接權W\mathbf{W}點乘(矩陣相乘)作爲隱藏層輸入,即Ihidden_input=WXI_{hidden\_input}=WX,輸入之後經過激活函數(activation function)作爲隱藏層的輸出,即Ohidden_output=sigmoid(WX)O_{hidden\_output}=sigmoid(WX),這裏選擇了sigmoid函數作爲激活函數。接着又將此輸出與隱藏層和輸出層之間的連接權點乘作爲輸出層的輸入,最後再將此輸入放入激活函數中作爲最後的output.至此一個簡單的含有一個隱藏層的神經網絡結束。
總結起來就是
在這裏插入圖片描述
上圖顯示的是三個層都是三個節點的情況。OkO_k表示最後的輸出。下圖表示對0-9的數字的識別。輸出層是0-9共10個節點。比如我們的真實值是5,那麼在十個輸出節點中,在標籤爲5的節點得到最大的概率,表示識別正確,而9的例子表示有兩個比較大的輸出,但我們通過四捨五入或者直接通過np.argmax()提取最大數的索引值,因此我們只需要關注最大的值即可。
在這裏插入圖片描述

需要注意以下幾點:

1)因爲輸出選擇的激活函數依然是sigmoid函數,如下圖
在這裏插入圖片描述
由圖可知道,sigmoid函數的輸出在(0,1)之間,因此如果WX(圖中的x=WXx=\text{WX})比較大,即輸入或者連接權比較大的話,那麼這會使得網絡飽和(或說使得激活函數飽和)。因爲當|x|很大的時候,函數變化平緩,梯度基本不變,這樣對於後續訓練改變權重非常不利。也正因爲輸出在(0,1)之間,不包括0和1,因此訓練的目標值(即預期值)也設置在0.010.990.01-0.99之間。同樣,輸入也將通過數學方法–縮放和位移(如除法和移動)改變使得在(0,1)之間(可以等於1,不能等於0)(常見的輸入範圍設置爲0.010.990.01-0.99或者-1.0到1.0之間)。
2)輸入不能爲零,權重也不能爲零。如果權重爲零,當進行BP反饋的時候權重將無法得到學習,對網絡達不到訓練的目的。
3)學習率也在(0,1)之間,可以取1.從下面的權重迭代更新公式可以看出,實際上學習率就是步長的一種顯示形式,如果學習率很大,那麼勢必會在使用梯度下降方法中造成訓練結果誤差大的局勢。
在這裏插入圖片描述
4)由於最後的誤差E實際上是參數權重W的函數,所以觀察如何改變權重是的誤差最小是我們的最終目的
在這裏插入圖片描述
最後,代碼如下,並做詳細分析

第二部分:程序實現

數據集來自—MNIST數據

import numpy as np
# scipy.special for the sigmoid function expit()
import scipy.special   #導入這個是爲了導入sigmoid函數,當然這個函數很簡單,也可以自己手動定義
import matplotlib.pyplot as plt
import time   #爲了輸出這個代碼跑了多長時間
start_time = time.time()
class neuralNetwork:
    def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
        #set number of nodes in each input, hidden, output layer
        #設置節點數
        self.inodes = inputnodes
        self.hnodes = hiddennodes
        self.onodes = outputnodes
        
        #link weight matrices, wih (w_input_hidden) and who (w_hidden_output)
        #隨機初始化權重,當然權重在0到1之間(不含)。這裏通過高斯函數產生權重
        #其中均值是零,方差是1/sqrt(連接數),當然連接數等於節點數。後一個是矩陣hnodes行,inodes列。
        self.wih = np.random.normal(0.0, pow(self.hnodes, -0.5), (self.hnodes,self.inodes))
        self.who = np.random.normal(0.0, pow(self.onodes, -0.5), (self.onodes,self.hnodes))
        # learning rate
        self.lr = learningrate
        # activation function is the sigmoid function
        self.activation_function = lambda x:scipy.special.expit(x)
    def train(self,inputs_list, targets_list):
        inputs = np.array(inputs_list, ndmin=2).T
        targets = np.array(targets_list, ndmin=2).T  #設置目標值
        #隱層輸入與輸出
        hidden_inputs = np.dot(self.wih, inputs)
        hidden_outputs = self.activation_function(hidden_inputs)
        #輸出
        final_inputs = np.dot(self.who,hidden_outputs)
        final_outputs = self.activation_function(final_inputs)
        #誤差
        output_errors = targets - final_outputs
        hidden_errors = np.dot(self.who.T, output_errors)
        #權重更新
        self.who += self.lr*np.dot((output_errors*final_outputs*(1.0 - final_outputs)),np.transpose(hidden_outputs))
        self.wih += self.lr*np.dot((hidden_errors*hidden_outputs*(1.0-hidden_outputs)),np.transpose(inputs))
        pass
    
    def query(self, inputs_list):
        # convert inputs list to 2d array
        inputs = np.array(inputs_list, ndmin=2).T
        
        hidden_inputs = np.dot(self.wih, inputs)
        hidden_outputs = self.activation_function(hidden_inputs)
        
        final_inputs = np.dot(self.who, hidden_outputs)
        final_outputs = self.activation_function(final_inputs)
        return final_outputs
        
 #設置節點數,隱藏層爲100,但實際上是不知道隱藏該放多少的,最好的辦法就是試驗,一次次試驗直到想要的
 #輸入節點是因爲訓練數據是28*28=784個像素的圖,輸出因爲是數字,從0-9共10個。
input_nodes = 784
hidden_nodes = 100
output_nodes = 10

learning_rate = 0.3

#create instance of neural network 實例
n = neuralNetwork(input_nodes,hidden_nodes,output_nodes,learning_rate)
#導入訓練數據
training_data_file = open('C:/Users/Lenovo/Desktop/mnist_train_100.csv','r')
training_data_list = training_data_file.readlines()
training_data_file.close()
#進行數據訓練
for record in training_data_list:
    all_values = record.split(',')
    #輸入數據數學處理,使其在0.01到1之間.顏色的範圍是[0,255]
    inputs = (np.asfarray(all_values[1:])/255.0*0.99)+0.01
    #初始化目標值,使其在0.01到0.99之間
    targets = np.zeros(output_nodes) + 0.01
    targets[int(record[0])] = 0.99
    n.train(inputs,targets)
    pass
#對數據測試
if __name__ == '__main__':
    print('running...')
    #導入測試數據,可以嘗試np.loadtxt
    test_data_file = open('C:/Users/Lenovo/Desktop/mnist_test_10.csv','r')
    test_data_list = test_data_file.readlines()
    test_data_file.close()
    #通過逗號分割數據
    all_values = test_data_list[0].split(',')
    correct_label = int(all_values[0])
    print('real value ==>>: ',correct_label)
    image_array = np.asfarray(all_values[1:]).reshape((28,28))
    plt.imshow(image_array,cmap='Greys')
    inputs = (np.asfarray(all_values[1:])/255.0*0.99) + 0.01
    outputs = n.query(inputs)
    label = np.argmax(outputs)  #argmax返回最大值的索引值
    print("network's answer ==>> ",label)
    end_time = time.time()
    print('running time',end_time-start_time)

1)一個測試數據

上面if _name_==…後是一個測試數據的情況
上面進行數據訓練,當完成第一個數據的訓練後會更新開始隨機初始化的權重,於是接着開始第二個數據輸入,再更新權重,第三個數據輸入…這樣權重被一次次更新。在最後一個數據訓練結束之後,權重更新完畢。接着使用更新後的權重來進行數據的測試。
最後的結果爲:
在這裏插入圖片描述
可見真實值是7,網絡判斷的值也是7.

2)多個測試數據

上面是進行的一個測試數據,如果有多個測試數據,可以將後面的部分改爲:

if __name__ == '__main__':
    start_time = time.time()
    print('running...')
    test_data_file = open('C:/Users/Lenovo/Desktop/mnist_test_10.csv','r')
    test_data_list = test_data_file.readlines()
    test_data_file.close()
    scorecard = []
    for record in test_data_list:
        all_values = record.split(',')
        correct_label = int(all_values[0])
        print('real value ==>>: ',all_values[0])
#        image_array = np.asfarray(all_values[1:]).reshape((28,28))
#        plt.imshow(image_array,cmap='Greys')
        inputs = (np.asfarray(all_values[1:])/255.0*0.99) + 0.01
        outputs = n.query(inputs)
        label = np.argmax(outputs)  #argmax返回最大值的索引值
        print("network's answer ==>> ",label)
        if (label == correct_label):
            scorecard.append(1)
        else:
            scorecard.append(0)
    print('\n','########  scorecard  #########')
    print(scorecard)
    scorecard_array = np.asarray(scorecard)
    print('performance ==>> ',scorecard_array.sum()/scorecard_array.size)
    end_time = time.time()
    print('running time',end_time-start_time)

在這裏插入圖片描述
輸出結果表示,有6個結果正確(1表示對的,0表示錯),四個分類錯誤,正確率60%。
上面使用的訓練數據是100個,所以導致準確率並不高,於是改用60000個訓練數據,結果得到的準確率到0.9

嘗試改進

1)調整學習率

上面的學習率是我們隨意設置的0.3,在經過多次試驗後得到的準確性和學習率的關係爲
在這裏插入圖片描述
學習率太大,即步長太大是不利的。

2)多次運行
上面的訓練數據只訓練了一次,稱爲一個epoch (世代),現在下面的代碼表示多次訓練的過程

#進行數據訓練
epochs = 3  #A
for e in range(epochs):   #B
    for record in training_data_list:
        all_values = record.split(',')
        inputs = (np.asfarray(all_values[1:])/255.0*0.99)+0.01
        targets = np.zeros(output_nodes) + 0.01
        targets[int(record[0])] = 0.99
        n.train(inputs,targets)
        pass

這裏只需要改正訓練數據部分即可,相當於在前代碼中只增加了A,B部分,這樣會得到更好的結果。但是並不是epochs越大越好,會出現過擬合,準確率和epochs關係如下:
在這裏插入圖片描述
另外,當我們把epochs調大之後,還可以通過降低學習率來調得更好的結果。
3)改變網絡結構
隱藏層可以增加節點,也可以增加層數。上面的代碼默認是100個節點,一個層數。下面的結果表示節點與性能的關係
在這裏插入圖片描述
比如我改到5個節點,那麼準確率可能只有30%
最後我將上面涉及的代碼附上:與上面不同之處在於增加了epochs

import numpy as np
# scipy.special for the sigmoid function expit()
import scipy.special
import matplotlib.pyplot as plt
import time

start_time = time.time()

class neuralNetwork:
    def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
        #set number of nodes in each input, hidden, output layer
        self.inodes = inputnodes
        self.hnodes = hiddennodes
        self.onodes = outputnodes
        
        #link weight matrices, wih (w_input_hidden) and who (w_hidden_output)
        self.wih = np.random.normal(0.0, pow(self.hnodes, -0.5), (self.hnodes,self.inodes))
        self.who = np.random.normal(0.0, pow(self.onodes, -0.5), (self.onodes,self.hnodes))
        # learning rate
        self.lr = learningrate
        # activation function is the sigmoid function
        self.activation_function = lambda x:scipy.special.expit(x)
    def train(self,inputs_list, targets_list):
        inputs = np.array(inputs_list, ndmin=2).T
        targets = np.array(targets_list, ndmin=2).T
        
        hidden_inputs = np.dot(self.wih, inputs)
        hidden_outputs = self.activation_function(hidden_inputs)
        
        final_inputs = np.dot(self.who,hidden_outputs)
        final_outputs = self.activation_function(final_inputs)
        
        output_errors = targets - final_outputs
        hidden_errors = np.dot(self.who.T, output_errors)
        
        self.who += self.lr*np.dot((output_errors*final_outputs*(1.0 - final_outputs)),np.transpose(hidden_outputs))
        self.wih += self.lr*np.dot((hidden_errors*hidden_outputs*(1.0-hidden_outputs)),np.transpose(inputs))
        pass
    
    def query(self, inputs_list):
        # convert inputs list to 2d array
        inputs = np.array(inputs_list, ndmin=2).T
        
        hidden_inputs = np.dot(self.wih, inputs)
        hidden_outputs = self.activation_function(hidden_inputs)
        
        final_inputs = np.dot(self.who, hidden_outputs)
        final_outputs = self.activation_function(final_inputs)
        return final_outputs
        
    
input_nodes = 784
hidden_nodes = 200
output_nodes = 10

learning_rate = 0.3

#create instance of neural network
n = neuralNetwork(input_nodes,hidden_nodes,output_nodes,learning_rate)

training_data_file = open('C:/Users/Lenovo/Desktop/mnist_train_100.csv','r')
training_data_list = training_data_file.readlines()
training_data_file.close()

epochs = 3
for e in range(epochs):
    for record in training_data_list:
        all_values = record.split(',')
        inputs = (np.asfarray(all_values[1:])/255.0*0.99)+0.01
        targets = np.zeros(output_nodes) + 0.01
        targets[int(record[0])] = 0.99
        n.train(inputs,targets)
        pass

if __name__ == '__main__':
    print('running...')
    test_data_file = open('C:/Users/Lenovo/Desktop/mnist_test_10.csv','r')
    test_data_list = test_data_file.readlines()
    test_data_file.close()
    scorecard = []
    for record in test_data_list:
        all_values = record.split(',')
        correct_label = int(all_values[0])
        print('real value ==>>: ',all_values[0])
#        image_array = np.asfarray(all_values[1:]).reshape((28,28))
#        plt.imshow(image_array,cmap='Greys')
        inputs = (np.asfarray(all_values[1:])/255.0*0.99) + 0.01
        outputs = n.query(inputs)
        label = np.argmax(outputs)  #argmax返回最大值的索引值
        print("network's answer ==>> ",label)
        if (label == correct_label):
            scorecard.append(1)
        else:
            scorecard.append(0)
    print('\n','########  scorecard  #########')
    print(scorecard)
    scorecard_array = np.asarray(scorecard)
    print('performance ==>> ',scorecard_array.sum()/scorecard_array.size)
    end_time = time.time()
    print('running time',end_time-start_time)

第三部分:手寫數字識別

自己手寫一個數字,比如下面的數字8
在這裏插入圖片描述
在A4紙上寫完拍照另存爲8.png/8.jpg都可以;然後使用windows自帶的畫圖將圖片切成2828像素的正方形(因爲我們在上面的訓練數據中或者MNIST數據集上找到的是2828的圖片)。
然後需要經過一些處理:
使用scipy.misc將圖片轉換成像素大小(在高版本1.2.0的scipy中已經將misc去掉,換成了imageio.imread)
所以上面的if __name…部分變成了如下代碼

#在文件頭添加導入
import scipy.misc
#glob.glob對於批量導入文件非常有用,此處只有一張圖片,故不用此函數
#import glob
import imageio
'''
...
'''
if __name__ == '__main__':
    start_time = time.time()
    print('running...')
    #讀取文件目錄
    image_file_name = (r'C:/Users/Lenovo/Desktop/8.png')
    #將圖片變成像素數組
    img_array = scipy.misc.imread(image_file_name, flatten = True)
    #加載圖片提示,如果圖片比較多很實用
    #print('loading ... ',image_file_name)  
    #imageio.imread與scipy.misc.imread二擇其一
#    img_array = imageio.imread(image_file_name, as_gray=True)
  #用255減是因爲通常來說,0指黑色,255指白色,而MNIST數據集使用相反的方式表達
    img_data = 255.0 - img_array.reshape(784)
    img_data = (img_data/255.0*0.99) + 0.01
    #顯示圖片
    plt.imshow(img_data.reshape((28,28)),cmap='Greys')
    inputs = img_data
    outputs = n.query(inputs)
    print(outputs)
    #找到輸出結果值
    label = np.argmax(outputs)  #argmax返回最大值的索引值
    print("network's answer ==>> ",label)
    end_time = time.time()
    print('running time',end_time-start_time)

以下爲完整代碼:

import numpy as np
# scipy.special for the sigmoid function expit()
import scipy.special
import matplotlib.pyplot as plt
import time

import scipy.misc
#import glob
import imageio

start_time = time.time()

class neuralNetwork:
    def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
        #set number of nodes in each input, hidden, output layer
        self.inodes = inputnodes
        self.hnodes = hiddennodes
        self.onodes = outputnodes
        
        #link weight matrices, wih (w_input_hidden) and who (w_hidden_output)
        self.wih = np.random.normal(0.0, pow(self.hnodes, -0.5), (self.hnodes,self.inodes))
        self.who = np.random.normal(0.0, pow(self.onodes, -0.5), (self.onodes,self.hnodes))
        # learning rate
        self.lr = learningrate
        # activation function is the sigmoid function
        self.activation_function = lambda x:scipy.special.expit(x)
    def train(self,inputs_list, targets_list):
        inputs = np.array(inputs_list, ndmin=2).T
        targets = np.array(targets_list, ndmin=2).T
        
        hidden_inputs = np.dot(self.wih, inputs)
        hidden_outputs = self.activation_function(hidden_inputs)
        
        final_inputs = np.dot(self.who,hidden_outputs)
        final_outputs = self.activation_function(final_inputs)
        
        output_errors = targets - final_outputs
        hidden_errors = np.dot(self.who.T, output_errors)
        
        self.who += self.lr*np.dot((output_errors*final_outputs*(1.0 - final_outputs)),np.transpose(hidden_outputs))
        self.wih += self.lr*np.dot((hidden_errors*hidden_outputs*(1.0-hidden_outputs)),np.transpose(inputs))
        pass
    
    def query(self, inputs_list):
        # convert inputs list to 2d array
        inputs = np.array(inputs_list, ndmin=2).T
        
        hidden_inputs = np.dot(self.wih, inputs)
        hidden_outputs = self.activation_function(hidden_inputs)
        
        final_inputs = np.dot(self.who, hidden_outputs)
        final_outputs = self.activation_function(final_inputs)
        return final_outputs
        
    
input_nodes = 784
hidden_nodes = 500
output_nodes = 10

learning_rate = 0.2

#create instance of neural network
n = neuralNetwork(input_nodes,hidden_nodes,output_nodes,learning_rate)

training_data_file = open('C:/Users/Lenovo/Desktop/mnist_train_100.csv','r')
training_data_list = training_data_file.readlines()
training_data_file.close()

epochs = 2
for e in range(epochs):
    for record in training_data_list:
        all_values = record.split(',')
        inputs = (np.asfarray(all_values[1:])/255.0*0.99)+0.01
        targets = np.zeros(output_nodes) + 0.01
        targets[int(record[0])] = 0.99
        n.train(inputs,targets)
        pass
if __name__ == '__main__':
    start_time = time.time()
    print('running...')
    image_file_name = (r'C:/Users/Lenovo/Desktop/8.png')
#    img_array = scipy.misc.imread(image_file_name, flatten = True)
    #print('loading ... ',image_file_name)
    img_array = imageio.imread(image_file_name, as_gray=True)
    img_data = 255.0 - img_array.reshape(784)
    img_data = (img_data/255.0*0.99) + 0.01
    plt.imshow(img_data.reshape((28,28)),cmap='Greys')
    inputs = img_data
    outputs = n.query(inputs)
    print(outputs)
    label = np.argmax(outputs)  #argmax返回最大值的索引值
    print("network's answer ==>> ",label)
    end_time = time.time()
    print('running time',end_time-start_time)

在將epochs設爲2,hidden_nodes設置爲500後得到識別結果
在這裏插入圖片描述
但是識別結果精度不高,因爲運行多次會一直改變。

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