k-近邻(KNN)算法详解

k-近邻(K-Nearest Neighbor)算法也称为最近邻或k-最近邻算法,是一种用于分类的机器学习算法,属于监督学习。该方法也是一种最为简单的分类方法,具有精度高、对异常值不敏感等优点。

算法原理

KNN在某种程度上与距离判别法有着很多相似的地方。在距离判别法中,我们是判断未知类别的数据点距离样本中各类数据中心的距离,离哪一类的中心最近,就认为该组数据属于哪一类;对于KNN,它是计算未知类别的数据点与样本所有点的距离,然后由近到远排序,选取最近的k个点,这k个点中属于哪一类的最多,就认为该组数据属于哪一类。

关于各组数据点之间距离的度量,与距离判别法采用的方法是相同的,并且有着非常多的距离度量的方法,详见兔兔的《相似性度量(距离度量)》方法系列文章,尤其是其中的样本距离度量方法。在本文中,为了讲解方便,就采用欧式距离进行度量。数据X(i)与X(j)之间欧式距离的公式为:

d=\sum_{k=1}^{p}(X_{ik}-X_{jk})^{2}

p表示指标(变量)数。

所以,对于KNN,其算法流程为:

(1)对于未知类别的一组数据X(i),计算它到样本中各组数据的距离(样本含有n组数据,并且每组数据都对应一种类别,样本的指标与未知数据的指标一致)。

(2)将样本数据根据距离由小到大排列。

(3)选取最近的k组数据。

(4)统计k组数据中属于哪一类的最多,该未知数据类别即为k组数据中含有个数最多的那一类。

算法实现

兔兔在这里以 dry bean dataset数据集为例,该数据集有13611组数据,16个变量,7个类别。

import numpy as np
import pandas as pd
class KNN:
   def __init__(self,x,dataset,label,k=10):
      self.x=x #待分类的一组数据,为行向量
      self.dataset=dataset #数据集
      self.label=label #数据集中各组数据对应类别
      self.k=k #选取最近点的个数
      self.n,self.p=dataset.shape #n为样本数,p为指标数
   def distance(self,x,y):
      '''数据之间的欧式距离'''
      return np.sqrt(np.sum((x-y)**2))
   def main(self):
      '''KNN主程序'''
      dis=[];label=[]
      for i in range(self.n):
         dis.append(self.distance(self.x,self.dataset[i]))
         label.append(self.label[i])
      a=pd.Series(label,dis)
      b=a.sort_index() #按照距离排序
      b=b.values
      count={}
      for i in range(self.k):
         if b[i] not in count:
            count[b[i]]=1
         else:
            count[b[i]]+=1
      num=max(count.values()) #选取个数最多的一类
      for key,value in count.items():
         if value==num:
            return key
if __name__=='__main__':
   df=pd.DataFrame(pd.read_csv('Dry_Bean_Dataset.csv'))
   dataset=np.array(df.loc[0:10000,'Area':'ShapeFactor4'])
   y=np.array(df.loc[0:10000,'Class'])
   knn=KNN(x=np.array(df.loc[100,'Area':'ShapeFactor4']),dataset=dataset,label=y,k=100)
   result=knn.main()
   print(df.loc[100,'Class'])
   print(result)

在实现算法的过程中,关键在于如何根据距离进行排序,同时各个距离值与对应的类别是要对应的。兔兔在这里采用pandas中的Series进行处理,这样根据距离排序的同时对应的类别也随之变化,当然,也可以采用DataFrame,或者采取字典等方法来实现。统计各类别的个数时这里采用字典的方法,在这里其实也可以采用pandas中groupby函数来统计。

为了初步观察该模型效果,兔兔采用样本中第100条相同的数据来进行测试,结果result为SEKER,事实上它也的确是SEKER。

若进一步测试效果,则可以采用下面的方法。

def test(traindata,testdata):
   '''测试准确率'''
   count=0
   n=len(testdata[0])
   for t in range(n):
      knn=KNN(x=testdata[0][t],dataset=traindata[0],label=traindata[1])
      if testdata[1][t] is knn.main():
         count+=1
      else:
         continue
   return count/n
if __name__=='__main__':
   df=pd.DataFrame(pd.read_csv('Dry_Bean_Dataset.csv'))
   df=np.array(df)
   np.random.shuffle(df) #随机打乱数据
   df=pd.DataFrame(df)
   traindata=[np.array(df.loc[0:10000,0:15]),np.array(df.loc[0:10000,16])] #选取前10000组数据作为训练集
   testdata=[np.array(df.loc[10001:11000,0:15]),np.array(df.loc[10001:11000,16])] #选取1000组数据作为测试集
   test=test(traindata=traindata,testdata=testdata)
   print(test)

由于计算量较大,程序可能会运行较长时间才能显示结果,具体的运行时间可以在程序中写入time函数来统计。兔兔的最终运行结果为0.727,准确率并不是很高,但也还算可以。

对于KNN ,其应用十分广泛,不仅可以用于普通的数据的分类,理论上也可以用于图像、文本、音频等分类。例如手写数字黑白图像,把各个像素点转成0或1,图像的m×n维矩阵转换成mn×1维的向量,采用海明距离等序列距离度量方法计算向量之间的距离(即图片之间的距离),然后用KNN计算未知图片属于哪一类。但是对于更为复杂的问题,KNN效果有时并不很好。

总结

KNN 算法作为一种最为简单的机器学习算法,具有很多优点,并且算法简便,无需过多的公式推导,关键在于算法的实现。但是缺点也很明显:计算复杂度高、空间复杂度高等。所以在使用KNN 时,需要根据实际问题进行分析,如果效果不好或者不适合使用KNN,就需要选择其它的算法了。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

生信小兔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值