1. 程式人生 > 其它 >20KNN實現手寫識別

20KNN實現手寫識別

KNN實現手寫識別



任務介紹

  • 本例項利用sklearn來訓練一個K最近鄰(k-NearestNeighbor, KNN)分類器,用於識別資料集DBRHD的手寫數字。
  • 比較KNN的識別效果與多層感知機的識別效果。

KNN的輸人

  • DBRHD資料集的每個圖片是一個由0或1組成的32*32的文字矩陣;

  • KNN的輸入為圖片矩陣展開的一個1024維的向量。



KNN實現手寫識別

實驗步驟:

  • 步驟1:建立工程並導人sklearn包

  • 步驟2:載入訓練資料

  • 步驟3:構建KNN分類器

  • 步驟4:測試集評價


具體步驟

步驟1:建立工程並匯入sklearn包

(1)建立sklearnKNN.py檔案

(2)在sklearnKNN.py檔案中導人sklearn相關包

步驟2:載入訓練資料

(1)在sklearnKNN.py檔案中,定義img2vector函式,將載入的32*32的圖片矩陣展開成一列向量。

(2)在sklearnKNN.py檔案中定義載入訓練資料的函式readDataSet。

(3)在sklearnKNN.py檔案中,呼叫read DataSet和img2vector函式載入資料,將訓練的圖片存放在train_dataSet中,對應的標籤則存在train_hwLabels中。

步驟3:構建KNN分類器

在sklearnKNN.py檔案中,構建KNN分類器:設定查詢演算法以及鄰居點數量(k)值。

  • KNN是一種懶惰學習法,沒有學習過程,只在預測時去查詢最近鄰的點,資料集的輸入就是構建KNN分類器的過程。
  • 構建KNN時我們同時呼叫了fit()函式。

步驟4:測試集評價

(1)載入測試集

(2)使用構建好的KNN分類器對測試集進行預測,並計算預測的錯誤率


具體程式碼

import numpy as np  # 匯入numpy工具包
from os import listdir  # 使用listdir模組,用於訪問本地檔案
from sklearn import neighbors


def img2vector(fileName):
    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):
    fileList = listdir(path)  # 獲取資料夾下的所有檔案
    numFiles = len(fileList)  # 統計需要讀取的檔案的數目
    dataSet = np.zeros([numFiles, 1024], int)  # 用於存放所有的數字檔案
    hwLabels = np.zeros([numFiles])  # 用於存放對應的標籤(與神經網路的不同)
    for i in range(numFiles):  # 遍歷所有的檔案
        filePath = fileList[i]  # 獲取檔名稱/路徑
        digit = int(filePath.split('_')[0])  # 通過檔名獲取標籤
        hwLabels[i] = digit  # 直接存放數字,並非one-hot向量
        dataSet[i] = img2vector(path + '/' + filePath)  # 讀取檔案內容
    return dataSet, hwLabels


# read dataSet
train_dataSet, train_hwLabels = readDataSet('digits/trainingDigits')
knn = neighbors.KNeighborsClassifier(algorithm='kd_tree', n_neighbors=3)
knn.fit(train_dataSet, train_hwLabels)

# read  testing dataSet
dataSet, hwLabels = readDataSet('digits/testDigits')

res = knn.predict(dataSet)  # 對測試集進行預測
error_num = np.sum(res != hwLabels)  # 統計分類錯誤的數目
num = len(dataSet)  # 測試集的數目
print("Total num:", num, " Wrong num:", \
      error_num, "  TrueRate:", 1-(error_num / float(num)))



實驗效果

鄰居數量K影響分析:設定K為1、3、5、7的KNN分類器,對比他們的實驗效果。

設定K為1的KNN分類器:

Total num: 946  Wrong num: 13   TrueRate: 0.9862579281183932

設定K為3的KNN分類器:

Total num: 946  Wrong num: 12   TrueRate: 0.9873150105708245

設定K為5的KNN分類器:

Total num: 946  Wrong num: 19   TrueRate: 0.9799154334038055

設定K為7的KNN分類器:

Total num: 946  Wrong num: 22   TrueRate: 0.9767441860465116

結論:

K=3時正確率最高,當K>3時正確率開始下降,這是由於當樣本為稀疏資料集時(本例項只有946個樣本),其第k個鄰居點可能與測試點距離較遠,因此投出了錯誤的一票進而影響了最終預測結果。



對比實驗

KNN分類器 vs. MLP多層感知機:

我們取在上節對不同的隱藏層神經元個數、最大迭代次數、學習率進行的各個對比實驗中準確率最高(H)與最差(L)的MLP分類器來進行對比。其各個MLP的引數設定如下:

MLP代號 隱藏層神經元個數 最大迭代次數 優化方法 初始學習率/學習率
MLP-YH 200 2000 adam 0.0001
MLP-YL 50 2000 adam 0.0001
MLP-DH 100 2000 adam 0.0001
MLP-DL 100 500 adam 0.0001
MLP-XH 100 2000 sgd 0.1
MLP-XL 100 2000 sgd 0.0001

將效果最好的KNN分類器(K=3)和效果最差的KNN分類器(K=7)與各個MLP分類器作對比如下:

(MLP的資料要去看前一個實驗,以真實資料為主)

分類器 MLP隱藏層神經元個數(MLP-Y) MLP迭代次數(MLP-D) MLP學習率(MLP-X) KNN鄰居數量
最好 錯誤量 37 33 33 12
最好 正確率 0.9608 0.9651 0.9651 0.9873
最差 錯誤量 43 54 242 22
最差 正確率 0.9545 0.9429 0.7441 0.9767

結論:

  • KNN的準確率遠高於MLP分類器,這是由於MLP在小資料集上容易過擬合的原因。
  • MLP對於引數的調整比較敏感,若引數設定不合理,容易得到較差的分類效果,因此引數的設定對於MLP至關重要。



最後的思考

這次實驗的原理KNN,K近鄰演算法,在12基本分類模型中,可以複習一下。

這次的程式需要匯入資料集digits.rar放在檔案目錄下。

程式碼不是很難,也寫上了標註。和原視訊中不一樣的是,改動了最後輸出正確率,輸出結果和視訊給出的有一些小差異。

為了實驗的對比,設定不同的K值為1、3、5、7,需要改動的是KNeighborsClassifier()中n_neighbors的值。

還有,因為要和MLP多層感知機進行對比,所以做了個表,去上一個實驗中找資料。

最後得出的結論是KNN的準確率遠高於MLP分類器。

天氣好冷,想吃火鍋,想吃小蛋糕。