KNN算法介绍及代码实现

k-近邻法简介

    k近邻法(k-nearest neighbor, k-NN)是1967年由Cover T和Hart P提出的一种基本分类与回归方法。它的工作原理是:存在一个样本数据集合,也称作为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一个数据与所属分类的对应关系。输入没有标签的新数据后,将新的数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。

     举个简单的例子,我们可以使用k-近邻算法分类一个电影是爱情片还是动作片。

 

       表1.1就是我们已有的数据集合,也就是训练样本集。这个数据集有两个特征,即打斗镜头数和接吻镜头数。除此之外,我们也知道每个电影的所属类型,即分类标签。用肉眼粗略地观察,接吻镜头多的,是爱情片。打斗镜头多的,是动作片。以我们多年的看片经验,这个分类还算合理。如果现在给我一部电影,你告诉我这个电影打斗镜头数和接吻镜头数。不告诉我这个电影类型,我可以根据你给我的信息进行判断,这个电影是属于爱情片还是动作片。而k-近邻算法也可以像我们人一样做到这一点,不同的地方在于,我们的经验更"牛逼",而k-邻近算法是靠已有的数据。比如,你告诉我这个电影打斗镜头数为2,接吻镜头数为102,我的经验会告诉你这个是爱情片,k-近邻算法也会告诉你这个是爱情片。你又告诉我另一个电影打斗镜头数为49,接吻镜头数为51,我"邪恶"的经验可能会告诉你,这有可能是个"爱情动作片",画面太美,我不敢想象。  但是k-近邻算法不会告诉你这些,因为在它的眼里,电影类型只有爱情片和动作片,它会提取样本集中特征最相似数据(最邻近)的分类标签,得到的结果可能是爱情片,也可能是动作片,但绝不会是"爱情动作片"。当然,这些取决于数据集的大小以及最近邻的判断标准等因素。

距离度量

       我们已经知道k-近邻算法根据特征比较,然后提取样本集中特征最相似数据(最邻近)的分类标签。那么,如何进行比较呢?比如,我们还是以表1.1为例,怎么判断红色圆点标记的电影所属的类别呢?如图1.1所示。

        我们可以从散点图大致推断,这个红色圆点标记的电影可能属于动作片,因为距离已知的那两个动作片的圆点更近。k-近邻算法用什么方法进行判断呢?没错,就是距离度量。这个电影分类的例子有2个特征,也就是在2维实数向量空间,可以使用我们高中学过的两点距离公式计算距离,如图1.2所示。

 

    通过计算,我们可以得到如下结果:

  • (101,20)->动作片(108,5)的距离约为16.55

  • (101,20)->动作片(115,8)的距离约为18.44

  • (101,20)->爱情片(5,89)的距离约为118.22

  • (101,20)->爱情片(1,101)的距离约为128.69

    通过计算可知,红色圆点标记的电影到动作片 (108,5)的距离最近,为16.55。如果算法直接根据这个结果,判断该红色圆点标记的电影为动作片,这个算法就是最近邻算法,而非k-近邻算法。那么k-邻近算法是什么呢?k-近邻算法步骤如下:

 

    1.计算已知类别数据集中的点与当前点之间的距离;

    2.按照距离递增次序排序;

    3.选取与当前点距离最小的k个点;

    4.确定前k个点所在类别的出现频率;

    5.返回前k个点所出现频率最高的类别作为当前点的预测分类。

 

     比如,现在我这个k值取3,那么在电影例子中,按距离依次排序的三个点分别是动作片(108,5)、动作片(115,8)、爱情片(5,89)。在这三个点中,动作片出现的频率为三分之二,爱情片出现的频率为三分之一,所以该红色圆点标记的电影为动作片。这个判别过程就是k-近邻算法。

knn算法分类时,分类器的性能也会受到多种因素的影响,如分类器设置和数据集等。不同的算法在不同数据集上的表现可能完全不同。

import numpy as np
import matplotlib.pyplot as plt
def init():
    #四组数据 两个参数表示武打镜头次数以及亲吻镜头次数
    group = np.array([[1,101],[5,89],[108,5],[115,8]])
    #四组数据的标签 类型
    labels = ['爱情片','爱情片','动作片','动作片']
    return group,labels
def draw(X):
    group,label = init()
    #print(group[:,0],group[:,1])
    plt.scatter(group[:,0],group[:,1])
    plt.scatter(X[0],X[1])
    plt.show()
def solve(group,k,labels,input):
    x = group.shape[0]#行数
    new_array = np.tile(input,(x,1))#线性代数矩阵思维
    #  tile函数的作用 重复n行m列
    #  1 2 3
    #  (2,3)
    #   1 2 3 1 2 3 1 2 3
    #   1 2 3 1 2 3 1 2 3
    new_array -= group
    new_array **= 2
    new_array = np.sum(new_array,axis = 1)#每行相加获得的是行向量 axi=0 每列求和
    diatance = new_array**0.5 #距离列表
    #对距离进行排序
    sorted_distance = np.argsort(diatance) #小到大排序返回下标的列表
    map = {}#装k个
    for i in range(k):
        #sorted_distance labels k
        #3 A 0
        #1 A 1
        #0 B 2
        string = labels[sorted_distance[i]]
        map[string] = map.get(string,0)+1#用字典模拟c++中map的作用
    cnt = 0
    for key,value in map.items():#找出出现次数最多的string
        if value > cnt:
            cnt = value
            res_string = key
    return res_string

if __name__=='__main__':
    x = int(input())
    y = int(input())
    group,labels = init()
    draw((x,y))
    res = solve(group,2,labels,np.array([x,y]))
    print('经过预测结果是',res)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值