k近邻法(KNN)是无显式学习,我的理解是它不具有像感知机模型 f ( x ) = s i g n ( w ⋅ x + b ) f(x)=sign(w\cdot x +b) f(x)=sign(w⋅x+b)这样明确的模型表达式。KNN是一种基本的分类和回归方法,小蓝书主要讨论其在分类问题中的用法,笼统地说,对于一个实例的分类主要是通过 多数表决等 方式决定的。
3.1 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
∈
χ
⊆
R
n
x_i\in \chi \subseteq R^n
xi∈χ⊆Rn是实例的特征向量,
y
i
∈
γ
=
{
c
1
,
c
2
,
.
.
.
,
c
k
}
y_i \in \gamma=\{c_1,c_2,...,c_k\}
yi∈γ={c1,c2,...,ck}是k个类别,
i
=
1
,
2
,
.
.
.
,
N
;
i=1,2,...,N;
i=1,2,...,N;实例特征向量
x
x
x。
输出:实例
x
x
x所属的类
y
y
y。
(1)根据给定的距离度量在训练集中找出与
x
x
x最近的k个点,涵盖这k个点的x的领域记作
N
k
(
x
)
N_k(x)
Nk(x);
(2)在
N
k
(
x
)
N_k(x)
Nk(x)中根据分类决策规则(比如多数表决)来决定
x
x
x所属的类别
y
y
y:
y
=
a
r
g
m
a
x
c
j
∑
x
i
∈
N
k
(
x
)
I
(
y
i
=
c
j
)
,
i
=
1
,
2
,
.
.
.
,
N
;
j
=
1
,
2
,
.
.
.
,
K
(3-1)
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\tag{3-1}
y=arg maxcjxi∈Nk(x)∑I(yi=cj),i=1,2,...,N; j=1,2,...,K(3-1)
其中
I
I
I为指示函数,即当
y
i
=
c
i
y_i=c_i
yi=ci时
I
I
I为1,否则
I
I
I为0。
3.2 k近邻模型
k近邻模型中的几个关键问题:距离的度量、k值的选择、分类决策规则。
3.2.1 距离的度量
直观上说特征空间两个实例点的距离可以反映两个点的相似程度,k近邻模型的特征空间一般是n维的实数向量空间
R
n
R^n
Rn,使用的距离可以是欧氏距离、
L
p
L_p
Lp距离、Minkowski距离等,其定义分别如下:
设特征空间
χ
\chi
χ是n维实数向量空间
R
n
R^n
Rn,取两实例点
x
i
,
x
j
∈
χ
,
x
i
=
(
x
i
(
1
)
,
x
i
(
2
)
,
.
.
.
,
x
i
(
n
)
)
T
,
x
j
=
(
x
j
(
1
)
,
x
j
(
2
)
,
.
.
.
,
x
j
(
n
)
)
x_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)})
xi,xj∈χ,xi=(xi(1),xi(2),...,xi(n))T,xj=(xj(1),xj(2),...,xj(n)),则
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
(3-2)
L_p(x_i,x_j)=(\sum_{l=1}^n|x_i^{(l)}-x_j^{(l)}|^p)^{\frac{1}{p}}\tag{3-2}
Lp(xi,xj)=(l=1∑n∣xi(l)−xj(l)∣p)p1(3-2)
p
≥
1
p\geq1
p≥1,当
p
=
2
p=2
p=2时就是 欧氏距离:
L
2
(
x
i
,
x
j
)
=
(
∑
l
=
1
n
∣
x
i
(
l
)
−
x
j
(
l
)
∣
2
)
1
2
(3-3)
L_2(x_i,x_j)=(\sum_{l=1}^n|x_i^{(l)}-x_j^{(l)}|^2)^{\frac{1}{2}}\tag{3-3}
L2(xi,xj)=(l=1∑n∣xi(l)−xj(l)∣2)21(3-3)
当
p
=
1
p=1
p=1时,就是曼哈顿距离:
L
1
(
x
i
,
x
j
)
=
∑
l
=
1
n
∣
x
i
(
l
)
−
x
j
(
l
)
∣
(3-4)
L_1(x_i,x_j)=\sum_{l=1}^n|x_i^{(l)}-x_j^{(l)}|\tag{3-4}
L1(xi,xj)=l=1∑n∣xi(l)−xj(l)∣(3-4)
而当
p
=
∞
p=\infty
p=∞时,是各个坐标距离的最大值:
L
∞
(
x
i
,
x
j
)
=
m
a
x
l
∣
x
i
(
l
)
−
x
j
(
l
)
∣
(3-5)
L_{\infty}(x_i,x_j)=max_{l}|x_i^{(l)}-x_j^{(l)}|\tag{3-5}
L∞(xi,xj)=maxl∣xi(l)−xj(l)∣(3-5)
显然不同距离度量会导致所确定的最近邻点是不同的。
3.2.2 k值的选择
u1s1,
k
k
k值的选择对KNN还是有很大影响的。
若选择较小的k值,那相当于只有与输入实例相似的训练实例才会对预测结果起到作用,若不巧近邻的实例点是噪声点的话,那结果可想而知就不太行了,k值减小意味着整体模型变得复杂,容易发生过拟合,估计误差会增大。
若选择较大的k值,可减少估计误差,但此时距离较远的实例点也会对预测起到作用,使得预测发生错误,k值的增加意味着模型变得简单,比如若
k
=
N
k=N
k=N时,那么无论输入什么预测结果都是实例点中最多的类。
实际中k值会取一个比较小的值,通常采用交叉验证法来选取最优的k值。
3.2.3 分类决策规则
KNN中通常用 多数表决 来作为分类决策规则。为什么用多数表决呢?有如下解释:
若分类的损失函数为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},对给定的实例
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,那么误分类率为:
P
(
Y
≠
f
(
X
)
)
=
1
−
P
(
Y
=
f
(
X
)
)
(3-6)
P(Y\neq f(X))=1-P(Y=f(X))\tag{3-6}
P(Y=f(X))=1−P(Y=f(X))(3-6)
即
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
)
(3-7)
\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)\tag{3-7}
k1xi∈Nk(x)∑I(yi=cj)=1−k1xi∈Nk(x)∑I(yi=cj)(3-7)
要使误分类率最小即经验风险最小,之前有说过多数表决相当于使
∑
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)最大,所以多数表决等价于经验风险最小化。
3.3 k近邻法的实现:kd树(减少计算距离的次数)
那么问题又来了,是不是对于每一个输入实例都要计算它与每一个实例点的距离呢?其实这就是线性扫描,当训练集很大时工作量也会很大。那么为了提高k近邻搜索的效率,考虑使用特殊的结构存储训练数据,以减少计算距离的次数,如下就是其中一种方法:kd树方法。
3.3.1 构造
kd树是二叉树,表示对k维空间的一个划分,构造kd树相当于不断地用垂直于坐标轴的超平面将k维空间切分,从而形成一系列k维超矩形区域,而kd树的每一个结点对应于一个k维超矩形区域。(结点 ↔ \leftrightarrow ↔超矩形区域)
构造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
x_i=(x_i^{(1)},x_i^{(2)},...,x_i^{(k)})^T
xi=(xi(1),xi(2),...,xi(k))T
输出:kd树
(1)首先构造根结点,根结点对应于包含T的k维空间的超矩形区域。选择
x
(
1
)
x^{(1)}
x(1)为坐标轴,以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)大于切分点的子区域。那么其余的落在切分超平面上的点就保存在根结点。
(2)重复(坐标轴选取):对深度为
j
j
j的结点,选择
x
(
l
)
x^{(l)}
x(l)为切分的坐标轴,
l
=
j
(
m
o
d
k
)
+
1
l=j(mod\ k)+1
l=j(mod k)+1,同(1)中的做法将该结点对应区域中的所有实例的
x
l
x^{l}
xl坐标的中位数作为切分点,继续划分。
(3)直到两个子区域没有实例存在时停止。
例3.1:
给定一个二维空间的数据集:
T
=
{
(
2
,
3
)
T
,
(
5
,
4
)
T
,
(
9
,
6
)
T
,
(
4
,
7
)
T
,
(
8
,
1
)
T
,
(
7
,
2
)
T
}
T=\{(2,3)^T,(5,4)^T,(9,6)^T,(4,7)^T,(8,1)^T,(7,2)^T\}
T={(2,3)T,(5,4)T,(9,6)T,(4,7)T,(8,1)T,(7,2)T},构造一个平衡kd树。
解:
首先选取
x
(
1
)
x^{(1)}
x(1)为切分坐标轴,因为这里是6个点,其
x
(
1
)
x^{(1)}
x(1)的中位数可以取5也可以取7,这里取的7以平面
x
(
1
)
=
7
x^{(1)}=7
x(1)=7将空间划分为左右两个矩形区域;左边区域继续取
x
(
2
)
=
4
x^{(2)}=4
x(2)=4来划分空间,右边区域取
x
(
2
)
=
6
x^{(2)}=6
x(2)=6来划分,如此下去得到空间划分方案和kd树如图3-1所示:
3.3.2 搜索
那么构造的kd树怎么用呢?这就是接下来要讨论的问题:利用kd树进行k近邻搜索,省去对大部分数据点的搜索。蓝皮书这里是以最近邻为例来说明,k近邻同理。
输入:已构造的kd树和目标点x
输出:x的最近邻点
(1)在kd树中找出包含目标点x的叶结点,从根结点出发递归向下访问kd树,看目标点当前维(上图kd树左边有标哪一维)是否小于切分点的坐标,若小于则移至左子结点,否则移至右子结点,这样一直到子结点为叶子结点为止。
(2)将此叶子结点记为当前最近点。
(3)接着递归向上往回找,若当前找到的结点比当前最近点离目标点更近,则将当前结点作为当前最近点;另外检查当前找到的结点的另一个子结点对应的区域是否与以目标点x为球心、x同当前最近点的距离为半径的圆相交:若相交说明该区域有可能存在更近的点,移至该子结点递归搜索;若不相交则向上回退。
(4)当回退到根结点时,搜索结束,最后的当前最近点即为x的最近邻点。
注:kd树更适用于训练实例数远大于空间维数时的k近邻搜索。