KNN(k-nearest Neighbor)是一种常用的监督学习的分类方法。工作原理非常简单:给定测试样本,基于某种距离找出训练集中与其最靠近的k个样本,然后基于这k个邻居采用“投票法”,即把k个样本中出现次数最多的类别作为预测结果。
从下面图中可以很容易看出其原理:
kNN是懒惰学习(lazy learning)的典型代表,它在训练阶段仅仅保存样本,训练时间开销为0,预之相反的称为eager learning。
kNN的优点是简单,但是它的泛化错误率不超过贝叶斯最优分类器的错误率的两倍,效果还不错。其缺点也很明显,它无法给出任何数据的基础结构信息,我们也不知道实例有什么特征。
相应的Python代码实现也比较简单,这里简要分析以下:
def kNN(inX, dataSet, labels, k):
dataSetSize = dataSet.shape[0]
diffMat = tile(inX, (dataSetSize,1)) - dataSet
sqDiffMat = diffMat**2
sqDistances = sqDiffMat.sum(axis=1)
distances = sqDistances**0.5
sortedDistIndicies = distances.argsort()
classCount={}
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]] classCount[voteIlabel]=
classCount.get(voteIlabel,0) + 1
sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
inX是要分类的向量,dataSet是数据集,labels是对应标签,k是选择的邻居数。
shape函数读取矩阵的行列数,shape[0]就是指行数,tile函数把输入向量复制成与dataSet维度相同的向量,后面就是算的马氏距离,这里距离可以根据需要换成其他度量。然后根据距离排序,对前k个统计其标签,按照标签排序,返回其最前的那个。
这里有个问题,就是每个属性的距离在计算之前需要归一化,否则距离比较大的属性对结果的影响就会很大。
下面就是归一化的代码:
def autoNorm(dataSet):
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
ranges = maxVals - minVals
normDataSet = zeros(shape(dataSet))
m = dataSet.shape[0]
normDataSet = dataSet - tile(minVals, (m,1))
normDataSet = normDataSet/tile(ranges, (m,1))
return normDataSet, ranges, minVals
还有挺有趣的约会kNN分析例子,代码也很简单。有个可以根据问答选择感兴趣的配偶的小程序。可以可以试着跑跑看。我把数据集和代码都放在这里。
在命令行运行:
python kNN.py
即可运行程序。
kNN的三个基本要素:k值的选择,距离的度量,分类决策规则(如多数表决)。
为了提高kNN搜索效率,一般的实现形式采用kd树。
kd树是二叉树,表示对k维空间的一个划分,构造kd树相当于不断地用垂直于坐标轴的超平面将k维空间切分,构成一系列的k维超矩形区域,kd树的每个结点对应于一个k维超矩形空间。