KNN在Mnist上的实现

KNN在Mnist上的实现

原理博客:统计学习方法|K近邻原理剖析及实现 | Dodo (pkudodo.com)

数据集:Statistical-Learning-Method_Code/Mnist at master · Dod-o/Statistical-Learning-Method_Code (github.com)

总结

2022年10月15日

  • 数据集来源也有李航《统计学习方法》的代码实现,我也是参考了他的,优化了一下存储空间而已

  • 也在学习李航的《机器学习方法》这本书,老师让看一下代码,能独立实现最好,但我。。。,我参考别人的,技术不到位。。加油吧。

  • 《机器学习》也是《统计学习方法》的第三版

读取数据

数据格式:

​ 第一列为label,后面为data

KNN在Mnist上的实现_第1张图片

# 读取数据
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个数据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])

你可能感兴趣的:(python,python,机器学习,人工智能,knn,pytorch)