KNN算法,简单的说明白啦~

本文详细介绍了K近邻(KNN)算法的基本思想、算法步骤和实现过程,并通过Python代码展示了如何使用sklearn库进行KNN分类。同时,讨论了KNN在回归分析中的应用,即通过邻居的平均值来预测目标值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

KNN算法

算法思想

K近邻算法的思想可以这样来理解:

假设在某一个世界有一个定理,一个人肯定和自己的邻居相似。现在你已经知道了一些邻居的类型,怎么判断这个人的类型呢?

你肯定觉得这是一个简单的问题,直接看这个人的邻居是什么类型不就好了

但是如果这个人的邻居有多种类型怎么办呢?

KNN给出的办法是,看他的邻居的种类,认为这个人的种类是邻居中最多的那个种类。比如说,我假设他有3个邻居,其中两个是A,一个是B,那么我们就认为这个人是A。(因为A比B多)

这个思想非常的简单,但是在现实生活中解决问题的时候,有一些事情需要进一步的考虑
即,怎么样才能算作邻居?
为了回答这个问题,需要弄清楚2个问题:

1)邻:多远叫做邻?为此我们需要构建一个指标,用这个指标来衡量邻的程度。
在这里可以很直观的选择,距离作为这个指标,并且认为,距离越近的,就是越合格的邻居,因为距离越近也会越有相似性。
在计算中,我们通常使用欧氏距离进行计算。

而因为这个距离是多个维度的一个综合距离,因此需要根据数据的量级和实际的含义,决定是否需要进行归一化处理

2)选几个邻居:邻居选几个才能判断?
我们已经知道了彼此之间的距离,并且认为距离近就更有可能是一个合格的邻居,那么在接下来的邻居决定此人的类型的过程中,我们应该选取几个邻居来看呢?这便是KNN中,k值的选取的问题。一般情况下,可以用交叉验证的方式进行选择k值。

交叉验证:将数据集进行划分,划分为训练集和测试集,之后再对训练集划分和组合,得到不同的新的训练集和验证集,在测试集上面进行误差计算,得到对于模型衡量的情况。(如下图,图片来源:https://www.cnblogs.com/volcao/p/9291831.html)
在这里插入图片描述

  • k的值如果选取的太少了,则只考虑离的最近的一些的影响,但是离的近的数据可能只是在这个数据集中这样,在其他的数据集中可能就不是这样,因此容易发生过拟合
  • k的值如果选取的太多了,则会把样本中更多的值都进行考虑,模型整体变得简单,近似误差会变大

算法步骤

假设现在有一些已知的有标签的数据和选择好的邻居数量k,存在一个未知类型的点,怎么用KNN来求其类型呢?
1.计算每一个现存的点到这个点的距离
2.按照之前选取好的k,得到这个点的邻居们
3.根据邻居们的类别,判断得到这个点的类别

算法实现(python)

在python中可以直接利用sklearn库中的neighbors.KNeighborsClassifier()对数据进行训练,得到KNN的结果,也可以只利用sklearn中的鸢尾花的数据,自己编程实现KNN。

按照算法逻辑,编写程序

因为对于每一个新的点来说,邻居和距离都会变化,因此我们建立邻居的列表也会不断变化,因此不需要设置为属性

另外,可以在编写程序的时候,加入排序的思想,先建立一个k个邻居的列表,再每次拿后面的距离和已计算的k个距离的最大值进行比较,如果比最大值小,就将列表中对应的值进行修改

本程序可以对数据进行划分,计算在选取不同的k和用不同的方式计算距离的时候,在测试集上的正确率

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

class KNN:
    def __init__(self, X_train, y_train, n_neighbors=3, p=2):
        """
        parameter: n_neighbors 临近点个数
        parameter: p 距离度量
        """
        self.n = n_neighbors
        self.p = p
        self.X_train = X_train
        self.y_train = y_train

    def predict(self, X):
        # 通过计算得到k个邻居
        knn_list = []
        # 先计算前n个邻居
        for i in range(self.n):
            dist = np.linalg.norm(X - self.X_train[i], ord=self.p)
            knn_list.append((dist, self.y_train[i]))

        # 在比较中计算后n个邻居
        for i in range(self.n, len(self.X_train)):
            max_index = knn_list.index(max(knn_list, key=lambda x: x[0]))  # 找到目前距离最远的在列表中的位置,为修改做准备
            dist = np.linalg.norm(X - self.X_train[i], ord=self.p)  # np.linalg.norm 求范数
            if knn_list[max_index][0] > dist:   # 对距离进行比较
                knn_list[max_index] = (dist, self.y_train[i])

        # 对邻居的情况进行统计,得到这个点的种类
        vote = {}
        for i in knn_list:
            if i[1] not in vote.keys():
                vote[i[1]]=1
            else:
                vote[i[1]]+=1
            return max(zip(vote.keys(),vote.values()))[0]

    def score(self, X_test, y_test):
        right_count = 0
        n = 10
        for X, y in zip(X_test, y_test):
            label = self.predict(X)
            if label == y:
                right_count += 1
        return right_count / len(X_test)

if __name__=="__main__":
    iris = load_iris()  # 加载数据集
    df = pd.DataFrame(iris.data, columns=iris.feature_names)
    df['label'] = iris.target  # 加入一列为分类标签
    df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']

    data = np.array(df.iloc[:100, [0, 1, -1]])  # 取出100行, 第0列第1列和最后一列
    X, y = data[:, :-1], data[:, -1]
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4)  # 数据划分
    clf = KNN(X_train, y_train)
    print(clf.score(X_test, y_test))

这里得到的正确率为1。

直接调用sklearn库中方法

from sklearn import neighbors
from sklearn.datasets import load_iris

#导入数据集
iris=load_iris()  # 这里返回的是一个字典
#print(iris)  # 查看信息,这样就可以看到相应的类别名称信息

#对数据进行训练
knn=neighbors.KNeighborsClassifier()
knn.fit(iris.data,iris.target)    # 前面是数据,后面是对应的数据类别

# 对点[0.1,0.2,0.3,0.4]进行预测
predict = knn.predict([[0.1,0.2,0.3,0.4]])  # 返回的是一个数组
predict_list = iris.target_names
for i in predict:
    print("预测的点为:[0.1,0.2,0.3,0.4]")
    print("预测的结果为:"+predict_list[i])

最后得到分类结果:
在这里插入图片描述

延申:怎么用KNN做回归分析?

思路还是找邻居,但是不是让邻居投票来说明一个数据的种类,而是用邻居的均值当作这个点的值。在选取交叉验证的时候,可以计算残差平方和来判断k选择的好坏。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值