【ML学习笔记】12:k-近邻算法的demo

来源:互联网 发布:win7系统数据恢复 编辑:程序博客网 时间:2024/06/18 07:12

k-近邻算法(KNN)是一种有监督学习的分类算法,属于非概率分类器

其基本思路就是,实例的每一个特征都可以赋予一个值去度量,如果有n个特征,那么也就是相当于实例在n维空间中(n个方向各自不同,显然这种假定是建立在我们认为这些特征互不相关的基础上的)。

而k-近邻算法也就是,把这个投入到学习机器去做预测的实例的特征向量,认定是表达了n维空间上的一个点,这个空间不见得是欧式空间,然后去寻找训练集中k个和这个点距离最近(这个距离自然是定义在这个空间上的距离)的点对应的实例,去看这些实例对应的标签,出现频数最多的那个标签就成为我们kNN算法预测的标签。

下面这个demo是跟着书上做的,加了一些可视化的成分,有很多细节值得注意,算是回顾了python的小部分语法。

这是一个后缀名为.py的模块,写好以后,在命令行进入到这个模块文件所在的目录,然后再进入python回显界面去使用它!

#-*-coding:utf-8-*-from numpy import * #科学计算包import operator     #运算符模块from matplotlib import pyplot as plt#上一行的末尾不能打注释!#创建训练集和标签def createDataSet():    #训练集的特征    group=array(        [[1.0,1.1],[1.0,1.0],[0.1,0],[0,0.1],        [0.9,1.0],[1.0,0.7],[0.2,0],[0.1,0.1],        [1.1,1.0],[0.8,1.1],[0.1,0.4],[0.3,0.2]])    #对应的标签    labels=['A','A','B','B',        'A','A','B','B',        'A','A','B','B']    return group,labels#k-近邻算法(输入的特征向量,训练集特征向量矩阵,训练集标签向量,k值)def KJL(inX,dataSet,labels,k):    #用shape[0]读取训练集第一维的长度(行数即训练集实例数)    dataSetRow=dataSet.shape[0]    #用shape[1]读取训练集第二维的长度(列数即特征数)    dataSetCol=dataSet.shape[1]    #读取输入向量的列数(特征数)    inXCol=len(inX)    #训练集标签向量的个数    labelsLen=len(labels)    #对训练集的判定    if labelsLen!=dataSetRow:        print "训练集的特征集和标签集实例数目不等"        return    #对输入的判定    if inXCol!=dataSetCol:        print "用于测试的的实例的特征数目和训练集特征数目不等"        return    #用tile()将输入的特征向量重复成和训练集特征向量一样多的行    #变成2维,里面的维度重复1次,外面一层重复dataSetRow次    diffMat=tile(inX,(dataSetRow,1))    #减去训练集特征向量矩阵得到存偏差的矩阵    diffMat=diffMat-dataSet    #将减出来的偏差矩阵每个元素平方    sqDiffMat=diffMat**2    #对行求和,表示这个实例和这行对应的训练集实例的L2范数的平方    sqDistances=sqDiffMat.sum(axis=1)    #为了方便就不开根号(**0.5)了    #argsort()返回其从小到大排序的排序索引序列    sortIndex=sqDistances.argsort()    #空字典,用来存各个标签在前k邻居中出现的次数    classCount={}    #找前k个距离最近的,也就是排序下标从0~k-1的    for i in range(k):        #暂存第i近(从0计数)训练集实例的标签        voteIlab=labels[sortIndex[i]]        #先取字典中以这个标签为key的value值,如果没有则返回0        #加上1作为以这个标签为key的value值        classCount[voteIlab]=classCount.get(voteIlab,0)+1    #把classCount用iteritems()方法变成可迭代对象传入    #用operator.itemgetter()方法定义一个函数给参数key,这个函数按1号域排序    #将reverse参数显示修正为True,表示降序排序(找频数最大的)    sortedClassCount=sorted(                            classCount.iteritems(),            key=operator.itemgetter(1),            reverse=True)    #排序好后,第0个对象就是要找的那个频率最高的实例的[标签,频率]了    #只返回标签    return sortedClassCount[0][0]#显示训练集和输入的实例(二维特征)#(输入的特征向量,训练集特征向量矩阵,训练集标签向量,x轴名称,y轴名称,标题)def show(inX,dataSet,labels,xlab,ylab,tit):    #用shape[0]读取训练集第一维的长度(行数即训练集实例数)    dataSetRow=dataSet.shape[0]    #用shape[1]读取训练集第二维的长度(列数即特征数)    dataSetCol=dataSet.shape[1]    #读取输入向量的列数(特征数)    inXCol=len(inX)    #训练集标签向量的个数    labelsLen=len(labels)    #对训练集的判定    if labelsLen!=dataSetRow:        print "训练集的特征集和标签集实例数目不等"        return    #对维度的判定    if dataSetCol!=2:        print "特征不是两维的,暂时不能用这个方法"        return    #对输入实例的判定    if inXCol!=dataSetCol:        print "用于测试的的实例的特征数目和训练集特征数目不等"        return    #绘制窗口    plt.figure(0)    #横纵坐标名称,标题名称    plt.xlabel(xlab)    plt.ylabel(ylab)    plt.title(tit)    #需要网格    plt.grid(True)    #用来管理颜色表    idx=0    clr=['r','b','y','g','c','m']    bqzd={}    #循环画每一个点(实际上也可以传入list画一群点)    for i in range(labelsLen):        #如果这个标签没放入字典        if bqzd.get(labels[i],0)==0:            bqzd[labels[i]]=clr[idx] #放入字典            idx=(idx+1)%6 #取新的颜色表下标        #绘制这个点        plt.scatter(            dataSet[i][0],            dataSet[i][1],            color=bqzd[labels[i]],            marker='x',            label=labels[i])    #绘制给定的实例点    plt.scatter(        inX[0],        inX[1],        color='k',        marker='o',        label='Test Data')    plt.show()    return

导入模块,然后使用它:
这里写图片描述
运行结果:
这里写图片描述
在这里我使用的是L2范数距离(欧式距离)的平方,开根号不影响比较大小。

此外,为了显示点的标签,可以用:

#右上角打标注plt.legend(loc = 'upper right')

但是这个程序里是每个点画一次,不是传入相同标签的点的集和,所以直接这样用结果是这样:
这里写图片描述
所以可以去改一下for循环,只对新发现的类的那个点传入label参数:

#循环画每一个点(实际上也可以传入list画一群点)for i in range(labelsLen):    #如果这个标签没放入字典    if bqzd.get(labels[i],0)==0:        bqzd[labels[i]]=clr[idx] #放入字典        idx=(idx+1)%6 #取新的颜色表下标        #绘制这个点        plt.scatter(            dataSet[i][0],            dataSet[i][1],            color=bqzd[labels[i]],            marker='x',            label=labels[i])    else:        #绘制这个点        plt.scatter(            dataSet[i][0],            dataSet[i][1],            color=bqzd[labels[i]],            marker='x')

为了不遮挡,放在左上角:

#左上角打标注plt.legend(loc = 'upper left')

这里写图片描述

原创粉丝点击