KNN
k近邻法(k-nearest neighbor,k-NN) 是一种基本分类与回归方法。其算法如下:
K近邻模型
k近邻算法中,当训练集、距离度量、k值及分类决策规则确定之后,对于任何一个新的输入的实例,它所属的类唯一地确定。这相当于根据上述将特征空间划分为一些子空间,确定子空间的每个点所属的类。特征空间中,对每个训练实例点
xi
, 距离该点比其他点更近的所有点组成一个区域,叫做单元。每个训练实例点拥有一个单元,所有训练实例点的单元构成特征空间的一个划分。最近邻法(k=1时)将实例
xi
的类
yi
作为七单元中所有点的类标记。这样每个单元实例点的类别都是确定的。
KNN三要素
距离的度量
设特征空间
是n维实数向量空间
Rn
,
xi,xj∈,xi=(x1i,⋯,xni)⊤,xj=(x1j,⋯,xnj)⊤
,
xi,xj
的
Lp
距离定义为:
当 p=1 时,称为曼哈顿距离(Manhattan distance)
当 p=2 时,称为欧式距离(Euclidean distance)
当 p=∞ 时,就是去各个坐标的最大值,即:
用图表示如下:
k值的选择
k值较小意味着整体模型变得复杂,容易发生过拟合
k值较大相当于用较大邻域中的训练实例进行预测,较远的实例也会对预测起作用,容易使预测发生错误
分类决策规则
分类的损失函数为 0-1损失函数
误分类率为:
要使误分类率最小即经验最小,就要是的分类准确率最大即 1k∑xi∈Nk(x)I(yi=cj) 最大,所以多数表决规则等价于经验风险最小化
kd-tree算法
输入: k维空间数据集
T
输出:kd-tree
- 构造根节点,根节点对应于包含
T 的 k 维空间的超矩形区域. 选择x(1) 为坐标轴,以 T 中所有实例的x(1) 坐标的中位数为切分点,将根节点对应的超矩形区域切分为两个子区域。切分由通过切分点与坐标轴 x(1) 垂直的超平面实现,则左子节点对应坐标 x(1) 小于切分点的子区域,右节点对应的坐标则大于切分点的子区域- 递归的继续划分区域,直到两个子区域没有实例则停止
代码如下:
import os class node(object): def __init__(self,point): self.point = point self.left = None self.right = None pass def median(data): m = len(data)/2 return data[m], m def build_kd_tree(data,flag): data = sorted(data,key = lambda x: x[flag]) print(data) p,m = median(data) tree = node(p) del data[m] if (m > 0): tree.left = build_kd_tree(data[:m],not flag) if (len(data) > 1): tree.right = build_kd_tree(data[m:],not flag) return tree if __name__ == '__main__': T = [[2,3],[5,4],[9,6],[4,7],[8,1],[7,2]] kd_tree = build_kd_tree(T,0) print(kd_tree)
代码参考http://www.hankcs.com/ml/k-nearest-neighbor-method.html
搜索kd-tree
给定一个目标点,搜索其最近邻。首先找到包含目标点的叶节点;然后从该叶节点出发,一次回退到父节点;不断查找与目标节点最近邻的节点,当确定不可能存在更近的节点时终止,这样搜索就被限制在空间的局部区域上,效率大为提高。
搜索跟二叉树一样来,是一个递归的过程。先找到目标点的插入位置,然后往上走,逐步用自己到目标点的距离画个超球体,用超球体圈住的点来更新最近邻(或k最近邻)
import os class node(object): def __init__(self,point): self.point = point self.left = None self.right = None self.parent = None def set_left(self,left): if left == None: pass left.parent = self self.left = left def set_right(self,right): if right == None: pass right.parent = self self.right = right def median(data): m = len(data) / 2 return data[m],m def build_kd_tree(data,flag): data = sorted(data,key = lambda x: x[flag]) p,m = median(data) tree = node(p) del data[m] if m > 0: tree.set_left(build_kd_tree(data[:m],not flag)) if (len(data) > 1): tree.set_right(build_kd_tree(data[m:],not flag)) return tree def distance(x,y): return ((x[0]-y[0]) ** 2 + (x[1] - y[1]) ** 2) **0.5 def search_kd_tree(tree,d,target): if target[d] < tree.point[d]: if tree.left != None: return search_kd_tree(tree.left,not d,target) else: if tree.right != None: return search_kd_tree(tree.right,not d,target) def update_best(t,best): if t == None: return t = t.point d = distance(t,target) if (d < best[1]): best[1] = d best[0] = t best = [tree.point,100000.0] while(tree.parent != None): update_best(tree.parent.left,best) update_best(tree.parent.right,best) tree = tree.parent return best[0] if __name__ == '__main__': T = [[2, 3], [5, 4], [9, 6], [4, 7], [8, 1], [7, 2]] tree = build_kd_tree(T,0) print(search_kd_tree(tree,0,[5,6])) ##查找[5,6]的最近邻输出结果为[4,7]