赞
踩
描述:
使用k-近邻分类器构造只能识别数字0-9的手写识别系统。
需要识别的数字已处理成宽高都是32像素的黑白图像,使用文本格式存储。
流程:
trainingDigits文件夹中包含了大约 2000 个例子,每个数字大约有 200 个样本;
testDigits文件夹中包含了大约 900 个测试数据。
使用trainingDigits中的数据来训练分类器,使用testDigits中的数据来测试分类器的效果。两组数据没有覆盖。
编写函数,将图像转换为向量:
from numpy import * def img2vector(filename): """ 将图像转换为向量 :param filename:文件名 :return:numpy数组 """ # 创建1*1024的numpy数组 returnVect = zeros((1, 1024)) # 打开指定文件 with open(filename) as fr: # 循环每行 for i in range(32): lineStr = fr.readline() # 将每行的前32个字符值存储到numpy数组中 for j in range(32): returnVect[0, 32 * i + j] = int(lineStr[j]) # 返回数组 return returnVect
运行测试效果:
filename = 'data/digits/testDigits/0_0.txt'
test_vector = img2vector(filename)
print(test_vector[0,0:31])
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0.]
将数据输入到分类器,检测分类器的执行效果。对于手写数字,如果预测数字与实际数字不同,则出错次数加1。
手写数字识别系统的测试分类器代码:
1.先分别获取训练数据的向量信息及分类信息(属于哪个数字,作为标签)
2.获取到测试数据的向量信息及真正的分类信息
3.将每一个测试向量通过kNN算法【通过计算和训练数据向量组成的矩阵数据集进行计算,从而获得前k个钟出现频率最高的那个分类】获得到预测的分类
4.将预测分类和真实分类进行比较,得到错误率
文件中的值在0和1之前,这里不用在进行归一化操作
import operator from os import listdir def classify0(inX, dataSet, labels, k): """ k-近邻算法 :param inX: 用于分类的输入向量 :param dataSet: 输入的训练样本集 :param labels: 标签向量 :param k: 表示用于选择最近邻居的数目 :return: 前k个点中出现频率最高的那个分类,作为当前点的预测分类 """ 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() # 选取前K个最短距离, 选取这K个中最多的分类类别 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] def handwritingClassTest(): """ 手写数字识别系统的测试代码 :return: """ # 1. 导入训练数据 hwLabels = [] # 列出给定目录的文件名 trainingFileList = listdir('data/digits/trainingDigits') m = len(trainingFileList) # 初始化矩阵 trainingMat = zeros((m, 1024)) # hwLabels存储0~9对应的index位置, trainingMat存放的每个位置对应的图片向量 for i in range(m): # 文件名称,如0_0.txt fileNameStr = trainingFileList[i] # 不带 .txt文件后缀的文件名 fileStr = fileNameStr.split('.')[0] # 所表示的数字 classNumStr = int(fileStr.split('_')[0]) # 保存分类数字 hwLabels.append(classNumStr) # 将 32*32的矩阵转化为1*1024的矩阵,每一行存储一个图像 trainingMat[i, :] = img2vector('data/digits/trainingDigits/%s' % fileNameStr) # 2. 导入测试数据 testFileList = listdir('data/digits/testDigits') # 错误次数 errorCount = 0.0 # 测试数据大小 mTest = len(testFileList) for i in range(mTest): fileNameStr = testFileList[i] # 不带 .txt文件后缀的文件名 fileStr = fileNameStr.split('.')[0] # 实际所表示的数字 classNumStr = int(fileStr.split('_')[0]) # 测试数据的向量矩阵,每行数据表示一个图像向量 vectorUnderTest = img2vector('data/digits/testDigits/%s' % fileNameStr) # kNN分类得到的结果 classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3) # 分类结果和实际结果不同 if (classifierResult != classNumStr): errorCount += 1.0 print("kNN分类的结果得出的数字是: %d, 真实的数字是: %d" % (classifierResult, classNumStr)) print("\n总的分类错误的次数是: %d" % errorCount) print("\n错误率为: %f" % (errorCount / float(mTest)))
测试函数的输出结果:
handwritingClassTest()
kNN分类的结果得出的数字是: 7, 真实的数字是: 1
kNN分类的结果得出的数字是: 9, 真实的数字是: 3
kNN分类的结果得出的数字是: 3, 真实的数字是: 5
kNN分类的结果得出的数字是: 6, 真实的数字是: 5
kNN分类的结果得出的数字是: 6, 真实的数字是: 8
kNN分类的结果得出的数字是: 3, 真实的数字是: 8
kNN分类的结果得出的数字是: 1, 真实的数字是: 8
kNN分类的结果得出的数字是: 1, 真实的数字是: 8
kNN分类的结果得出的数字是: 1, 真实的数字是: 9
kNN分类的结果得出的数字是: 7, 真实的数字是: 9
总的分类错误的次数是: 10
错误率为: 0.010571
使用画图工具写出4个数字4,放到了路径data/digits/image/
下,手写图片如下:
通过简单的处理将手写的图片直接转换为向量形式,进行使用。通过下边的classify_handwriting程序进行预测:
from PIL import Image def image2vector(filename): """ 将图片直接转换为向量 :param filename: 图片地址 :return: 图片向量 """ with Image.open(filename) as image: im=image.convert('L') im.resize((32, 32)) im_array = array(im) im_array=where(im_array<255,1,im_array) im_array=where(im_array==255,0,im_array) im_vector=im_array.ravel() return im_vector def classify_handwriting(): """ 手写数字分类预测 :return: 预测分类 """ # 1. 导入训练数据 hwLabels = [] # 列出给定目录的文件名 trainingFileList = listdir('data/digits/trainingDigits') m = len(trainingFileList) # 初始化矩阵 trainingMat = zeros((m, 1024)) # hwLabels存储0~9对应的index位置, trainingMat存放的每个位置对应的图片向量 for i in range(m): # 文件名称,如0_0.txt fileNameStr = trainingFileList[i] # 不带 .txt文件后缀的文件名 fileStr = fileNameStr.split('.')[0] # 所表示的数字 classNumStr = int(fileStr.split('_')[0]) # 保存分类数字 hwLabels.append(classNumStr) # 将 32*32的矩阵转化为1*1024的矩阵,每一行存储一个图像 trainingMat[i, :] = img2vector('data/digits/trainingDigits/%s' % fileNameStr) imageFileList = listdir('data/digits/image') mInput = len(imageFileList) for i in range(mInput): fileNameStr = imageFileList[i] # 输入数据的向量矩阵 in_vector = image2vector('data/digits/image/%s' % fileNameStr) # kNN分类得到的结果 classifierResult = classify0(in_vector, trainingMat, hwLabels, 3) print(f"手写的数字是:{classifierResult}")
实际运行的效果如下:
classify_handwriting()
手写的数字是:4
手写的数字是:4
手写的数字是:4
手写的数字是:4
改变变量K的值、修改函数中随机选取的训练样本、改变训练样本的数目,都会对k-近邻算法的错误率产生影响。
k-近邻算法缺点:
k-近邻算法的执行效率并不高,必须对数据集中的每个数据计算距离值。
必须有接近实际数据的训练样本数据。
必须保存全部数据集,如果训练数据集很大,必须使用大量的存储空间。
无法给出任何数据的基础结构信息
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。