自己写简单的K邻近算法

自己动手写K邻近分类算法(KNN)

1 什么是KNN(k-nearest neighbor)?

​ 在样本空间中,找到训练样本中与新实例距离最近的K个已知样本,使用这k个样本中数量较多的类别作为新实例的类别。这就是K邻近,找到距离最近的K个点,然后共同投票。

2 KNN算法

​ 输入:实例的特征向量、实例的类别

​ 输出:新实例所属的类别 x x

​ 步骤:

  1. 根据距离度量,找到与x最邻近的 k k 个点,涵盖这个k个点的邻域记为 Nk(x) N k ( x )

    • Nk(x) N k ( x ) 中根据分类决策的规则决定 x x 的类别y
    • ​ 其中,距离度量可以使用 Lp L p 距离:其中 p1 p ≥ 1

      Lp(xi,xj)=(l=1n|x(l)ix(l)j|p)1p L p ( x i , x j ) = ( ∑ l = 1 n | x i ( l ) − x j ( l ) | p ) 1 p

      ​ 当 p=2 p = 2 时,这个公式为欧氏距离, p=1 p = 1 时为马哈顿距离, p=\infin p = \infin 时,表示的各坐标的最大值。

      ​ 实际当中,最常用的是欧氏距离。

      • K值的选择

        使用太小的K值,会产生过拟合,尤其在噪声节点较多,那么预测结果会很差,但是如果K值设置的太大的话,不相关的节点也会对结果产生影响,所以K值得选择很重要。

        实际中通常使用交叉验证的方法,进行K值的选择。

      3 代码实现

      3.1 对数据线性扫描(简单,粗暴)

      from math import sqrt
      
      class KNN:
          def __init__(self,k):
              self.k = k
      
          def fit(self,trainX,trainY):
              self.trainX=trainX
              self.trainY=trainY
      
          def distance(self,x,y):
              # 距离度量
              res = 0.0
              for i in range(len(x)):
                  res += abs(x[i]**2-y[i]**2)
              res = sqrt(res)
              return res
      
          def findKN(self,dist):
              # 找到距离最近的K个节点
              def fun(a):
                  return a[1]
              indexed_dist = [ [v,k] for v,k in enumerate(dist)]
              new_dist = sorted(indexed_dist,key=fun)
              res = []
              # 如果训练数据数量小于k的处理
              if len(new_dist) > self.k:
                  for i in range(self.k):
                      res.append(new_dist[i][0])
              else:
                  for i in new_dist:
                      res.append(i[0])
              return res
      
          def predicate(self,testX):
              label = []
              for test in testX:
                  dist = []
                  # 计算当前节点到所有节点的距离
                  for k,v in enumerate(self.trainX):
                      dist.append(self.distance(v,test))
                  # 找到最近的K个节点
                  kn = self.findKN(dist)
                  nnl = []
                  for i in kn:
                      nnl.append(self.trainY[i])
                  nnl.sort()
                  res = [ 1 ,nnl[0]]
                  # 找到出现次数最多的类别
                  for i in range(1,len(nnl)):
                      if nnl[i] == res[1]:
                          res[0]+=1
                      else:
                          res[0] -= 1
                          if res[0] <= 0:
                              res[0] = 0
                              res[1] = nnl[i]
                  label.append(res[1])
              return label
      
      
      if __name__ == '__main__':
          knn = KNN(2)
          # 训练数据
          trainX = [[1,2],[4,3],[2,2],[1,1]]
          trainY = [1,2,3,1]
          knn.fit(trainX,trainY)
          # 测试数据
          testX = [[2,3],[1,0]]
          res = knn.predicate(testX)
          print(res)

      3.2 使用kd树进行搜索

      下回分解

      4 总结

      K邻近同样是一个简单的算法,没有显式的学习过程,就是看着哪种情况最多就用哪个结果,其思想也很简单,但是在求K近邻时按照传统的线性方法,计算量会过于巨大。以后会介绍使用kd树搜索,来简化这一过程。

      初学乍练,请多多指正!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值