机器学习——KNN算法

发布于:2025-07-26 ⋅ 阅读:(12) ⋅ 点赞:(0)

一、算法简述

KNN 可以说是最简单的分类算法之一,同时,它也是最常用的分类算法之一。注意:KNN 算法是有监督学习中的分类算法,它看起来和另一个机器学习算法 K-means 有点像(K-means 是无监督学习算法),但却是有本质区别的。

二、运行原理

2.1、算法核心思想

KNN 的全称是 K Nearest Neighbors,意思是 K 个最近的邻居。从这个名字我们就能看出一些 KNN 算法的蛛丝马迹了。K 个最近邻居,毫无疑问,K 的取值肯定是至关重要的,那么最近的邻居又是怎么回事呢?其实,KNN 的原理就是当预测一个新的值 x 的时候,根据它距离最近的 K 个点是什么类别来判断 x 属于哪个类别。听起来有点绕,还是看看图吧。

图中绿色的点就是我们要预测的那个点,假设 K=3。那么 KNN 算法就会找到与它距离最近的三个点(这里用圆圈把它圈起来了),看看哪种类别多一些,比如这个例子中是蓝色三角形多一些,新来的绿色点就归类到蓝三角了。

但是,当 K=5 的时候,判定就变成不一样了。这次变成红圆多一些,所以新来的绿点被归类成红圆。从这个例子中,我们就能看得出 K 的取值是很重要的。
明白了大概原理后,我们就来说一说细节的东西吧,主要有两个,K 值的选取和点距离的计算。

2.2、距离计算

要度量空间中点距离的话,有好几种度量方式,比如常见的曼哈顿距离计算、欧式距离计算等等。不过通常 KNN 算法中使用的是欧式距离。这里只是简单说一下,拿二维平面为例,二维空间两个点的欧式距离计算公式如下:

这个高中应该就有接触到的了,其实就是计算(x1,y1)和(x2,y2)的距离。拓展到多维空间,则公式变成这样:

2.3、K值选择

通过上面那张图我们知道 K 的取值比较重要,那么该如何确定 K 取多少值好呢?答案是通过交叉验证(将样本数据按照一定比例,拆分出训练用的数据和验证用的数据,比如6:4拆分出部分训练数据和验证数据),从选取一个较小的 K 值开始,不断增加 K 的值,然后计算验证集合的方差,最终找到一个比较合适的 K 值。

通过交叉验证计算方差后可以得到一个有一定趋向的线图,然后通过观察找到最适合的k值。
当你增大 K 的时候,一般错误率会先降低,因为有周围更多的样本可以借鉴了,分类效果会变好。但注意,和 K-means 不一样,当 K 值更大的时候,错误率会更高。这也很好理解,比如说你一共就35个样本,当你 K 增大到30的时候,KNN 基本上就没意义了。

三、算法实现

3.1、Sklearn KNN参数概述

要使用 Sklearn KNN 算法进行分类,我们需要先了解 Sklearn KNN 算法的一些基本参数:

def KNeighborsClassifier\
(n_neighbors=5,  # 邻居数量(K值),默认5
weights='uniform',  # 权重方式:'uniform'(等权)或'distance'(距离反比)
algorithm='',  # 近邻搜索算法:'ball_tree'、'kd_tree'、'brute'或'auto'
leaf_size='30',  # 树结构的叶节点大小,影响效率
p=2,  # 距离度量参数:1=曼哈顿距离,2=欧氏距离
metric='minkowski',  # 距离度量方式,默认明可夫斯基距离
metric_params=None,  # 距离度量的附加参数
n_jobs=None  # 并行计算数量,-1表示使用所有CPU核心
):

参数的含义:
n_neighbors这个值就是指 KNN 中的 “K”了。前面说到过,通过调整 K 值,算法会有不同的效果。

weights(权重):最普遍的 KNN 算法无论距离如何,权重都一样,但有时候我们想搞点特殊化,比如距离更近的点让它更加重要。这时候就需要 weight 这个参数了,这个参数有三个可选参数的值,决定了如何分配权重。参数选项如下:

  • ‘uniform’:不管远近权重都一样,就是最普通的 KNN 算法的形式。

  • ‘distance’:权重和距离成反比,距离预测目标越近具有越高的权重。

  • 自定义函数:自定义一个函数,根据输入的坐标值返回对应的权重,达到自

定义权重的目的。

algorithm在 Sklearn 中,要构建 KNN 模型有三种构建方式:

  1. 暴力法,就是直接计算距离存储比较的那种方式。

  2. 使用 Kd 树构建 KNN 模型。

  3. 使用球树构建。

其中暴力法适合数据较小的方式,否则效率会比较低。如果数据量比较大一般会选择用 Kd 树构建 KNN 模型,而当 Kd 树也比较慢的时候,则可以试试球树来构建 KNN。参数选项如下:

* ‘brute’ :蛮力实现;

* ‘kd_tree’:KD 树实现 KNN;

* ‘ball_tree’:球树实现 KNN ;

* ‘auto’: 默认参数,自动选择合适的方法构建模型。

p和 metric 结合使用,当 metric 参数是 “minkowski” 的时候,p=1 为曼哈顿距离, p=2 为欧式距离。默认为p=2。

3.2、 KNN代码实例

KNN 算法算是机器学习里面最简单的算法之一了,我们来看 Sklearn 官方给出的例子是怎样使用KNN 的。

数据集使用的是著名的鸢尾花数据集,用 KNN 来对它做分类。我们先看看鸢尾花长的啥样:

上面这个就是鸢尾花了,这个鸢尾花数据集主要包含了鸢尾花的花萼长度、花萼宽度、花瓣长度、花瓣宽度4个属性(特征)

我们由前四个数据将花分类成两个种类,标记为1和0

现在需要由这些数据训练模型,来识别一朵没记录过的花的类别

在使用 KNN 算法之前,我们要先决定 K 的值是多少。要选出最优的 K 值,可以使用 Sklearn 中的交叉验证方法,代码如下:

from sklearn.datasets import load_iris
from sklearn.model_selection  import cross_val_score
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsClassifier

#读取鸢尾花数据集
iris = load_iris()
x = iris.data
y = iris.target
k_range = range(1, 31)
k_error = []
#循环,取k=1到k=31,查看误差效果
for k in k_range:
    knn = KNeighborsClassifier(n_neighbors=k)
    #cv参数决定数据集划分比例,这里是按照5:1划分训练集和测试集
    scores = cross_val_score(knn, x, y, cv=6, scoring='accuracy')
    k_error.append(1 - scores.mean())

#画图,x轴为k值,y值为误差值
plt.plot(k_range, k_error)
plt.xlabel('Value of K for KNN')
plt.ylabel('Error')
plt.show()

运行后,我们可以得到下面这样的图:

有了这张图,我们就能明显看出 K 值取多少的时候误差最小,这里明显是 K=11 最好。当然在实际问题中,如果数据集比较大,那为减少训练时间,K 的取值范围可以缩小。

示例代码:

# 导入必要的库
import numpy as np  # 用于数值计算
import pandas as pd  # 用于数据处理和分析
from sklearn.neighbors import KNeighborsClassifier  # 导入K近邻分类器

# 读取训练数据和测试数据
# 从Excel文件中读取鸢尾花的训练数据和测试数据
train_data = pd.read_excel("鸢尾花训练数据.xlsx")
test_data = pd.read_excel("鸢尾花测试数据.xlsx")

# 准备训练特征和标签
# 从训练数据中提取特征列(萼片长、萼片宽、花瓣长、花瓣宽)
train_x = train_data[['萼片长(cm)','萼片宽(cm)','花瓣长(cm)','花瓣宽(cm)']]
# 从训练数据中提取标签列(花的类型,已转换为数字)
train_y = train_data[['类型_num']]

# 创建KNN分类器模型,设置近邻数为5
knn = KNeighborsClassifier(n_neighbors=11)
# 使用训练数据拟合模型(训练模型)
knn.fit(train_x,train_y)

# 准备测试数据
# 从测试数据中提取特征列
test_X = test_data[['萼片长(cm)','萼片宽(cm)','花瓣长(cm)','花瓣宽(cm)']]
# 从测试数据中提取真实标签列
test_Y = test_data[['类型_num']]

# 使用训练好的模型对测试数据进行预测
test_predicted = knn.predict(test_X)
# 计算模型在测试数据上的准确率
score = knn.score(test_X,test_Y)

# 输出模型准确率
print("模型准确率为:",score)

四、实战案例

现在有一所大学的校长在考虑大一新生的宿舍分配问题

即想要减少矛盾,又想要让学生考研率上市

这是一位聪明的辅导员发现了一个现象

相同爱好的同学在一起,矛盾的发生概率会大大减少 

学习好的学生在一起大概率会更好

校长觉得很有道理

但是入学的新生高考分数都差不多

有老师给校长建言献策 :

可以由大三大四学生的日常行为和成绩好坏来推测出相同行为的大一新生的成绩的未来走向

于是校长整来了一份数据,

大三大四学生(每年旅游公里数,每天零食消耗量,每天游戏时长)和成绩好坏的对应关系

并获取了所有大一新生的(每年旅游公里数,每天零食消耗量,每天游戏时长)的数据

现在让你训练一个模型来推测大一新生的未来,并用20%的老生数据测试一下模型的准确率

你该怎么做

和之前的案例一样,先确定knn算法的X和Y

显然,X是前三个数据,Y是学习成绩好坏

原始数据如上图所示

示例代码如下

import pandas as pd
from sklearn.neighbors import KNeighborsClassifier
# 读取学生数据文件
# 参数说明:
# - "学生数据.txt":数据文件路径
# - header=None:表示文件没有表头行
data = pd.read_csv("学生数据.txt", header=None)

# 提取特征数据X(所有行,除最后一列外的所有列)
# .values将DataFrame转换为NumPy数组,方便后续模型处理
X = data.iloc[:, :-1].values

# 提取标签数据y(所有行,最后一列)
# 标签是我们要预测的目标变量
y = data.iloc[:, -1].values

# 计算训练集和测试集的分割点,取总数据量的80%作为训练集
split_index = int(len(X) * 0.8)
# 前80%数据作为训练集特征
train_x = X[:split_index]
# 前80%数据作为训练集标签
train_y = y[:split_index]
# 后20%数据作为测试集特征
test_x = X[split_index:]
# 后20%数据作为测试集标签
test_y = y[split_index:]

# 创建KNN分类器实例,设置近邻数为15
# n_neighbors是KNN算法的核心参数,表示预测时参考的邻居数量
knn = KNeighborsClassifier(n_neighbors=15)
# 使用训练数据训练模型
# fit方法会让模型"学习"训练数据中的特征与标签之间的关系
knn.fit(train_x, train_y)

# 使用训练好的模型对测试集进行预测
test_predicted = knn.predict(test_x)
# 计算模型在测试集上的准确率
# 准确率=正确预测的样本数/测试集总样本数
score = knn.score(test_x, test_y)

# 输出测试集的预测结果
print("预测结果:", test_predicted)
# 输出模型在测试集上的准确率
print("模型准确率:", score)

五、算法优缺点

优点:
简单易用。相比其他算法,KNN 算是比较简洁明了的算法,即使没有很高的数学基础也能搞清楚它的原理。
模型训练时间快。
对异常值不敏感。
缺点:
对内存要求较高,因为该算法存储了所有训练数据。
预测阶段可能很慢。
对不相关的功能和数据规模敏感。


网站公告

今日签到

点亮在社区的每一天
去签到