机器学习笔记1 - k近邻法

来源:互联网 发布:linux 自动运行脚本 编辑:程序博客网 时间:2024/06/05 04:29

Table of Contents

  • 1 概念
    • 1.1 k值的选择
    • 1.2 距离量度方法
    • 1.3 如何确定分类
  • 2 kNN的实现
    • 2.1 线性扫描
    • 2.2 k-d树
      • 2.2.1 kd树构建
      • 2.2.2 添加元素
      • 2.2.3 删除元素
      • 2.2.4 kd树查找
  • 3 已有工具库
    • 3.1 R: FNN包
    • 3.2 其它工具库

1 概念

k近邻法(k nearest neighbor, k-NN)是一种基本的分类和回归方法,简单、直观。当用来分类时,给定一个训练集,对于新输入实例,找到最近的k个训练样例,然后根据训练样例确定新样例的分类。
从这里可以看出,kNN有几大要素:

  1. k的选择
  2. 距离量度方法
  3. 如何确定分类

1.1 k值的选择

选择的k值越小,模型负责度越高,容易发生过拟合。可以这样直观的理解:设想极端情况,k=1。新输入样例的分类就取决于最近的训练馆样例。如果恰好遇到噪音,那么就完全错误了。
随着k值正大,模型泛化能力也增大,但丢失的信息也增多。设想k=N,那么任意新输入样例的分类就等于训练样例中实例数最多的分类。
实践中,先选择一个较小的k值,然后通过交叉验证的方法找到最优值。

1.2 距离量度方法

主要用Lp系列函数作为量度方法:

Lp(xi,xj)=(l=1n|x(l)ix(l)j|p)1p
当p=2时,称为欧式距离(Euclidean distance),即:
L2(xi,xj)=(l=1n|x(l)ix(l)j|2)1p
其物理意义是2点的直线距离。
当p=1时,称为曼哈顿距离(Manhattan distance),即:
L1(xi,xj)=l=1n|x(l)x(l)ji|
其物理意义类似在曼哈顿的街区从一点走到另一点,不能穿过建筑物,只能贴着建筑物边缘绕过去。

1.3 如何确定分类

ok,假设现在已经找到了k个近邻的点。最直观的确定分类的方法就是"多数表决"。把新实例分到k个点所属分类最多的类。

2 kNN的实现

2.1 线性扫描

线性扫描(linear scan)是最简单粗暴的kNN算法。每次扫描所有点,计算和新实例的距离,然后找出最近的k个点。假设n个样本点,m个新实例,复杂度为O(mn)

2.2 k-d树

kNN的本质是对特征空间的划分,kd树的思想就是用线段树来表示这种划分,使得搜索效率提高为O(mlog(n)) 
下图为直观的k-d树对特征空间的划分。1

./kdtree_space_spliting.png

k-d树

2.2.1 kd树构建

构建过程简单来说就是辗转用特征向量的每一维的中位数切分特征空间。python代码如下1

class Node: passdef kdtree(point_list, depth=0):    if not point_list:        return None    # Select axis based on depth so that axis cycles through all valid values    k = len(point_list[0]) # assumes all points have the same dimension    axis = depth % k    # Sort point list and choose median as pivot element    point_list.sort(key=lambda point: point[axis])    median = len(point_list) // 2 # choose median    # Create node and construct subtrees    node = Node()    node.location = point_list[median]    node.left_child = kdtree(point_list[:median], depth + 1)    node.right_child = kdtree(point_list[median + 1:], depth + 1)    return node

2.2.2 添加元素

和建树过程类似,从根节点开始,辗转用某维特征和原节点比较。整个过程和一般的搜索数插入节点类似。

2.2.3 删除元素

最简单的方法是将待删除节点的所有叶子节点重新建树。

2.2.4 kd树查找

因为树已经将空间进行了划分。查找变得很简单。只需要从root节点出发,查找新节点地"插入位置"。沿途将"空间划分点"入栈。找到插入位置后,依次弹出栈,计算距离,找到距离最短的k个节点。

3 已有工具库

3.1 R: FNN包

R的FNN包(Fast Nearest Neighbour)提供了几种kNN算法。使用方法举例:

  1. 准备训练样例:
    > m <- matrix(c(2,5,9,4,8,7,3,4,6,7,1,2), nrow=6, ncol=2)
  2. 准备输入实例:
    > q <- matrix(c(9,6), nrow=1, ncol=2)
  3. 得到kNN结果
    > get.knnx(m, q, k=6, "kd_tree")$nn.index     [,1] [,2] [,3] [,4] [,5] [,6][1,]    3    6    2    5    4    1$nn.dist     [,1]     [,2]     [,3]    [,4]    [,5]     [,6][1,]    0 4.472136 4.472136 5.09902 5.09902 7.615773

    $nn.index是按照距离远近排序地训练样例下标,可以看到下标1,也就是{9, 6}和输入样例{9, 6}距离最近。$nn.dist为欧式距离列表。

更多FNN包的介绍请参考说明文档:http://cran.r-project.org/web/packages/FNN/FNN.pdf

3.2 其它工具库

此外还有很多kNN的工具库:

  • libkdtree++:http://libkdtree.alioth.debian.org/
  • ANN:http://www.cs.umd.edu/~mount/ANN/

Footnotes:

1 Wikipedia, K-d tree, http://en.wikipedia.org/wiki/K-d_tree

原创粉丝点击