KNN算法識別手寫數字


需求:

利用一個手寫數字“先驗數據”集,使用knn算法來實現對手寫數字的自動識別;

先驗數據(訓練數據)集:

♦數據維度比較大,樣本數比較多。

♦ 數據集包括數字0-9的手寫體。

♦每個數字大約有200個樣本。

♦每個樣本保持在一個txt文件中。

♦手寫體圖像本身的大小是32x32的二值圖,轉換到txt文件保存后,內容也是32x32個數字,0或者1,如下:

♦目錄trainingDigits存放的是大約2000個訓練數據

♦目錄testDigits存放大約900個測試數據。

trainingDigits文件夾中為訓練數據,里面存儲的都是32*32的txt格式的數字圖像數值矩陣。testDigits文件夾中為測試數據,存儲格式與trainingDigits中相同。文件格式名例如:0_1.txt,0為數字的標簽(即數字本身),1為表示數字0的第一個文件。訓練數據是多張32*32手寫圖像的二維矩陣,所謂二維矩陣就是整個圖像空白的地方使用0描述,寫字的地方使用1描述,

代碼python:https://github.com/kongxiaoshuang/KNN

#-*- coding: utf-8 -*-
from numpy import *
import operator

import matplotlib
import matplotlib.pyplot as plt

def createDataSet():
    group = array([[1.0, 1.1], [1.0, 1.0], [0, 0], [0, 0.1]])
    labels = ['A', 'A', 'B', 'B']
    return group, labels

def classify0(inX, dataSet, labels, k): #inX為用於分類的輸入向量,dataSet為輸入的訓練樣本集, labels為訓練標簽,k表示用於選擇最近的數目
    dataSetSize = dataSet.shape[0] #dataSet的行數
    diffMat = tile(inX, (dataSetSize, 1)) - dataSet #將inX數組復制成與dataSet相同行數,與dataSet相減,求坐標差
    sqDiffMat = diffMat**2 #diffMat的平方
    sqDistances = sqDiffMat.sum(axis=1) #將sqDiffMat每一行的所有數相加
    distances = sqDistances**0.5 #開根號,求點和點之間的歐式距離
    sortedDistIndicies = distances.argsort() #將distances中的元素從小到大排列,提取其對應的index,然后輸出到sortedDistIndicies
    classCount = {} #創建字典
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]] #前k個標簽數據
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 #判斷classCount中有沒有對應的voteIlabel,
        # 如果有返回voteIlabel對應的值,如果沒有則返回0,在最后加1。為了計算k個標簽的類別數量
    sortedClassCount = sorted(classCount.items(),
                              key=operator.itemgetter(1), reverse=True) #生成classCount的迭代器,進行排序,
                # operator.itemgetter(1)以標簽的個數降序排序
    return sortedClassCount[0][0] #返回個數最多的標簽

def file2matrix(filename):
    fr = open(filename)
    arrayOLines = fr.readlines() #讀入所有行
    numberOfLines = len(arrayOLines) #行數
    returnMat = zeros((numberOfLines, 3))  #創建數組,數據集
    classLabelVector = [] #標簽集
    index = 0
    for line in arrayOLines:
        line = line.strip()   #移除所有的回車符
        listFromLine = line.split('\t')  #把一個字符串按\t分割成字符串數組
        returnMat[index,:] = listFromLine[0:3] #取listFromLine的前三個元素放入returnMat
        classLabelVector.append(int(listFromLine[-1])) #選取listFromLine的最后一個元素依次存入classLabelVector列表中
        index += 1
    return returnMat, classLabelVector

def autoNorm(dataSet):
    minVals = dataSet.min(0) #0表示從列中選取最小值
    maxVals = dataSet.max(0) #選取最大值
    ranges = maxVals-minVals
    normDataSet = zeros(shape(dataSet))  #創建一個與dataSet大小相同的零矩陣
    m = dataSet.shape[0] #取dataSet得行數
    normDataSet = dataSet - tile(minVals, (m, 1))  #將minVals復制m行 與dataSet數據集相減
    #歸一化相除
    normDataSet = normDataSet/tile(ranges, (m, 1)) #將最大值-最小值的值復制m行 與normDataSet相除,即歸一化
    return normDataSet, ranges, minVals #normDataSet為歸一化特征值,ranges為最大值-最小值

def datingClassTest():
    hoRatio = 0.10 #測試數據占總數據的百分比
    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt') #將文本信息轉成numpy格式
    #datingDataMat為數據集,datingLabels為標簽集
    normMat, ranges, minVals = autoNorm(datingDataMat)  #將datingDataMat數據歸一化
    #normMat為歸一化數據特征值,ranges為特征最大值-最小值,minVals為最小值
    m = normMat.shape[0] #取normMat的行數
    numTestVecs = int(m*hoRatio) #測試數據的行數
    errorCount = 0.0 #錯誤數據數量
    for i in range(numTestVecs):
        classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 3)
        #classify0為kNN分類器,normMat為用於分類的輸入向量,normMat為輸入的訓練樣本集(剩余的90%)
        #datingLabels為訓練標簽,3表示用於選擇最近鄰居的數目
        print("the classifier came back with: %d, the real answer is: %d" %(classifierResult, datingLabels[i]))
        if (classifierResult != datingLabels[i]):errorCount += 1.0 #分類器結果和原標簽不一樣,則errorCount加1
    print("the total error rate is : %f" %(errorCount/float(numTestVecs)))


# datingClassTest()



# datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
#
# normDataSet, ranges, minVals = autoNorm(datingDataMat)

# fig = plt.figure()
# ax = fig.add_subplot(111) #一行一列一個
# ax.scatter(datingDataMat[:,1], datingDataMat[:,2],
#            15.0*array(datingLabels), 15.0*array(datingLabels))   #scatter畫散點圖,使用標簽屬性繪制不同顏色不同大小的點
# plt.show()

# #測試分類器
# group, labels = createDataSet()
# label = classify0([1,1], group, labels, 3)
# print(label)

from os import listdir

def img2vector (filename):
    returnVect = zeros((1, 1024)) #創建一個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('E:/digits/trainingDigits') #listdir獲取訓練集的文件目錄
    m = len(trainingFileList) #文件數量
    trainingMat = zeros((m, 1024)) #一個數字1024個字符,創建m*1024的數組
    for i in range(m):
        fileNameStr = trainingFileList[i] #獲取文件名
        fileStr = fileNameStr.split('.')[0] #以'.'將字符串分割,並取第一項,即0_0.txt取0_0
        classNumStr = int(fileStr.split('_')[0]) #以'_'將字符串分割,並取第一項
        hwLabels.append(classNumStr) #依次存入hwLabels標簽集
        trainingMat[i, :] = img2vector('E:/digits/trainingDigits/%s' % fileNameStr) #將每個數字的字符值依次存入trainingMat
    testFileList = listdir('E:/digits/testDigits') #讀入測試數據集
    errorCount = 0.0 #測試錯誤數量
    mTest = len(testFileList) #測試集的數量
    for i in range(mTest):
        fileNameStr = testFileList[i]
        fileStr = fileNameStr.split('.')[0]
        classNumStr = int(fileStr.split('_')[0]) #測試數據標簽
        vectorUnderTest = img2vector('E:/digits/testDigits/%s' % fileNameStr) #讀入測試數據
        classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3) #分類器kNN算法,3為最近鄰數目
        print("the calssifier came back with: %d, the real answer is : %d" %(classifierResult, classNumStr))
        if (classifierResult != classNumStr): errorCount +=1.0
    print("\nthe total number of errors is : %f" % errorCount)
    print("\nthe total error rate is :%f" % (errorCount/float(mTest)))

handwritingClassTest()

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM