数据挖掘算法原理与实践:k-近邻 动手实现knn算法 201226
educoder 答案
任务描述
本关任务:使用python实现knn算法,并对手写数字进行识别。
相关知识
为了完成本关任务,你需要掌握:1.加权投票,2.knn算法流程。
数据集介绍
手写数字数据集一共有1797个样本,每个样本有64个特征。每个特征的值为0-255之间的像素,我们的任务就是根据这64个特征值识别出该数字属于0-9十个类别中的哪一个。
我们可以使用sklearn直接对数据进行加载,代码如下:
from sklearn.datasets import load_digits
#加载手写数字数据集
digits = load_digits()
#获取数据特征与标签
x,y = digits .data,digits .target
当然,每一个样本就是一个数字,我们可以把它还原为8x8的大小进行查看:
import matplotlib.pyplot as plt
img = x[0].reshape(8,8)
plt.imshow(img)
然后我们划分出训练集与测试集,训练集用来训练模型,测试集用来检测模型性能。代码如下:
from sklearn.model_selection import train_test_split
#划分训练集测试集,其中测试集样本数为整个数据集的20%
train_feature,test_feature,train_label,test_label = train_test_split(x,y,test_size=0.2,random_state=666)
加权投票
通过上一关,我们已经知道如何找出最近的k个样本,但是,现在还有一个问题要我们来解决:如果有两个类型的样本数一样且最多,那么最终该样本应该属于哪个类型?
其实,knn算法最后决定样本属于哪个类别,其实好比就是在投票,哪个类别票数多,则该样本属于哪个类别。而如果出现票数相同的情况,我们可以给每一票加上一个权重,用来表示每一票的重要性,这样就可以解决票数相同的问题了。很明显,距离越近的样本所投的一票应该越重要,此时我们可以将距离的倒数作为权重赋予每一票。
如上图,虽然蓝色正方形与红色三角形数量一样,但是根据加权投票的规则,绿色的圆应该属于蓝色正方形这个类别。
knn算法流程
knn算法不需要训练模型,只是根据离样本最近的几个样本类型来判别该样本类型,所以流程非常简单:
计算出新样本与每一个样本的距离
找出距离最近的k个样本
根据加权投票规则得到新样本的类别
编程要求
根据提示,在右侧编辑器Begin-End处补充代码,实现knn算法。
测试说明
正确率大于0.95则视为通关。
通关代码
# encoding=utf8
import numpy as np
def topK(i, k, sam, x, y):
'''
input:
i(int):第i个样本
k(int):最近邻样本个数
x(ndarray):数据特征
y(ndarray):数据标签
output:
topK(list):样本i的最近k个样本标签
'''
# 计算样本到所有样本的距离
dis = []
for s in range(len(x)):
tmp = sam - x[s]
S = 0
for t in tmp:
S += abs(t)
dis.append([S, s])
# 除样本本身外的最近的k个样本的索引
dis = sorted(dis, key=lambda x: x[0])
# 除样本本身外的最近的k个样本的标签
for a in range(k):
dis[a].append(y[dis[a][1]])
'''
output:
dis中每一项是个三元组,具体为:(样本的距离,样本的索引,样本的标签)
'''
return dis[:k]
def knn_clf(k, train_feature, train_label, test_feature):
'''
input:
k(int):最近邻样本个数
train_feature(ndarray):训练样本特征
train_label(ndarray):训练样本标签
test_feature(ndarray):测试样本特征
output:
predict(ndarray):测试样本预测标签
'''
predict = []
# 对测试集每一个样本进行遍历
for ti in range(len(test_feature)):
# 测试集第i个样本到训练集每一个样本的距离
distancee = topK(ti, k, test_feature[ti], train_feature, train_label)
# 初始化进行投票的字典,字典的键为标签,值为投票分数
dictionary = {}
for d in distancee:
# 进行投票
if d[2] not in dictionary:
# 如果标签不在字典中则将标签加入字典的键,同时计入相应的分数
dictionary[d[2]] = 1.0 / d[0]
else:
# 如果标签在字典的键中则投票计分
dictionary[d[2]] += 1.0 / d[0]
# 计入投票评分最高的类别最为预测结果
predict.append(sorted(list(dictionary.items()), key=lambda x: x[1], reverse=True)[0][0])
return predict