python实现KNN算法

前言

一、KNN是什么?

1.1 KNN伪代码

1.2 范数的计算

二、复现KNN

1.引入库

2.封装成类

三、与skleanr中knn进行对比

3.1 数据预处理

3.2 使用sklearn中的KNeighborsClassifier类

3.3 使用复现的KNN算法

3.4 范数ord设定的影响

总结


前言

使用numpy复现KNN算法。
(1)更加了解算法以及熟练复现能力。

(2)为后续优化改进KNN算法做铺垫。

(3)涉及到的运算使用numpy,加快运算。

一、KNN是什么?

1.1 KNN伪代码

以下是 K 最近邻算法(K-Nearest Neighbors,KNN)的伪代码:
函数 KNN:
    输入:
        - 训练数据集 X_train
        - 训练数据集的标签 y_train
        - 待预测样本 x_test
        - 参数 K
        
    输出:
        - 预测样本的标签 y_pred
        
    # 计算距离
    distances = 计算 x_test 与 X_train 中每个样本的距离
    
    # 根据距离排序样本
    sorted_indices = 对 distances 进行排序,返回索引
    
    # 取前 K 个样本的标签
    k_nearest_labels = 取出 sorted_indices 的前 K 个样本的标签 y_train
    
    # 根据投票选择获胜的标签
    y_pred = 统计 k_nearest_labels 中出现最频繁的标签
    
    返回 y_pred

KNN 算法的主要步骤包括计算待预测样本与训练样本之间的距离、排序样本、选择最近的 K 个样本,并通过投票方式决定预测样本的标签。

需要注意的是,在实际应用中,通常需要根据具体情况选择合适的距离度量方法(例如欧氏距离、曼哈顿距离等),以及相应的 K 值。此外,还需要进行数据预处理、特征选择和模型评估等步骤,以获得更好的预测性能。

1.2 范数的计算

np.linalg.norm 函数用于计算向量或矩阵的范数(norm)。它可以计算各种范数,包括 L1 范数、L2 范数、无穷范数等。

具体而言,np.linalg.norm(x, ord=None, axis=None, keepdims=False) 函数接受以下参数:

  • x:要计算范数的向量或矩阵。
  • ord:可选参数,用于指定要计算的范数类型。默认为None,表示计算矩阵或向量的 Frobenius 范数。常用的范数类型有:
    • ord=None:计算 Frobenius 范数。对于向量,等同于 L2 范数。
    • ord=1:计算 L1 范数(向量元素绝对值之和)。
    • ord=2:计算 L2 范数(向量元素的平方和的平方根)。默认情况下使用的范数。
    • ord=np.inf:计算无穷范数(向量中绝对值最大的元素)。
    • ord=-np.inf:计算负无穷范数(向量中绝对值最小的元素)。
  • axis:可选参数,用于指定计算的轴方向。默认是 None,表示计算整个矩阵或向量的范数。当 axis=0 时,计算列范数;当 axis=1 时,计算行范数。
  • keepdims:可选参数,指定是否保持计算后的维度。如果设置为 True,则输出结果会保持输入的维度;如果设置为 False(默认),则输出结果是一个标量或一维数组。

故有参数ord是进行设置的,通常默认为ord=2,表示取得是欧式距离。

二、复现KNN

1.引入库

import numpy as np 
from scipy.stats import mode

2.封装成类

并给出详细的注释

class KNN(): 
    def __init__(self,k,ord=2):
        self.k = k 
        self.ord = ord 



    def fit(self,x_train,y_train):
        self.x_train = x_train
        self.y_train = y_train

        # 涉及到的操作都是numpy类型
        if type(self.x_train) != np.ndarray:
            self.x_train = np.array(self.x_train)
        if type(self.y_train) != np.ndarray:
            self.y_train = np.array(self.y_train)
        if len(self.y_train.shape)==1:  # 维度进行延伸
            self.y_train = np.expand_dims(self.y_train, axis=1)


    # 计算的是data2中每一行数据与data1所有数据之间的各种距离,且用distances保存
    # data2:k行m列
    # data1:n行m列
    # distances:k行n列
    def calculate_distances(self,dataset1,dataset2,ord):        # 默认使用欧式距离
        distances = np.zeros((len(dataset2), len(dataset1)))       # 初始化距离矩阵
        for i in range(len(dataset2)):
            distances[i] = np.linalg.norm(dataset1 - dataset2[i], axis=1,ord=ord)
        return distances
    

    def predict(self,x_test):
        if type(x_test) != np.ndarray:
            x_test = np.array(x_test)

        distances = self.calculate_distances(self.x_train,x_test,self.ord)                # 获取距离矩阵
        partitioned_indexes = np.argpartition(distances, self.k, axis=1)[:, :self.k]      # 找到每行最小的前k个值的索引位置
                                                                                          # 即获取距离最近得前k个样本
        sub_labels = self.y_train[partitioned_indexes, -1]                                # 获取选定得最近前k个样本对应的标签
        modes, counts = mode(sub_labels, axis=1,keepdims=False)                           # 获取每行的众数以及出现次数
        return modes                                                                      # 即为预测数

三、与skleanr中knn进行对比

3.1 数据预处理

from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score


import pandas as pd 
data = pd.read_csv(r"heart.csv")
for column in data.columns: 
    data[column].replace("N",np.nan,inplace=True)  # 将错误的值换成缺失值格式np.Nan
print(data.shape)
data = data.dropna() # 删除nan值
print(data.shape)
data["sex"].replace("male",0,inplace=True) 
data["sex"].replace("female",1,inplace=True) 
data["thal"].replace("fixed defect",0,inplace=True) 
data["thal"].replace("reversable defect",1,inplace=True)  
data["thal"].replace("normal",2,inplace=True)
Y = data['target'] 
X = data.drop("target",axis=1) 
# 切分数据集,设置测试集占比为30%
x_train, x_test, y_train, y_test = train_test_split(X,Y,test_size=0.2, random_state=3220822)

3.2 使用sklearn中的KNeighborsClassifier类

# 创建 KNN 模型对象
knn = KNeighborsClassifier(n_neighbors=3,)
# 训练模型
knn.fit(x_train, y_train)
# 进行预测
y_pred = knn.predict(x_test)
# 评估模型
accuracy = accuracy_score(y_test, y_pred)
print("准确率:", accuracy)

3.3 使用复现的KNN算法

MYknn = KNN(k=3)# 默认范数ord=2,即默认为欧式距离
# 训练模型
MYknn.fit(x_train, y_train)
# 进行预测
y_pred = MYknn.predict(x_test)
# 评估模型
accuracy = accuracy_score(y_test, y_pred)
print("准确率:", accuracy)

3.4 范数ord设定的影响

我们可以通过设定范数ord分别为1,2,3的情况,看其效果如何 。

 可以看到范数ord取值不同对结果的影响是很大的,但并不是越大越好,或越小越好。

而是不同的数据集有不同的最佳选择,因此需要我们多加测试,选出数据集对应的最优参数值。

总结

后续,将会如kmeans算法一样,优化算法。使得knn算法的效果提升的更好。

  • 14
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值