KNN算法拓展及实现

前言

K近邻算法是一种思想极其简单,而分类效果比较优秀的分类算法,最重要的是该算法是很多高级机器学习算分基础,并且在后面我们将要学习的集成算法中,k近邻也经常被用来做基础分类器。它的基本思想我们已经在上节介绍过了,在此我们不在赘述,本节主要讲一下有关它的拓展知识以及实现。

模型:所有的空间划分,判别模型
策略:距离最近的k个邻居
方法:多数表决(注意,这里没有可计算的优化方法,可能我也没有说清楚,自己体会一下)
训练过程:交叉验证的方法,来调整k值得选择,使得最终的预测估计误差最小。

拓展

  KNN算法思想简单容易实现,但是它是一种没有优化(因为分类决策为多数投票)的暴力方法(线性搜索方法),所以当数据量比较大的时候,算法效率容易达到瓶颈。例如,样本个数为N,特征维数位D的时候,该算法的时间复杂度为O(D*N)。所以,通常情况下,KNN的实现会把训练数据建成Kd-tree(K-dimensional tree,kd-tree搜索方法),构建过程很快,甚至不用计算D为欧氏距离,而搜索速度高达O(D*log(N))。但是建立kd-tree搜索方法也有一个缺点是:当D维度过高的时候,会产生所谓的“维度灾难”,最终的效率会降低到与暴力法一样。
kd树更适合用于训练实例树远大于空间维数时的k近邻搜索。当空间维数接近于训练实例数的时候,它的效率会迅速下降,几乎接近于线性扫描。

  当维度D>20以后,最好使用更高效率的ball-tree, 其时间复杂度为仍为O(D*log(N))。

  人们经过长期的实践发现,KNN算法适用于样本分类边界不规则的情况。由于KNN主要依靠周围有限的邻近样本,而不是靠判别类域的方法来确定所属类别,因此对于类域的交叉或重叠较多的待分样本集来说,KNN算法比其他方法要更有效。

  该算法在分类时有个主要的不足是,当样本不平衡的时候,如果一个类的样本容量很大,而其他类样本容量很小的时候,有可能导致当输入一个新样本的时候,该样本的K个邻居中大容量类的样本占多数。因此可以采用权值的方法(和该样本距离小的邻居取值大)来改进。
  该方法的另一个不足之处是计算量比较大,因为对每一个待分类的文本都要计算它到全体已知样本的距离(当然了我们也提到过多次kd-tree的优化算法,可以避免这种情况),才能求得它的K个最近邻。目前常用的解决方法是事先对已知样本点进行剪辑,事先去除对分类作用不大的样本。该算法比较适用于样本容量较大的类域的自动分类,而那些样本容量较小的类域采用这种方法比较容易产生误分。

算法描述

总的来说就是我们已经存在了一个带标签的数据库,然后输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似(最近邻)的分类标签。一般来说,只选择样本数据库中前k个最相似的数据。最后,选择k个最相似数据中出现次数最多的分类。其算法描述如下:

1)计算已知类别数据集中的点与当前点之间的距离;

2)按照距离递增次序排序;

3)选取与当前点距离最小的k个点;

4)确定前k个点所在类别的出现频率;

5)返回前k个点出现频率最高的类别作为当前点的预测分类。
#算法一 调用sklearn中的方法
# -*- encoding:utf-8-*-

'''
调用sklearn中的方法,并且使用sklearn中自己的数据
@author:Ada
'''
print __doc__
import numpy as np
from sklearn import neighbors,datasets

#下载数据
#64维 1797个样本
datas=datasets.load_digits()
totalNum=len(datas.data)#1797
#print totalNum
#print len(datas.data[0])

#分割数据集
#选出80%样本作为训练集,剩余的20%作为测试集
trainNum=int(0.8*totalNum)
trainX=datas.data[0:trainNum]
trainY=datas.target[0:trainNum]
testX=datas.data[trainNum:]
testY=datas.target[trainNum:]

#设置k值,一般情况下k<20
n_neighbors=10

#建立分类器
clf=neighbors.KNeighborsClassifier(n_neighbors=n_neighbors,weights='uniform',algorithm='auto')
#训练分类器
clf.fit(trainX,trainY)

#利用训练好的分类器进行预测
answer=clf.predict(testX)

print "误分率为:%.2f%%"%((1-np.sum(answer==testY)/float(len(testY)))*100)

#说明:
#KNeighborsClassifier可以设置3种算法:‘brute’,‘kd_tree’,‘ball_tree’。
#如果不知道用哪个好,设置‘auto’让KNeighborsClassifier自己根据输入去决定。

关于kd-tree和ball-tree基础参见这里

#方法二:自己实现一个线性KNN算法
from numpy import *
import operator

#create a dataset which coantains 4 samples with 2 classes

def createDataSet():
    #create a matrix:each row as a sample
    group=array([[1.5,1.4],[1.6,1.5],[0.1,0.2],[0.0,0.1]])# each sample has two features
    labels=['A','A','B','B']#four samples with two labels
    return group,labels

#calssify using KNN
def KNNClassify(newInput,dataSet,labels,k):
    numSamples=dataSet.shape[0]#shape[0] stands for the num of row

    #Step 1:calculate Euclidean distance
    #tile(A,reps):construce an array by repeating A reps times
    # eg:
       #A=[[1.2,1.0]]
       #diff =tile(A,(4,3))#也就是说把A看成一个整体,把A赋值成4行3列
       # diff:
       #[[ 1.2  1.   1.2  1.   1.2  1. ]
       # [ 1.2  1.   1.2  1.   1.2  1. ]
       # [ 1.2  1.   1.2  1.   1.2  1. ]
       # [ 1.2  1.   1.2  1.   1.2  1. ]]
    #the following copy numSamples rows for dataSet
    diff =tile(newInput,(numSamples,1))-dataSet#计算测试集与每个训练集的差值
    squareDiff=diff**2#差的平方
    squareDist=sum(squareDiff,axis=1)#按行求和,也就是对每个样本进行操作计算,平方和
    distance=squareDist**0.5

    #Step 2: sort the distance by asce
    #argsort() returns the distances that would sort an array in a ascending order
    sortedDistance=argsort(distance)

    classCount={}#定义一个字典(对应到每个样本)
    for i in xrange(k):
        #Step 3:choose the min k distance
        voteLabel=labels[sortedDistance[i]]

        # Step 4:count the times labels occur
        # when the key voteLabel is not in dictionary classCount ,get()
        # will return 0
        classCount[voteLabel]=classCount.get(voteLabel,0)+1

    # Step 5:the max voted class will return
    maxCount=0
    for key,value in classCount.items():
        if value>maxCount:
            maxCount=value
            maxIndex=key

    return maxIndex


if __name__ == '__main__':
    dataSet,labels=createDataSet()
    testData=array([1.2,1.0])
    k=3
    predictlabel=KNNClassify(testData,dataSet,labels,k)
    print '测试数据为:',testData,'预测结果类别为:',predictlabel

    testData=array([0.1,0.3])
    predictlabel=KNNClassify(testData,dataSet,labels,k)
    print '测试数据为:',testData,'预测结果类别为:',predictlabel

实验结果为:

测试数据为: [ 1.2  1. ] 预测结果类别为: A
测试数据为: [ 0.1  0.3] 预测结果类别为: B

对于kd-tree搜索的KNN算法我们将在下节进行介绍。

《完》

所谓的不平凡就是平凡的N次幂。
                        ---- By Ada
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱科研的徐博士

请各位看官赏赐,小仙女笔芯笔芯

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值