KNN算法实战之手写数字识别
程序员文章站
2022-07-14 20:04:37
...
文章目录
一、说明
我是在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