一、原理和算法
K-近邻(k-Nearest Neighbor,KNN)是分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。该方法的思路是:如果一个样本在特征空间中的 k 个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。
算法思路:
1) 算距离:给定测试对象,计算它与训练集中的每个对象的距离
2) 找邻居:圈定距离最近的 k 个训练对象,作为测试对象的近邻
3) 做分类:根据这 k 个近邻归属的类别进行投票,对测试对象分类。
注意:如果选择较小的 K 值,训练实例集合就会变小,训练的近似误差会变的很小,训练数据与输入的新数据相近才能有效果。K 值变小的情况下,容易受到异常值的影响,更容易出现过拟合问题。 选择较大的 K 值,训练实例会变大,训练的近似误差亦会变的很大,如果训练数据与新输入的新数据不相关也会产生不同效果,容易出现样本均衡的问题,使模型变得简单,出现欠拟合问题。所以,K 值的选择是 KNN 算法的要点。
算法流程:
1) 计算已知类别数据集中的点与当前点之间的距离
2) 按照距离递增次序排序
3) 选取与当前点距离最小的 k 个点
4) 确定前 k 个点所在类别对应的出现频率
5) 返回前 k 个点出现频率最高的类别作为当前点的预测分类
二、KNN的实现
import numpy as np
import random
from sklearn import preprocessing
from sklearn.decomposition import PCA
from sklearn.datasets import load_digits # 导入数据
import matplotlib.pyplot as plt
import pandas as pd
def knn(k, testdata, traindata, labels):
#计算训练集的长度
traindatasize = traindata.shape[0]
#将测试集扩展至训练集的长度,再求差值
dif = np.tile(testdata,(traindatasize,1)) - traindata
#求差值的平方
sqrdif = dif**2
#求平方和
sumsqrdif = sqrdif.sum(axis=1)
#再开根号,即所有的距离
distance = sumsqrdif**0.5
#对距离进行排序,返回排序后的索引
sorted_distance = distance.argsort()
#准备一个空字典,存放投票结果
count = {}
for i in range(0,k):
#提取索引多对应的标签值作为字典的key
vote = labels[sorted_distance[i]]
#票数作为字典的value
count[vote] = count.get(vote,0)+1
#对最后的投票结果进行排序
sorted_count = sorted(count.items(),key=lambda x:x[1],reverse=True)
#返回得票最多的标签
return sorted_count[0][0]
三、基于KNN的手写数字识别
数据集介绍:digits 数据集是 python 中 sklearn库中的一个手写数字数据集,该数据集包含1797个样本,每个样本包括8*8像素的图像和一个[0, 9]整数的标签。
digits 手写数据集部分图例如下图所示。
该数据集有 64 个特征值,也就是说有 64 个维度,用PCA把原始的64维数据降到18维。
以训练集:测试集=7:3的比例对digits数据集进行划分,通过设置随机数来随机选取样本,并设置随机数种子2,使我们的结果稳定不变,具有复现性。
# Load data
digits = load_digits()
data = digits.data
target = digits.target
#split dataset
number=[x for x in range(1797)]
random.seed(2)
random.shuffle(number)
number=number[:1258]
exnumber=[x for x in range(1797) if x not in number]
train_x=data[number]
train_y=target[number]
test_x=data[exnumber]
test_y=target[exnumber]
#PCA
pca = PCA(n_components=18)
train_x = pca.fit_transform(train_x)
test_x = pca.transform(test_x)
# Test
result_y=np.zeros(test_x.shape[0])
for i in range(test_x.shape[0]):
result_y[i]=knn(3, test_x[i], train_x, train_y)
correct_rate = np.mean(result_y == test_y)
print('Total correct rate: %.2f%%'% (correct_rate*100))
四、实验结果
下表列出了本文实现的 KNN 算法在 digits测试集上的预测结果。从表中可以看出,本文实现的 KNN 算法在digits测试集上识别总的准确率为 99.44%。在539条测试样本中,识别错误的总数是3。下表还详细列出了每类数字图像的准确率,除数字9外,其余数字的的识别准确率均高于平均值,准确率达到了100%;而数字9的识别准确率低于95%。
数字 类别 | 该类样本总数 | 识别错误总数 | 准确率 |
0 | 51 | 0 | 100% |
1 | 55 | 0 | 100% |
2 | 54 | 0 | 100% |
3 | 58 | 0 | 100% |
4 | 49 | 0 | 100% |
5 | 59 | 0 | 100% |
6 | 55 | 0 | 100% |
7 | 50 | 0 | 100% |
8 | 55 | 0 | 100% |
9 | 53 | 3 | 94.34% |
ALL | 539 | 3 | 99.44% |