【机器学习】一文搞懂K近邻算法(KNN),附带多个实现案例

【机器学习】一文搞懂K近邻算法(KNN),附带多个实现案例

 

一、KNN回顾

kkk近邻算法

kkk 近邻学习是一种常用的监督学习方法,比如:判断一个人的人品,只需要观察与他来往最密切的几个人的人品好坏就可以得出,即“近朱者赤,近墨者黑”。

理论/原理:“物以类聚,人以群分”

  • 相同/近似样本在样本空间中是比较接近的,所以可以使用和当前样本比较近的其他样本的目标属性值作为当前样本的预测值。

kkk 近邻法的工作机制很简单:

  • 给定测试样本,基于某种距离度量(一般使用欧几里德距离)找出训练集中与其最靠近的k个训练样本,然后基于这k个“邻居”的信息来进行预测。

二、KNN三要素

1、K值的选择

  • 对于K值的选择,一般根据样本分布选择一个较小的值,然后通过交叉验证来选择一个比较合适的最终值;
  • 当选择比较小的K值的时候,表示使用较小领域中的样本进行预测,训练误差会减小,但是会导致模型变得复杂,容易导致过拟合;
  • 当选择较大的K值的时候,表示使用较大领域中的样本进行预测,训练误差会增大,同时会使模型变得简单,容易导致欠拟合;

2、距离度量

  • 一般使用欧几里德距离
  • 关于距离度量,还有其他方式

3、决策规则

KNNKNNKNN在做回归和分类的主要区别在于最后做预测时的决策方式不同:

(1)分类预测规则:一般采用多数表决法或者加权多数表决法

在这里插入图片描述

假设图中 “?”表示待预测样本,红色圆表示一类,蓝色方块表示一类,2和3表示到待预测样本的距离

1. 多数表决法:

  • 每个邻近样本的权重是一样的,也就是说最终预测的结果为出现类别最多的那个类;
  • 如图,待预测样本被预测为红色圆

2. 加权多数表决法:

  • 每个邻近样本的权重是不一样的,一般情况下采用权重和距离成反比的方式来计算,也就是说最终预测结果是出现权重最大的那个类别;
  • 如图,红色圆到待预测样本的距离为3,蓝色方块到待预测样本的距离为2,权重与距离成反比,所以蓝色的权重比较大,待预测样本被预测为蓝色方块。

(2)回归预测规则:一般采用平均值法或者加权平均值法
在这里插入图片描述
假设上图中的2和3表示邻近样本的目标属性值(标签值),此时没有类别,只有属性值

  1. 平均值法
  • 每个邻近样本的权重是一样的,也就是说最终预测的结果为所有邻近样本的目标属性值的均值;
  • 如图,均值为(3+3+3+2+2)/5=2.6(3+3+3+2+2)/5 = 2.6(3+3+3+2+2)/5=2.6
  1. 加权平均值法

在这里插入图片描述

图中,双箭头线上的数表示到待预测样本的距离

  • 每个邻近样本的权重是不一样的,一般情况下采用权重和距离成反比的方式来计算,也就是说在计算均值的时候进行加权操作;
  • 如图,权重分别为(各自距离反比占距离反比总和的比例):
    属性值为3的权重:17=1212∗3+1∗2\frac{1}{7} = \frac{\frac{1}{2}}{\frac{1}{2} * 3 + 1*2}71​=21​∗3+1∗221​​
    属性值为2的权重:27=112∗3+1∗2\frac{2}{7} = \frac{1}{\frac{1}{2} * 3 + 1*2}72​=21​∗3+1∗21​
  • 待预测样本的加权平均值为:
    3∗17∗3+2∗27∗2=2.433 * \frac{1}{7}*3 + 2 * \frac{2}{7}*2 = 2.433∗71​∗3+2∗72​∗2=2.43

三、手写kkk 近邻算法

实现kNN分类算法的伪代码:

对未知类别属性的数据集中的每个点依次执行一下操作:

  • (1)计算已知类别数据集中的点与当前点之间的距离
  • (2)按照距离递增次序排序
  • (3)选取与当前点距离最小的k个点
  • (4)确定前k个点所在类别的出现频数
  • (5)返回当前k个点出现频数最高的类别作为当前点的预测分类

欧氏距离公式:

d=∑mi=1(xi−yi)2−−−−−−−−−−−−√d = \sqrt{\sum_{i=1}^m(x_i - y_i)^2}d=i=1∑m​(xi​−yi​)2​
例如求点(1,0,0,1)(1,0,0,1)(1,0,0,1)和(7,6,9,4)(7,6,9,4)(7,6,9,4)之间的距离:
(7−1)2+(6−0)2+(9−0)2+(4−1)2−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−√\sqrt{(7-1)^2+(6-0)^2+(9-0)^2+(4-1)^2}(7−1)2+(6−0)2+(9−0)2+(4−1)2​

检测分类器效果:

  • 可以使用已知类别的数据(当然不告诉分类器),检验分类器给出的结果是否与已知类别相同,通过大量的测试数据,我们可以计算出分类器的错误率。

以上算法的实现是用于分类的,决策规则使用了多数表决法;此算法通过改变决策规则,同样可以用于回归。

源代码可见:Github下 01_k近邻算法.py

四、使用手写kkk 近邻算法的案例

1、案例1:约会网站的配对效果

样本包括3种特征:

  • 每年获得的飞行常客里程数
  • 玩视频游戏所耗时间百分比
  • 每周消费的冰淇淋公升数

样本包括3种标签:

  • 不喜欢的人
  • 魅力一般的人
  • 极具魅力的人

部分数据格式为:

每年获得的飞行常客里程数玩视频游戏所耗时间百分比每周消费的冰淇淋公升数标签
409208.3269760.9539523
144887.1534691.6739042
260521.4418710.8051241

代码可见:02_约会网站的配对效果.py

2、案例2:手写数字识别系统

  • 数据集包括训练集和测试集
  • 数据是32*32的二进制文本文件
  • 需要将文本数据转换为Numpy数组

如下是0的一种表示:

<span style="color:#000000"><code>00000000000001100000000000000000
00000000000011111100000000000000
00000000000111111111000000000000
00000000011111111111000000000000
00000001111111111111100000000000
00000000111111100011110000000000
00000001111110000001110000000000
00000001111110000001110000000000
00000011111100000001110000000000
00000011111100000001111000000000
00000011111100000000011100000000
00000011111100000000011100000000
00000011111000000000001110000000
00000011111000000000001110000000
00000001111100000000000111000000
00000001111100000000000111000000
00000001111100000000000111000000
00000011111000000000000111000000
00000011111000000000000111000000
00000000111100000000000011100000
00000000111100000000000111100000
00000000111100000000000111100000
00000000111100000000001111100000
00000000011110000000000111110000
00000000011111000000001111100000
00000000011111000000011111100000
00000000011111000000111111000000
00000000011111100011111111000000
00000000000111111111111110000000
00000000000111111111111100000000
00000000000011111111110000000000
00000000000000111110000000000000
</code></span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

预测错误的总数为:10
手写数字识别系统的错误率为:0.010571

代码可见:03_手写数字识别系统.py

六、KD树

KNN算法的重点在于找出K个最邻近的点,主要方法如下:

1、蛮力实现(brute)

  • 计算出待预测样本到所有训练样本的训练数据,然后选择最小的K个距离即可得到K个最邻近点;
  • 当特征数比较多,样本数比较多的时候,算法的执行效率比较低

2、KD树(KD_Tree)

  • KD_Tree算法中,首先是对训练数据进行建模,构建KD树,然后再根据构建好的模型来获取邻近样本数据
  • KD_Tree是KNN算法中用于计算最近邻的快速、便捷构建方式

除此之外,还有一些从KD_Tree修改后的求解最邻近点的算法,比如:Ball Tree、BBF Tree、MVP Tree等

  • 当样本数据量少的时候,我们可以使用brute这种暴力的方式进行求解最近邻,即计算到所有样本的距离。
  • 当样本量比较大的时候,直接计算所有样本的距离,工作量有点大,所以在这种情况下,我们可以使用kd tree来快速的计算。

(1)KD数的构建

\quad\quadKD树采用从 mmm 个样本的 nnn 维特征中,分别计算 nnn 个特征取值的方差,用方差最大的第 kkk 维特征nkn_knk​作为根节点。对于这个特征,选择取值的中位数 nkvn_{kv}nkv​ 作为样本的划分点,对于小于该值的样本划分到左子树,对于大于等于该值的样本划分到右子树,对左右子树采用同样的方式找方差最大的特征作为根节点,递归即可产生KD树。

假设二维样本为:{(2, 3), (5, 4), (9, 6), (4, 7), (8, 1), (7, 2)}\{(2,\ 3),\ (5,\ 4),\ (9,\ 6),\ (4,\ 7),\ (8,\ 1),\ (7,\ 2)\}{(2, 3), (5, 4), (9, 6), (4, 7), (8, 1), (7, 2)},下面我们来构建KD树:

  • 1、计算每个特征的方差,取方差最大的作为根节点

x¯1=2+5+9+4+8+76=5\bar{x}_1 = \frac{2+5+9+4+8+7}{6} = 5xˉ1​=62+5+9+4+8+7​=5
x¯2=3+4+6+7+1+26≈3.83\bar{x}_2 = \frac{3+4+6+7+1+2}{6} \approx 3.83xˉ2​=63+4+6+7+1+2​≈3.83
s2x1=(2−5)2+(5−5)2+(9−5)2+(4−5)2+(8−5)2+(7−5)26=6.5s^2_{x_1} = \frac{(2-5)^2+(5-5)^2+(9-5)^2+(4-5)^2+(8-5)^2+(7-5)^2}{6} = 6.5sx1​2​=6(2−5)2+(5−5)2+(9−5)2+(4−5)2+(8−5)2+(7−5)2​=6.5
s2x2≈4.7s^2_{x_2} \approx 4.7sx2​2​≈4.7

方差表示数据的离散程度,离散程度越大,值越大
因此,选择第1维特征 x1x_1x1​ 作为根节点

  • 2、选取中位数作为划分点

x1x_1x1​取值为2,4,5,7,8,92,4,5,7,8,92,4,5,7,8,9中位数取7来划分,小于该值的放在左子树,大于该值的放在右子树

在这里插入图片描述

  • 3、特征空间划分:

在这里插入图片描述

(2)KD tree查找最近邻

当我们生成KD树以后,就可以取预测测试集里面的样本目标点了。

  • 对于一个目标点,我们首先在KD树里面寻找包含目标点的叶子节点;
  • 以目标点为圆心,以目标点到叶子节点样本实例的距离为半径,得到一个超球体,最近邻的点一定在这个超球体内部;
  • 然后返回叶子节点的父节点,检查另一个子节点包含的超矩形体是否和超球体相交,如果相交就到这个子节点寻找是否有更加近的近邻,有点话就更新最近邻;如果不相交那就直接返回父节点的父节点,在另一个子树继续搜索最近邻。
  • 当回溯到根节点时,算法就结束了,保存最近邻节点就是最终的最近邻。

假设要寻找点(2,2.5)(2,2.5)(2,2.5)的最近邻点:

在这里插入图片描述

七、KNN案例

1、鸢尾花数据分类

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

使用Logistic算法和KNN算法对鸢尾花数据进行分类,比较结果;
具体内容在代码的注释中有介绍
画出下面两张图,需要将代码整合起来,才能画出来。

代码可见:Github下的02_Logistic回归目录下05_鸢尾花数据分类.py,以及03_KNN目录下04_鸢尾花数据分类.py

2、信贷审批

在这里插入图片描述

在这里插入图片描述

具体内容将代码介绍

代码可见:Github下的02_Logistic回归目录下03_信贷审批.py,以及03_KNN目录下05_信贷审批.py

八、总结

  • 本篇主要通过简单的暴力求解的方式实现KNN算法,有助于理解KNN算法
  • 后面又介绍了KD树找K个最近邻,此算法是最快捷的
  • 最后通过sklearn库下的KNeighborsClassifier实现了两个案例,来属性KNN模型的构建
  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
K近邻算法是一种简单有效的无监督学习算法,它的基本思想是通过计算样本之间的距离来判断它们之间的相似性,从而实现分类或回归任务。下面是一个简单的K近邻算法实现过程: 1. 计算样本之间的距离,可以使用欧氏距离、曼哈顿距离等常用的距离度量方法。 2. 根据距离的大小将样本按照从近到远的顺序排序。 3. 选择前K个离目标样本最近的样本作为邻居。 4. 对于分类任务,根据邻居的标签进行投票,得票最高的标签作为目标样本的分类结果;对于回归任务,根据邻居的值进行加权平均,得到目标样本的回归预测值。 下面是一个Python实现的K近邻算法代码: ```python import numpy as np from collections import Counter class KNN: def __init__(self, k=3, distance='euclidean'): self.k = k self.distance = distance def fit(self, X, y): self.X_train = X self.y_train = y def predict(self, X): y_pred = [] for x in X: # 计算距离 distances = [] for x_train in self.X_train: if self.distance == 'euclidean': dist = np.sqrt(np.sum((x - x_train) ** 2)) elif self.distance == 'manhattan': dist = np.sum(np.abs(x - x_train)) else: raise ValueError('Invalid distance metric.') distances.append(dist) # 找到前K个邻居 k_nearest_indices = np.argsort(distances)[:self.k] k_nearest_labels = self.y_train[k_nearest_indices] # 进行投票或加权平均 if len(np.unique(k_nearest_labels)) == 1: y_pred.append(k_nearest_labels[0]) else: if self.distance == 'euclidean': weights = 1 / (distances[k_nearest_indices] + 1e-6) y_pred.append(np.bincount(k_nearest_labels, weights=weights).argmax()) elif self.distance == 'manhattan': y_pred.append(Counter(k_nearest_labels).most_common()[0][0]) return np.array(y_pred) ``` 在上面的代码中,我们使用了numpy库进行向量化计算,使用Counter类进行投票和计数操作。同时,我们还实现了两种常用的距离度量方法:欧氏距离和曼哈顿距离。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值