机器学习笔记之KNN

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 距离定义为:

Lp(xi,xj)=(l=1nxlixljp)1p

  • p=1 时,称为曼哈顿距离(Manhattan distance)

  • p=2 时,称为欧式距离(Euclidean distance)

  • p= 时,就是去各个坐标的最大值,即:

L(xi,xj)=maxxlixlj

用图表示如下:
这里写图片描述

k值的选择

k值较小意味着整体模型变得复杂,容易发生过拟合
k值较大相当于用较大邻域中的训练实例进行预测,较远的实例也会对预测起作用,容易使预测发生错误

分类决策规则

分类的损失函数为 0-1损失函数

误分类率为:

1kxiNk(x)I(yicj)=11kxiNk(x)I(yi=cj)

要使误分类率最小即经验最小,就要是的分类准确率最大即 1kxiNk(x)I(yi=cj) 最大,所以多数表决规则等价于经验风险最小化

kd-tree算法

输入: k维空间数据集 T
输出:kd-tree

  1. 构造根节点,根节点对应于包含 T k 维空间的超矩形区域. 选择 x(1) 为坐标轴,以 T 中所有实例的 x(1) 坐标的中位数为切分点,将根节点对应的超矩形区域切分为两个子区域。切分由通过切分点与坐标轴 x(1) 垂直的超平面实现,则左子节点对应坐标 x(1) 小于切分点的子区域,右节点对应的坐标则大于切分点的子区域
  2. 递归的继续划分区域,直到两个子区域没有实例则停止

代码如下:

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]

猜你喜欢

转载自blog.csdn.net/u014046022/article/details/79940931