机器学习之K-近邻算法
在博客园的第一篇文章,但不会是最后一篇文章。虽说机器学习这个名字听起来挺唬人的,但是我们知道每一个貌似专业的名词都是用来忽悠小白的。因此对于那些貌似专业的名词,我们需要弄懂它们到底在说的什么,也许这就是我一直所追求的Hacker精神。
K-近邻算法是机器学习中比较简单的一个算法,但是也不会是小白那样的简单,而是相比于机器学习的其它算法来说比较简单而已。首先来说说K-近邻算法的精要思想:比较测试样本与训练样本集中每一个样本的距离,然后按照距离递增的顺序排序,下来选取前K个与测试样本距离比较近的样本的类别标签,然后选取在K个标签中,出现频率最高的类别,这个类别就是K-近邻算法基于训练样本集给出的测试样本的预测类别。
在上面的精要思想阐述中,有这样几个问题。
- 怎样计算测试样本和训练样本集中样本之间的距离;
- 训练样本集有什么要求;
- 按照距离递增排序是对训练样本集排序吗?还是对什么进行排序;
- 对每个测试样本都需要和训练样本集中的每个样本进行距离的计算,这样是不是计算量很大;
- 数值K如何确定;
相应问题的讨论:
- 针对上面的这4个问题,我们来一个个进行思考,或许这样才是最佳的学习方法。首先来考虑第1个问题,样本之间的距离到底是如何进行计算的。在考虑计算之前,得首先明确样本到底是什么结构的,要不想要对一个自己根本不知道是什么东西的东计算距离,这不是开玩笑嘛。这里所提到的样本都是已经经过处理数据,如果说具体格式的话,这里所提到的样本都是已经处理成的一个n维向量,每一维的数值代表一个样本的属性。这样的话,我们就会想到几何学中,计算两个向量之间的距离的方法。没错,就是欧式距离的计算方法(将设有两个向量V1和V2,V1=<a1,a2>,V2=<b1,b2>,V1和V2之间的距离=
)。因此这里的关键就是样本数据必须格式化为向量形式,其中的类别标签属性不需要放在向量中,可以将整个训练集的类别标签对应训练集中样本的顺序存放在另外的列表中。 - 从上面的讨论已经知道,训练样本集中的样本必须都处理为数值向量格式,这是后面计算距离的要求。另一方面,从算法的精要思想可以看出,因为是计算与训练样本集中样本的距离来预测测试样本的分类,那么训练样本集的质量基本上决定了算法的好坏。如果训练样本集存在问题,那么怎么预测都不会正确的。因此在算法开始之前,必须确保训练样本集的质量。这里的质量包括样本集的数量大小,以及是否接近实际数据等等多个方面。
- 从上一个讨论中,我们就会发现,如果样本集中样本的数量很大,那么对训练样本集排序是不明智的做法,这样会消耗大量的计算资源。因此我们可以考虑对样本集进行索引排序,也就是不对训练样本集进行移动,只是根绝距离的大小,用训练样本集的索引代替样本本身进行排序。这个Python有很好的支持。
- 毋庸置疑,计算量的大小随训练样本集的增大呈线性增长,并且每一测试样本的预测,都需要这样的一次计算。而且随着训练样本集的增大,存储空间也会占用更多。
- 数值K的选取很微妙,因为随着K的不同可能会导致分类器的错误率也不同,因此最好的办法就是多测试几次来选取比较好的K值。
下面我们就来看一下用Python来进行实现的一些实例:
假如像下面这样创建了一个数据集
1 def createDataSet(): 2 group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]]) 3 labels = ['A','A','B','B'] 4 return group,labels
其中的group变量是Numpy中的数组,3行2列,表示3个样本即3个2维数值向量。labels列表里面存储的是与训练样本集group中样本相对应的类别标签。这样我们就可以得到一个训练样本集以及所对应的类别标签列表。
假如我们现在有一个测试向量a=[1.0,1.0],我们需要给向量a根据训练样本集预测一个类别,a到底是属于类别A呢,还是属于B呢?如下就是K-近邻算法的实现:
1 def classifyKNN(inX, trainingDataSet, trainingLabels, k): 2 dataSetSize = trainingDataSet.shape[0] 3 diffMat = tile(inX, (dataSetSize,1)) - trainingDataSet 4 sqDiffMat = diffMat**2 5 sqDistances = sqDiffMat.sum(axis=1) 6 distances = sqDistances**0.5 7 sortedDistIndicies = distances.argsort() #返回排序的索引结果 8 classCount = {} 9 for i in range(k): 10 votedLabel = trainingLabels[sortedDistIndicies[i]] 11 classCount[votedLabel] = classCount.get(votedLabel,0) + 1 12 sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True) 13 return sortedClassCount[0][0]
然后在Python shell中输入
1 classifyKNN([1.0,1.0],group,labels,3)
获取输出结果为
1 'A'
一切看起来都是那么的自然简单,但是现实的情况远远没有这么简单。在实际问题中我们还需要考虑,怎么才能将实际中的数据化为向量表示?而且我们由欧式距离的计算公式可以看出,如果其中某一维的数值取值范围远远大于其他取值范围的话,那么取值范围大的属性将会对距离产生很大的影响,所以如何认为每一个属性都同样重要的话,那么就需要对这些不同取值范围的属性继续进行处理。还有,有些样本的属性是标称型的,不是数值型,这样又该如何处理呢?
大家不要心急,请期待下一篇文章讨论如何在实际中应用K-近邻算法。
posted on 2015-05-28 10:38 Long-Hacker 阅读(147) 评论(0) 收藏 举报
)。因此这里的关键就是样本数据必须格式化为向量形式,其中的类别标签属性不需要放在向量中,可以将整个训练集的类别标签对应训练集中样本的顺序存放在另外的列表中。
浙公网安备 33010602011771号