k近邻法(k-nearest neighbor,KNN)是一种基本分类与回归方法。
k k k近邻算法(分类)
k k k近邻法假设给定一个训练数据集,其中的实例类别已定。分类时,对新的实例,根据其 k k k个最近邻的训练实例的类别,通过多数表决方式进行预测。因此, k k 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
∈
X
⊆
R
p
x_i\in\mathcal{X}\subseteq\mathcal{R}^p
xi∈X⊆Rp为实例的特征向量,
y
i
∈
Y
=
{
c
1
,
.
.
.
,
c
K
}
y_i\in\mathcal{Y}=\{c_1,...,c_K\}
yi∈Y={c1,...,cK}为实例的类别,
i
=
1
,
.
.
.
,
n
i=1,...,n
i=1,...,n
输出:实例
x
x
x所属的类
y
y
y
- 根据给定的距离度量,在训练集 T T T中找出与 x x x最邻近的 k k k个点,涵盖这 k k k个点的 x x x的邻域记作 N k ( x ) N_k(x) Nk(x);
- 在
N
k
(
x
)
N_k(x)
Nk(x)中根据分类决策规则(如多数表决)决定
x
x
x的类别
y
y
y:
y = arg max c j ∑ x i ∈ N k ( x ) I { y i = c j } , i = 1 , . . . , n ; j = 1 , . . . , K y=\arg\max_{c_j}\sum_{x_i\in N_k(x)}I\{y_i=c_j\},\quad i=1,...,n;j=1,...,K y=argcjmaxxi∈Nk(x)∑I{yi=cj},i=1,...,n;j=1,...,K
k k k近邻法的特殊情况是 k = 1 k=1 k=1的情形,称为最近邻算法。
- KNN算法本身简单有效,它是一种lazy-learning算法,分类器不需要使用训练集进行训练,训练时间复杂度为0;
- KNN分类的计算复杂度和训练集中的文档数目成正比,也就是说,如果训练集中的文档总数为 n n n,那么KNN的分类时间复杂度为 O ( n ) O(n) O(n);
该算法在分类时有个问题是,当样本不平衡时,有可能导致当输入一个新样本时,该样本内的 k k k个邻居中大容量类的样本占多数,解决办法是,对不同的样本给予不同的权重。
k k k近邻模型三要素
k
k
k近邻法中,当训练集、距离空间、
k
k
k值及分类决策规则确定后,对于任何一个输入的实例,它所属的类别唯一确定。特征空间中,对每一个训练实例点
x
i
x_i
xi,距离该点比其他点更近的所有点组成一个区域,称作单元(cell)。每个训练实例点拥有一个单元,所有训练实例点的单元构成对特征空间的一个划分。
k
k
k近邻法实际上利用训练数据集对特征向量空间进行划分,并作为其分类的“模型”。距离空间、
k
k
k值及分类决策规则是
k
k
k近邻模型的三个基本要素。
距离度量
特征空间中两个实例点的距离是两个实例点相似程度的反映。
设
k
k
k近邻模型的特征空间
X
\mathcal{X}
X为
n
n
n为实数向量空间
R
n
\mathcal{R}^n
Rn,
x
i
,
x
j
∈
X
x_i,x_j\in\mathcal{X}
xi,xj∈X,
x
i
=
(
x
i
(
1
)
,
.
.
.
,
x
i
(
n
)
)
T
x_i=(x_i^{(1)},...,x_i^{(n)})^T
xi=(xi(1),...,xi(n))T,
x
j
=
(
x
j
(
1
)
,
.
.
.
,
x
j
(
n
)
)
T
x_j=(x_j^{(1)},...,x_j^{(n)})^T
xj=(xj(1),...,xj(n))T,
x
i
,
x
j
x_i,x_j
xi,xj的
L
p
(
p
≥
1
)
L_p(p\geq1)
Lp(p≥1)距离定义为:
L
p
(
x
i
,
x
j
)
=
(
∑
l
=
1
n
∣
x
i
(
l
)
−
x
j
(
l
)
∣
p
)
1
p
L_p(x_i,x_j)=\left(\sum_{l=1}^n|x_i^{(l)}-x_j^{(l)}|^p\right)^{\frac{1}{p}}
Lp(xi,xj)=(l=1∑n∣xi(l)−xj(l)∣p)p1
-
p
=
2
p=2
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)=\left(\sum_{l=1}^n|x_i^{(l)}-x_j^{(l)}|^2\right)^{\frac{1}{2}} L2(xi,xj)=(l=1∑n∣xi(l)−xj(l)∣2)21 -
p
=
1
p=1
p=1时,称为曼哈顿距离(Manhattan distance)
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
=
∞
p=\infty
p=∞时,它是各个坐标距离的最大值
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)∣
二维空间中
p
p
p取不同值时,与圆点的
L
p
L_p
Lp距离:
k k k值的选择
k k k值的选择会对 k k k近邻法的结果产生重大影响。
-
k
k
k值较小,整体模型复杂,容易发生过拟合;
较小的 k k k值意味着用较小的邻域中的训练实例进行预测,只有与输入实例较近的(相似的)训练实例才会对预测结果起作用;但预测结果对近邻的实例点非常敏感,如果邻近的实例点恰巧是噪声,预测就会出错。 -
k
k
k值较大,模型较简单。
用较大的邻域中的训练实例进行预测,与输入实例较远的(不相似的)训练实例也会起预测作用。
在应用中, k k k值一般取一个比较小的数值。通常采用交叉验证法来选择最优的 k k k值。
分类决策规则
k
k
k近邻法中的分类决策规则往往是多数表决。多数表决规则(majority voting rule)有如下解释:如果分类的损失函数为0-1损失函数,分类函数为
f
:
R
n
→
{
c
1
,
.
.
.
,
c
K
}
f:\mathcal{R}^n\rightarrow\{c_1,...,c_K\}
f:Rn→{c1,...,cK}
那么误分类的概率是:
P
(
Y
≠
f
(
X
)
)
=
1
−
P
(
Y
=
f
(
X
)
)
P(Y\neq f(X))=1-P(Y=f(X))
P(Y=f(X))=1−P(Y=f(X))
那对于给定的实例
x
∈
X
x\in\mathcal{X}
x∈X,其最近邻的
k
k
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\neq 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 k k近邻法的实现:kd树
实现
k
k
k近邻法时,主要考虑的问题是如何对训练数据进行快速
k
k
k近邻搜索。这点在特征空间的维数大及训练数据容量大时尤其必要。
k
k
k近邻法最简单的实现方法是线性扫描,即计算输入实例与每一个训练实例的距离,当训练集很大时,计算非常耗时,这种方法是不可行的。为了提高
k
k
k近邻的搜索效率,可以考虑使用特殊的结构存储训练数据,以减少计算距离的次数。
k
d
kd
kd树是一种对
k
k
k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构。
构造 k d kd kd树
k
d
kd
kd树是二叉树,表示对
k
k
k维空间的一个划分。构建
k
d
kd
kd树相当于不断地用垂直于坐标轴的超平面切分
k
k
k维空间,构成一系列的
k
k
k维超矩形区域,
k
d
kd
kd树的每个结点对应于一个
k
k
k维超矩形区域。
通常,依次选择坐标轴对空间切分,选择训练实例点在选定坐标轴上的中位数为切分点,这样得到的
k
d
kd
kd树是平衡的,但是平衡的
k
d
kd
kd树搜索时的效率未必是最优的。
构造平衡
k
d
kd
kd树算法流程
输入:
k
k
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
输出:
k
d
kd
kd树
-
开始:构造根结点,根结点对应于包含 T T T的 k k k维空间的超矩形区域。
选择 x ( 1 ) x^{(1)} x(1)为坐标轴,以 T T T中所有实例的 x ( 1 ) x^{(1)} x(1)坐标的中位数为切分点,将根结点对应的超矩形区域切分为两个子区域,切分由通过切分点并与坐标轴 x ( 1 ) x^{(1)} x(1)垂直的超平面实现。
由根结点生成深度为1的左、右子结点:左子结点对应坐标 x ( 1 ) x^{(1)} x(1)小于切分点的子区域,右子结点对应于坐标 x ( 1 ) x^{(1)} x(1)大于切分点的子区域。将落在切分超平面上的实例点保存在根结点。 -
重复:对深度为 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)垂直的超平面实现。
由根结点生成深度为 j + 1 j+1 j+1的左、右子结点:左子结点对应坐标 x ( 1 ) x^{(1)} x(1)小于切分点的子区域,右子结点对应于坐标 x ( 1 ) x^{(1)} x(1)大于切分点的子区域。将落在切分超平面上的实例点保存在根结点。 -
直到两个子区域没有实例存在时停止,从而形成 k d kd kd树的区域划分。
搜索 k d kd kd树
用
k
d
kd
kd树的最近邻搜索
输入:已构造的
k
d
kd
kd树;目标点
x
x
x;
输出:
x
x
x的最近邻
- 在 k d kd kd树中找出包含目标点 x x x的叶结点:从叶结点出发,递归地访问 k d kd kd树,若目标点 x x x当前维的坐标小于切分点的坐标,则移动到左子结点,否则移动到右子结点,直到子结点为叶结点为止;
- 以此叶结点作为“当前最近点”;
- 递归地向上回退,在每个结点进行以下操作:
a. 如果该结点保存的实例点比当前最近点距离目标点更近,则以该实例点为“当前最近点”;
b. 当前最近点一定存在于该结点一个子结点对应的区域,检测另一子结点对应的区域是否与以目标点为球心、以目标点与“当前最近点”间的距离为半径的超球体相交:如果相交,可能在另一个子结点对应的区域内存在距目标点更近的点,移动到另一个子结点,接着,递归地进行最近邻搜索;如果不相交,向上回退。 - 当回退到根结点时,搜索结束,最后的“当前最近点”即为 x x x的最近邻点。
搜索 k d kd kd树示例
- 查找目标点 ( 3 , 4.5 ) (3,4.5) (3,4.5)的最近邻点:首先找出包含目标点的叶结点, ( 7 , 2 ) → ( 5 , 4 ) → ( 4 , 7 ) (7,2)\rightarrow(5,4)\rightarrow (4,7) (7,2)→(5,4)→(4,7),取 ( 4 , 7 ) (4,7) (4,7)为当前最近邻点。以目标查找点为圆心,目标查找点到当前最近点的距离2.69为半径确定一个红色的圆。
- 然后回溯到 ( 5 , 4 ) (5,4) (5,4),计算其与查找点之间的距离为2.06,则该结点比当前最近点距目标点更近,以 ( 5 , 4 ) (5,4) (5,4)为当前最近点。用同样的方法再次确定一个绿色的圆,可见该圆和 y = 4 y = 4 y=4超平面相交,所以需要进入 ( 5 , 4 ) (5,4) (5,4)结点的另一个子空间进行查找。
- ( 2 , 3 ) (2,3) (2,3)结点与目标点距离为1.8,比当前最近点要更近,所以最近邻点更新为 ( 2 , 3 ) (2,3) (2,3),最近距离更新为1.8,同样可以确定一个蓝色的圆。接着根据规则回退到根结点 ( 7 , 2 ) (7,2) (7,2),蓝色圆与 x = 7 x=7 x=7的超平面不相交,因此不用进入 ( 7 , 2 ) (7,2) (7,2)的右子空间进行查找。至此,搜索路径回溯完,返回最近邻点 ( 2 , 3 ) (2,3) (2,3),最近距离1.8。
如果实例点是随机分布, k d kd kd树搜索的平均计算复杂度是 O ( log n ) O(\log n) O(logn), n n n为训练实例数。
k d kd kd树更适用于训练实例数远大于空间维数时的 k k k近邻搜索。当空间维数接近训练实例数时,它的效率会迅速下降,几乎接近线性扫描。
The CIFAR-10 dataset
CIFAR-10数据集说明
KNN实现CIFAR-10数据集识别
斯坦福CS231n项目实战(一):k最近邻(kNN)分类算法
参考:
统计学习方法—李航
KNN(三)–KD树详解及KD树最近邻算法
KNN算法与Kd树