第三章 k近邻法笔记

让人想犯罪 __ 提交于 2020-01-12 02:51:03

CH03 k近邻法

在这里插入图片描述

前言

章节目录

  1. k近邻算法
  2. k近邻模型
    1. 模型
    2. 距离度量
    3. k值选择
    4. 分类决策规则
  3. k近邻法的实现: KDTree
    1. 构造KDTree
    2. 搜索KDTree

导读

kNN是一种基本分类与回归方法。

  • kNN是机器学习中被分析的最透彻的算法之一。
  • 多数表决规则等价于0-1损失函数下的经验风险最小化,支持多分类, 有别于前面的感知机算法
  • kNN的k和KDTree的k含义不同,书上这部分有注释说明(最近邻的k个点;k维空间)
  • KDTree是一种存储k维空间数据的树结构,KDTree是平衡二叉树
  • KNN应用的一个实践问题是如何建立高效的索引。建立空间索引的方法在点云数据处理中也有广泛的应用,KDTree和八叉树在3D点云数据组织中应用比较广
  • 书中的KDTree搜索实现的时候针对了一种k=1k=1的特殊的情况,实际是近邻搜索
  • KDTree的搜索问题分为k近邻查找范围查找,一个是已知kk,求点集范围,一个是已知范围,求里面有k个点。范围查找问题在维度高的时候复杂度非常高,不太推荐用KDTree做范围查找。
  • K近邻问题在杭电ACM里面有收录,HUD4347
  • 图像的特征点匹配,数据库查询,图像检索本质上都是同一个问题–相似性检索问题。Facebook开源了一个高效的相似性检索工具Faiss,用于有效的相似性搜索和稠密矢量聚类。
  • 这一章有个经典的图,在很多文章和教材上都能看到,就是第一个图3.1。这个图画出了1NN算法在实例空间上的决策面形状。这种类型的图经常被称为在训练集上的的Voronoi图,也叫Thiessen Polygons。可以通过检索Delaunay三角剖分和Voronoi划分进一步了解。
  • 在scipy.spatial.KDTree中有KDTree的实现,KDTree在创建马赛克照片的时候可以用到。

最近邻算法

k=1k=1的情形,称为最近邻算法。书中后面的分析都是按照最近邻做例子,这样不用判断类别,可以略去一些细节。

k近邻模型

算法

输入: KaTeX parse error: Undefined control sequence: \cal at position 51: …y_N)\}, x_i\in \̲c̲a̲l̲{X}\sube{\bf{R}…; 实例特征向量xx

输出: 实例所属的yy

步骤:

  1. 根据指定的距离度量,在TT中查找xx最近邻的kk个点,覆盖这kk个点的xx的邻域定义为Nk(x)N_k(x)

  2. Nk(x)N_k(x)中应用分类决策规则决定xx的类别yy
    y=argmaxcjxiNk(x)I(yi=cj),i=1,2,,N,j=1,2,,K y=\arg\max_{c_j}\sum_{x_i\in N_k(x)}I(y_i=c_j), i=1,2,\dots,N, j=1,2,\dots,K

这里提到了kk近邻模型的三要素,如算法描述中黑体标注的部分, 注意这里的三要素和前面说的统计学习方法的三要素不是一个东西。后面讲到隐马尔可夫模型的时候也有三要素。

在这里插入图片描述

距离度量

特征空间中的两个实例点的距离是两个实例点相似程度的反映。

书中是如上描述的,这里要注意距离越近(数值越小), 相似度越大

这里用到了LpL_p距离, 可以参考Wikipedia上LpL_p Space词条[^1]

  1. p=1p=1 对应 曼哈顿距离
  2. p=2p=2 对应 欧氏距离
  3. 任意pp 对应 闵可夫斯基距离

Lp(xi,xj)=(l=1nxi(l)xj(l)p)1pL_p(x_i, x_j)=\left(\sum_{l=1}^{n}{\left|x_{i}^{(l)}-x_{j}^{(l)}\right|^p}\right)^{\frac{1}{p}}

在这里插入图片描述

考虑二维的情况,上图给出了不同的pp值情况下与原点距离为1的点的图形。

这个图有几点理解下:

  1. 与原点的距离
  2. 与原点距离为1的点
  3. 前一点换个表达方式,图中的点向量(x1x_1, x2x_2)的pp范数都为1
  4. 图中包含多条曲线,关于p=1并没有对称关系
  5. 定义中p1p\geqslant1,这一组曲线中刚好是凸的

这里要补充一点:

范数是对向量或者矩阵的度量,是一个标量,这个里面两个点之间的LpL_p距离可以认为是两个点坐标差值的pp范数。

参考下例题3.1的测试案例,这个实际上没有用到模型的相关内容。

不同的距离度量确定的最近近邻点是不同的

kk值选择

  1. 关于kk大小对预测结果的影响,书中给的参考文献是ESL,这本书还有个先导书叫ISL。
  2. 通过交叉验证选取最优kk,算是超参数
  3. 二分类问题,kk选择奇数有助于避免平票

k值过小——噪声影响——模型复杂过拟合

k值过大——远端样本影响——模型简单欠拟合

k=N时结果永远是类别最多的那一类

分类决策规则

Majority Voting Rule

误分类率

1kxiNk(x)I(yici)=11kxiNk(x)I(yi=ci)\frac{1}{k}\sum_{x_i\in N_k(x)}{I(y_i\ne c_i)}=1-\frac{1}{k}\sum_{x_i\in N_k(x)}{I(y_i= c_i)}

如果分类损失函数是0-1损失,误分类率最低即经验风险最小

关于经验风险,参考书上CH01第一章 (1.11)和(1.16)

实现

主要考虑的问题:如何对训练数据进行快速 k 近邻搜索

kNN在实现的时候,要考虑多维数据的存储,这里会用到树结构。

在Scipy Cookbook里面有个kd树具体的实现[^2]可参考

构造KDTree

KDTree的构建是一个递归的过程

注意: KDTree左边的点比父节点小,右边的点比父节点大。

这里面有提到,KDTree搜索时效率未必是最优的,这个和样本分布有关系。随机分布样本KDTree搜索(这里应该是近邻搜索)的平均计算复杂度是O(logN)O(\log N)空间维数KK接近训练样本数NN时,搜索效率急速下降,几乎O(N)O(N)

看维度,如果维度比较高,搜索效率很低。当然,在考虑维度的同时也要考虑样本的规模。

在这里插入图片描述

考虑个例子

[[1, 1],
 [2, 1],
 [3, 1],
 [4, 1],
 [5, 1],
 [6, 1],
 [100, 1][1000, 1]]

这个数据,如果找[100, 1]

搜索KDTree

这部分书中的例子是最近邻的搜索例子。

kk近邻查找

KNN查找已知查询点pp,树当前节点oo,近邻数目kk

可以用一个优先队列存储最优的kk个点,每次比对回溯节点是否比当前最优点更优的时候,就只需用当前最优中距离pp最远的节点来对比,而这个工作对于优先队列来说是O(1)O(1)的[^3]

范围查询

给定一个范围,问其中有多少点。比较常见的应用是GIS类应用,使用者附近多大半径内包含多少单车,多少酒店等。

实际上为了实现快速搜索, 在空间数据的存储结构上要有考虑。

例子

例3.1

分析pp值对最近邻点的影响,这个有一点要注意关于闵可夫斯基距离的理解:

  • 两点坐标差的pp范数

具体看相关测试案例的实现

例3.2

KDTree创建

例3.3

KDTree搜索

graph TD
	subgraph 对应图3.5
	A[A]---B((B))
	A---C((C))
	B(B)---F((F))
	B---D((D))
	C(C)---G((G))
	C---E((E))
	end


这个例子说明了搜索的方法,理解一下书中的图3.5,对应的KDTree如上。

其他资料

KNN 特点

KNN是一种非参的惰性的算法模型。什么是非参,什么是惰性呢?

非参的意思并不是说这个算法不需要参数,而是意味着这个模型不会对数据做出任何的假设,与之相对的是线性回归(我们总会假设线性回归是一条直线)。也就是说KNN建立的模型结构是根据数据来决定的,这也比较符合现实的情况,毕竟在现实中的情况往往与理论上的假设是不相符的。

惰性又是什么意思呢?想想看,同样是分类算法,逻辑回归需要先对数据进行大量训练(tranning),最后才会得到一个算法模型。而KNN算法却不需要,它没有明确的训练数据的过程,或者说这个过程很快。

KNN算法的优势和劣势

了解KNN算法的优势和劣势,可以帮助我们在选择学习算法的时候做出更加明智的决定。那我们就来看看KNN算法都有哪些优势以及其缺陷所在!

  • 优点
  1. 简单易用,相比其他算法,KNN算是比较简洁明了的算法。即使没有很高的数学基础也能搞清楚它的原理。
  2. 模型训练时间快,上面说到KNN算法是惰性的,这里也就不再过多讲述。
  3. 预测效果好。
  4. 对异常值不敏感
  • 缺点
  1. 对内存要求较高,因为该算法存储了所有训练数据
  2. 预测阶段可能很慢
  3. 对不相关的功能和数据规模敏感

简单得说,当需要使用分类算法,且数据比较大的时候就可以尝试使用KNN算法进行分类了。

Sklearn 实现

def KNeighborsClassifier(n_neighbors = 5,
                       weights='uniform',
                       algorithm = '',
                       leaf_size = '30',
                       p = 2,
                       metric = 'minkowski',
                       metric_params = None,
                       n_jobs = None
                       )
                                        
- 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': 默认参数,自动选择合适的方法构建模型
不过当数据较小或比较稀疏时,无论选择哪个最后都会使用 'brute'
        
- leaf_size:如果是选择蛮力实现,那么这个值是可以忽略的,当使用KD树或球树,它就是是停止建子树的叶子节点数量的阈值。默认30,但如果数据量增多这个参数需要增大,否则速度过慢不说,还容易过拟合。
- p:和metric结合使用的,当metric参数是"minkowski"的时候,p=1为曼哈顿距离, p=2为欧式距离。默认为p=2- metric:指定距离度量方法,一般都是使用欧式距离。
        • 'euclidean' :欧式距离
        • 'manhattan':曼哈顿距离
        • 'chebyshev':切比雪夫距离
        • 'minkowski': 闵可夫斯基距离,默认参数
- n_jobs:指定多少个CPU进行运算,默认是-1,也就是全部都算。

参考链接

深入浅出KNN算法(一) 介绍篇

深入浅出KNN算法(二) 实践篇

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!