KNN算法實戰之手寫數字識別

一、說明

我是在jupyter完成的,然後導出成markdown格式,ipynb文件導出爲markdown的命令如下:

jupyter nbconvert --to markdown xxx.ipynb

二、題目

  構造使用K-近鄰分類器的手寫識別系統。由於能力有限,這裏構造的系統只能識別0-9。需要識別的數字已經使用圖形處理軟件,處理成具有相同的色彩和大小:32像素*32像素的黑白圖像。
例如:
在這裏插入圖片描述
在這裏插入圖片描述

  當前使用文本格式存儲圖像,即使不能有效的利用空間,但是爲了方便理解,還是將圖像轉換成文本格式。

示例:使用k-近鄰算法的手寫識別系統

(1)收集數據:提供文本文件。

(2)處理數據:編寫img2vector()函數,將圖像格式轉換成分類器使用的向量格式。

(3)分析數據:在Python命令提示符中檢查數據,確保它符合要求。

(4)訓練算法:此步驟不適用於k-近鄰算法。

(5)測試算法:編寫函數使用提供的部分數據集作爲測試樣本,對學習算法進行測試。

(6)使用算法:本例沒有完成此步驟

三、實踐部分

3.1 準備數據:將圖像轉換爲測試向量

  trainingDigits中包含了大約2000個例子,每個數字大約有200個樣本;測試文件testDigits中包含了大約900個測試數據。兩組數據沒有重疊。爲了使用kNN算法分類器必須將一個3232的二進制矩陣轉換爲11024的向量,以便我們使用分類器處理數字圖像信息。

  首先定義img2vector()函數,將3232的二進制矩陣轉換成11024的矩陣並返回。

# 圖片 to 向量
def img2vector(filename):
    returnVect = zeros((1, 1024))
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVect[0, 32 * i + j] = int(lineStr[j])
    return returnVect

該函數使用方法

testVector = img2vector("testDigits/0_1.txt")
    print(testVector[0, 0:31])
    print(testVector[0, 32:61])

在這裏插入圖片描述



3.2 KNN算法

# kNN分類器
def classify0(inX, dataSet, labels, k):
	'''
    :param inX:
    :param dataSet:  數據集合  矩陣
    :param labels:  類別名
    :param k: K值      int
    :return:
    '''
    dataSetSize = dataSet.shape[0]  #得到數據總量
    diffMat = tile(inX,(dataSetSize,1)) - dataSet #將輸入數據擴充成與數據集同樣大小的矩陣並作差
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1) #axis = 1 參數是維度參數等於1在此處表示將一個矩陣的每一行向量相加
    distances = sqDistances** 0.5
    sortedDistancesIndicies = distances .argsort() #將列表值進行對比返回一個按照數值升序的下標值
    classCount={}
    for i in range(k):
        voteIlabel = labels[sortedDistancesIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel,0)+1
        #dict.get("key") 返回value  dict.get("key",default= None)如果能找到就返回對應的value找不到返回默認值
    sortedClassCount = sorted(classCount.items(),key = operator.itemgetter(1),reverse=True)
    #sorted 返回一個list  operator.itemgetter(x,y)表示根據x+1維度的第y+1維度
    return sortedClassCount[0][0]



3.3 測試算法:使用kNN識別手寫數字

def handwritingClassTest():
    hwLabels = []  # 訓練數據真實值數組
    trainingFileList = listdir("trainingDigits")  # 獲取trainingDigits文件子目錄的列表
    m = len(trainingFileList)  # 獲得訓練數據總數
    trainingMat = zeros((m, 1024))  # 初始化訓練數據矩陣
    for i in range(m):  # 循環將trainingDigits文件下的訓練數據文本文件放入矩陣traningMat中,真實值放入hwLabels中
        fileNameStr = trainingFileList[i]  # 獲取該次循環的文件名字符串
        fileStr = fileNameStr.split('.')[0]  # 將獲得的字符串按分隔符'.'分隔並取第一個即去拓展名的文件名
        classNumber = int(fileStr.split('_')[0])  # 獲取訓練數據的真實值 非numpy數據需要指定數據類型int
        hwLabels.append(classNumber)  # 將得到的單個真實值按順序加入到真實值列表hwLabels中
        trainingMat[i, :] = img2vector("trainingDigits/%s" % fileNameStr)  # 把32*32的二進制文本文件轉換成1*1024矩陣並按行存儲到訓練數據總矩陣中
    testFileList = listdir("testDigits")
    errorCount = 0.0  # 錯誤預測計數器
    mTest = len(testFileList)  # 測試數據總量
    for i in range(mTest):
        fileNameStr = testFileList[i]
        fileStr = fileNameStr.split('.')[0]
        classNumber = int(fileStr.split('_')[0])
        vectorUnderTest = img2vector("testDigits/%s" % fileNameStr)
        classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)  # 用kNN分類算法分類
        if (classifierResult != classNumber):  # 判斷預測是否正確,不正確計數器+1打印錯誤預測
            errorCount += 1.0
            print("預測值爲:%d ,真實值爲:%d " % (classifierResult, classNumber))
    print("測試總數:%d,預測錯誤總數:%d ,錯誤率爲:%f" % (mTest, errorCount, errorCount / float(mTest)))

在這裏插入圖片描述
在這裏插入圖片描述



3.4 截圖

在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述



四、源代碼

4.1 KNN.py

# 自定義knn分類器
def classify0(inX, dataSet, labels, k):
    '''
    :param inX:
    :param dataSet:  數據集合  矩陣
    :param labels:  類別名
    :param k: K值      int
    :return:
    '''
    dataSetSize = dataSet.shape[0]
    diffMat = tile(inX, (dataSetSize, 1)) - dataSet
    sqDiffMat = diffMat ** 2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances ** 0.5
    sortedDistIndicies = distances.argsort()
    classCount = {}
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]


# 圖片 to 向量
def img2vector(filename):
    returnVect = zeros((1, 1024))
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVect[0, 32 * i + j] = int(lineStr[j])
    return returnVect


# 手寫數據集測試類
def handwritingClassTest():
    hwLabels = []
    trainingFileList = listdir('trainingDigits')  # load the training set
    m = len(trainingFileList)
    trainingMat = zeros((m, 1024))
    for i in range(m):
        fileNameStr = trainingFileList[i]
        fileStr = fileNameStr.split('.')[0]  # take off .txt
        classNumStr = int(fileStr.split('_')[0])
        hwLabels.append(classNumStr)
        trainingMat[i, :] = img2vector('trainingDigits/%s' % fileNameStr)
    testFileList = listdir('testDigits')  # iterate through the test set
    errorCount = 0.0
    mTest = len(testFileList)
    for i in range(mTest):

        fileNameStr = testFileList[i]

        fileStr = fileNameStr.split('.')[0]  # take off .txt

        classNumStr = int(fileStr.split('_')[0])

        vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)

        classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)

        print("the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr))

        if (classifierResult != classNumStr):
            errorCount += 1.0

    print("\nthe total number of errors is: %d" % errorCount)
    print("\nthe total error rate is: %f" % (errorCount / float(mTest)))

4.2 KNN.ipynb

import numpy as np
from sklearn import datasets
import matplotlib.pyplot as plt 
import time

# 自定義一個kNN分類器類
class MyKNNClassfier(object):

    def __init__(self, k=5, distance='euc'):
        self.k = k
        self.distance = distance
        self.x = None
        self.y = None

    def fit(self, X, Y):
        '''
        :param X: X_train  array-like [n_samples,shape]
        :param Y: Y_train  array-like [n_samples,1]
        :return:
        '''
        self.x = X
        self.y = Y

    def predict(self, X_test):
        '''
        用X_test預測出Y_test_predict
        Y_test : array-like [n_samples,1]
        :param X_test: array-like [n_samples,shape]
        :return:   output  array-like [n_samples,1]
        '''
        output = np.zeros((X_test.shape[0], 1))
        for i in range(X_test.shape[0]):
            dis = []
            for j in range(self.x.shape[0]):
                if self.distance == 'euc':  # 歐式距離
                    dis.append(np.linalg.norm(X_test[i] - self.x[j, :]))
            labels = []
            index = sorted(range(len(dis)), key=dis.__getitem__)
            for j in range(self.k):
                labels.append(self.y[index[j]])
            counts = []
            for label in labels:
                counts.append(labels.count(label))
            output[i] = labels[np.argmax(counts)]
        return output

    def score(self, x, y):
        '''
        分數評估,用來比較test數據集,查看預測成功的百分數
        :param x: X_test
        :param y: Y_test
        :return:  準確率xx%
        '''
        pred = self.predict(x)
        err = 0.0
        for i in range(x.shape[0]):
            if pred[i] != y[i]:
                err = err + 1
        return 1 - float(err / x.shape[0])

# 加載手寫圖片數據集
# 數據集詳情說明: https://blog.csdn.net/Asun0204/article/details/75607948
digits = datasets.load_digits()
x = digits.data
y = digits.target
digits.keys()
dict_keys(['data', 'target', 'target_names', 'images', 'DESCR'])
x[:3]
array([[ 0.,  0.,  5., 13.,  9.,  1.,  0.,  0.,  0.,  0., 13., 15., 10.,
        15.,  5.,  0.,  0.,  3., 15.,  2.,  0., 11.,  8.,  0.,  0.,  4.,
        12.,  0.,  0.,  8.,  8.,  0.,  0.,  5.,  8.,  0.,  0.,  9.,  8.,
         0.,  0.,  4., 11.,  0.,  1., 12.,  7.,  0.,  0.,  2., 14.,  5.,
        10., 12.,  0.,  0.,  0.,  0.,  6., 13., 10.,  0.,  0.,  0.],
       [ 0.,  0.,  0., 12., 13.,  5.,  0.,  0.,  0.,  0.,  0., 11., 16.,
         9.,  0.,  0.,  0.,  0.,  3., 15., 16.,  6.,  0.,  0.,  0.,  7.,
        15., 16., 16.,  2.,  0.,  0.,  0.,  0.,  1., 16., 16.,  3.,  0.,
         0.,  0.,  0.,  1., 16., 16.,  6.,  0.,  0.,  0.,  0.,  1., 16.,
        16.,  6.,  0.,  0.,  0.,  0.,  0., 11., 16., 10.,  0.,  0.],
       [ 0.,  0.,  0.,  4., 15., 12.,  0.,  0.,  0.,  0.,  3., 16., 15.,
        14.,  0.,  0.,  0.,  0.,  8., 13.,  8., 16.,  0.,  0.,  0.,  0.,
         1.,  6., 15., 11.,  0.,  0.,  0.,  1.,  8., 13., 15.,  1.,  0.,
         0.,  0.,  9., 16., 16.,  5.,  0.,  0.,  0.,  0.,  3., 13., 16.,
        16., 11.,  5.,  0.,  0.,  0.,  0.,  3., 11., 16.,  9.,  0.]])
y[:3]
array([0, 1, 2])
myknn_start_time = time.time()
clf = MyKNNClassfier(k=5)
clf.fit(x,y)
print('myknn score:',clf.score(x,y))
myknn_end_time = time.time()
print('myknn uses time:',myknn_end_time-myknn_start_time)
myknn score: 0.991652754590985
myknn uses time: 20.894031763076782
from sklearn.neighbors import KNeighborsClassifier
sklearnknn_start_time = time.time()
clf_sklearn = KNeighborsClassifier(n_neighbors=5)
clf_sklearn.fit(x,y)
print('sklearn score:',clf_sklearn.score(x,y))
sklearnknn_end_time = time.time()
print('sklearn uses time:',sklearnknn_end_time-sklearnknn_start_time)
sklearn score: 0.9905397885364496
sklearn uses time: 0.44499993324279785
# 對數據進行切分,即分出數據集和測試集
from sklearn.model_selection import train_test_split   #引入數據集拆分的模塊
# 劃分訓練集
(X_train,
 X_test,
 Y_train,
 Y_test) = train_test_split(x, y, train_size=0.8, random_state=1)

X_train[:3]
array([[ 0.,  0.,  0.,  2., 15.,  8.,  0.,  0.,  0.,  0.,  1., 15., 13.,
         3.,  0.,  0.,  0.,  0.,  9., 13.,  1.,  0.,  0.,  0.,  0.,  1.,
        15.,  6.,  0.,  5., 11.,  0.,  0.,  7., 14.,  0.,  1., 15.,  8.,
         0.,  0.,  8., 15.,  9., 15., 16.,  3.,  0.,  0.,  1., 11., 16.,
        16., 10.,  0.,  0.,  0.,  0.,  0.,  2., 15.,  5.,  0.,  0.],
       [ 0.,  0.,  0.,  7., 16., 16., 11.,  0.,  0.,  0.,  6., 16., 16.,
        16., 16.,  0.,  0.,  0., 11., 16., 16., 16.,  9.,  0.,  0.,  0.,
         2.,  9., 11., 14., 10.,  0.,  0.,  0.,  0.,  0.,  0., 10.,  6.,
         0.,  0.,  0.,  0.,  0.,  4., 11.,  1.,  0.,  0.,  0.,  0.,  2.,
        14.,  2.,  0.,  0.,  0.,  0.,  0., 11.,  3.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  6., 11.,  0.,  0.,  0.,  0.,  0.,  0., 15., 10.,
         0.,  0.,  0.,  0.,  0.,  7., 15.,  2.,  0.,  0.,  0.,  0.,  0.,
        16.,  6.,  0.,  0.,  0.,  0.,  0.,  3., 16.,  7.,  5.,  5.,  0.,
         0.,  0.,  2., 16., 13.,  9., 13., 11.,  0.,  0.,  0.,  8., 13.,
         7.,  5., 15.,  3.,  0.,  0.,  0.,  5., 11., 13., 12.,  2.]])
Y_train[:3]
array([4, 9, 6])
# 新創建一個knn分類器
knn01 = KNeighborsClassifier()
# 創建模型
knn01.fit(X_train, Y_train)
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
                     metric_params=None, n_jobs=None, n_neighbors=5, p=2,
                     weights='uniform')
# 查看預測的準確性
knn01.score(X_test, Y_test)
0.9944444444444445
knn01.predict(X_test[0:3])
array([1, 5, 0])
# 採用支持向量機分類器來嘗試看看
from sklearn import svm
#在數字數據集的情況下,任務是給出圖像來預測其表示的數字,共有10個可能類,在這些類上擬合一個估計
clf01 = svm.SVC(gamma=0.001,C=100.)# 估計器實例命名爲clf01
#使用除了最後一張意外的所有圖像,用[:-1]語法選擇這個訓練集
clf01.fit(digits.data[:-1],digits.target[:-1])
SVC(C=100.0, break_ties=False, cache_size=200, class_weight=None, coef0=0.0,
    decision_function_shape='ovr', degree=3, gamma=0.001, kernel='rbf',
    max_iter=-1, probability=False, random_state=None, shrinking=True,
    tol=0.001, verbose=False)
# 預測
clf01.predict(digits.data[-1:])
array([8])
# 建立svm模型
clf01.fit(X_train,Y_train)
SVC(C=100.0, break_ties=False, cache_size=200, class_weight=None, coef0=0.0,
    decision_function_shape='ovr', degree=3, gamma=0.001, kernel='rbf',
    max_iter=-1, probability=False, random_state=None, shrinking=True,
    tol=0.001, verbose=False)
# 查看準確率
clf01.score(X_test,Y_test)
0.9916666666666667

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