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

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')

这里写图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值