21.k最近邻算法
(本篇R代码对应本系列博客《17 R语言手册(第十站 k均值)》。)
1.距离函数
数据分析师定义距离度量来计算相似性。距离度量或距离函数由实值函数d定义,例如对任一坐标x、y和z,有如下性质:
(1)
d
(
x
,
y
)
⩾
0
,
当
且
仅
当
x
=
y
时
,
d
(
x
,
y
)
=
0
。
d(x, y) \geqslant 0,当且仅当x=y时,d(x,y)=0。
d(x,y)⩾0,当且仅当x=y时,d(x,y)=0。
(2)
d
(
x
,
y
)
=
d
(
y
,
x
)
d(x, y)=d(y, x)
d(x,y)=d(y,x)。
(3)
d
(
x
,
z
)
⩽
d
(
x
,
y
)
+
d
(
y
,
z
)
d(x, z) \leqslant d(x, y)+d(y, z)
d(x,z)⩽d(x,y)+d(y,z)。
解读一下,性质1表明距离必须是非负的,距离等于0的唯一可能就是坐标相同。性质2表明距离函数要满足交换律。性质3表明引入第3个点的时候,其值总是大于两外两个点的距离。
(这一部分详细参考本系列博客《21.1 什么是切比雪夫、曼哈顿、欧几里德、闵可夫斯基、马哈拉诺比斯距离》,实际上,我们也可以使用不同的距离来进行最近邻的计算。)
2.欧式距离
那么按照这样的定义说起来,最常见的就是欧氏距离了,这个函数是人类在计算现实世界距离时最常用的方法。
d
Fucliden
(
x
,
y
)
=
∑
i
(
x
i
−
y
i
)
2
d_{\text { Fucliden }}(x, y)=\sqrt{\sum_{i}\left(x_{i}-y_{i}\right)^{2}}
d Fucliden (x,y)=i∑(xi−yi)2
即使从图像上来看,这个距离的计算也非常的直观。
当然,在度量距离的时候,可能由于某些变量数值上更大,会掩盖那些数值较小的变量,因此,我们需要对此进行标准化处理。这是在本系列博客《07 主成分分析(基础版)》中就详细阐述了的。
通常,我们采用两种方法:
- MMN 最大-最小规范化
X ∗ = X − min ( X ) range ( X ) = X − min ( X ) max ( X ) − min ( X ) X^{*}=\frac{X-\min (X)}{\operatorname{range}(X)}=\frac{X-\min (X)}{\max (X)-\min (X)} X∗=range(X)X−min(X)=max(X)−min(X)X−min(X)
(稍微注意这个函数就可以发现,它的取值始终实在0和1之间。注意之后如果有取值在0-1之间的分类变量的话,分类变量对它的取值影响就很大。) - Z-score标准化
X ∗ = X − mean ( X ) SD ( X ) X^{*}=\frac{X-\operatorname{mean}(X)}{\operatorname{SD}(X)} X∗=SD(X)X−mean(X)
(经验上来讲,这个函数的取值在-3和3之间,范围更宽。)
此外对于分分类变量来说,欧氏距离并不合适。为此,我们需要一个“差异”函数,用于比较一对记录中的第 i 个属性值的大小。
( x i , y i ) = { 0 若 x i = y i 1 否则 \left( x_i,y_i \right) =\left\{ \begin{array}{l} \text{0 若}x_i=y_i\\ \text{1 否则}\\ \end{array} \right. (xi,yi)={0 若xi=yi1 否则
其中 x i x_i xi和 y i y_i yi是分类值。我们可以用 D i f f e r e n t ( x i , y i ) 函 数 Different(x_i,y_i)函数 Different(xi,yi)函数 替换上述分类变量的第 i i i 项。当然,在不同情况的时候,可以适当的对这个函数进行加权。
3.加权投票
在一个新的k-近邻算法里,对于一个新的纪录,大家可能会认为与新记录更接近或更相似的近邻应该比那些与新记录更远的近邻得到更大的权重。与新记录距离较远的浅灰色记录获得和与新记录距离较近的深灰色记录同样的投票权,这种方式公平吗?这样做也许是不公平的。为此,分析人员可能会采用加权投票方式,距离近的近邻在分类决策中将比距离远的近邻获得更大的投票权。基于加权的投票将会大大减少平局发生的可能性。
采用加权投票,特定记录对分类新记录的影响与其和新记录的距离成反比。
给个例子:
原有3条数据,两个分类,对他们进行 最大最小规范化 ,然后我们计算他们之间的距离:
d
(
new, A
)
=
(
0.05
−
0.0467
)
2
+
(
0.25
−
0.2471
)
2
=
0.004393
d
(
new,B
)
=
(
0.05
−
0.0533
)
2
+
(
0.25
−
0.1912
)
2
=
0.58893
d
(
new,
C
)
=
(
0.05
−
0.0917
)
2
+
(
0.25
−
0.2794
)
2
=
0.051022
\begin{aligned} d\left( \,\,\text{new, A} \right) &=\sqrt{\left( 0.05-0.0467 \right) ^2+\left( 0.25-0.2471 \right) ^2}=0.004393\\ d\left( \text{new,B} \right) &=\sqrt{\left( 0.05-0.0533 \right) ^2+\left( 0.25-0.1912 \right) ^2}=0.58893\\ d\left( \text{new,}C \right) &=\sqrt{\left( 0.05-0.0917 \right) ^2+\left( 0.25-0.2794 \right) ^2}=0.051022\\ \end{aligned}
d(new, A)d(new,B)d(new,C)=(0.05−0.0467)2+(0.25−0.2471)2=0.004393=(0.05−0.0533)2+(0.25−0.1912)2=0.58893=(0.05−0.0917)2+(0.25−0.2794)2=0.051022
这些记录的投票权重将按照距离的平方倒数获得。
记录(A)为新记录投票给深灰色记录(选择药物B和C),因此该分类的权重投票如下:
V
o
t
e
(
d
a
r
k
g
r
a
y
)
=
1
d
(
new
,
A
)
2
=
1
0.00439
3
2
≅
51818
Vote(dark gray)=\frac{1}{d(\text { new }, \mathrm{A})^{2}}=\frac{1}{0.004393^{2}} \cong 51818
Vote(darkgray)=d( new ,A)21=0.00439321≅51818
记录(B和C)为新记录投票给中灰色记录(选择药物A和X),因此该分类的权重投票如下:
Votes (medium gray)
=
1
d
(
n
e
w
,
B
)
2
+
1
d
(
n
e
w
,
C
)
2
=
1
0.05889
3
2
+
1
0.05102
2
2
≅
672
\begin{aligned} & \text {Votes (medium gray) } \\=& \frac{1}{d(n e w, B)^{2}}+\frac{1}{d(n e w, C)^{2}} \\=& \frac{1}{0.058893^{2}}+\frac{1}{0.051022^{2}} \\ \cong & 672 \end{aligned}
==≅Votes (medium gray) d(new,B)21+d(new,C)210.05889321+0.05102221672
此时,我们比较结果,则选择深灰色的类级。
4.轴伸缩
除了根据距离来选择加权而来归类之外,我们还可以使用 轴伸缩 的方法。为何呢?以决策树为例,在决策树分类中,只考虑使用那些与分类有关的属性。在k-最近邻算法中,默认情况下要计算所有属性的距离。可能存在一些相关记录,其所有的重要的变量都与新记录相似,而在不重要的方面却与新记录距离甚远,导致其总体上与新记录有相当大的距离,由此在分类决策中未被考虑。因此分析师可以对算法作出一些限制,让算法去考虑对分类新记录重要的字段,或者至少让算法不考虑那些不相关的字段。
例如,假设通过交叉验证或者领域专家推荐,在药物分类时,钠/钾比的重要性是年龄的3倍。那么可以得到KaTeX parse error: Expected '}', got 'EOF' at end of input: z_{Na/K)=3及$Z_{age}=1。针对前述的示例,记录A、B、C与新记录的距离如下:
d
(
new, A
)
=
(
0.05
−
0.0467
)
2
+
[
3
(
0.25
−
0.2471
)
]
2
=
0.009305
d
(
new ,B
)
=
(
0.05
−
0.0533
)
2
+
[
3
(
0.25
−
0.1912
)
]
2
=
0.17643
d
(
new ,
C
)
=
(
0.05
−
0.0917
)
2
+
[
3
(
0.25
−
0.2794
)
]
2
=
0.09756
\begin{aligned} d\left( \,\,\text{new, A} \right) &=\sqrt{\left( 0.05-0.0467 \right) ^2+\left[ 3\left( 0.25-0.2471 \right) \right] ^2}=0.009305\\ d\left( \,\,\text{new ,B} \right) &=\sqrt{\left( 0.05-0.0533 \right) ^2+\left[ 3\left( 0.25-0.1912 \right) \right] ^2}=0.17643\\ d\left( \,\,\text{new ,}C \right) &=\sqrt{\left( 0.05-0.0917 \right) ^2+\left[ 3\left( 0.25-0.2794 \right) \right] ^2}=0.09756\\ \end{aligned}
d(new, A)d(new ,B)d(new ,C)=(0.05−0.0467)2+[3(0.25−0.2471)]2=0.009305=(0.05−0.0533)2+[3(0.25−0.1912)]2=0.17643=(0.05−0.0917)2+[3(0.25−0.2794)]2=0.09756
简单地说起来,这其实就是在你觉得关键的变量上乘以一个系数,使它的影响力变的更大一点点。这里的例子中呢,钠/钾比伸缩并未改变分类的结果,仍然选择采用深灰色点。然而,在实际工作中,轴伸缩可以得到更精确的分类,因为采用该方法可量化每个变量在分类决策中的相关性。
5.k-最近邻算法用于评估和预测
最普遍的,我们用KNN来进行分类。然而,其实它还可以用于连续性目标变量的评估和预测。
以使用过的例子来讲解:
这个例子中,我们希望估计年龄为17岁,Na/K为1.5的病人的收缩压(目标变量)。令k=3,我们得到表中3个最近邻,我们对Na/K的加权值为3。
局部加权平均将采用血压加权平均来估计3个最近邻的血压,与我们前例采用同样的方法,以距离的平方倒数为权重。目标变量值
y
^
\hat{y}
y^计算如下:
y
^
n
c
w
=
∑
i
w
i
y
i
∑
i
w
i
\hat{y}_{\mathrm{ncw}}=\frac{\sum_{i} w_{i} y_{i}}{\sum_{i} w_{i}}
y^ncw=∑iwi∑iwiyi
在这个公式里,其中有已存在的记录
x
1
,
x
2
,
…
,
x
k
x_{1}, x_{2}, \dots, x_{k}
x1,x2,…,xk,其
w
i
=
1
/
d
(
w_{i}=1 / d(
wi=1/d( new
,
x
i
)
2
, x_{i})^{2}
,xi)2。
这个例子里,我们的计算如下:
y
^
ncw
=
∑
i
w
i
y
i
∑
i
w
i
=
(
120/
0.00930
5
2
)
+
(
122/
0.1764
3
2
)
+
(
130/
0.0975
6
2
)
(
1/
0.00930
5
2
)
+
(
1/
0.1764
3
2
)
+
(
1/
0.0975
6
2
)
=
120.0954
\hat{y}_{\text{ncw}}=\frac{\sum_i{w}_iy_i}{\sum_i{w}_i}=\frac{\left( \text{120/}0.009305^2 \right) +\left( \text{122/}0.17643^2 \right) +\left( \text{130/}0.09756^2 \right)}{\left( \text{1/}0.009305^2 \right) +\left( \text{1/}0.17643^2 \right) +\left( \text{1/}0.09756^2 \right)}=120.0954
y^ncw=∑iwi∑iwiyi=(1/0.0093052)+(1/0.176432)+(1/0.097562)(120/0.0093052)+(122/0.176432)+(130/0.097562)=120.0954
结果与预期吻合,估计的血压值与数据集中和新记录最近(在伸缩属性空间中)的记录的血压值非常接近。换句话说,因为记录A与新记录更靠近,所以其血压值120在估计新记录的血压值时获得的权重更大一些。
6.k值的选择
这个可以按照经验,也可以采用类似前面使用 轴伸缩 时用的交叉验证过程。通过选择不同的k值来训练随机选择的训练集,获得能够最小化分类或评估误差的k值。