【传统机器学习】之KNN分类算法(一)

参考博文:https://blog.csdn.net/saltriver/article/details/52502253

一、算法介绍

    K最近邻 (k-Nearest Neighbors,KNN) 算法是一种分类算法,也是最简单易懂的机器学习算法,没有之一。1968年由 Cover 和 Hart 提出,应用场景有字符识别文本分类图像识别等领域。该算法的思想是:一个样本与数据集中的k个样本最相似,如果这k个样本中的大多数属于某一个类别,则该样本也属于这个类别。

二、分类算法步骤

1、计算样本到数据集中其它每个样本的距离。

2、按照距离的远近排序。

3、选取与当前样本最近的k个样本,作为该样本的邻居。

4、统计这k个邻居的类别频率。

5、k个邻居里频率最高的类别,即为该样本的类别。

三、分类demo

需求数据集中序号1-12为已知的电影分类,分为喜剧片、动作片、爱情片三个种类,使用的特征值分别为搞笑镜头、打斗镜头、拥抱镜头的数量。那么来了一部新电影《唐人街探案》,它属于上述3个电影分类中的哪个类型?用KNN是怎么做的呢?

(1)构建一个已分好类的数据集。

movie_data = {"宝贝当家": [45, 2, 9, "喜剧片"],
              "美人鱼": [21, 17, 5, "喜剧片"],
              "澳门风云3": [54, 9, 11, "喜剧片"],
              "功夫熊猫3": [39, 0, 31, "喜剧片"],
              "谍影重重": [5, 2, 57, "动作片"],
              "叶问3": [3, 2, 65, "动作片"],
              "伦敦陷落": [2, 3, 55, "动作片"],
              "我的特工爷爷": [6, 4, 21, "动作片"],
              "奔爱": [7, 46, 4, "爱情片"],
              "夜孔雀": [9, 39, 8, "爱情片"],
              "代理情人": [9, 38, 2, "爱情片"],
              "新步步惊心": [8, 34, 17, "爱情片"]}

 (2)计算一个新样本与数据集中所有数据的距离

新样本:"唐人街探案": [23, 3, 17, "?片"]

距离:d=\sqrt{\sum_{i=1}^{n}(x_i-y_i)^2}(其中,xy为2个样本,n为维度,x_iy_ixyi个维度上的特征值。)

例如,x为:"唐人街探案": [23, 3, 17, "?片"],y为:"伦敦陷落": [2, 3, 55, "动作片"],则两者之间的距离为:

=43.42

#求所有已知分类样本与新样本的距离
import math
x = [23, 3, 17]
KNN = []
for key, v in movie_data.items():
    d = math.sqrt((x[0] - v[0]) ** 2 + (x[1] - v[1]) ** 2 + (x[2] - v[2]) ** 2)
    KNN.append([key, round(d, 2)])
print(KNN)

输出结果:

[['谍影重重', 43.87], 
['伦敦陷落', 43.42], 
['澳门风云3', 32.14], 
['叶问3', 52.01], 
['我的特工爷爷', 17.49], 
['新步步惊心', 34.44], 
['宝贝当家', 23.43], 
['功夫熊猫3', 21.47], 
['奔爱', 47.69], 
['美人鱼', 18.55], 
['夜孔雀', 39.66], 
['代理情人', 40.57]]

(3)按照距离大小进行递增排序。

KNN.sort(key=lambda dis: dis[1])

输出结果:

[['我的特工爷爷', 17.49], 
['美人鱼', 18.55], 
['功夫熊猫3', 21.47], 
['宝贝当家', 23.43], 
['澳门风云3', 32.14], 
['新步步惊心', 34.44], 
['夜孔雀', 39.66], 
['代理情人', 40.57], 
['伦敦陷落', 43.42], 
['谍影重重', 43.87], 
['奔爱', 47.69],
['叶问3', 52.01]]

(4)选取距离最小的k个样本

例如取k=5;

KNN=KNN[:5]

输出结果:

[['我的特工爷爷', 17.49], 
['美人鱼', 18.55], 
['功夫熊猫3', 21.47], 
['宝贝当家', 23.43], 
['澳门风云3', 32.14]]

(5)统计距离最小的k个样本各个类别样本的频率(条数),并输出出现频率最高的类别。 

labels = {"喜剧片":0,"动作片":0,"爱情片":0}
for s in KNN:
    label = movie_data[s[0]]
    labels[label[3]] += 1
    

labels = sorted(labels.items(), key=lambda l:l[1], reverse=True)
print(labels,labels[0][0],sep='\n')

输出结果:

[('喜剧片', 4), ('动作片', 1), ('爱情片', 0)]

喜剧片

四、总结

KNN有几个特点:

(1)KNN属于惰性学习(lazy-learning)

这是与急切学习(eager learning)相对应的,因为KNN没有显式的学习过程!也就是说没有训练阶段,从上面的例子就可以看出,数据集事先已有了分类和特征值,待收到新样本后直接进行处理。

(2)KNN的计算复杂度较高

我们从上面的例子可以看到,新样本需要与数据集中每个数据进行距离计算,计算复杂度和数据集中的数据数目n成正比,也就是说,KNN的时间复杂度为O(n),因此KNN一般适用于样本数较少的数据集

(3)k取不同值时,分类结果可能会有显著不同。

上例中,如果k取值为k=1,那么分类就是动作片,而不是喜剧片。

一般k的取值不超过20,上限是n的开方。

在实际应用中,K值一般取一个比较小的数值,例如采用交叉验证法(简单来说,就是一部分样本做训练集,一部分做测试集)来选择最优的K值。


五、附录

(1)增量学习

增量学习是指一个学习系统能不断地从新样本中学习新的知识,并能保存大部分已经学习到的知识。其非常类似于人类自身的学习模式,设想一下,当我们学会了 0-9 识别与书写,再学习 A-Z 字母表的识别与书写后我们仍然记得 0-9 数字的识别与书写,而目前传统分类任务是一个网络学习了 0-9 数字分类,再用这个网络学习 A-Z 字母分类后,就会难以对数字有一个很好的分类,如果想兼顾 0-9 和 A-Z 则需要将所有种类的样本都输入到神经网络进行训练,这无疑是很耗时耗力的。

(2)惰性学习与急切学习

积极学习方法,这种学习方法是指在利用算法进行判断之前,先利用训练集数据通过训练得到一个目标函数,在需要进行判断时利用已经训练好的函数进行决策,这种方法是在开始的时候需要进行一些工作,到后期进行使用的时候会很方便.

例如

以很好理解的决策树为例,通过决策树进行判断之前,先通过对训练集的训练建立起了一棵树,比如很经典的利用决策树判断一各新发现的物种是否为哺乳动物的例子,首先根据已经已知的训练集进行训练,提炼出哺乳动物的各种特征,这就是一个建立模型的过程,模型建立好之后,再有新物种需要进行判断的时候,只需要按照训练好的模型对照新物种挨个对比,就能很容易的给出判断结果.

消极学习方法,这种学习方法在最开始的时候不会根据已有的样本创建目标函数,只是简单的把训练用的样本储存好,后期需要对新进入的样本进行判断的时候才开始分析新进入样本与已存在的训练样本之间的关系,并据此确定新实例(新进入样本)的目标函数值.

例如

消极学习的典型算法KNN,KNN不会根据训练集主动学习或者拟合出一个函数来对新进入的样本进行判断,而是单纯的记住训练集中所有的样本,并没有像上边决策树那样先对训练集数据进行训练得出一套规则,所以它实际上没有所谓的"训练"过程,而是在需要进行预测的时候从自己的训练集样本中查找与新进入样本最相似的样本,即寻找最近邻来获得预测结果

积极学习方法和消极学习方法各自的特点

积极学习方法:在训练时考虑到了训练集中所有数据,训练时间比较长,有新样本进入需要判断的时候决策时间短.但是也是由于这个原因,做增量拓展的时候比较虐.

消极学习方法:几乎没有训练时间,在新样本进入做判断的时候计算开销大,时间长;但是呢天生支持增量学习.

发布了348 篇原创文章 · 获赞 210 · 访问量 87万+

猜你喜欢

转载自blog.csdn.net/u010916338/article/details/105111438