实验目的
本实验将尝试使用KNN方法实现MNIST手写字分类
方法简介
KNN
KNN算法是k近邻算法,是一种既可以用来分类,也可以用来回归的算法。主要计算过程如下
1、计算训练样本与测试样本间的距离
2、对样本距离升序排列
3、选前k个值距离最小的样本
4、根据样本对数据进行投票,得到分类结果
数据集
本实验使用的数据集来自于keras内置数据集
实验环境
Python 3.8
Jupyter notebook 6.4.8
Jetbrains DataSpell
Intel core i9-9980HK
实验开始
加载运行必要的库
1 2 3 |
from tensorflow import keras import numpy as np import matplotlib.pyplot as plt |
加载keras自带的mnist数据集
1 2 |
(train_images, train_labels), (test_images, test_labels) = keras.datasets.mnist.load_data() print(train_images.shape,train_labels.shape,test_images.shape,test_labels.shape) |
训练集共60000张照片,每张大小为28*28,测试集共10000张照片
数据预处理
1 2 3 4 5 6 |
#移除深浅信息,只保留01信息 train_images=train_images.astype('bool').astype('uint8') test_images=test_images.astype('bool').astype('uint8') #拉伸为一维 train_images=train_images.reshape(train_images.shape[0],train_images.shape[1]*train_images.shape[2]) test_images=test_images.reshape(test_images.shape[0],test_images.shape[1]*test_images.shape[2]) |
分类函数主体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
def classify(in_image,train_images,train_labels,k): train_images_size=train_images.shape[0] #求高斯距离 different=np.tile(in_image,(train_images_size,1))-train_images sq_different=different**2 sq_distance=sq_different.sum(axis=1) distance=sq_distance**0.5 sorted_distance=distance.argsort() #统计分类结果 class_count={} for i in range(k): label=train_labels[sorted_distance[i]] class_count[label]=class_count.get(label,0)+1 sorted_class_count=sorted(class_count.items(),key=lambda k:k[1],reverse=True) return sorted_class_count |
为画图做准备
1 2 3 4 5 6 7 8 9 10 11 |
def plot_confusion_matrix(cm, classes, normalize=False, title='Confusion matrix', cmap=plt.cm.Blues): plt.imshow(cm, interpolation='nearest', cmap=cmap) plt.title(title) plt.colorbar() tick_marks = np.arange(len(classes)) plt.xticks(tick_marks, classes, rotation=45) plt.yticks(tick_marks, classes) plt.tight_layout() plt.ylabel('True label') plt.xlabel('Predicted label') plt.show() |
运行函数
1 2 3 4 5 6 7 8 9 10 |
def run(k:int,n:int,train_num,test_num): right=0 conf_matrix=np.zeros((10,10)) for i in range(test_num): ans=classify(test_images[i],train_images[0:train_num],train_labels,k) ans=[ans[i][0] for i in range(min(n,len(ans)))] conf_matrix[test_labels[i]][ans[0]]+=1 if test_labels[i] in ans: right+=1 return right/test_num*100,conf_matrix |
测试并画出混淆矩阵
1 2 3 4 |
k=10;n=1;train_num=1000;test_num=1000 acc,matrix=run(k,n,train_num,test_num) print('k={},训练集数量为{},测试{}组数据,Top-{} 精度为 {}%'.format(k,train_num,test_num,n,acc)) plot_confusion_matrix(matrix,['0','1','2','3','4','5','6','7','8','9']) |

由图可以看到准确率还是比较高的
测试不同k与不同top-n精度
1 2 3 4 5 6 7 |
k=5;n=3 accs=np.zeros((k,n)) for i in range(k): for j in range(n): accs[i][j]=run(i+1,j+1,5000,300)[0] #print('k={},n={},acc={}'.format(i+1,j+1,accs[i][j])) print(accs) |
使用sk-learn官方库测试精度
1 2 |
from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score |
1 2 3 4 5 |
model = KNeighborsClassifier() model.fit(train_images, train_labels) predict = model.predict(test_images) score = accuracy_score(predict, test_labels) print(score) |
实验总结
1、本次实验通过自己手写knn算法完成mnist数字分类项目并获得了一定准确度
2、相比于sklearn官方库来说准确度还是较低且运行速度慢
3、通过本次实验加深了对knn与分类的了解,提高了使用理论解决实际问题的能力