KNN算法原理与实现

一、算法原理

0、惰性学习法

Bayes,决策树, SVM等分类方法均属于急切学习法,它在接收到待分类的新元组前就构建了分类模型,新无级直接经过模型就可以得到结果。

而惰性学习法则相反,它会在接收到待分类的新元组时才开始处理训练数据(或者之前只做一些简单的预处理),新元组会与训练数据逐一匹配,从而得出分类结论。

1、基本思路

算法思路:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。 
看下面这幅图:


KNN的算法过程是是这样的:

从上图中我们可以看到,图中的数据集是良好的数据,即都打好了label,一类是蓝色的正方形,一类是红色的三角形,那个绿色的圆形是我们待分类的数据。
如果K=3,那么离绿色点最近的有2个红色三角形和1个蓝色的正方形,这3个点投票,于是绿色的这个待分类点属于红色的三角形
如果K=5,那么离绿色点最近的有2个红色三角形和3个蓝色的正方形,这5个点投票,于是绿色的这个待分类点属于蓝色的正方形
我们可以看到,KNN本质是基于一种数据统计的方法!其实很多机器学习算法也是基于数据统计的。
KNN是一种memory-based learning,也叫instance-based learning,属于lazy learning。即它没有明显的前期训练过程,而是程序开始运行时,把数据集加载到内存后,不需要进行训练,就可以开始分类了。
具体是每次来一个未知的样本点,就在附近找K个最近的点进行投票。


2、一个真实示例

对于某个ID,它会有很多的属性值,形成一个向量K,以身高、体重、性别为例,假如已经有以下训练数据:

ID1    172    60    1

ID2    163    50    0

ID3    188    70    1

ID4    155    40    0

现在有一个需要分类的数据进来:

ID5    161    54

我们需要计算其性别,取k=3,即取3个与ID5最类似的样本:ID1, ID2, ID4,这3个ID中有2个属于性别0,因此我们判断ID5的性别也是0。

同理可分类:

ID6    199    82

ID7    172    50

。。。


3、与Bayes的比较

Bayes使用所有的训练数据为新元组分类,而KNN只使用与新元组最类似的K个训练数据。如果Bayes的前提条件成立(条件独立),则其效果更佳,否则相反。

比如上面说的身高和体重其实不是独立的因素的,身高高的大部分会体重较重。


4、距离的计算

如何判断2个元组是否类似呢?

一个最基本的方法就是欧氏距离。

一个补充是先要将各个属性的值进行归一化,否则部分属性的值由于数值较大,对结果的影响会更大,比如上面的身高数值就比体重数值要大。基本的方法是:

V' = (V - min)/(max-min)

min和max分别是这个属性值里面的最大和最小值。

对于类别属性值,如颜色,一种简单的方法是如果相同则记为1,不同则记为0。

欧氏距离为每个属性赋予了相同的权重,如果事实并非如此,可以考虑为各个属性加上权重。


5、KNN用于数值预测

求出K个最相近的元组后,用这些元组对应的数值的平均值作为最终结果。


6、K的选择

可以从K=1开始,逐步增加,用检验数据来分析正确率,从而选择最优K。这个结果要均衡考虑正确率与计算量,比如K=3时,正确率为90%,而K=10时,正确率为91%,则需要考虑计算量换来的1%提升是否合算了。


7、计算量

由于每一个待分类的元组(M个)都需要和所有的样本数据(N个)进行比较,从而找出最相近的K个近邻,因此计算量为M*N。即O(n^2)

一些优化技巧:

(1)如果可能的话先对样本数据进行排序,从而知道只需要与哪些数据进行比较。但对于高维数据,这几乎是不可行的。

(2)将样本数据划分为多个子集合,待分类数据只需要与其中的一个或者多个子集合进行比较。比如属性是经纬度,距离是2个经纬度点之间的距离,则可以将样本根据经纬度的整数部分将各个样本分到不同的子集合去,待分类元组只需要跟与自己整数部分相同的子集合进行比较即可,当子集合内的样本数据不足K时,再和邻近的集合进行比较。



二、算法实现

数据使用上面的示例,完成代码请见:https://github.com/lujinhong/lujinhong-commons/tree/master/lujinhong-commons-spark/src/main/scala/com/lujinhong/commons/spark/ml/knn

package com.lujinhong.commons.spark.ml.knn

import org.apache.spark.SparkContext
import scala.collection.mutable.{SortedSet,HashMap}

/**  * AUTHOR: LUJINHONG  * CREATED ON: 17/3/2 14:06  * PROJECT NAME: lujinhong-commons  * DESCRIPTION: KNN算法的示例。  * 示例数据如下:  * $ cat knn_training_data.txt    ID1 172 60 1    ID2 163 50 0    ID3 188 70 1    ID4 155 40 0    $ cat knn_to_do_data.txt    ID5 164 54    ID6 199 82    ID7 172 50  * 输出如下:  * $ hadoop fs -cat /tmp/ljhn1829/ml/knn/result/\*    ID5 0    ID6 1    ID7 0  *  */object KNNDemo {
  val TRAINING_DATA_PATH = "/tmp/ljhn1829/ml/knn/training_data";
  val TO_DO_DATA_PATH = "/tmp/ljhn1829/ml/knn/to_classify_data"  val OUTPUT_PATH = "/tmp/ljhn1829/ml/knn/result"  val SEPARATOR = " "  val K = 3  val MALE_LABEL = "1"  val FEMALE_LABLE = "0"
  def main(args: Array[String]): Unit = {
    val sc = new SparkContext()
    var traingDataSetBroadcast = sc.broadcast(sc.textFile(TRAINING_DATA_PATH).collect().toSet);
    sc.textFile(TO_DO_DATA_PATH).map(line => classify(line, traingDataSetBroadcast.value)).saveAsTextFile(OUTPUT_PATH)
  }


  def classify(line: String, traingDataSet: Set[String]): String = {
    //记录与待分类元组最小的3个距离    var minKDistanceSet = SortedSet[Double]()
    //记录与待分类元组最小的3个距离及其对应的分类。    var minKDistanceMap = HashMap[Double, Int]()
    for (i <- 1 to K) {
      minKDistanceSet += Double.MaxValue    }

    val info = line.trim.split(SEPARATOR)
    val id = info(0)
    val height = info(1).toDouble
    val weight = info(2).toDouble
    for (trainSampleItem <- traingDataSet) {
      val sampleInfo = trainSampleItem.trim().split(SEPARATOR)
      val distance = Math.sqrt(Math.pow((height - sampleInfo(1).toDouble), 2) + Math.pow((weight - sampleInfo(2).toDouble), 2))
      if (distance < minKDistanceSet.lastKey) {
        minKDistanceSet -= minKDistanceSet.lastKey
        minKDistanceSet += distance
        minKDistanceMap += ((distance, sampleInfo(3).toInt))
        if (minKDistanceMap.size >= 3) {
          minKDistanceMap -= minKDistanceSet.lastKey
        }
      }

    }
    //根据距离最近的3个样本分类,得出最终分类结果。    var count = 0    for (entry <- minKDistanceMap) {
      count += entry._2
    }
    var result = FEMALE_LABLE    if (count > K / 2) {
      result = MALE_LABEL    }
    return id + SEPARATOR + result
  }


}


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: knn算法(k-近邻算)是一种分类和回归的算,被广泛应用于模式识别、数据挖掘、图形识别等领域。其原理是根据数据本身的特征,将测试集中的每一个样本按照特征相似度进行分类。其中,k的值表示选择多少个最相似的邻居作为判断依据,通常采用欧氏距离来计算相似度。 在knn算法实现过程中,需要先将数据集分为训练集和测试集。接着,通过计算测试集中每一个样本与训练集中每一个样本的欧氏距离(或曼哈顿距离等),选择距离最近的k个邻居。最后,采用“多数表决”的方式选择样本类别,并将该类别赋给测试集中的样本。 在Python中,可以使用scikit-learn库实现knn算法。以下是一个简单的代码示例: from sklearn.neighbors import KNeighborsClassifier # 创建训练集和测试集 train_x = [[0], [1], [2], [3]] train_y = [0, 0, 1, 1] test_x = [[1.5]] # 创建knn分类器(k=2) knn = KNeighborsClassifier(n_neighbors=2) # 拟合模型 knn.fit(train_x, train_y) # 进行预测 print(knn.predict(test_x)) 以上代码中,第一行引用了scikit-learn库下的KNeighborsClassifier类,用于创建一个knn分类器。接着,分别创建了训练集和测试集,并针对训练集中的两类样本对应标签进行了标记。接下来,创建k值为2的knn分类器,并使用fit()方对训练集进行拟合。最后,通过predict()方进行实际的预测,并输出测试样本的分类结果。 总体来说,knn算法是一种简单易用的分类和回归算,具有可解释性强、不受算实现形式的特点,同时能够适应各种数据类型和特征。在Python中,采用scikit-learn库实现knn算法也非常方便。 ### 回答2: KNN算法是一种基于实例的学习,通过计算样本之间的距离来确定新样本的类别。KNN算法是一种简单而有效的分类方,尤其适用于小数据集。算原理是基于这样一种思想:样本空间中的每个样本都可以用它最近的K个邻居来代表。其中K是一个正整数,是预定的参数。当K=1时,为最近邻分类算,即只考虑最近的一个邻居。 具体实现步骤: 1.读入数据集,并将其分为训练集和测试集。 2.对数据集进行归一化处理。 3.对每个测试实例,计算其与训练集中所有实例之间的距离。 4.按照距离的大小降序排列。 5.选取前K个距离最小的实例,得到它们所属的类别中出现次数最多的那个类别作为该测试实例的预测类别。 6.计算预测结果与实际结果的差异。 在Python中实现KNN算法需要用到一些基本的库:Numpy和Scikit-learn。具体步骤如下: 1.导入Numpy库。 2.导入数据集并将其分为训练集和测试集。 3.使用Scikit-learn库中的MinMaxScaler函数进行数据归一化处理。 4.使用Scikit-learn库中的KNeighborsClassifier函数进行训练,设定参数k和metric。 5.使用Scikit-learn库中的predict函数进行预测,得到预测结果。 6.计算预测结果与实际结果的差异,得到预测准确率。 KNN算法的优点是简单易懂,精度高;缺点是计算复杂度高,对数据的大小敏感。当数据维度较高时,其计算复杂度会变得极高,而且KNN算法对数据的距离非常敏感,如果数据特征选取不当,会导致预测精度大幅下降。因此,在使用KNN算法的时候需要注意数据特征的选取和K值的选择。 ### 回答3: K近邻(k-NN)算是最简单的基于实例的学习之一,它的主要思想是使用距离度量来对特征空间中的样本进行分类。KNN算法中的K代表选择邻居的数量,邻居是指在训练集中与测试数据距离最近的样本点。KNN算法的基本步骤如下: 1. 计算测试数据与所有训练数据之间的距离。 2. 根据距离度量,对距离最近的K个样本进行投票。 3. 根据投票结果,决定测试数据属于哪一类别。 KNN算法的优点是简单易用,能够处理多分类和回归问题;缺点是计算量大,对训练数据敏感,需要进行归一化处理,并需要选择合适的距离度量和K值。 Python实现KNN算法需要使用Scikit-learn或Numpy等相关库。下面给出一个简单的Python代码实现,该代码实现了一个基于欧氏距离的KNN分类器: ``` import numpy as np from sklearn.neighbors import KNeighborsClassifier # 生成训练数据 X_train = np.array([[1, 2], [3, 4], [5, 6], [7, 8]]) y_train = np.array([0, 0, 1, 1]) # 创建KNN分类器,选择K=3 clf = KNeighborsClassifier(n_neighbors=3) # 训练分类器 clf.fit(X_train, y_train) # 测试数据,预测其所属类别 X_test = np.array([[2, 3], [4, 5], [6, 7]]) y_test = clf.predict(X_test) print(y_test) ``` 该代码中,通过Numpy库生成了一个4个样本点的训练数据集,其中前两个样本属于类别0,后两个样本属于类别1。同时,也生成了3个测试数据点。然后使用Scikit-learn库中的KNN分类器,在训练数据上训练模型,选择K=3。最后,对测试数据进行分类,并输出分类结果。 以上就是KNN算法的基本原理和Python实现,希望对读者有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值