机器学习系列(3)——k近邻法(k-NN)

本文介绍k近邻法(k-nearest neighbor, k-NN)和kd树算法。

 

0x01、k近邻法简介

k近邻法是基本且简单的分类与回归方法。k近邻法的基本做法是:对给定的训练实例点和输入实例点,首先确定输入实例点的k个最近邻训练实例点,然后利用这k个训练实例点的类的多数来预测输入实例点的类。

k近邻模型对应于基于训练数据集对特征空间的一个划分。k近邻法中,当训练集、距离度量、k值及分类决策规则确定后,其结果唯一确定。常用的距离度量是欧氏距离及更一般的 L_p 距离。k 值小时,k近邻模型更复杂,k值大时,k近邻模型更简单。k值的选择反映了对近似误差与估计误差之间的权衡,通常由交叉验证选择最优的k。常用的分类决策规则是多数表决,对应于经验风险最小化。

k近邻法的实现需要考虑如何快速搜索k个最近邻点。kd树是一种便于对k维空间中的数据进行快速检索的数据结构,kd树是二叉树,表示对k维空间的一个划分,其每个结点对应于k维空间划分中的一个超矩形区域。利用kd树可以省去对大部分数据点的搜索,从而减少搜索的计算量。

 

0x02、k近邻模型

k近邻法使用的模型实际上对应于特征空间的划分。模型由三个基本要素:距离度量、k值的选择和分类决策规则决定。特征空间中,对于每个训练实例点,距离该点比其他点更近的所有点组成一个区域,叫做单元(cell)。每个训练实例点拥有一个单元,所有训练实例点的单元构成对特征空间的一个划分。最近邻法将实例 x_i 的类 y_i 作为其单元中所有点的类标记(class label),这样,每个单元的实例点的类别是确定的。

1、距离度量

设特征空间 \chi 是 n 维实数向量空间 R^nx_i, x_j \in \chi, x_i=(x_{i}^{(1)},x_{i}^{(2)},...,x_{i}^{(n)})^T ,x_j=(x_{j}^{(1)},x_{j}^{(2)},..., x_{j}^{(n)})^T ,x_i, x_j 的 L_p 距离定义为:

        L_p(x_i,x_j)=\left\(\sum_{i=1}^{n}|x_{i}^{(l)}-x_{j}^{(l)}|^p \right\)^{\frac{1}{p}}  

这里 p\geq 1

当 p=2 时,称为欧氏距离(Euclidean distance),即:

        L_2(x_i,x_j)=\left\(\sum_{l-1}^{n}|x_{i}^{(l)}-x_{j}^{(l)}|^2 \right\)^{\frac{1}{2}}  

当 p=1 时,称为曼哈顿距离(Manhattan distance),即:

        L_1(x_i,x_j)=\sum_{l=1}^{n}|x_{i}^{(l)}-x_{j}^{(l)}|  

当 p=\infty 时,它是各个坐标距离的最大值(闵氏距离),即:

        L_{\infty}(x_i,x_j)=\max_{l}|x_{i}^{(l)} - x_{j}^{(l)}|   

2、k值的选择

k值的选择会对k近邻法的结果产生重大影响。k值的减小就意味着整体模型变得复杂,容易发生过拟合;k值的增大意味着整体的模型变得简单。

如果选择较小的k值,就相当于用较小的邻域中的训练实例进行预测,“学习”的近似误差(approximation error)会减小,只有与输入实例较近的(相似的)训练实例才会对预测结果起作用。但缺点是“学习”的估计误差(estimation error)会增大,预测结果会对近邻的实例点非常敏感。如果邻近的实例点恰巧是噪声,预测就会出错。

如果选择较大的k值,就相当于用较大邻域中的训练实例进行预测。其优点是可以减少学习的估计误差,缺点是学习的近似误差会增大,这时与输入实例较远的(不相似的)训练实例也会对预测起作用,使预测发生错误。

在应用中k值一般取一个比较小的数值。通常采用交叉验证法来选取最优的k值。

3、分类决策规则

k近邻法中的分类决策规则往往是多数表决,即由输入实例的k个邻近的训练实例中的多数类决定输入实例的类。

多数表决规则(majority voting rule):如果分类的损失函数为0-1损失函数,分类函数为:f:R^{n}\rightarrow \left\{c_1,c_2,...,c_K \right\} ,

那么误分类的概率是:P(Y\neq f(X)) = 1-p(Y=f(X)) 。对给定的实例 x \in \chi ,其近邻的k个训练实例点构成集合 N_k(x) 。如果涵盖 N_k(x) 的区域的类别是 c_j ,那么误分类率是:

        \frac{1}{k} \sum_{x_i \in N_k(x)} I(y_i \neq c_j) = 1-\frac{1}{k} \sum_{x_i \in N_k(x)} I(y_i,c_j)  

要是误分类率最小即经验风险最小,就要使   \sum_{x_i \in N_k(x)} I(y_i=c_j)  最大,所以多数表决规则等价于经验风险最小。

4、kd树

kd树表示对k维空间的一个划分(partition),构造kd树相当于不断地用垂直于坐标轴的超平面将k维空间切分,构成一系列的k维超矩形区域,kd树的每个结点对应于一个k维超矩形区域。

(1)构造kd树的方法

方法如下:构造根结点,使根结点对应于k维空间中包含所有实例点的超矩形区域:通过递归,不断地对k维空间进行切分,生成子节点。通常,依次选择坐标轴对空间切分,选择训练实例点在选定坐标轴上的中位数(median)为切分点,这样得到的kd树是平衡的。注意平衡的kd树搜索时的效率未必是最优的。

(2)搜索kd树

给定一个目标点,搜索其最近邻。首先找到包含目标点的叶结点,然后从该叶结点出发,依次回退到父结点,不断查找与目标点最邻近的结点,当确定不可能存在更近的结点时终止。这样搜索就被限制在空间的局部区域上,效率大为提高。

 

0x03、k近邻算法

1、基本k近邻算法

k近邻算法简单直观:给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的k个实例,这k个实例的多数属于某个类,就把该输入实例分为这个类。k近邻法没有显式的 学习过程。

输入:训练数据集 T=\left\{(x_1,y_1),(x_2,y_2),..., (x_N,y_N) \right\} ,其中,x_i \in \chi \sqsubseteq R^n  为实例的特征向量,y_i \in \gamma = \left\{ c_1,c_2,..., c_K \right\} 为实例的类别,i=1,2,...,N ;实例特征向量 x ;

输出:实例 x 所属的类 y

(1)根据给定的距离度量,在训练集 T 中找出与 x 最邻近的 k 个点,涵盖这 k 个点的 x 的邻域记作 N_k(x) ;

(2)在 N_k(x) 中根据分类决策规则(如多数表决)决定 x 的类别 y

         y= arg~\min_{c_j}\sum_{x_i \in N_k(x)}I(y_i=c_j),~~i=1,2,...,N;~~j=1,2,...,K  

其中,I 为指示函数,即当 y_i=c_j 时 I 为1,否则 I 为0 。

2、构造平衡kd树算法

输入:k 维空间数据集 T=\left\{(x_1,y_1),(x_2,y_2),..., (x_N,y_N) \right\} ,其中 x_i=(x_{i}^{(1)},x_{i}^{(2)},...,x_{i}^{(n)})^T, ~i=1,2,...,N ;

输出: kd树 。

(1)开始:构造根结点,根结点对应于包含 T 的k维空间的超矩形区域。

选择 x^{(1)} 为坐标轴,以 T 中所有的实例的 x^{(1)} 坐标的中位数为切分点,将根结点对应的超矩形区域切分为两个字区域。切分由通过切分点并与坐标轴 x^{(1)} 垂直的超平面实现。

由根结点生成深度为1 的左右子结点:左子结点对应坐标 x^{(1)} 小于切分点的子区域,右子结点对应于坐标 x^{(1)} 大于切分点的子区域。

将落在切分点超平面上的实例点保存在根结点。

(2)重复:对深度为 j 的结点,选择 x^{(l)} 为切分点的坐标轴,l=j(mod k) + 1,以该结点的区域中所有实例的 x^{(l)} 坐标的中位数为切分点,将该结点对应的超矩形区域切分为两个子区域。切分由通过切分点并与坐标轴 x^{(l)} 垂直的超平面实现。

由该结点生成深度为 j+1 的左右子结点:左子结点对应坐标 x^{(l)} 小于切分点的子区域,右子结点对应坐标 x^{(l)} 大于切分点的子区域。将落在切分超平面上的实例点保存在该结点。

(3)知道两个子区域没有实例存在时停止,从而形成kd树的区域划分。

3、用kd树的最近邻搜索算法

输入:已构造的kd树,目标点 x ;

输出:x 的最近邻。

(1)在kd树中找出包含目标点 x 的叶结点:从根结点出发,递归地向下访问kd树。若目标点 x 当前维的坐标小于切分点的坐标,则移动到左子结点,否则移动到右子结点。知道子结点为叶结点为止;

(2)以此叶结点为“当前最近点”;

(3)递归地向上回退,在每个结点进行以下操作:

        (a)如果该结点保存的实例点比当前最近点距离目标点更近,则以该实例点为“当前最近点”;

        (b)当前最近点一定存在与该结点一个子结点对应的区域。检查该子结点的父结点的另一子结点对应的区域是否有更近的点。具体地,检查另一子结点对应的区域是否与以目标点为球心、以目标点与“当前最近点”间的距离为半径的超球体相交。如果相交,可能在另一个子结点对应的区域内存在距目标点更近的点,移动到另一个子结点,接着递归地进行最近邻搜索。如果不相交,向上回退。

(4)当回退到根结点时,搜索结束。最后的“当前最近点”即为 x 的最近邻点。

 

0x04、k近邻法在sklearn中的实现

sklearn中的API接口是:sklearn.neighbors。常用方法有:sklearn.neighbors.KNeighborsClassifier等。

参数 参数类型 默认值 参数含义 详细说明
n_neighbors int 5 邻近的结点数量 k近邻中的邻近结点数量。
weights {'uniform','distance'}
或 指定向量
"uniform" 权重 预测中使用的权重。
Uniform:所有结点权重相等;
distance:离簇中心越近的点权重越高;
callable: 用户自定义数据点权重。
algorithm {'auto','ball_tree',
'kd_tree','brute'}
"auto" 计算最近邻结点的算法 auto:自动选择算法;
ball_tree:使用BallTree;
kd_tree:使用KDTree;
brute:使用brute-force搜索。
leaf_size int 30 构造树的大小 leaf_size传递给BallTree或者KDTree,表示构造树的大小,用于影响模型构建的速度和树需要的内存数量,最佳值是根据数据来确定的
p int 2 设置Minkowski 距离的Power参数 当p=1时,等价于曼哈顿距离;当p=2等价于欧拉距离;当p>2时,就是Minkowski距离。
metric 字符串 或 自定义 "minkowski" 设置计算距离的方法 树计算距离的度量
metric_params dict None 传递给计算距离方法的参数 附加的关键词
n_jobs int None 并发执行 并发执行的job数量,用于查找邻近的数据点。默认值1,选取-1占据CPU比重会减小,但运行速度也会变慢,所有的core都会运行。

 

参考:

[1]. 李航. 统计学习方法(第二版)

[2]. sklearn学习第三篇:knn分类 - 博客园

你可能感兴趣的:(机器学习)