现在要来实现我们的预测目的了,想象一下现在一学期快过完了,张三同学马上要考试了,他想知道自己能考的怎么样,他在数学老师那里查到了自己的到课率85%,作业质量是90,那么怎么实现预测呢?张三可以看做是(85,90)这个点–也被称之为测试样本,首先,我们计算张三到其他6位同学(训练样本)的距离,点到点的距离相信我们初中就学了吧(一般用的欧氏距离)。再选取前K个最近的距离,例如我们选择k=3,那么我们就找出距离最近的三个样本分别属于哪个类别,此例中,自然三个都是A等,所以可预测出张三的数学期末成绩可能是A等(优秀)。倘若李四现在也想进行预测,据他较近的3个中两个D,一个A,那么李四的数学期末成绩被预测为D。这也就是最开始所说的:在前k个样本中选择频率最高的类别作为预测类别。。。
总结其计算步骤如下:
算距离:给定测试对象,计算它与训练集中的每个对象的距离
找邻居:圈定距离最近的k个训练对象,作为测试对象的近邻
做分类:根据这k个近邻归属的主要类别,来对测试对象分类
好了,经过上述过程,你是否对KNN算法基本思想有了一定了解。也许你会问我,我大学不去上课,不交作业,照样考A,这预测根本不准确嘛,O(∩_∩)O哈哈~,首先说明一下这个例子举的确实不太恰当,因为我们的特征(到课率和作业质量)选取的不当,在很多分类预测算法中,决定其分类预测上限的往往是好的特征的选取,好的特征也就是对其最终结果的影响比较大的。。。原理就说到这吧。。。
算法复杂度
kNN是一种lazy-learning算法,分类器不需要使用训练集进行训练,因此训练时间复杂度为0;kNN分类的计算复杂度和训练集中的文档数目成正比,也就是说,如果训练集中文档总数为n,那么kNN的分类时间复杂度为O(n);因此,最终的时间复杂度是O(n)。
1)简单、有效。
2)重新训练的代价较低(类别体系的变化和训练集的变化,在Web环境和电子商务应用中是很常见的)。
3)计算时间和空间线性于训练集的规模(在一些场合不算太大)。
4)由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合。
5)该算法比较适用于样本容量比较大的类域的自动分类,而那些样本容量较小的类域采用这种算法比较容易产生误分。
KNN算法缺点:
1)KNN算法是懒散学习方法(lazy learning,基本上不学习),一些积极学习的算法要快很多。
2)类别评分不是规格化的(不像概率评分)。
3)输出的可解释性不强,例如决策树的可解释性较强。
4)该算法在分类时有个主要的不足是,当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的K个邻居中大容量类的样本占多数。该算法只计算“最近的”邻居样本,某一类的样本数量很大,那么或者这类样本并不接近目标样本,或者这类样本很靠近目标样本。无论怎样,数量并不能影响运行结果。可以采用权值的方法(和该样本距离小的邻居权值大)来改进。
5)计算量较大。目前常用的解决方法是事先对已知样本点进行剪辑,事先去除对分类作用不大的样本。
3.1 首先我们介绍一下代码实现步骤:
计算已知类别数据集中的点与当前点之间的距离
按距离递增次序排序
选取与当前点距离最小的k个点
统计前k个点所在的类别出现的频率
返回前k个点出现频率最高的类别作为当前点的预测分类
3.2 实现:我们先创建一个名为knn.py的文件,整体实现代码如下:
from numpy import * #导入numpy科学计算包
import operator #导入运算符模块
#加载数据的方法,返回样本数据(每一行是一个样本)和样本标签
def createDataSet():
group = array([[90,100],[88,90],[85,95],[10,20],[30,40],[50,30]]) #样本点数据
labels = ['A','A','A','D','D','D']
return group,labels
#分类方法 传入的dataset需是array数组
def classify0(inX, dataSet, labels, k): #inX为输入样本,例如[85,90]
dataSetSize = dataSet.shape[0] #求出输入数据矩阵的行数(样本个数)
diffMat = tile(inX, (dataSetSize,1)) - dataSet #求矩阵差
sqDiffMat = diffMat ** 2
sqDistance = sqDiffMat.sum(axis = 1) #平方和
distance = sqDistance ** 0.5 #测试样本点距离每个样本点的距离
sortedDistance = distance.argsort() #将距离按升序排列
classCount = {
}
for i in range(k):
voteLabel = labels[sortedDistance[i]] #遍历前k个样本的标签
classCount[voteLabel] = classCount.get(voteLabel,0) + 1 #对标签进行计数,即每一类出现的次数
sortedClassCount = sorted(classCount.items(),key = operator.itemgetter(1),reverse = True) #将计数后的标签按降序进行排列,得到元组列表
return sortedClassCount[0][0]