K近邻算法笔记
(主要参照李航-统计学习方法)
K近邻学习摘要
K近邻计算
代码实例
K近邻学习摘要
K近邻(k-Nearest Neighbor,简称 kNN)学习是一种常用的监督学习方法,是一种基本分类与回归方法。其工作机制非常简单:给定测试样本,基于某种距离度量找出训练集中与其最靠近的k个训练样本,然后基于这k个"邻居"的信息来进行预测。通常,在分类任务中可使用“投票法”,即选择这k个样本中出现最多的类别标记作为预测结果:在回归任务中使用“平均法”,即将这k个样本的实值输出标记的平均值作为预测结果:还可基于距离远近进行加权或加权投票,距离越近的样本权重越大。k近邻法是在1968年由Cover和Hart提出的。
算法1.1(引自李航统计学习方法)
训练数据集
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
n
为
实
例
的
特
征
向
量
,
y
i
∈
Y
=
{
c
1
,
c
2
,
.
.
.
,
c
k
}
x_{i}\in \mathcal X \subseteq R^{n} 为实例的特征向量,y_{i} \in\mathcal Y=\{c_1,c_2,...,c_k\}
xi∈X⊆Rn为实例的特征向量,yi∈Y={c1,c2,...,ck} 为实例的类别, i =,1,2,…,N;实例特征向量x;
输出:实例x所属的类y。
(1)根据给定的距离度量,在训练集T中找出与x最邻近的k个点,涵盖这个k个点的邻域记作
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
)
n
I
(
y
i
=
c
i
)
,
i
=
1
,
2
,
.
.
.
,
N
;
j
=
1
,
2
,
.
.
,
K
y=arg \max\limits_{c_j} \sum\limits_{X_i \subseteq N_K(x)}^n I(y_i=c_i), i=1,2,...,N;j=1,2,..,K
y=argcjmaxXi⊆NK(x)∑nI(yi=ci),i=1,2,...,N;j=1,2,..,K
I
I
I为指示函数,即当
y
i
=
c
i
y_i=c_i
yi=ci时
I
I
I为1,否则
I
I
I为0.
当k=1时称为最近邻算法,最近邻法将训练数据集中与x最邻近点作为x的类。k近邻法没有显示的学习过程,k近邻算法没有模型。
上述描述简而言之:就是将需要分类的输入实例x,在训练集中计算出k个与其最相近的点,然后计算出这些点对应类别最多的那个类。将输入实例划分归为计算出的最多的那一类。
K近邻计算
k近邻算法有三个基本要素,距离度量、k值的选择、分类决策规则。
优点:精度高、对异常值不敏感、无数据输入假定。
缺点:计算复杂度高、空间复杂度高。
适用范围:数值型和标称型。
1.距离度量
两个实例点的距离用来计算两个实例点的相似度。这里可以使用的距离计算方法主要是三种,1、欧氏距离,2、
L
p
L_p
Lp距离(
L
1
,
L
2
L_1,L_2
L1,L2等),3、Minkowski距离。以下为
L
p
L_p
Lp距离计算方法:
设
X
是
n
维
实
数
向
量
空
间
R
(
n
)
,
x
i
,
x
j
∈
X
,
\mathcal X是n维实数向量空间R^{(n)},x_i,x_j\in\mathcal X,
X是n维实数向量空间R(n),xi,xj∈X,
x
i
=
(
x
i
(
1
)
,
x
i
(
2
)
,
.
.
.
x
i
(
n
)
)
T
,
x
j
=
(
x
j
(
1
)
,
x
j
(
2
)
,
.
.
.
x
j
(
n
)
)
T
,
x
i
,
x
j
的
L
p
距
离
定
义
为
x_i=(x_i^{(1)},x_i^{(2)},...x_i^{(n)})^T,x_j=(x_j^{(1)},x_j^{(2)},...x_j^{(n)})^T,x_i,x_j的L_p距离定义为
xi=(xi(1),xi(2),...xi(n))T,xj=(xj(1),xj(2),...xj(n))T,xi,xj的Lp距离定义为
L
p
(
x
i
,
x
j
)
=
(
∑
i
=
1
n
∣
x
i
(
l
)
−
x
j
(
j
)
∣
p
)
1
p
L_p(x_i,x_j)=\big(\sum\limits_{i=1}^n |x_i^{(l)}-x_j^{(j)}|^p \big)^{\dfrac{1}{p}}
Lp(xi,xj)=(i=1∑n∣xi(l)−xj(j)∣p)p1
当p=2时称为欧氏距离即:
L
2
(
x
i
,
x
j
)
=
(
∑
i
=
1
n
∣
x
i
(
l
)
−
x
j
(
j
)
∣
2
)
1
2
L_2(x_i,x_j)=\big(\sum\limits_{i=1}^n |x_i^{(l)}-x_j^{(j)}|^2 \big)^{\dfrac{1}{2}}
L2(xi,xj)=(i=1∑n∣xi(l)−xj(j)∣2)21
当p=1时称为曼哈顿距离即:
L
1
(
x
i
,
x
j
)
=
(
∑
i
=
1
n
∣
x
i
(
l
)
−
x
j
(
j
)
∣
)
L_1(x_i,x_j)=\big(\sum\limits_{i=1}^n |x_i^{(l)}-x_j^{(j)}|\big)
L1(xi,xj)=(i=1∑n∣xi(l)−xj(j)∣)
当
p
=
∞
p=\infty
p=∞时,他是各个坐标距离的最大值,即
L
∞
(
x
i
,
x
j
)
=
(
max
l
∣
x
i
(
l
)
−
x
j
(
j
)
∣
)
L_\infty(x_i,x_j)=\big(\max\limits_l |x_i^{(l)}-x_j^{(j)}| \big)
L∞(xi,xj)=(lmax∣xi(l)−xj(j)∣)
2.k值选择
k值的选择会直接对k近邻的结果产生重大影响。如果k值过小,就相当于使用较小邻域中的训练实例进行预测,获得的"近似误差"会减小,只有与输入实例较近的训练实例才会对预测结果起作用。缺点是估计误差会增大预测结果对近邻的实例点非常敏感,如果近邻点恰好是噪声就会出错。就意味着发生过拟合。相反k值较大就会发生欠拟合。
以此k值一般是一个较小的数,通常采用交叉验证老获取最优的k值。
3.分类决策规则
k近邻中的分类决策规则往往是多数表决,即由输入实例的k个近邻的训练实例中的多数类决定输入实例的类。
多数表决的=规则有如下解释:如果分类的损失函数为0-1损失函数。分类函数为:
f
:
R
n
−
>
{
c
1
,
c
2
,
.
.
c
K
}
f:R^n->\{c_1,c_2,..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个训练实例点构成集合
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
)
\dfrac{1}{k}\sum\limits_{x_i\in N_k(x)} I(y_i \neq c_j)=1- \dfrac{1}{k}\sum\limits_{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\limits_{x_i\in N_k(x)} I(y_i=c_j)
xi∈Nk(x)∑I(yi=cj)最大,所以多数表决规则等价于经验风险最小化。
代码实例
代码改自机器学习实战,将py2改为py3可运行,修改处为classCount.items(),py2为classCount.iteritems()
import numpy as np
import operator
#创建虚拟数据集
def createDataSet():
trainSet = np.array([[1.0, 1.1], [1.0, 1.0], [0.0, 0.0], [0.0, 0.1]])
labels = ["A", "A", "B", "B"]
return trainSet, labels
#构建分类器
def classify0(inputX, trainSet, labels, k):
trainSetLen = len(trainSet)
inputXMat = np.tile(inputX, (trainSetLen, 1))
diffMat = inputXMat - trainSet
sqDiffMat = diffMat**2
sqDistances = sqDiffMat.sum(axis=1)
distances = sqDistances**0.5
sortedDistIndicies = distances.argsort()
classCount = {}
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
trainSet, labels = createDataSet()
print(classify0([0.1, 0], trainSet, labels, 3))