19神經網路實現手寫識別
神經網路實現手寫識別
任務介紹
手寫數字識別是一個多分類問題,共有10個分類,每個手寫數字影象的類別標籤是0~9中的其中一個數。例如下面這三張圖片的標籤分別是0,1,2。
任務:利用sklearn來訓練一個簡單的全連線神經網路,即多層感知機(Multilayer perceptron,MLP)用於識別資料集DBRHD的手寫數字。
MLP的輸入
- DBRHD資料集的每個圖片是一個由0或1組成的32 X 32的文字矩陣。
- 多層感知機的輸入為圖片矩陣展開的1 X 1024個神經元。
由於資料集的標籤數字只能作為類別判別而不具有數值大小作用,即類別之間並無優先高低之分。
MLP的輸出
MLP輸出:“one-hot vectors“
- 一個one-hot向量除了某一位的數字是1以外其餘各維度數字都是0。
- 圖片標籤將表示成一個只有在第n維度(從0開始)數字為1的10維向量。比如,標籤0將表示成[1,0,0,0,0,0,0,0,0,0]。即,MLP輸出層具有10個神經元。
MLP結構
-
MLP的輸入與輸出層,中間隱藏層的層數和神經元的個數設定都將影響該MLP模型的準確率。
-
在本例項中,我們只設置一層隱藏層,在後續實驗中比較該隱藏層神經元個數為50、100、200時的MLP效果。
MLP手寫識別例項構建
實現步驟:
- 步驟1:建立工程並導人sklearn包
- 步驟2:載入訓練資料
- 步驟3:訓練神經網路
- 步驟4:測試集評價
具體步驟
步驟1:建立工程並匯入sklean包
(1)建立MLP.py檔案
(2)在MLP.py檔案中匯入sklearn相關包
步驟2:載入訓練資料
(1)定義img2vector函式,將載入的32*32的圖片矩陣展開成一列向量。
(2)定義載入訓練資料的函式readDataSet,並將樣本標籤轉化為one-hot向量。
(3)呼叫readDataSet和img2vector函式載入資料,將訓練的圖片存放在train_dataSet中,對應的標籤則存在train_hwLabels中。
步驟3:訓練神經網路
(1)構建神經網路:設定網路的隱藏層數、各隱藏層神經元個數、啟用函式、學習率、優化方法、最大迭代次數。
- 設定含100個神經元的隱藏層。
- hidden layer sizes存放的是一個元組,表示第i層隱藏層裡神經元的個數。
- 使用logistic啟用函式和adam優化方法,並令初始學習率為0.0001,迭代2000次。
(2)構建神經網路:設定網路的隱藏層數、各隱藏層神經元個數、啟用函式、學習率、優化方法、最大迭代次數。
(3)使用訓練資料訓練構建好的神經網路。
- fit函式能夠根據訓練集及對應標籤集自動設定多層感知機的輸入與輸出層的神經元個數。
- 例:train_dataSet為n X 1024的矩陣,train hwLabels為n X 10的矩陣,則fit函式將MLP的輸入層神經元個數設為1024,輸出層神經元個數為10。
步驟4:測試集評價
(1)載入測試集。
(2)使用訓練好的MLP對測試集進行預測,並計算錯誤率。
具體程式碼
import numpy as np # 匯入numpy工具包
from os import listdir # 使用listdir模組,用於訪問本地檔案
from sklearn.neural_network import MLPClassifier
def img2vector(fileName):
""" 將載入的32*32的圖片矩陣展開成一列向量"""
retMat = np.zeros([1024], int) # 定義返回的矩陣,大小為1*1024
fr = open(fileName) # 開啟包含32*32大小的數字檔案
lines = fr.readlines() # 讀取檔案的所有行
for i in range(32): # 遍歷檔案所有行
for j in range(32): # 並將01數字存放在retMat中
retMat[i * 32 + j] = lines[i][j]
return retMat
def readDataSet(path):
"""載入訓練資料,並將樣本標籤轉化為one-hot向量"""
fileList = listdir(path) # 獲取資料夾下的所有檔案
numFiles = len(fileList) # 統計需要讀取的檔案的數目
dataSet = np.zeros([numFiles, 1024], int) # 用於存放所有的數字檔案
hwLabels = np.zeros([numFiles, 10]) # 用於存放對應的one-hot標籤
for i in range(numFiles): # 遍歷所有的檔案
filePath = fileList[i] # 獲取檔名稱/路徑
digit = int(filePath.split('_')[0]) # 通過檔名獲取標籤,注意型別轉換
hwLabels[i][digit] = 1.0 # 將對應的one-hot標籤置1
dataSet[i] = img2vector(path + '/' + filePath) # 讀取檔案內容
return dataSet, hwLabels
# 讀取訓練集
train_dataSet, train_hwLabels = readDataSet('digits/trainingDigits')
'''
構建神經網路:設定網路的隱藏層數、各隱藏層神經元個數、啟用函式、學習率、優化方法、最大迭代次數。
設定含100個神經元的隱藏層,hidden_layer_sizes 存放的是一個元組,表示第i層隱藏層裡神經元的個數
使用logistic啟用函式和adam優化方法,並令初始學習率為0.0001,
後期改動數值,以及使用logistic啟用函式和sgd優化方法
'''
clf = MLPClassifier(hidden_layer_sizes=(100,),
activation='logistic', solver='adam',
learning_rate_init=0.0001, max_iter=2000)
print(clf)
'''
fit函式能夠根據訓練集及對應標籤集自動設定多層感知機的輸入與輸出層的神經元個數
例如train_dataSet為n*1024的矩陣,train_hwLabels為n*10的矩陣,則fit函式將MLP的輸入層神經元個數設為1024,輸出層神經元個數為10
'''
clf.fit(train_dataSet, train_hwLabels)
# 讀取測試集
dataSet, hwLabels = readDataSet('digits/testDigits')
res = clf.predict(dataSet) # 對測試集進行預測
error_num = 0 # 統計預測錯誤的數目
num = len(dataSet) # 測試集的數目
for i in range(num): # 遍歷預測結果
# 比較長度為10的陣列,返回包含01的陣列,0為不同,1為相同
# 若預測結果與真實結果相同,則10個數字全為1,否則不全為1
if np.sum(res[i] == hwLabels[i]) < 10:
error_num += 1
print("Total num:", num, " Wrong num:", \
error_num, " TureRate:", 1-(error_num / float(num)))
實驗效果
實驗輸出結果:
MLPClassifier(activation='logistic', learning_rate_init=0.0001, max_iter=2000)
Total num: 946 Wrong num: 42 TureRate: 0.9556025369978858
以下是改動各個引數,各隱藏層神經元個數,最大迭代次數,學習率的實驗結果:
隱藏層神經元個數影響
執行隱藏層神經元個數為50、100、200的多層感知機,對比實驗效果。
執行隱藏層神經元個數為50:
MLPClassifier(activation='logistic', hidden_layer_sizes=(50,),
learning_rate_init=0.0001, max_iter=2000)
Total num: 946 Wrong num: 43 TureRate: 0.9545454545454546
執行隱藏層神經元個數為100:
MLPClassifier(activation='logistic', learning_rate_init=0.0001, max_iter=2000)
Total num: 946 Wrong num: 42 TureRate: 0.9556025369978858
執行隱藏層神經元個數為200:
MLPClassifier(activation='logistic', hidden_layer_sizes=(200,),
learning_rate_init=0.0001, max_iter=2000)
Total num: 946 Wrong num: 37 TureRate: 0.9608879492600423
- 隨著隱藏層神經元個數的增加,MLP的正確率持上升趨勢。
- 大量的隱藏層神經元帶來的計算負擔與對結果的提升並不對等,因此,如何選取合適的隱藏神經元個數是一個值得探討的問題。
迭代次數影響分析:
設隱藏層神經元個數為100,初始學習率為0.0001,最大迭代次數分別為500、1000、1500、2000。
最大迭代次數分別為500:
MLPClassifier(activation='logistic', learning_rate_init=0.0001, max_iter=500)
Total num: 946 Wrong num: 54 TureRate: 0.9429175475687104
最大迭代次數分別為1000:
MLPClassifier(activation='logistic', learning_rate_init=0.0001, max_iter=1000)
Total num: 946 Wrong num: 40 TureRate: 0.9577167019027484
最大迭代次數分別為1500:
MLPClassifier(activation='logistic', learning_rate_init=0.0001, max_iter=1500)
Total num: 946 Wrong num: 41 TureRate: 0.9566596194503171
最大迭代次數分別為2000:
MLPClassifier(activation='logistic', learning_rate_init=0.0001, max_iter=2000)
Total num: 946 Wrong num: 33 TureRate: 0.9651162790697675
- 過小的迭代次數可能使得MLP早停,造成較低的正確率。
- 當最大迭代次數>1000時,正確率基本保持不變,這說明MLP在第1000迭代時已收斂,剩餘的迭代次數不再進行。
- —般設定較大的最大迭代次數來保證多層感知機能夠收斂,達到較高的正確率。
學習率影響分析:
改用隨機梯度下降優化演算法即將MLPclassifer的引數( solver='sgd',),設隱藏層神經元個數為100,最大迭代次數為2000,學習率分別為:0.1、0.01、0.001、0.0001。
學習率為0.1:
MLPClassifier(activation='logistic', learning_rate_init=0.1, max_iter=2000,solver='sgd')
Total num: 946 Wrong num: 33 TureRate: 0.9651162790697675
學習率為0.01:
MLPClassifier(activation='logistic', learning_rate_init=0.01, max_iter=2000,solver='sgd')
Total num: 946 Wrong num: 39 TureRate: 0.9587737843551797
學習率為0.001:
MLPClassifier(activation='logistic', max_iter=2000, solver='sgd')
Total num: 946 Wrong num: 47 TureRate: 0.9503171247357294
學習率為0.0001:
MLPClassifier(activation='logistic', learning_rate_init=0.0001, max_iter=2000,solver='sgd')
Total num: 946 Wrong num: 242 TureRate: 0.7441860465116279
結論:較小的學習率帶來了更低的正確率,這是因為較小學習率無法在2000次迭代內完成收斂,而步長較大的學習率使得MLP在2000次迭代內快速收斂到最優解。因此,較小的學習率一般要配備較大的迭代次數以保證其收斂。
最後的思考
這次的程式碼比較簡單,就是需要花時間理解。
這次的程式需要匯入資料集digits.rar放在檔案目錄下。
在看懂了程式碼之後,自己修改運行了一下,發現得到的資料和視訊教學裡得到的資料還是有差距的,絕知此事要躬行啊。
這兩天在轉團關係,累累的。