K-近邻算法百度百科解释为“K最近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。该方法的思路是:在特征空间中,如果一个样本附近的k个最近(即特征空间中最邻近)样本的大多数属于某一个类别,则该样本也属于这个类别。”
简单来说KNN就是测量不同特征值之间的距离然后进行分类,属于无监督学习的一种。
本文主要分享一下我学习KNN原理的过程,及KNN的简单描述,阅读本文大约耗时15分钟。
一、KNN的优缺点
优缺点 | 描述 |
---|---|
优点 | 精度高、对异常值不敏感、无数据输入假定 |
缺点 | 计算时间、空间复杂度高 |
适用数据范围 | 数值型、标称型 |
二、KNN的一般流程
1、收集数据:可以使用任何方法,一般有条件的可以从Excel、MYsql导入;或利用爬虫获取;或手工输入
2、准备数据:此步骤主要是数据的清洗和结构化
3、分析数据:可以使用任何方法,此步骤也是你为何应用KNN算法的依据,分析数据使用何种方法比使用方法本身更重要!
4、测试算法:计算错误率或计算分类性能
5、使用算法:用KNN判定输入数据属于哪个类,执行后续的操作
三、自己构建KNN算法
当然,可以利用python—sklearn.cluster.KMeans调用KNN,但我主要从原理上理解,所以自行构建一个KNN。
3.1 构建数据集
手工构建一个简单的数据集,包含4个点和1个特征,即:
import numpy as np
group = np.array([[1.0, 1.1],
[1.0, 1.],
[0.0, 0.0],
[0.0, 0.1]])
labels = ['A', 'A', 'B', 'B']
其中labels的个数等于group矩阵的行数,此处我们人为设定前两行数据类型为A,后两行数据类型为B。当然你也可以设定为其他类型,也可以是多种类型,但相应的K值就要改变。接下来我们来实施KNN算法。
3.2 KNN算法原理
前面已经说过KNN算法主要是计算各特征值之间的距离,K代表与当前点距离最小的K个点,因此k值可以人为设定。具体步骤如下:
1、计算已知数据集中的点与当前点的距离
我们学过计算两点之间的距离,利用欧式距离公式,则两个向量点xA和xB之间的距离为
d = ( x A 0 − x B 0 ) 2 + ( x A 1 − x B 1 ) 2 d=\sqrt{(xA_0-xB_0)^2+(xA_1-xB_1)^2} d=(xA0−xB0)2+(xA1−xB1)2
如点(0,0)和点(3,4)之间的距离为:
( 0 − 3 ) 2 + ( 0 − 4 ) 2 = 5 \sqrt{(0-3)^2+(0-4)^2}=5 (0−3)2+(0−4)2=5
多个特征值时,如点(1,0,0,1)和点(7,6,9,4)之间的距离为:
( 1 − 7 ) 2 + ( 0 − 6 ) 2 + ( 0 − 9 ) 2 + ( 1 − 4 ) 2 \sqrt{(1-7)^2+(0-6)^2+(0-9)^2+(1-4)^2} (1−7)2+(0−6)2+(0−9)2+(1−4)2
此部分的代码如下:
def classfiy0(inX, dataset, labels, k):
dataSetSize = dataset.shape[0]
# print("dataSetSize", dataSetSize)
diffMat = np.tile(inX, (dataSetSize, 1)) - dataset
# print("diffMat", diffMat)
sqDiffMat = diffMat**2
# print("sqDiffMat", sqDiffMat)
sqDistances = sqDiffMat.sum(axis=1)
# print("sqDistances", sqDistances)
distances = sqDistances**0.5
函数参数解释:
参数 | 意义 |
---|---|
inX | 输入向量 |
dataset | 训练样本集 |
labels | 标签向量 |
k | 选取与当前点距离最小的k个点 |
在上面代码里,我们首先获取了训练样本集dataset矩阵的行数,利用tile函数构造输入向量相同函数的矩阵,然后计算二者之间的差值,进而平方,求和,最后是开方。
2、按照距离值递增排序,即升序排列
升序排列我们用argsort函数进行,argsort是获取排序后的索引,为什么要获取索引呢?因为我们要利用索引去查找对应的标签,而索引和标签的索引恰恰相同。假设我们的四个点分别为A(1.0,1.1),B(1.0,1.0),C(0.0,0.1),D(0.0,0.0);我们输入向量inX为(0,0),则第一步中输出结果为[2.21,2.0,0.0,0.01],则第2步输出为[2,3,1,0],正好对应相应的标签值。代码如下:
sortedDistIndicies = distances.argsort()
3、获取与当前点距离最小的k个点及出现的频率
前面我们知道k值可以认为设定,因此主要在于如何获取点和对应出现的频率。我们利用dict的get经典方法获取对应的标签和频率,利用for循环进行查找构建dict
代码如下:
classCount = {}
for i in range(k):
voteILabel = labels[sortedDistIndicies[i]] # 获取对应的标签
classCount[voteILabel] = classCount.get(voteILabel, 0) + 1 # 获取出现的频率
4、返回前K个点频率出现最高的类别作为输入向量inX的预测值
在这一步,我们继续利用sort函数对classCount.items进行降序排序,排序依据为Value,此时可利用字典排序算法对其排序,常见的有lambda或者operator函数,lambda为:
sortedClassCount = sorted(classCount.items(), key=lambda x:x[1], reverse=True)
return sortedClassCount[0][0]
operator 为:
import operator
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
四、测试
此时我们输入向量为(0,0),结果预测应该为B,证明分类器没有问题。
# -* encoding:utf-8 -*-
import numpy as np
import operator
group = np.array([[1.0, 1.1],
[1.0, 1.],
[0.0, 0.0],
[0.0, 0.1]])
labels = ['A', 'A', 'B', 'B']
# print(group, labels)
def classfiy0(inX, dataset, labels, k):
dataSetSize = dataset.shape[0]
# print("dataSetSize", dataSetSize)
diffMat = np.tile(inX, (dataSetSize, 1)) - dataset
# print("diffMat", diffMat)
sqDiffMat = diffMat**2
# print("sqDiffMat", sqDiffMat)
sqDistances = sqDiffMat.sum(axis=1)
# print("sqDistances", sqDistances)
distances = sqDistances**0.5
sortedDistIndicies = distances.argsort()
classCount = {}
for i in range(k):
voteILabel = labels[sortedDistIndicies[i]]
classCount[voteILabel] = classCount.get(voteILabel, 0) + 1
# print("classCount", classCount)
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
# print("sortedClassCount", sortedClassCount)
return sortedClassCount[0][0]
predict = classfiy0([0,0],group,labels,3)
print(predict)
对此一个简单的KNN算法就这样实现了,但如何测试分类器和改进分类器性能,在后续我回陆续推出相应文章。
查看我的其他博客请点击这里