机器学习算法之一 K近邻算法

来源:互联网 发布:大数据主要来源于什么 编辑:程序博客网 时间:2024/05/17 17:40

K-近邻算法

1.简介

右图中,绿色圆要被决定赋予哪个类,是红色三角形还是蓝色四方形?如果K=3,由于红色三角形所占比例为2/3,绿色圆将被赋予红色三角形那个类,如果K=5,由于蓝色四方形比例为3/5,因此绿色圆被赋予蓝色四方形类。
KNN算法的决策过程KNN算法的决策过程
K最近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。该方法的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。 KNN方法虽然从原理上也依赖于极限定理,但在类别决策时,只与极少量的相邻样本有关。由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合。
KNN算法不仅可以用于分类,还可以用于回归。通过找出一个样本的k个最近邻居,将这些邻居的属性的平均值赋给该样本,就可以得到该样本的属性。更有用的方法是将不同距离的邻居对该样本产生的影响给予不同的权值(weight),如权值与距离成反比。

2.算法思想

K最近邻 (k-Nearest Neighbors,KNN) 算法的核心思想是如果一个样本在特征空间上与训练样本集中的K个样本最相似,并且这K个样本中的大多数样本属于某一个类别,则这个样本也属于该类别。(思想是“近朱者赤,近墨者黑”,由你的邻居来推断出你的类别。

3.算法流程

该算法的流程图如下:


python 代码实现:
#coding:utf-8from numpy import *import operator
def cfiy(intx,dataset,labels,k):    m=shape(dataset)[0]
    intmarix=tile(intx,(m,1)) #tile 用来生成数组    submax=dataset-intmarix    submax=submax**2    tepmax=submax.sum(axis=1)    dismax=tepmax**0.5    indmax=dismax.argsort()  #排序只生成索引    classtag={}    for i in range(k):        label=labels[indmax[i]]        classtag[label]=classtag.get(label,0)+1    # 对字典进行排序的结果是列表,形式如[(,),(,)(,)]    sortclass=sorted(classtag.iteritems(),key=operator.itemgetter(1),reverse=True)    return sortclass[0][0]

4.算法应用


KNN算法的应用领域主要有:文本分类、聚类分析、预测分析、模式识别、图像处理,详细情况可查看:KNN在各个领域的应用
下面是KNN应用在约会网站和手写识别各种情况下的例子:

1.使用KNN算法改进约会网站的配对效果

约会网站上一般给出的数据是个人的一些情况信息,每个人可以作为一个样本,假设这些样本包含以下三个特征:
a. 每年获得的飞行常客里数

b. 玩视频游戏时间所占的百分比

c. 每周消费的冰淇淋公升数

根据这三个特征,可以把约会对方分为不喜欢的人、魅力一般的人和极具魅力的人三类。
使用算法的步骤如下:

1.收集数据:提供文本文件

2.准备数据:用python解析文本文件。

3.分析数据:使用matplot 画二维扩散图。

4.训练算法:不适合KNN。

5.测试算法:使用文本文件中提供的部分数据做测试数据。

6.使用算法:根据输入的一些特征判断对方是不是自己喜欢的人。

python实现如下:
#coding:utf-8from numpy import *import osimport operatorimport matplotlibimport randomimport matplotlib.pylab as plt#准备数据:从文本文件中解析数据def cfiy(intx,dataset,labels,k):    m=shape(dataset)[0]    intmarix=tile(intx,(m,1))    submax=dataset-intmarix    submax=submax**2    tepmax=submax.sum(axis=1)    dismax=tepmax**0.5    indmax=dismax.argsort()    classtag={}    for i in range(k):        label=labels[indmax[i]]        classtag[label]=classtag.get(label,0)+1    # 对字典进行排序的结果是列表,形式如[(,),(,)(,)]    sortclass=sorted(classtag.iteritems(),key=operator.itemgetter(1),reverse=True)    return sortclass[0][0]def convert(tag):    num=0    if isinstance(tag,str):        if tag=="didntLike":            num=1        if  tag=="smallDoses":            num=2        if  tag=="largeDoses":            num=3    else:        if tag==1:            num="didntLike"        if tag==2:            num="smallDoses"        if tag==3:            num="largeDoses"    return numdef creatdata(filename):    f=open(filename)    rawlist=f.readlines()    numberofline=len(rawlist)    dataset=zeros((numberofline,3))    labels=[]    ind=0    #read()读取整个文件,将整个文件的内容读成一个字符串;    #readline()每次读取文件的一行,并把一行的内容转换成字符串;    #readlines()读取整个文件,但自动把文件分析成由每一行内容组成的字符串列表    for line in rawlist:        line=line.strip()        line=line.split("\t")        dataset[ind,:]=line[0:3]        #labels.append(int(line[-1]))        labels.append(convert(line[-1]))        ind+=1    return dataset,labels#归一化特征值def autonorm(dataset):    m=dataset.shape[0]    columnmin=dataset.min(0)    columnmax=dataset.max(0)    ranges=columnmax-columnmin    valuesmarix=zeros(shape(dataset))    minmarix=tile(columnmin,(m,1))    rangemarix=tile(ranges,(m,1))    submarix=dataset-minmarix    #/代表点除,在numpy中,如果矩阵除法需用linalg.solve(matA,matB)    valuesmarix=submarix/rangemarix    return valuesmarix,ranges,columnmin#分析数据def   analy(dataset,labels):    fig=plt.figure()    #参数111的意思是:将画布分割成1行1列,图像画在从左到右从上到下的第1块    bx=fig.add_subplot(111)    #15.0*array(labels)参数是根据labels的类型数用不同的颜色画图    bx.scatter(dataset[:,1],dataset[:,2],15.0*array(labels),15.0*array(labels))    plt.show()#测试算法def algtest(valuesmarix,labels):    horate=0.1    m=valuesmarix.shape[0]    t=int(m*horate)    indx=random.sample(range(0,m),t)    print "lenof indx=%d"%(len(indx))    traindata=zeros((m-len(indx),3))    trainlabel=[]    tempind=0    for i in range(m):        if i not in indx:            traindata[tempind,:]=valuesmarix[i,:]            trainlabel.append(labels[i])            tempind+=1    errorcount=0    for j in indx:        answer=cfiy(valuesmarix[j,:],traindata,trainlabel,3)        print "测试前:%d,测试后:%d" %(labels[j],answer)        if(answer!=labels[j]):            errorcount+=1    print "错误率是:%f" %(errorcount/len(indx))#使用算法file="E:\datingTestSet.txt"dataset,labels=creatdata(file)analy(dataset,labels)valuesmarix,ranges,columnmin=autonorm(dataset)algtest(valuesmarix,labels)mile=float(raw_input("please input the mile:\n"))time=float(raw_input("please input the video time:\n"))ice=float(raw_input("please input the icecream:\n"))pred=array([time,mile,ice])predict=(pred-columnmin)/rangescfyresult=cfiy(predict,valuesmarix,labels,3)print "you will probably like the person:",convert(cfyresult)

5.算法优缺点

该算法涉及3个主要因素:训练集、距离或相似的衡量、k的大小。
A.关于训练集的问题

1、训练样本是否要一视同仁?(假设不同标签的样本数量相等)
在训练集中,有些样本可能是更值得依赖的。
可以给不同的样本施加不同的权重,加强依赖样本的权重,降低不可信赖样本的影响。

2、训练集中不同标签的样本在数量上如何适当的分配?
正如开头正方形和三角形的例子,在K值固定时,如果某一标签类的样本数量占大多数,可能会影响最终的预测结果。
可以先进行预处理,对结果影响较小的样本可以先忽略掉


B.关于相似的衡量问题

1、相似如何进行衡量?
可以利用距离衡量
什么是合适的距离衡量?距离越近应该意味着这两个点属于一个分类的可能性越大。
觉的距离衡量包括欧式距离、夹角余弦,等。
对于文本分类来说,使用余弦(cosine)来计算相似度就比欧式(Euclidean)距离更合适。

2、类型如何判定最合适?
投票决定:少数服从多数,近邻中哪个类别的点最多就分为该类。
加权投票法:根据距离的远近,对近邻的投票进行加权,距离越近则权重越大(权重为距离平方的倒数)

C.关于K的选择的问题

1、k值设定为多大?
k太小,分类结果易受噪声点影响;k太大,近邻中又可能包含太多的其它类别的点。(对距离加权,可以降低k值设定的影响)
k值通常是采用交叉检验来确定(以k=1为基准)
经验规则:k一般低于训练样本数的平方根

算法优点:
1、简单,易于理解,易于实现,无需估计参数,无需训练
2、适合对稀有事件进行分类(例如当流失率很低时,比如低于0.5%,构造流失预测模型)
3、特别适合于多分类问题(multi-modal,对象具有多个类别标签),例如根据基因特征来判断其功能分类,kNN比SVM的表现要好


算法缺点:

KNN算法简单有效,但没有优化的暴力法效率容易达到瓶颈。

如样本个数为N,特征维度为D的时候,该算法时间复杂度呈O(DN)增长。

所以通常KNN的实现会把训练数据构建成K-D Tree(K-dimensional tree),构建过程很快,甚至不用计算D维欧氏距离,而搜索速度高达O(D*log(N))。

不过当D维度过高,会产生所谓的”维度灾难“,最终效率会降低到与暴力法一样。
因此通常D>20以后,最好使用更高效率的Ball-Tree,其时间复杂度为O(D*log(N)












原创粉丝点击