機器學習(八)-基於KNN分類算法的手寫識別系統

1 項目介紹

基於k-近鄰分類器(KNN)的手寫識別系統, 這裏構造的系統只能識別數字0到9。python

數據集和項目源代碼git

  • 難點: 圖形信息如何處理?

圖像轉換爲文本格式github

2 準備數據

將圖像轉換爲測試向量算法

  • 訓練集:app

    • 目錄trainingDigits
    • 大約2000個例子
    • 每一個數字大約有200個樣本;
  • 測試集測試

    • 目錄testDigits
    • 大約900個測試數據。

將圖像格式化處理爲一個向量。咱們將把一個32×32的二進制圖像矩陣轉換爲1×1024的向量, 以下圖所示,
在這裏插入圖片描述優化

import numpy as np
def img2vector(filename):
    """
    # 將圖像數據轉換爲(1,1024)向量
    :param filename: 
    :return: (1,1024)向量
    """
    # 生成一個1*1024且值全爲0的向量;
    returnVect = np.zeros((1, 1024))
    # 讀取要轉換的信息;
    file = open(filename)
    # 依次填充
    # 讀取每一行數據;
    for i in range(32):
        lineStr = file.readline()
        # 讀取每一列數據;
        for j in range(32):
            returnVect[0, 32 * i + j] = int(lineStr[j])
    return returnVect

3 實施 KNN 算法

對未知類別屬性的數據集中的每一個點依次執行如下操做, 與上一個案例代碼相同:
(1) 計算已知類別數據集中的點與當前點之間的距離;
(2) 按照距離遞增次序排序;
(3) 選取與當前點距離最小的k個點;
(4) 肯定前k個點所在類別的出現頻率;
(5) 返回前k個點出現頻率最高的類別做爲當前點的預測分類。spa

def classify(inX, dataSet, labels, k):
    """
    :param inX: 要預測的數據
    :param dataSet: 咱們要傳入的已知數據集
    :param labels:  咱們要傳入的標籤
    :param k: KNN裏的k, 也就是說咱們要選幾個近鄰
    :return: 排序的結果
    """
    dataSetSize = dataSet.shape[0]  # (6,2) 6
    # tile會重複inX, 把他重複成(datasetsize, 1)型的矩陣
    # print(inX)
    # (x1 - y1), (x2- y2)
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
    # 平方
    sqDiffMat = diffMat ** 2
    # 相加, axis=1 行相加
    sqDistance = sqDiffMat.sum(axis=1)
    # 開根號
    distances = sqDistance ** 0.5
    # print(distances)
    # 排序 輸出的是序列號index,並非值
    sortedDistIndicies = distances.argsort()
    # print(sortedDistIndicies)

    classCount = {}
    for i in range(k):
        voteLabel = labels[sortedDistIndicies[i]]
        classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
        # print(classCount)
    sortedClassCount = sorted(classCount.items(), key=lambda d: float(d[1]), reverse=True)
    return sortedClassCount[0]

4 測試算法

使用 k-近鄰算法識別手寫數字code

  • 測試集裏面的信息;

在這裏插入圖片描述

def handWritingClassTest(k):
    """
    # 測試手寫數字識別錯誤率的代碼
    :param k:
    :return:
    """
    hwLabels = []
    import os
    # 讀取全部的訓練集文件;
    trainingFileList = os.listdir('data/knn-digits/trainingDigits')
    # 獲取訓練集個數;
    m = len(trainingFileList)
    # 生成m行1024列全爲0的矩陣;
    trainingMat = np.zeros((m, 1024))
    # 填充訓練集矩陣;
    for i in range(m):
        fileNameStr = trainingFileList[i]    # fileNameStr: 0_0.txt
        fileStr = fileNameStr.split('.')[0]  # fileStr: 0_0
        classNumStr = int(fileStr.split('_')[0])    # (數字分類的結果)classNumStr: 0
        # 填寫真實的數字結果;
        hwLabels.append(classNumStr)
        # 圖形的數據: (1,1024)向量
        trainingMat[i, :] = img2vector("data/knn-digits/trainingDigits/%s" % fileNameStr)

    # 填充測試集矩陣;
    testFileList = os.listdir('data/knn-digits/testDigits')
    # 默認錯誤率爲0;
    errorCount = 0.0
    # 測試集的總數;
    mTest = len(testFileList)
    # 填充測試集矩陣;
    for i in range(mTest):
        fileNameStr = testFileList[i]
        fileStr = fileNameStr.split('.')[0]
        classNumStr = int(fileStr.split('_')[0])
        vectorTest = img2vector("data/knn-digits/testDigits/%s" % fileNameStr)

        # 判斷預測結果與真實結果是否一致?
        result = classify(vectorTest, trainingMat, hwLabels, k)

        if result != classNumStr:
            # 若是不一致,則統計出來, 計算錯誤率;
            errorCount += 1.0
            print("[預測失誤]:分類結果是:%d, 真實結果是:%d" % (result, classNumStr))
    print("錯誤總數:%d" % errorCount)
    print("錯誤率:%f" % (errorCount / mTest))
    print("模型準確率:%f" %(1-errorCount / mTest))
    return errorCount


print(handWritingClassTest(2))
  • 效果展現

在這裏插入圖片描述

5 KNN算法手寫識別的缺點

算法的執行效率並不高。排序

  • 每一個測試向量作2000次距離計算,每一個距離計算包括了1024個維度浮點運算,總計要執行900次;
  • 須要爲測試向量準備2MB的存儲空間

有沒有更好的方法?

  • k決策樹就是k-近鄰算法的優化版,能夠節省大量的計算開銷。
相關文章
相關標籤/搜索