[关闭]
@zsh-o 2018-08-14T10:03:21.000000Z 字数 4257 阅读 1056

3 - k近邻

《统计学习方法》


  1. %matplotlib inline
  2. import numpy as np
  3. from matplotlib import pyplot as plt
  4. epsilon = 1e-5
  1. import pydot
  2. from IPython.display import Image, display
  3. def viewPydot(pdot):
  4. plt = Image(pdot.create_png())
  5. display(plt)

  1. def kNN(x, X, Y, k, p): # y [0, N-1]
  2. m,n = X.shape
  3. class_N = np.max(Y) + 1
  4. diffs = np.abs(np.tile(x, (m, 1)) - X)
  5. if p is np.inf:
  6. diss = np.max(diffs, axis=1)
  7. else:
  8. diss = np.power(np.sum(np.power(diffs, p), axis=1), 1./p)
  9. args = np.argsort(diss)[:k]
  10. countY = np.zeros(class_N)
  11. for index in args:
  12. countY[index] = countY[index] + 1
  13. return np.argmax(countY)

例3.1

  1. X = np.array([
  2. [5,1],
  3. [4,4],
  4. ])
  5. Y = np.array([
  6. [0],
  7. [1],
  8. ])
  1. for i in range(1,6):
  2. print("%d -> %d"%(i, kNN(x=np.array([1,1]), X=X, Y=Y, k=1, p=i)))
1 -> 0
2 -> 0
3 -> 1
4 -> 1
5 -> 1

最近邻性能分析

《西瓜书》给出了最近邻的性能分析,这里只是只是表示最近邻(用距离最近的训练样本的类别作为输入的类别)

大样本(独立同分布,且样本量无穷)下最近邻规则的泛化错误率不超过贝叶斯最优分类器(所有概率关系已知)的错误率的两倍

测试样本为,其最近邻样本为,则最近邻分类器出错的概率为的标记与不同的概率


这里的概率均是在所有概率已知的情况下的分析,泛化错误率,则贝叶斯最优分类器的结果为

对应的错误率为

由于样本符合独立同分布且有无限的样本,所以对任意的和任意小正整数,在附近范围内总能找到一个训练样本;换成人话就是,在无限样本且符合独立同分布下,样本与其近邻无限靠近,故

还有个关于最近邻的不等式,是《西瓜书》习题上的,是上面不等式的细化,设R为在整个n个样本集中的错误率的期望

整理


由柯西-施瓦茨不等式得到

可以得到

最后有

则对所有数据集

kd树

  1. A = np.array([
  2. [2,3],
  3. [5,4],
  4. [9,6],
  5. [4,7],
  6. [8,1],
  7. [7,2]
  8. ])
  1. def D(X):
  2. return np.mean(np.power(X - np.mean(X,axis=0), 2), axis=0)
  1. class Node(object):
  2. def __init__(self, p=None, dim=None, lc=None, rc=None):
  3. self.p = p
  4. self.dim = dim
  5. self. lc = lc
  6. self.rc = rc
  7. self.visited = False
  1. def kd_Tree(X):
  2. if(len(X) == 0):
  3. return None;
  4. N = X.shape[0]
  5. ## 选择需要划分的维度
  6. Ds = D(X)
  7. dim = np.argmax(Ds)
  8. X = np.array(sorted(X, key=lambda x:x[dim]))
  9. root = Node()
  10. root.p = X[int(N/2)]
  11. root.dim = dim
  12. root.lc = kd_Tree(X[:int(N/2)])
  13. root.rc = kd_Tree(X[int(N/2)+1:])
  14. return root

搜索

搜索也很简单,首先按照二分的方法搜索找到最优的候选叶子节点,然后向上回溯,查看父节点的未访问到的节点是否有小于候选节点与目标节点距离,小于则更新候选节点
判断目标节点到该轴的距离是否小于最小距离

  1. def kd_search(root, x):
  2. global min_dis
  3. global min_node
  4. min_dis = np.inf
  5. min_node = None
  6. def _search(t): ## 递归
  7. global min_dis
  8. global min_node
  9. if ((x[t.dim] < t.p[t.dim]) & (t.lc is None)) | ((x[t.dim] >= t.p[t.dim]) & (t.rc is None)): ## 叶子节点,二分查找的最优候选节点
  10. dis = np.sqrt(np.sum(np.power(t.p - x, 2)))
  11. if dis <= min_dis:
  12. min_dis = dis
  13. min_node = t.p
  14. t.visited = True
  15. return
  16. else:
  17. if x[t.dim] < t.p[t.dim]:
  18. _search(t.lc)
  19. else:
  20. _search(t.rc)
  21. if np.abs(x[t.dim] - t.p[t.dim]) >= min_dis:
  22. t.visited = True
  23. else:
  24. if x[t.dim] > t.p[t.dim]:
  25. _search(t.rc)
  26. else:
  27. _search(t.lc)
  28. dis = np.sqrt(np.sum(np.power(t.p - x, 2)))
  29. if dis <= min_dis:
  30. min_dis = dis
  31. min_node = t.p
  32. t.visited = True
  33. _search(root)
  34. return min_node, min_dis
  1. tree = kd_Tree(A)
  1. kd_search(tree, np.array([4,3]))
(array([5, 4]), 1.4142135623730951)
  1. dot = pydot.Dot()
  2. global level
  3. level = 1
  4. def create_dot(p):
  5. global level
  6. p_name = "%d # %d, %s" % (level, p.dim, str(p.p))
  7. dot.add_node(pydot.Node(name=p_name))
  8. if p.lc is not None:
  9. level = level + 1
  10. c = p.lc
  11. c_name = "%d # %d, %s" % (level, c.dim, str(c.p))
  12. dot.add_edge(pydot.Edge(dst=c_name, src=p_name))
  13. create_dot(c)
  14. level = level -1
  15. if p.rc is not None:
  16. level = level + 1
  17. c = p.rc
  18. c_name = "%d # %d, %s" % (level, c.dim, str(c.p))
  19. dot.add_edge(pydot.Edge(dst=c_name, src=p_name))
  20. create_dot(c)
  21. level = level -1
  1. create_dot(tree)
  2. viewPydot(dot)

output_19_0.png-23.1kB

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注