机器学习(三)k近邻(kNN)

  今天开始回头复习基础算法,emmmmm……从kNN开始~~

1.kNN简介

  k近邻(k-Nearest Neighbor,简称kNN)是一种监督学习算法,其工作机制是:给定测试样本,基于某种距离度量找出训练集中与其靠近的k个训练样本,然后基于这k个“邻居”的信息来进行预测。
  
  敲黑板:
  
  <1> k近邻是监督学习算法;
  
  <2> k近邻并没有显示的训练过程,也就是说不需要在测试之前进行模型的训练学习(懒惰学习);
  
  <3> k近邻既可以做分类任务,也可以做回归任务。(也就是最后做预测时的决策方式不同)其中,分类任务采用投票法对k个样本中的类别进行选择;而回归任务则多数使用平均法,将k个样本的输出做平均值然后进行输出。

2.kNN三要素

  分别是:
  (1)k值的选取;  
    关于k值的选择,没有固定的经验,一般根据样本的分布,选择一个较小的值,可以通过交叉验证选择一个合适的值。注意:
    k值的减小会导致泛化误差的增大,也就是模型趋于复杂,容易过拟合;
    k值的增大则会产生与上面相反的结果。总之k值的选取对模型预测结果产生很重要的影响。
    
  (2)距离的度量方式;
    关于距离的度量,有很多常用的方式,但一般使用欧式距离:
    

D ( x , y ) = ( x 1 y 1 ) 2 + ( x 2 y 2 ) 2 + . . . + ( x n y n ) 2

    当然,别的距离度量也是可以的,比如曼哈顿距离(对应L1范数):
    
D ( x , y ) = | x 1 y 1 | + | x 2 y 2 | + . . . + | x n y n |

    以及闵可夫斯基距离(对应Lp范数):
    
D ( x , y ) = ( x 1 y 1 ) n + ( x 2 y 2 ) n + . . . + ( x n y n ) n n

   (3)分类/回归的决策规则;
    k近邻中的分类决策规则往往是多数表决,即由输入实例的k个邻近的训练实例中的多数类决定输入市里的类。多数表决规则等价于经验风险最小化。

3.kNN算法的实现:(在scikit-learn中)

暴力算法

  也就是最直接的方法:计算样本和所有训练样本的距离,然后找出最小的k个即可。这个方法简单直接,在样本量少并且特征少的时候有效。但是在实际中很多时候用不上,因为多数情况下样本都很多,算法的效率会很低。但是scikit-leain中确实给出了实现。scikit-learn中提供了一个KNeighborsClassifier类来实现k近邻法的分类模型,其原型是:
  sklearn.neighbors.KNeighborsClassifier(n_neighbors=5,weights=’uniform’, algorithm=’auto’,leaf_size=30,p=2,metric=’minkowski’,’metric_params=None,n_jobs=1,**kwargs)
  注意:上面参数中的algorithm就是指代的计算最近邻的算法,可以为
  ‘ball_tree’ : 使用球树算法
  ‘kd_tree’ : 使用kd树算法
  ‘brute’ : 这个就是使用暴力搜素法
  ‘auto’ : 自动选择最适合的算法
关于代码,我会单写一个出来的~~~

kd树

  kd树需要对样本进行建模,而模型就是kd树。kd树就是k个特征维度的树(注意:这里的k和kNN中的k不是一个概念)kd树的搜素效率为O(log N)
  
  (1)kd树的建立:
  从m个样本的n维特征中,分别计算n个特征的取值的方差,用方差最大的第k维特征 n k 作为根节点。对于这个特征我们选择特征 n k 的中位数 n k v 对应的样本作为划分点。对于该特征的取值小于 n k v 的样本划入左子树,大的则划入右子树。然后对于左子树和右子树进行递归的重复上面的过程,最后生成kd树。
  (2)kd树搜索最近邻
  对于一个目标点,我们首先在kd树里面找到包含目标点的叶子节点。以目标点为圆心,并以目标点到叶子节点样本实例的距离为半径,得到一个超球体,最近邻的点一定在这个超球体内部。然后返回叶子节点的父节点,检查另外一个子节点包含的超矩形体是否和超球体相交:如果相交就到这个子节点寻找是否有更加近的近邻,有的话就更新最近邻;如果不想交,直接返回父节点的父节点,在另一子树中继续搜索最近邻。当返回到根节点的时候,算法结束,此时保存的最近节点就是最终的最近邻。
  (3)kd树预测
  在kd树搜索最近邻的基础上,我们选择到了第一个最近领样本,就把它置为已选。在第二轮中,我们忽略已选的样本,重新选择最近邻,这样跑k次,就得到了目标的k个最近邻,然后进行分类或者回归的预测即可。

球树

  kd树虽然提高了kNN的搜索效率,但是在某些时候的效率并不高。不管是近似方形,正方形,长方形,都不是最好的形状。因为它们都有角!因为角的问题可能会导致一些不必要的比较的出现,从而拉低效率。为了优化超矩形体的效率问题而引入了球树。
这里写图片描述
  (1)球树的建立
  球树:也就是每个分割块是超球体,而不是超矩形体。
  建树流程:
    1. 先构建一个超球体,这个超球体可以包含所有样本的最小球体。
    2.从球中选择一个离球的中心最远的点,然后选择第二个点离第一个点最远,将球中所有的点分配到离这两个聚类中心最近的一个上,然后计算每个聚类的中心,以及聚类能够包含它所有数据点所需的最小半径。这样得到了两个超子球体,和kd树里面的左右子树对应。
    3.对于两个超子球体,递归的进行步骤2,最后可以得到一个球树。
  球树得到的是节点样本组成的最小超球体,而kd树则是超矩形体,这个超球体要比对应的超矩形体小,这样做在进行最近邻搜素的时候,可以避免一些不必要的搜索。
  (2)球树搜索最近邻
  首先自上而下贯穿整棵树找出包含目标点的所在的叶子节点,并在这个球里找出与目标点最近的点,这个记为目标点距离它的最近邻点的一个上限值。然后和kd树的搜索一样,检查兄弟节点:如果目标点到兄弟节点中心的距离超过兄弟节点的半径与当前的上限值之和,那么兄弟节点里面不可能存在一个更近的点;否则的话,必须进一步检查兄弟节点以下的子树。检查完兄弟节点以后,向父节点回溯,重复上面的过程,直到根节点。

4.kNN算法的优缺点

优点:

     (1)思想简单、理论成熟,既可以用来分类也可以用于回归。
     (2)可用于非线性分类。
     (3)训练时间复杂度比SVM之类的低,为O(n).
     (4)和朴素贝叶斯之类的算法比,对数据没有假设,准确度高,对异常点不敏感。
     (5)由于kNN方法主要靠周围有限的邻近样本,而不是靠判别类域的方法确定所属类别的,因此对于类域的交叉或者重叠较多的样本来说,kNN较其它方法更适合。
     (6)该方法适合样本容量较大的类域的自动分类,而那些样本容量较少的比较容易产生误分类。

缺点:

     (1)计算量大,尤其是特征数非常多的时候。
     (2)样本不平衡的时候,对稀有类别的准确率低。
     (3)kd、球树之类的模型的构建需要大量的内存。
     (4)使用懒散学习方法,基本上不学习,导致预测速度比逻辑回归之类的慢。
     (5)相比决策树模型,kNN解释性不强。

  总而言之,kNN算法作为《统计学习方法》等很多材料的第一个或第二个讲述的算法,可以感觉到算法本身比较简单,容易理解。而且kNN在维度很高的时候也有很好的分类效果,因此应用比较广泛~~~
  下一篇是关于scikit-learn中kNN算法的使用和源码的部分解析哦~

猜你喜欢

转载自blog.csdn.net/katrina_ali/article/details/81061457