原理博客:统计学习方法|K近邻原理剖析及实现 | Dodo (pkudodo.com)
数据集:Statistical-Learning-Method_Code/Mnist at master · Dod-o/Statistical-Learning-Method_Code (github.com)
总结
2022年10月15日
数据集来源也有李航《统计学习方法》的代码实现,我也是参考了他的,优化了一下存储空间而已
也在学习李航的《机器学习方法》这本书,老师让看一下代码,能独立实现最好,但我。。。,我参考别人的,技术不到位。。加油吧。
《机器学习》也是《统计学习方法》的第三版
数据格式:
第一列为label,后面为data
# 读取数据
def read_data(addr):
data_csv = np.loadtxt(open(addr, 'r'), delimiter=',')
labels = data_csv[:, 0]
data = data_csv[:, 1:]
return data, labels
# 计算距离,采用欧式距离
def calc_distance(x1, x2):
# 欧式距离
return np.sqrt(np.sum(np.square(x1 - x2)))
使用的遍历,寻找距离最小的前k个数据label
# knn 寻找最近的k个节点,返回对应的标签
def knn(train_data, train_labels, target_data, top_K):
i = 0
# 保存k个的距离,方便比较,节省内存
top_k_dis = np.zeros([top_K])
# 保存对应的标签
top_k_label = np.zeros([top_K])
for data, label in zip(train_data, train_labels):
# 计算距离
dis = calc_distance(data, target_data)
# dis = calc_distance(data.reshape(1,data.shape[0]), target_data.reshape(1,data.shape[0]))
# 当循环在前k个数据时,top_k_dis是空的,直接放进top_k_dis即可
if i < top_K:
top_k_dis[i] = dis
top_k_label[i] = label
# i += 1
# 当在k个之后的数据,需要进行比较,谁的距离最小,最小的留在top_k_dis中
else:
# 找到当前top_k_dis中距离最大的索引,
# 和这次循环得到的dis距离比较,如何本次的dis距离小于在top_k_dis中的最大距离
# 则丢弃top_k_dis中最大的一个
# 到最后 top_k_dis中存储的就是和target_data距离最小的top_K个数据
# 也要同时更新对应的top_k_label标签
index = np.argmax(top_k_dis)
if top_k_dis[index] > dis:
top_k_dis[index] = dis
top_k_label[index] = label
return top_k_label
# 模型测试
def model_test(train_data, train_labels, top_K, test_data, test_labels):
# 预测正确的数量
right_count = 0
# 总的测试数量
sum_count = test_data.shape[0]
# 因为是手写数字识别Mnist数据
# ,所以建立一个shape为10的存储
# ,方便记录top_k_labels总每个数字对应的数量
pre_count = np.zeros([10])
# 预测多少个了,打印一下,别让人以为宕机了
num_pre_count = 0
for data, label in zip(test_data, test_labels):
# 调用knn预测
top_k_labels = knn(train_data, train_labels, data, top_K)
# 将得到的top_k_labels进行各个数字标签统计数量
# 例如top_k_labels=[1,2,3,1,1,1,1] 则pre_count[1]=5,pre_count[2]=1,pre_count[3]=1
for index in top_k_labels:
pre_count[int(index)] += 1
# 得到例如top_k_labels中数量最多的标签,作为预测标签
pre = np.argmax(pre_count)
# 如果预测的标签与真实标签相同,则right_count加1
if pre == label:
right_count += 1
num_pre_count += 1
# 预测完本次,要将pre_count归零,不然影响下次循环进来的预测结果
pre_count[:] = 0
if num_pre_count % 10 == 0:
print("以预测完到第{}个数据".format(num_pre_count))
print("共有{}个测试数据,准确率为{}".format(sum_count, right_count / sum_count))
import numpy as np
import math
import sys
# 读取数据
def read_data(addr):
data_csv = np.loadtxt(open(addr, 'r'), delimiter=',')
labels = data_csv[:, 0]
data = data_csv[:, 1:]
return data, labels
# 计算距离,采用欧式距离
def calc_distance(x1, x2):
# 欧式距离
return np.sqrt(np.sum(np.square(x1 - x2)))
# knn 寻找最近的k个节点,返回对应的标签
def knn(train_data, train_labels, target_data, top_K):
i = 0
# 保存k个的距离,方便比较,节省内存
top_k_dis = np.zeros([top_K])
# 保存对应的标签
top_k_label = np.zeros([top_K])
for data, label in zip(train_data, train_labels):
# 计算距离
dis = calc_distance(data, target_data)
# dis = calc_distance(data.reshape(1,data.shape[0]), target_data.reshape(1,data.shape[0]))
# 当循环在前k个数据时,top_k_dis是空的,直接放进top_k_dis即可
if i < top_K:
top_k_dis[i] = dis
top_k_label[i] = label
# i += 1
# 当在k个之后的数据,需要进行比较,谁的距离最小,最小的留在top_k_dis中
else:
# 找到当前top_k_dis中距离最大的索引,
# 和这次循环得到的dis距离比较,如何本次的dis距离小于在top_k_dis中的最大距离
# 则丢弃top_k_dis中最大的一个
# 到最后 top_k_dis中存储的就是和target_data距离最小的top_K个数据
# 也要同时更新对应的top_k_label标签
index = np.argmax(top_k_dis)
if top_k_dis[index] > dis:
top_k_dis[index] = dis
top_k_label[index] = label
return top_k_label
# 模型测试
def model_test(train_data, train_labels, top_K, test_data, test_labels):
# 预测正确的数量
right_count = 0
# 总的测试数量
sum_count = test_data.shape[0]
# 因为是手写数字识别Mnist数据
# ,所以建立一个shape为10的存储
# ,方便记录top_k_labels总每个数字对应的数量
pre_count = np.zeros([10])
# 预测多少个了,打印一下,别让人以为宕机了
num_pre_count = 0
for data, label in zip(test_data, test_labels):
# 调用knn预测
top_k_labels = knn(train_data, train_labels, data, top_K)
# 将得到的top_k_labels进行各个数字标签统计数量
# 例如top_k_labels=[1,2,3,1,1,1,1] 则pre_count[1]=5,pre_count[2]=1,pre_count[3]=1
for index in top_k_labels:
pre_count[int(index)] += 1
# 得到例如top_k_labels中数量最多的标签,作为预测标签
pre = np.argmax(pre_count)
# 如果预测的标签与真实标签相同,则right_count加1
if pre == label:
right_count += 1
num_pre_count += 1
# 预测完本次,要将pre_count归零,不然影响下次循环进来的预测结果
pre_count[:] = 0
if num_pre_count % 10 == 0:
print("以预测完到第{}个数据".format(num_pre_count))
print("共有{}个测试数据,准确率为{}".format(sum_count, right_count / sum_count))
if __name__ == '__main__':
addr = '../Mnist/mnist_train.csv'
test_addr = '../Mnist/mnist_test.csv'
data, labels = read_data(addr)
test_data, test_labels = read_data(test_addr)
top_k = 10
# 因为mnist数据集中test有10000个数据,太多,只取前200个
model_test(data, labels, top_k, test_data[:200], test_labels[:200])