算法流程
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),\dots, (x_N, y_N)\}
T={(x1,y1),(x2y2),…,(xN,yN)}
其中
x
i
⊂
X
⊂
R
n
x_i\subset \mathcal{X}\subset \mathbb{R}^n
xi⊂X⊂Rn表示实例的特征向量,
y
i
⊂
Y
=
{
c
1
,
c
2
,
…
,
c
k
}
y_i\subset \mathcal{Y}=\{c_1, c_2, \dots, c_k\}
yi⊂Y={c1,c2,…,ck}表示实例的类别.
- 设置距离函数,根据距离函数找到训练集中与实例 x x x最相近的 k k k个点,涵盖这 k k k个点的邻域为 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 ) y=\argmax\limits_{c_j}\sum\limits_{x_i\in N_k(x)}\mathcal{I}(y_i=c_j) y=cjargmaxxi∈Nk(x)∑I(yi=cj)
模型
k
k
k近邻算法实际上对应于特征空间的划分,模型的三个基本要素:距离度量,
k
k
k值选择和分类规则的确定.
在特征空间中,对于每个训练实例点
x
i
x_i
xi,距离该点比其他点更近的所有点组成一个区域为单元(cell),每个单元的标记是确定的.
距离
设特征空间
X
\mathcal{X}
X是
n
n
n维实数向量空间
R
n
\mathbb{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
(
2
)
,
…
,
x
i
(
n
)
)
T
x_i=(x_i^{(1)}, x_i^{(2)}, \dots, x_i^{(n)})^T
xi=(xi(1),xi(2),…,xi(n))T,距离
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
p=2
p=2时,称为欧氏距离
当
p
=
∞
p=\infty
p=∞时,是各个坐标距离的最大值
max
l
∣
x
i
(
l
)
−
x
j
(
l
)
∣
\max\limits_l|x_i^{(l)}-x_j^{(l)}|
lmax∣xi(l)−xj(l)∣
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
2
,
…
,
c
K
}
f:\mathbb{R}^n\to \{c_1, c_2, \dots, c_K\}
f:Rn→{c1,c2,…,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\limits_{x_i\in N_k(x)}\mathcal{I}(y_i\neq c_j)=1-\frac{1}{k}\sum\limits_{x_i\in N_k(x)}\mathcal{I}(y_i=c_j)
k1xi∈Nk(x)∑I(yi=cj)=1−k1xi∈Nk(x)∑I(yi=cj)
可以发现,其作用等价于ERM.
K-Dimensional树
k d kd kd树是一种对 k k k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构,其构造过程是不断用垂直于坐标轴的超平面将 k k k维空间切分,构成一系列 k k k维超矩形区域.
算法
输入 k k k维空间数据集 T = { x 1 , x 2 , … , x N } T=\{x_1, x_2, \dots, 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)}, \dots, x_i^{(k)})^T xi=(xi(1),xi(2),…,xi(k))T
- 构造根节点,对应于包含 T T T的 k k k维空间的超矩形区域. 选择 x ( 1 ) x^{(1)} x(1)作为坐标轴,以 T T T中所有实例的 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(\bmod{k})+1 l=j(modk)+1
- 直到两个子区域没有实例存在时停止.
案例
给定二维空间数据集
T
=
{
(
2
,
3
)
,
(
5
,
4
)
,
(
9
,
6
)
,
(
4
,
7
)
,
(
8
,
1
)
,
(
7
,
2
)
}
T=\{(2,3), (5, 4), (9, 6), (4, 7), (8, 1), (7, 2)\}
T={(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)}
构造平衡
k
d
kd
kd树.
解析
和线段树的方法类似,采用轮转划分法,直到所有集合都不能继续划分,得到
k
d
kd
kd树结构如下
代码
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=50005;
const int K=5;
int id, n, k, c;
struct PO{
int x[K];
bool operator<(const po &a) const{return x[id]<a.id;}
}b[N];
struct TR{
po e;
int o; // 当前划分的维度
}tr[N<<2];
void build(int l, int r, int d, int o){
if(l>=r) return;
int mid=l+r>>1;
int lc=d<<1;
int rc=d<<1|1;
id=o;
nth_element(b+l, b+mid, b+r); // 使第k大的元素处于第k的位置
tr[d].e=b[mid];
tr[d].o=o;
build(l, mid, lc, (o+1)%k); // 递归左侧划分
build(mid+1, r, rc, (o+1)%k); // 递归右侧划分
}
搜索
在构造的 k d kd kd树中找到目标点 x x x的近邻.
- 从根节点出发,递归向下访问 k d kd kd树,如果目标点 x x x当前维的坐标小于切分点的坐标,转左子节点;如果目标点 x x x当期维的坐标大于切分点坐标,转右子节点. 递归到叶子节点
- 回溯,更新最近点(超球体判定方法)