k-邻近算法kNN及其python实现

k-邻近算法kNN

   邻近法kNN是1967年提出的一种基本分类与回归方法。kNN中输入测试数据和训练样本,然后计算测试数据到每个训练样本之间的距离,接着选出距离最小的k个样本,根据k个样本的类别投票决定。K个样本中占多数的类别即为测试数据的类别。

一.k-邻近算法的优缺点:

1.优点:简单好用,容易理解,精度高,理论成熟,既可以用来做分类也可以用来做回归、适合于多分类问题、可用于数值型数据和离散型数据、对异常值不敏感;

2.缺点:计算复杂性高、空间复杂性高、样本不平衡问题(即有些类别的样本数量很多,而其它样本的数量很少)、一般数值很大的时候不用这个,计算量太大。但是单个样本又不能太少,否则容易发生误分、最大的缺点是无法给出数据的内在含义。

二.距离度量

    距离度量有很多种方式,要根据具体情况选择合适的距离度量方式。常用的是闵可夫斯基距离(Minkowski Distance),定义为:


其中p>=1。

当p=2时,是欧氏距离,当p=1时,是曼哈顿距离。关于距离度量的方法还有切比雪夫距离、马氏距离、巴氏距离等。

三.K值的选择

    K值的选择对给过影响很大。当k值太小时,样本的噪声或异常值会影响判断,发生过拟合;而k值太大时,结果显示的是样本中实例最多的类,发生欠拟合。在实际中,一般选择较小k并且k是奇数。通常采用交叉验证的方法来选取合适的k值。

四.分类规则

    计算测试数据与样本之间的距离,然后由测试样本的k个临近样本的多数类决定测试样本的类别。多数表决规则等价于经验风险最小化。

五.算法步骤

1. 初始化距离为最大值;

2. 计算测试数据和每个训练样本的距离d;

3.统计比较得出距离d最小的k个训练样本;

4. 统计k个最近邻样本中每个类标号出现的次数;

5. 选择出现频率最大的类作为测试数据所属的类。

六.距离加权最近邻算法

    对 k - 近邻算法的一个显而易见的改进是对k 个近邻的贡献加权,根据它们相对测试点的距离,将较大的权值赋给较近的近邻。我们可以根据每个近邻与测试点的距离平方的倒数加权这个近邻的“选举权”,即权值。

    按距离加权的 k - 近邻算法是一种非常有效的归纳推理方法。它对训练数据中的噪声有很好的鲁棒性,而且当给定足够大的训练集合时它也非常有效。注意通过取 k 个近邻的加权平均,可以消除孤立的噪声样例的影响。

七.最近邻法的实现:kd树

    当样本容量很大或者样本特征空间的维数很大时,运用kNN的线性扫描计算会非常耗时,这种方法是行不通的。我们为了提高k近邻搜索的效率,可以使用特殊的结构存储训练数据,以减少计算距离的次数。下面介绍其中的kd树(kd tree)方法。

1.kd树的结构

Kd-Tree是一棵二叉树,树中存储的是一些K维数据。在一个K维数据集合上构建一棵Kd-Tree代表了对该K维数据集合构成的K维空间的一个划分,即树 中的每个结点就对应了一个K维的超矩形区域。

(1)对于特征空间是k维的样本数据集,选择第i维作为坐标轴,将数据集第i维数据进行排列,选择中位数对应的数据作为根节点,并以此中位数为切分点,将根节点对应的超矩形区域划分成两个子区域。由根节点生成深度为1的左右子节点:左子节点对应坐标第i维数据小于切分点的子区域,右子节点对应坐标第i维数据大于切分点的子区域;

(2)接着对划分的子区域按照(1)中步骤划分,新划分出的节点成为上一步中节点的子节点,以此迭代,直到子区域内没有实例。

2.每次对子空间的划分时,怎样确定在哪个维度上进行划分

如果这些数据在某维度上分散的比较开,我们就更容易在这个维度上将它们划分开,因此,这就引出了我们选择维度的另一种方法:最大方差法(max invarince),即每次我们选择维度进行划分时,都选择具有最大方差维度。

3.kd树搜索

给定一个目标点,搜索其最近邻,首先找到包含目标点的叶节点,然后从该叶节点出发,依次退回到其父节点,不断查找是否存在比当前最近点更近的点,直到退回到根节点时终止,获得目标点的最近邻点。如果按照流程可描述如下:

(1)从根节点出发,若目标点x当前维的坐标小于切分点的坐标,则移动到左子节点,反之则移动到右子节点,直到移动到最后一层叶节点。

(2)以此叶结点为“当前最近点”

(3)递归的向上回退,在每个节点进行如下的操作:

a.如果该节点保存的实例点距离比当前最近点更小,则该点作为新的“当前最近点” ;

b.检查“当前最近点”的父节点的另一子节点对应的区域是否存在更近的点,如果存在,则移动到该点,接着,递归地进行最近邻搜索。如果不存在,则继续向上回退;

(4)当回到根节点时,搜索结束,获得最近邻点。

4.kd树实现最近邻法

以上分析的是k=1是的情况,当k>1时,在搜索时“当前最近点”中保存的点个数<=k的即可。相比于线性扫描,kd树搜索的平均计算复杂度为O(logN).但是当样本空间维数接近样本数时,它的效率会迅速降低,并接近线性扫描的速度。因此kd树搜索适合用在训练实例数远大于样本空间维数的情况。


八.python代码实现

from numpy import *
import operator
def createDataSet():
    group=array([[1,1],[1,2],[2,1],[2,2],[0.5,0.5],[4,4],[5,5],[6,6],[5,4],[4,5],[6,4]])
    labels=['A','A','A','A','A','B','B','B','B','B','B']
    return group,labels

def classifyKNN(testData,dataSet,labels,k):
    dataSetSize=dataSet.shape[0]   #得到dataSet的行数
    diffMat=tile(testData,(dataSetSize,1))-dataSet   ##tile(A,(m,n))是把矩阵A的行复制m个,列复制n个并连接
    sqDiffMat=diffMat**2                            #数组每个元素平方
    sqDistances=sqDiffMat.sum(axis=1)               #把数组每行元素相加得到一维数组,或大矩阵每一行元素相加得到一个一维列矩阵
    distances_list=[math.sqrt(x) for x in sqDistances]
    distances=array(distances_list)
    sortedDistIndices=distances.argsort()           #argsort()函数是将x中的元素从小到大排列,提取其对应的index(索引)输出
    classCount={}
    for i in range(k):
        voteLabel=labels[sortedDistIndices[i]]
        classCount[voteLabel]=classCount.get(voteLabel,0)+1    #gclassCount.get(key, default = None) 返回字典中key对应的值,
                                                                       #若key不存在字典中,则返回default的值(default默认为None)
    sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
  #key=operator.itemgetter(1)表示按照字典的第二个元素(即value)进行排序,reverse默认False为升序,True为降序。返回的任然是个字典.或者使用key=lambda x: x[1]
    return sortedClassCount[0][0]                    #返回字典的按value降序排好后的第一个key值,即类别

dataSet,labels=createDataSet()
classResult=classifyKNN([1,2.6],dataSet,labels,5)
print(classResult)







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值