统计学习方法学习笔记——K近邻法
k k k近邻法(k-nearest neighbor, k-NN)是一种基本 分类与回归方法。这里只讨论分类问题中的k近邻法。 k近邻法的输入为实例的特征向量,对应于特征空间中点;输出为实例的类别,可以取多类。k近邻法假设给定一个训练数据集,其中的实例类别已定。分类时,对新的实例,根据其k个最近邻的训练实例的类别,通过多数表决等方法进行预测。因此,
k近邻法不具有显示的学习过程
。k近邻法实际上利用训练数据集对特征向量空间进行划分,并作为其分类的“模型”。
k值的选择、
距离度量以及
分类决策规则是k近邻法的
三个基本要素。
1. k近邻算法
给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的k个实例,这k个实例的多数属于某个类,就把该输入实例分为这个类。
算法1:k近邻算法
输入:训练数据集
T
=
{
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
.
.
.
,
(
x
N
,
y
N
)
}
T=\{(x_1,y_1),(x_2,y_2),...,(x_N,y_N)\}
T={(x1,y1),(x2,y2),...,(xN,yN)},其中,
x
i
∈
χ
⊆
R
n
x_i\in \chi \subseteq R^n
xi∈χ⊆Rn为实例的特征向量,
y
i
∈
Y
=
{
c
1
,
c
2
,
.
.
.
,
c
K
}
y_i\in Y = \{c_1,c_2,...,c_K\}
yi∈Y={c1,c2,...,cK}为实例的类别,
i
=
1
,
2
,
.
.
.
,
N
i=1,2,...,N
i=1,2,...,N;实例特征向量
x
x
x;
输出:实例
x
x
x所属的类
y
y
y。
(1)根据给定的距离度量,在训练集T中找出与x最邻近的k个点,涵盖了这k个点的x的邻域记为
N
k
(
x
)
N_k(x)
Nk(x);
(2)在
N
k
(
x
)
N_k(x)
Nk(x)中根据分类决策规则(如多数表决)决定x的类别y:
y
=
a
r
g
max
c
j
∑
x
i
∈
N
k
x
I
(
y
i
=
c
j
)
,
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,...,N;j=1,2,...,K
y=argcjmaxxi∈Nkx∑I(yi=cj),i=1,2,...,N;j=1,2,...,K,上式中,
I
I
I为指示函数,即当
y
i
=
c
j
y_i=c_j
yi=cj时I为
1
1
1,否则
I
I
I为
0
0
0。
k近邻法的特殊情况是
k
=
1
k=1
k=1的情形,称为最近邻算法。对于输入的实例点(特征向量)x,最近邻法将训练数据集中与x最邻近点的类作为x的类。
注意:k近邻法没有显式的学习过程。
2. k近邻模型
k近邻模型的核心就是使用一种距离度量,获得距离目标点最近的k个点,根据分类决策规则,决定目标点的分类。所以k近邻法使用的模型实际上对应于特征空间的划分。模型由三个基本要素——距离度量
,k值的选择
和分类决策规则
决定。
2.1模型
特征空间中,对每个训练实例点 x i x_i xi,距离该点比其他点更近的所有点组成一个区域,叫做单元(cell)。每个训练实例点拥有一个单元,所有训练实例点的单元构成对特征空间的一个划分。最近邻法将实例 x y x_y xy的类 y i y_i yi作为其单元中所有点的类标记(class label)。这样,每个单元的实例点的类别是确定的。
2.2距离度量
特征空间中两个实例点的距离是两个实例点相似程度的反映。k近邻模型的特征空间一般是n维实数向量空间
R
n
R^n
Rn。使用的距离是欧式距离,但也可以是其他距离,如更一般的
L
p
L_p
Lp距离(
L
p
L_p
Lp distance)或Minkowski距离(Minkowski distance)。
我们熟知的欧式距离,则是
L
2
L_2
L2范式,也就是p=2的情况,而另一个很熟悉的距离曼哈顿距离,则是
L
1
L_1
L1范式。
设特征空间
χ
\chi
χ是n维实数向量空间
R
n
R^n
Rn,
x
i
,
x
j
∈
χ
x_i,x_j \in \chi
xi,xj∈χ,
x
i
=
(
x
i
(
1
)
,
x
i
(
2
)
,
.
.
.
,
x
i
(
n
)
)
T
x_i=(x_{i}^{(1)}, x_{i}^{(2)},...,x_i^{(n)})^T
xi=(xi(1),xi(2),...,xi(n))T,
x
j
=
(
x
j
(
1
)
,
x
j
(
2
)
,
.
.
.
,
x
j
(
n
)
)
T
x_j=(x_{j}^{(1)}, x_{j}^{(2)},...,x_j^{(n)})^T
xj=(xj(1),xj(2),...,xj(n))T,
x
i
,
x
j
x_i,x_j
xi,xj的
L
p
L_p
Lp距离的定义如下:
L
p
(
x
i
,
x
j
)
=
(
∑
l
=
1
N
∣
x
i
(
l
)
−
x
j
(
l
)
∣
p
)
1
p
L_p(x_i,x_j)=(\sum_{l=1}^N |x_i^{(l)}-x_j^{(l)}|^p)^{\frac{1}{p}}
Lp(xi,xj)=(l=1∑N∣xi(l)−xj(l)∣p)p1,这里
p
>
=
1
p>=1
p>=1。
当p=2时,称为欧氏距离(Euclidean distance)
,即
L
2
(
x
i
,
x
j
)
=
(
∑
l
=
1
N
∣
x
i
(
l
)
−
x
j
(
l
)
∣
2
)
1
2
L_2(x_i,x_j)=(\sum_{l=1}^N |x_i^{(l)}-x_j^{(l)}|^2)^{\frac{1}{2}}
L2(xi,xj)=(l=1∑N∣xi(l)−xj(l)∣2)21
当p=1时,称为曼哈顿距离(Manhattan distnace)
,即
L
1
(
x
i
,
x
j
)
=
∑
l
=
1
N
∣
x
i
(
l
)
−
x
j
(
l
)
∣
L_1(x_i,x_j)=\sum_{l=1}^N |x_i^{(l)}-x_j^{(l)}|
L1(xi,xj)=l=1∑N∣xi(l)−xj(l)∣
当p=∞时,称为切比雪夫距离(Chebyshev distance)
,它是各个坐标距离的最大值,即
L
∞
(
x
i
,
x
j
)
=
max
l
∣
x
i
(
l
)
−
x
j
(
l
)
∣
L_\infty (x_i,x_j)=\max_l|x_i^{(l)}-x_j^{(l)}|
L∞(xi,xj)=lmax∣xi(l)−xj(l)∣。
2.3 k值的选择
k值的选择会对k邻近法的结果产生重要影响。因为如果k的选择小了,则近似误差(approximation error)会减小,但估计误差(estimation error)会增大,k值的减少就意味着整体模型变得复杂,容易发生过拟合;相反k的选择大了,则近似误差会增大,估计误差会减小。这一点,我们会在近似误差与估计误差那一部分进一步讲解。
在实际中,k值一般选取一个比较小的数值。通常采用交叉验证法来选取最优的k值。
2.4 分类决策规则
k近邻的分类决策规则往往是多数表决,也就是在最近的K个点中,哪个标签数目最多,就把目标点的标签归于哪一类。实际上,也是可行的,也是唯一可行的分类决策规则。无论是全体一致规则(一票否决制)还是绝对多数规则,都不能在任何时候对目标点做出确切的预测,更不用提少数原则这种不靠谱的决策规则了。
多数表决规则(majority voting rule)
有如下解释:如果分类的损失函数为0-1损失函数,分类函数为
f
:
R
n
→
{
c
1
,
c
2
,
.
.
.
,
c
k
}
f:R^n\rightarrow \{c_1,c_2,...,c_k\}
f:Rn→{c1,c2,...,ck},那么误分类的概率是
P
(
Y
≠
f
(
X
)
)
=
1
−
P
(
Y
=
f
(
X
)
)
P(Y\ne f(X))=1-P(Y=f(X))
P(Y=f(X))=1−P(Y=f(X))
对于给定的实例
x
∈
χ
x\in \chi
x∈χ,其最近邻的k个训练实例点构成集合
N
k
(
x
)
N_k(x)
Nk(x)。如果涵盖
N
k
(
x
)
N_k(x)
Nk(x)的区域的类别是
c
j
c_j
cj,那么误分类率是
1
k
∑
x
i
∈
N
k
(
x
)
I
(
y
i
≠
c
j
)
=
1
−
1
k
∑
x
i
∈
N
k
(
x
)
I
(
y
i
=
c
j
)
\frac{1}{k}\sum_{x_i\in N_k(x)} I(y_i\ne c_j)=1-\frac{1}{k}\sum_{x_i\in N_k(x)}I(y_i=c_j)
k1xi∈Nk(x)∑I(yi=cj)=1−k1xi∈Nk(x)∑I(yi=cj)
要使得误分类率最小,也就是经验风险最小,就要使得
∑
x
i
∈
N
k
(
x
)
I
(
y
i
=
c
j
)
\sum_{x_i\in N_k(x)}I(y_i=c_j)
∑xi∈Nk(x)I(yi=cj)最大,所以多数表决规则等价于经验最小化。
其实,还可以使用权重加权的多数表决,对于K个最近点,根据其距离的远近来进行加权统计,从而获得一个折中的效果。
3. 估计误差与近似误差
3.1 估计误差
估计误差
主要包含四个部分:系统误差、随机误差、过失误差、精密度和精确度。
就K近邻法来讲,如果K值比较小,那么例如像噪点,错误的数据,不恰当的度量标准以及数据本身的缺陷等,都会很大程度上影响最终的结果,而如果K值比较大,那么以上缺陷就会尽可能的平均,从而减小对最终结果的影响。
3.2 近似误差
近似误差与估计误差的描述对象不同,估计误差度量的是预测结果与最优结果的相近程度,而近似误差是度量与最优误差之间的相似程度。就K近邻法来讲,K值越小,那么与目标点相近的点的标签对于其目标点的影响也就越大,其标签的一致性就越高,这样近似误差就会变小。
3.3 两者区别与联系
总而言之,近似误差指的是目标点对于其原样本点的可信度,误差越小,对于原样本点的信任度越高,也就是说,目标点可能只需要对最近的点确认一次就可以标注自己的标签,而无需去询问其他目标点。而估计误差则是原模型本身的真实性,也就是说,该模型所表现出的分类特性,就是真实的分类特性,比如有噪点影响,有错误数据记录,或者本身数据分布就不是很好,都会是影响估计误差的因素,而询问的点越多,那么这些坏点对于目标点的标签影响就越小。
这就像是你向别人征求意见,你对于别人意见的采纳率越高,则别人意见的近似误差越小。而别人意见越符合实际情况,则估计误差越小。这么说应该有个大致的理解了吧。
4. k近邻法的实现:kd树
实现k近邻法时,主要考虑的问题是如何对训练数据进行快速k近邻搜索。
k近邻法最简单的实现方法是线性扫描(linear scan)
。这时要计算输入实例与每一个训练实例的距离。当训练集较大时,计算复杂度较高。
为了提高k近邻搜索的效率,可以考虑使用特殊的结构存储训练数据,以减少计算距离的次数。下面介绍kd树(kd tree)方法。
4.1 构造kd树
kd树是一种对k维空间中的点的实例点进行存储以便对其进行快速检索的树形数据结构。主要用于多维空间关键数据的搜索。kd树是二进制空间分割树的特殊情况。也就是说kd树是二叉树
,表示对k维空间的一个划分(partition)。构造kd树相当于不断地用垂直于坐标轴的超平面将k维空间切分,构成一系列的k维超矩形区域。kd树的每个结点对应于一个k维超矩形区域。
索引结构中相似性查询有两种基本的方式:一种是范围查询,另一种是K近邻查询。
- 范围查询就是给定查询点和查询距离的阈值,从数据集中找出所有与查询点距离小于阈值的数据;
- K近邻查询是给定查询点及正整数K,从数据集中找到距离查询点最近的K个数据,当K=1时,就是最近邻查询。
而对于这类问题,解决办法有两类:
- 一类是
线性扫描法
,即将数据集中的点与查询点逐一进行距离比较,也就是穷举
,缺点很明显,就是没有利用数据集本身蕴含的任何结构信息,搜索效率较低; - 第二类是
建立数据索引,然后再进行快速匹配
。因为实际数据一般都会呈现出簇状的聚类形态,通过设计有效的索引结构可以大大加快检索的速度。
索引树属于第二类,其基本思想就是对搜索空间进行层次划分。根据划分的空间是否有混叠可以分为Clipping
和Overlapping
两种。前者划分空间没有重叠,其代表就是kd树
;后者划分空间相互有交叠,其代表为R树
。
构造kd树的方法根据不同的决策规则分为很多种,但最终都是平衡二叉树
。构造kd树的方法如下:
- 构造根节点,使根结点对应于k维空间中包含所有实例点的超矩形区域;
- 通过下面的递归方法,不断地对k维空间进行切分,生成子结点:
- 在超矩形区域(结点)上选择一个坐标轴和在此坐标轴上的一个切分点,确定一个超平面
- 这个超平面通过选定的切分点并垂直于选定的坐标轴,将当前超矩形区域切分为左右两个子区域(子结点)
- 这时,实例被分到两个子区域
- 这个过程直到子区域内内有实例时终止(终止时的结点为叶结点)。在此过程中,将实例保存在相应的结点上。
在这个方法中,有两个部分是可以进行调节的,第一个部分就是选取的维度的顺序,另一个部分就是选取分割点的度量标准。
- 在第一部分,我们可以使用
顺序采样
,即从第1维,第2维,第n维一直到分割完毕为止。也可以使用最大方差所在的维度,也可以使用维度主次优先级为顺序,以此等等。 - 在第二部分,我们可以使用的是
所在维度的中位数
作为切分点,也可以使用中值
作为切分点,以此等等。
这里,我们使用的是最朴素的方法,维度采用顺序采样,切分点选取中位数作为切分点。也就是说,依次选择坐标轴对空间切分,选择训练实例点在选定坐标轴上的中位数(median)为切分点,这样得到的kd树是平衡的。注意,平衡的kd树搜索时的效率未必是最优的。
算法2:构造平衡kd树
输入:k为空间数据集
T
=
{
x
1
,
x
2
,
.
.
.
,
x
N
}
T=\{x_1,x_2,...,x_N\}
T={x1,x2,...,xN},其中
x
i
=
(
x
i
(
1
)
,
x
i
(
2
)
,
.
.
.
,
x
i
(
k
)
)
T
,
i
=
1
,
2...
,
N
x_i=(x_{i}^{(1)}, x_{i}^{(2)},...,x_i^{(k)})^T,i=1,2...,N
xi=(xi(1),xi(2),...,xi(k))T,i=1,2...,N;
输出:kd树
(1)开始:构造根节点,根节点对应于包含T的k维空间的超矩形区域。选择
x
(
l
)
x^{(l)}
x(l)为坐标轴,以
T
T
T中所有实例的
x
(
l
)
x^{(l)}
x(l)坐标的中位数为切分点,这样,经过该切分点且垂直与
x
(
l
)
x^{(l)}
x(l)的超平面就将超矩形区域切分成2个子区域。由根节点生成深度为
l
l
l的左、右子结点,左子节点对应坐标
x
(
l
)
x^{(l)}
x(l)小于切分点的子区域,右边结点对应于坐标
x
(
l
)
x^{(l)}
x(l)大于切分点的子区域。将落在切分超平面上的实例点保存在根节点。
(2)重复:对深度为
j
j
j的节点,选择
x
(
l
)
x^{(l)}
x(l)为切分的坐标轴,
l
=
j
(
m
o
d
k
)
+
1
l = j(mod\;k) + 1
l=j(modk)+1 ,以该节点区域中所有实例的
x
(
l
)
x^{(l)}
x(l)坐标的中位数为切分点,将该节点对应的超平面切分成两个子区域。切分由通过切分点并与坐标轴
x
(
l
)
x^{(l)}
x(l)垂直的超平面实现。保存这个切分点为一般节点。
(3)直到两个子区域没有实例存在时停止,从而形成kd树的区域划分。
4.2 搜索kd树
给定一个目标点,搜索其最近邻。首先找到包含目标点的叶结点;然后从该叶结点出发,依次回退到父结点;不断查找与目标点最邻近的结点,当确定不可能存在更近的结点时终止。这样搜索就被限制在空间的局部区域上,效率大大提高。
算法3:用kd树的最近邻搜索
输入:已构造的kd树,目标点
x
x
x;
输出:
x
x
x的最近邻
- 在kd树种找出包含目标点x的叶结点:从根节点出发,递归地向下访问kd树。若目标点x当前维的坐标小于切分点的坐标,则移动到左子节点,否则移动到右子节点。直到子节点为叶子节点为止。
- 以此叶结点为“当前最近点”
- 递归地向上回退,在每个结点进行以下操作:
- 如果该结点保存的实例点比当前最近点距离目标点更近,则以该实例点为“当前最近点”。
- 当前最近点一定存在于该节点一个子节点对应的区域。检查该子节点的父节点的另一个子节点对应的区域是否有更近的点。具体地,检查另一个子节点对应的区域是否与以目标点为球心,以目标点与“当前最近点”间的距离为半径的超球体相交。
如果相交,可能在另一个子结点对应的区域内存在距离目标点更近的点,移动到另一个子结点,接着,递归地进行最近邻搜索;
如果不相交,向上回退。
- 当回退到根节点时,搜索结束。最后的“当前最近点”即为x的最近邻点。
其实只要把握住了这么几个部分:首先是当前最近点的初始值的确定,第二是如何回溯比较,第三是如何搜索可能存在的解。
总而言之,若实例点随机分布,则kd树搜索的时间复杂度为 O ( l o g N ) O(logN) O(logN),N为训练实例数。就具体而言,kd树更适用于训练实例数远大于空间维度的K近邻搜索。一般是20维以下的,效果比较好。
5. 补充
5.1K近邻算法
与K-means聚类算法
的区别:
KNN | K-Means |
---|---|
KNN是分类算法 | K-Means是聚类算法 |
KNN是监督学习 | K-Means非监督学习 |
没有明显的前期训练过程 | 有明显的前期训练过程 |
K的含义指的是判断依据来源个数 | K的含义是集合的分类数目 |
而这两者都用到了NN算法,一般使用kd树来实现。
5.2 kd树的若干改进算法
5.2.1 BBF算法
BBF(Best-Bin-First)查询算法
。这个算法是由发明sift算法的David Lowe在1997的一篇文章中针对高维数据提出的一种近似算法,此算法能确保优先检索包含最近邻点可能性较高的空间,此外,BBF机制还设置了一个运行超时限定。采用了BBF查询机制后,kd树便可以有效的扩展到高维数据集上。
BBF算法的改进思路为:将“查询路径”上的结点进行排序,如按各自分割超平面(也称bin)与查询点的距离排序,也就是说,回溯检查总是从优先级最高(Best Bin)的树结点开始。
5.2.2 球树
仅仅在kd树上进行BBF算法的改进,仍然还是不能够避免一些结构本身存在的弊端,当处理不均匀分布的数据集时便会呈现出一个基本冲突:既要求树有完美的平衡结构,又要求待查找的区域近似方形,但不管是近似方形,还是矩形,甚至正方形,都不是最好的使用形状,因为他们都有角。
其实这个问题的实质是因为,我们对于距离的度量使用的是圆形,也就是欧氏距离,如果是我们之前提到的像切比雪夫距离这种方形的,就可以在一定程度上减少这个冲突。因为无论是你的模板和样本,其度量标准是一致的,也就是要么是方形的,都是方形的,要是圆形的,都是圆形的。
ball tree
将在一系列嵌套的超球体上分割数据。也就是说:使用超球面而不是超矩形划分区域。虽然在构建数据结构的花费上大过于KDtree,但是在高维甚至很高维的数据上都表现的很高效。
球树递归地将数据划分为由质心
C
C
C和半径
r
r
r定义的节点,使得节点中的每个点都位于由
r
r
r和
C
C
C定义的超球内。通过使用三角不等式来减少邻居搜索的候选点数量的。
例如下面这个就是球树:
如上图所示,先构建一个超球体,这个超球体是可以包含所有样本的最小球体。从球中选择一个离球的中心最远的点,然后选择第二个点离第一个点最远,将球中所有的点分配到离这两个聚类中心最近的一个上,然后计算每个聚类的中心,以及聚类能够包含它所有数据点所需的最小半径。这样我们得到了两个子超球体,和KD树里面的左右子树对应。对于这两个子超球体,递归执行步骤最终得到了一个球树
使用球树找出给定目标点的最近邻方法是,首先自上而下贯穿整棵树找出包含目标点所在的叶子,并在这个球里找出与目标点最靠近的点,这将确定出目标点距离它的最近邻点的一个上限值,然后跟KD树查找一样,检查同胞结点,如果目标点到同胞结点中心的距离超过同胞结点的半径与当前的上限值之和,那么同胞结点里不可能存在一个更近的点;否则的话,必须进一步检查位于同胞结点以下的子树。检查完兄弟节点后,我们向父节点回溯,继续搜索最小邻近值。当回溯到根节点时,此时的最小邻近值就是最终的搜索结果。
下面是sklearn的代码示例:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn import neighbors, datasets
n_neighbors = 15
# import some data to play with
iris = datasets.load_iris()
# we only take the first two features. We could avoid this ugly
# slicing by using a two-dim dataset
X = iris.data[:, :2]
y = iris.target
h = .02 # step size in the mesh
# Create color maps
cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF'])
cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF'])
for weights in ['uniform', 'distance']:
# we create an instance of Neighbours Classifier and fit the data.
clf = neighbors.KNeighborsClassifier(n_neighbors, weights=weights)
clf.fit(X, y)
# Plot the decision boundary. For that, we will assign a color to each
# point in the mesh [x_min, x_max]x[y_min, y_max].
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
# Put the result into a color plot
Z = Z.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx, yy, Z, cmap=cmap_light)
# Plot also the training points
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=cmap_bold,
edgecolor='k', s=20)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
plt.title("3-Class classification (k = %i, weights = '%s')"
% (n_neighbors, weights))
plt.show()
5.2.3 其他方法
半径最近邻
,通过找到指定半径内最近的k个值,这种算法思想找到指定范围内的最近邻的k个值,来进行预测;质心最近邻
,通过在特征值中的平均值来获得质心点,最终该类别所有维度的n个平均值形成所谓的质心点。对于样本中的所有出现的类别,每个类别会最终得到一个质心点。当我们做预测时,仅仅需要比较预测样本和这些质心的距离,最小的距离对应的质心类别即为预测的类别。这个算法通常用在文本分类处理
上。
6. 总结
KNN的主要优点有:
1) 理论成熟,思想简单,既可以用来做分类也可以用来做回归
2) 可用于非线性分类
3) 训练时间复杂度比支持向量机之类的算法低,仅为O(n)
4) 和朴素贝叶斯之类的算法比,对数据没有假设,准确度高,对异常点不敏感
5) 由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合
6)该算法比较适用于样本容量比较大的类域的自动分类,而那些样本容量较小的类域采用这种算法比较容易产生误分
KNN的主要缺点有:
1)计算量大,尤其是特征数非常多的时候
2)样本不平衡的时候,对稀有类别的预测准确率低
3)KD树,球树之类的模型建立需要大量的内存
4)使用懒散学习方法,基本上不学习,导致预测时速度比起逻辑回归之类的算法慢
5)相比决策树模型,KNN模型可解释性不强
参考资料
- 《统计学习方法》 李航 著
- 统计学习方法——K近邻模型
- KNN的优化算法3:Ball-tree