欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

KNN算法实战之手写数字识别

程序员文章站 2022-07-14 20:04:37
...

一、说明

我是在jupyter完成的,然后导出成markdown格式,ipynb文件导出为markdown的命令如下:

jupyter nbconvert --to markdown xxx.ipynb

二、题目

  构造使用K-近邻分类器的手写识别系统。由于能力有限,这里构造的系统只能识别0-9。需要识别的数字已经使用图形处理软件,处理成具有相同的色彩和大小:32像素*32像素的黑白图像。
例如:
KNN算法实战之手写数字识别
KNN算法实战之手写数字识别

  当前使用文本格式存储图像,即使不能有效的利用空间,但是为了方便理解,还是将图像转换成文本格式。

示例:使用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])

KNN算法实战之手写数字识别



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)))

KNN算法实战之手写数字识别
KNN算法实战之手写数字识别



3.4 截图

KNN算法实战之手写数字识别
KNN算法实战之手写数字识别

KNN算法实战之手写数字识别

KNN算法实战之手写数字识别

KNN算法实战之手写数字识别

KNN算法实战之手写数字识别
KNN算法实战之手写数字识别



四、源代码

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

相关标签: 机器学习 python