[统计学习方法]-李航-k近邻法(含第三章习题解答)

本文详细介绍了k近邻算法,包括距离度量(LP距离、欧式距离、曼哈顿距离、切比雪夫距离)、k值的影响以及决策方法。重点讨论了如何利用KDTree数据结构加速k近邻搜索,并通过实例展示了KDTree的构建和搜索过程。此外,还探讨了不同距离度量对分类结果的影响。
摘要由CSDN通过智能技术生成

k近邻算法是非常自然的一种分类方法,即选取k个与输入数据距离最近的数据集中的点,这k个点的多数属于哪个类,就把输入数据归类为哪个类。而这其中只要数据集,距离度量,k值,决策方法有关,下面我们就从这几个方面进行分析。(这几周太懒了,没有坚持,以后一定经常更新)

距离度量

定义

这一小节我们把所有的统计学习方法中实例进行声明:对于输入的特征向量 x ∈ R n , x\in R^n, xRn,代表特征空间是n维的,其中对于一个给定的实例 x i = ( x i ( 1 ) , x i ( 2 ) , x i ( 3 ) . . . , x i ( l ) . . . , x i ( n ) ) ⊺ x_i=(x_i^{(1)},x_i^{(2)},x_i^{(3)}...,x_i^{(l)}...,x_i^{(n)})^\intercal xi=(xi(1),xi(2),xi(3)...,xi(l)...,xi(n)).

LP距离

L p ( x i , x j ) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ p ) 1 p ; p ≥ 1 L_p(x_i,x_j)=\bigg(\sum_{l=1}^n|x_i^{(l)}-x_j^{(l)}|^p\bigg)^{\frac{1}{p}};p\ge1 Lp(xi,xj)=(l=1nxi(l)xj(l)p)p1;p1

欧式距离

就是LP距离中 p = 2 p=2 p=2的情况:
L ( x i , x j ) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ 2 ) 1 2 L(x_i,x_j)=\bigg(\sum_{l=1}^n|x_i^{(l)}-x_j^{(l)}|^2\bigg)^{\frac{1}{2}} L(xi,xj)=(l=1nxi(l)xj(l)2)21

曼哈顿距离

就是LP距离中 p = 1 p=1 p=1的情况:
L ( x i , x j ) = ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ L(x_i,x_j)=\sum_{l=1}^n|x_i^{(l)}-x_j^{(l)}| L(xi,xj)=l=1nxi(l)xj(l)

切比雪夫距离

就是LP距离中 p = ∞ p=\infty p=的情况:
L ( x i , x j ) = m a x l ∣ x i ( l ) − x j ( l ) ∣ L(x_i,x_j)=\underset{l}{\mathrm max}|x_i^{(l)}-x_j^{(l)}| L(xi,xj)=lmaxxi(l)xj(l)

k值

很自然的,选择k值对整个模型是至关重要的,但是并不好确定。因为k值太小,则会导致输入点仅和输入点最近的实例点非常敏感,也就是发生过拟合。而k值太大,则会导致太过于照顾全局,而分类不准,也就是发生欠拟合。

决策方法

k近邻一般采用多数表决规则。

KD Tree

背景

k近邻的方法中最为关键的一步是寻找k个近邻的数据点,如果全量搜索工作量会很大,效率也是难以忍受的,因此KD Tree这种数据结构被提出用于方便寻找k近邻的搜索过程。

约定和说明

  • 划分点都为指定维度的中位数,这样得到的是平衡KD Tree
  • 平衡的KD Tree并不意味着搜索性能最高
  • 注意KD Tree中的k指的是每个数据的维度为k,和感知机一节的 n n n等效,但为了保持叫法我们还是叫做KD Tree,但k近邻指的是个数。两个k的含义并不相同。

构建方法

KD Tree的基本思想就是依次在数据的k个维度上进行切分,直至不能切分为止,切分的标准就是尽量使得切分后的空间内实例点个数相等。
举一个例子感受一下,有以下数据:
T = { ( 1 , 2 , 2 ) , ( 2 , 3 , 4 ) , ( 3 , 2 , 4 ) , ( 4 , 3 , 6 ) , ( 6 , 4 , 7 ) , ( 3 , 5 , 2 ) } T=\{(1,2,2),(2,3,4),(3,2,4),(4,3,6),(6,4,7),(3,5,2)\} T={(1,2,2),(2,3,4),(3,2,4),(4,3,6),(6,4,7),(3,5,2)}

第一维度小于3
第一维度大于等于3
第二维度小于3
第二维度小于3
第二维度小于4
第二维度大于等于4
(3,2,4),(3,5,2)
(2,3,4)
(6,4,7)
(1,2,2)
(4,3,6)

至此基于训练集 T T T的KD Tree就构建完成了。

搜索方法

以搜索最近邻的点为例,k近邻和最近邻类似

  1. 从上而下叶节点确定:从根节点开始,依次比较不同维度的数寻找到目标点所在的叶节点。
  2. 认定当前叶节点为最近邻点,然后计算距离成为最近邻距离,往上回溯至父节点1,判断目标点是否与父节点1的距离是否小于最近邻距离,如果小于,将该父节点1作为最近邻点,如果大于,判断目标点为中心,最近邻为半径的超球体是否与该父节点1所在的轴相交,如果相交,则对父节点1另一侧的空间进行搜索,如果不相交,则向上回退至父节点2。直至回到根节点。

算法复杂度为 l o g ( N ) log(N) log(N)

习题解答

3.1在二维空间中给出实例点,画出k=1和k=2时的k近邻法构成的特征划分。
回过头看图3.1,发现有些问题,在这里提出来,希望大家轻拍,这个图是有问题的,对于k=1的时候,交点应该偏好于三个相对近邻点的外心,因此不会形成砖块一样超出的形状,如下图红圈圈出的位置。(**可能是距离度量不是欧式距离? **)
k近邻的一个特征划分
为了验证我的想法,花了十分种简单写了段代码,用来复现图中的工作:

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(233)
train_x = np.array([[i for i in range(1000)] for j in range(1000)]).reshape(-1)
train_y = np.array([[j for i in range(1000)] for j in range(1000)]).reshape(-1)
test_x = np.random.rand(10)*1000
test_y = np.random.rand(10)*1000

label = []

for i in range(len(train_x)):
    distance = float('inf')
    for j in range(len(test_x)):
        temp = (test_y[j] - train_y[i]) ** 2 + (test_x[j] - train_x[i]) ** 2
        if temp < distance:
            distance = temp
            l = j
    label.append(l)

plt.figure(figsize=(10,10))
plt.scatter(train_x,train_y,c=label)
plt.scatter(test_x,test_y,c='red')

结果显示为:
欧氏距离

除了欧式距离,我还依次尝试了曼哈顿距离和切比雪夫距离:
曼哈顿距离
在这里插入图片描述

但没有一种是李航老师书中那种划分方式,因此我十分困惑,希望有读者知道原因的不吝赐教。
好了,回到正题:
k=1上面已经说了一部分,当k=2时,情况就有些复杂,如果两个最近邻点不属于同一类,此时我们将其划分为第三类也就是待决策类。为了更清楚说明,对于k=2的时候,我们将10个点分为2类,采用欧式距离进行计算。
k=2,对上面代码稍微修改

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(233)
train_x = np.array([[i for i in range(1000)] for j in range(1000)]).reshape(-1)
train_y = np.array([[j for i in range(1000)] for j in range(1000)]).reshape(-1)

test_x = np.random.rand(10)*1000
test_y = np.random.rand(10)*1000
test_label = [0]*5+[1]*5
label = []

for i in range(len(train_x)):
    distance = []
    for j in range(len(test_x)):
        temp = (test_y[j] - train_y[i]) ** 2 + (test_x[j] - train_x[i]) ** 2
        distance.append(temp)
    l_1,l_2 = np.argsort(distance)[:2]
    if test_label[l_1] == test_label[l_2]:
        label.append(test_label[l_1])
    else:
        label.append(-1)

plt.figure(figsize=(10,10))
plt.scatter(train_x,train_y,c=label)
plt.scatter(test_x[:5],test_y[:5],c='red',marker='x',label=0)
plt.scatter(test_x[5:],test_y[5:],c='red',label=1)
plt.legend()

k=2
注意:紫色为待定的区域
k=1时,修改label标注选择即可:

   # l_1,l_2 = np.argsort(distance)[:2]
   # if test_label[l_1] == test_label[l_2]:
   #     label.append(test_label[l_1])
   # else:
   #     label.append(-1)
   ##上面的代码改为
    l_1,l_2 = np.argsort(distance)[:2]
    label.append(test_label[l_1])

在这里插入图片描述

可以明显看出,两者差别还是非常大的,基本上k=2是k=1的子区域,相当于k
2将k=1时的置信度更加提高了,也就是只有最近的两个点都表决为同一类时才能确定为此类。
3.2 搜索路径为:
(4,7),(5,4),(2,3),(7,2)
3.3 参照算法3.3,写出输出为x的k近邻的算法
待补充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值