KNN算法原理及编程实现

Author:shoupt-Johnson•smith

原创,转载者请注明来处

一:KNN算法概述:
KNN算法又称之为K-最近邻算法。它是一种基本分类算法。其原理大致如下:
即一个样本在某个特征空间中有k个最近邻的样本,而这些中大多数样本属于同一类别,则该样本所属类别应与该类别一致。同时,该样本具有这个类别的样本特征。
如此该算法在确定分类的决策上只通过最近邻的k个样本的类别来决定待分类样本的所属类别。

二:KNN算法三要素:
从上述算法概述可以看出,其原理很简单。如此,KNN的实现过程同样也简单。
KNN算法的实现过程有三要素:k值的选择、样本间距离度量、决策(即用什么规则来确定)。首先我会从距离度量开始介绍:
2.1 样本间距离度量:
knn模型的特征空间一般是n维实数向量空间。而特征空间中两点间的距离可以看做是两个样本相似度的度量,反映为在空间上与集群距离的接近程度。进一步说,特征空间中两点间的距离又称之为向量的距离。
对于两个n维向量A(X1,X2,…,Xn)和B(Y1,Y2,…,Yn)。常用的距离有闵可夫斯基距离(里边又包含其他的距离)、欧氏距离、曼哈顿距离、切比雪夫距离、余弦距离。
闵可夫斯基距离:是一种通用距离,通过设定参数l的值来得到相应距离:
在这里插入图片描述
而l=1时,则是曼哈顿距离:差的绝对值之和
而l=2时,则是欧氏距离,在二维空间上表现为勾股定理的距离(默认距离)
②曼哈顿距离:又称为城市距离,是两个向量间各对应点差的绝对值之和:
在这里插入图片描述
③欧式距离:即两个向量间的空间距离,在二维平面上表现为勾股定理如:
在这里插入图片描述
在矩阵运算上表现为矩阵差与其差的转置乘积的开平方,即:
在这里插入图片描述
④切比雪夫距离:可以称为的l范数距离,是计算两点之间最大值的
⑤余弦距离:也称余弦相似度。实际上不是距离,是两个向量在方向上的相似度:
即两个向量的夹角的余弦值。如果两个向量夹角越接近0,则两个向
量相似度越高。
如此,我们将向量空间中两个向量的夹角余弦值作为衡量两个数据间差异的度量。而余弦值越接近1,则认为相似度越高,余弦值越接近0,则认为相似度越低。
其距离是
在这里插入图片描述
即两个向量的乘积除以两个向量模的乘积。
2.2 k值的选择:
通过上边的距离度量,我们假设某个特征空间中有两个集群,每个集群中有若干个元素。每个元素又是一个数据(比如矩阵或数组)。现在给出一个未知的待分类元素,首先我们将该元素每个集群中每个元素的距离求出,并将属于同一个类或群体的元素标记其组别,那么对于该空间中待分类元素与已有的每个元素的比较就得到与已有元素个数(假定为m)相匹配的m个数据di=[distance(new,already),group]。
现在对于已经得到的一个列表dist_list=[d1,d2,…,dm],我们根据其所选择的距离对其进行排序。比如选择欧式距离,那么显然需要将距离越小的排在越前边,然后得到一个以距离distance为依据的排序过的列表或数组new_dist_list。那么接下来就是k值选择的问题了,因为我们不需要其对所有的数据求绝大多数所属类,因为我们无法确定已有的类别下数据量是否相等。因此我们需要对new_dist_list选择前k个元素。即k个距离最近邻的数据,到这一步,我们只需要判断这k个数据当中哪一个类别group所属的数据最多,那么就可以知道其所属类别了。如图所示:
在这里插入图片描述
上文提到,我们无法确定已有的类别下数据量是否相等,因此k值的选择就成了一个问题。可以说,k值的选择对于模型的表现有着很大影响。
如果k值选择过小,那么其邻域过小,这种情况下“学习”的估计误差会增大。模型会变复杂,容易出现过拟合。可能会出现极端情况,比如周围刚好是几个噪声点,这样就会对结果造成很大影响。
同样的如果k值过大,模型会相对简单,容易出现欠拟合。比如如果一个特征空间中有两个集群A和B,如果A集群的元素量远大于B集群,而k值又过大,这种情况下对B集群是不公平的,判断结果也不公允。
因此在实际应用中,通常对于k我们选择一个较小的值,并会通过交叉验证等方法确定k的具体值。而一般情况下,k值的选择低于训练样本数的平方根。考虑到可能会出现1:1的情况,因此k值的选择尽量取奇数。

2.3 决策选择:
从我上文的描述当中,对于决策规则,我们一般使用多数表决的原则。即选择哪个类别进行投票,票数最高者为最终结果。

三:算法实现:
为了便于理解,现在用极端情况来表现knn测试结果:
现在已有的一个数据集,其每一个数据是[打斗次数,接吻次数],我们用‘k’来表示爱情片,‘r’表示打斗片:
movie={‘k’:[[3,98],[12,103],[1,81]],‘r’:[[101,10],[99,5],[98,2]]};
待分类元素为newmovie=[18,90],现在对其进行测试分类,源代码如下:

import numpy as np;
import matplotlib.pyplot as plot;

def dist_e(A,B): #欧氏距离
    A=np.array(A);
    B=np.array(B);
    a=A-B;
    # print(np.matmul(a,a.T));
    return np.linalg.norm(a);  #用于计算范数,默认情况为欧氏距离

def dist_man(A,B): #曼哈顿距离
    A=np.array(A);
    B=np.array(B);
    a=A-B;
    a=np.abs(a);
    a=a.ravel();  #矩阵降维
    # print(np.matmul(a,a.T));
    return sum(a);  

def dist_cos(A,B): #余弦距离,结果越接近1,相似度越高,越接近0,相似度越低
    A=np.array(A);
    B=np.array(B);
    d1=np.dot(A,B);
    d2=np.linalg.norm(A)*np.linalg.norm(B);
    return d1/d2;

def knn_test(dataset,newfeature,k=3,distfun=dist_e,dist_reverse=False):  #极端情况
    from collections import Counter;
    distance=[];
    for group in dataset:
        for candidate in dataset[group]:
            distance.append([distfun(newfeature,candidate),group]);
    dp=lambda l:l[0];
    distance=sorted(distance,key=dp,reverse=dist_reverse);
    distance=[d[1] for d in distance][:k];
    print(distance);
    return Counter(distance).most_common(1)[0][0];
    #参考 http://www.pythoner.com/205.html

def knn_result_show(dataset,newfeature):
    # 借鉴  https://blog.csdn.net/weixin_40941966/article/details/80896272
    for i in dataset:
        for j in dataset[i]:
            plot.scatter(j[0],j[1],s=50,color=i);
    plot.scatter(newfeature[0],newfeature[1],s=100);
    result=knn_test(dataset,newfeature);
    print(result);
    plot.scatter(newfeature[0],newfeature[1],s=150,color=result);
    plot.show();

movie={'k':[[3,98],[12,103],[1,81]],'r':[[101,10],[99,5],[98,2]]};
newmovie=[18,90];
dataset={'k':[[1,3],[2,4],[2,1]],'r':[[6,3],[7,7],[5,6]]};
newfeature=[1,4];
knn_result_show(dataset,newfeature);
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值