机器学习python实战----k近邻算法

  前一周学习了一些理论的知识,虽然懂了原理,但感觉真要写起代码来还是摸不着头脑,所以接下来的几天都打算在代码方面下一番功夫。由于接触python不久,且能力有限,难免有些错误的或者理解不到位的地方,希望读者不吝指正!

K-近邻算法(k-NN)的基本原理:存在一个样本数据集合,也叫训练样本集,并且样本集中的每个数据都存在标签,就是我们知道每个数据与所属分类的对应关系。输入没有标签的新数据后,我们将新数据的特征跟样本集中每个数据对应的特征进行比较,选择特征最相似的(最近邻)的分类标签认为是新数据的标签。一般来说我们只选择样本数据集中前k个最相似的数据,这就是k-近邻的出处。

  接下来我们进入代码实战,首先用一个简单的kNN的例子来上手:

第一步,我们创建一个小的数据集,python代码如下。这是带有标签的四个数据点

from numpy import *
import operator

def createDataSet():
    group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
    labels = ['A','A','B','B']
    return group,labels

 

有了数据集之后,我们接着来对新数据进行分类,看代码

def classify0(Inx,dataSet,labels,k):
    dataSetSize = dataSet.shape[0] #.shape[0]表示dataSet的行数
    diffMat = tile(Inx,(dataSetSize,1))-dataSet #某个点与数据集中点的x和y的坐标差
    sqdiffmat = diffMat**2
    sumdiffmat = sqdiffmat.sum(axis=1) #对sqdiffmat的行求和即(x-x1)^2+(y-y1)^2
    dis = sumdiffmat**0.5  #求平方根即距离
    sortdis = dis.argsort()
    classDict = {}  #定义一个存储字典
    for i in range(k):   
        keylabels = labels[sortdis[i]] 
        classDict[keylabels] = classDict.get(keylabels,0)+1
    sortclassDict = sorted(classDict.items(),key = operator.itemgetter(1),reverse = True)
                       #.items()方法是将classDict这个存储字典转化为list,operator.itemgetter(1)是按classDict的第一项排序
                       #reverse = True表示降序排列即从大到小
    return sortclassDict[0][0]

 这个函数classify0有四个输入参数,Inx表示分类的输入向量,dataSet表示训练数据集,labels表示标签向量,k表示取前k个最相似(最相近)的向量。这个函数是计算输入向量与训练数据集的欧式距离,然后对距离进行排序并选择前k个最小的距离,对这些类别进行统计,返回类别多的标签作为新数据的标签。classDict是定义的一个空的存储字典,.argsort()函数是对距离进行排序并返回从小到大排列的序号,注意,这里返回的序号就是已经排列好的(从小到大),所以在后面取sortdis[i]的时候就是前k个最小距离了。这个问题要注意一下,我这里想了很久才想明白,所以有必要提醒一下。之后的sorted()函数是按统计的标签数的多少降序排列,所以sortclassDict[0][0]就是统计标签数最多的类别了。

有了这两个程序我们就可以进行测试啦,结果如图,测试(1,1)这点属于'A'类

做完这个小例子,接下来我们就要对文件数据进行操作,首先需要从文本中解析数据,这样的一个文本dataSet_kNN.txt中存在1000行、4列数据,前三列为特征向量,最后一列为标签。我们需要将特征向量和标签分开放入两个矩阵中,代码如下:

def file2mat(filename):
    fr = open(filename)
    listoffile = fr.readlines()   #一次性读取文本的所有内容并以list形式返回
    numoflines = len(listoffile)  #得到文件的行数
    newfacMat = zeros((numoflines,3))#创建一个行数*3的零矩阵
    newclassVec = []             #创建一个类的向量
    index=0
    for line in listoffile:
        line = line.strip()  #strip函数用来截断所有的回车符
        listfromline = line.split('\t')#以'\t'进行分割
        newfacMat[index,:] = listfromline[0:3]
        newclassVec.append(int(listfromline[-1]))
        index+=1
    return newfacMat,newclassVec

一些函数的功能我注释出来了,可以自己先new一个短的txt文件去测试这些函数,确保功能正常。运行结果如下:

我们可以根据特征向量画出散点图,这里是特征向量第0列和第1列。

下面进行数据的归一化。这里就是我们之前在理论部分说的归一化是一个道理,不能让特征向量之间的差值太大而影响我们的结果,所以要进行归一化,公式也跟之前一样:newValue=(value-min)/(max-min)。代码如下:

def autoNorm(dataSet):
    valueMax = dataSet.max(0)
    valueMin = dataSet.min(0)
    Value_range = valueMax-valueMin
    normdataSet = zeros(shape(dataSet))
    m = dataSet.shape[0]
    normdataSet = dataSet-tile(valueMin,(m,1))
    normdataSet = normdataSet/tile(Value_range,(m,1))
    return normdataSet,Value_range,valueMin

到这里我们的kNN算法需要的函数就完了,下面介绍一个评价该算法的方法。这里将样本数据分为训练数据集和测试数据集(8:2),错误率定义为 预测错误的总数/测试样本总数

def datingSetTest():
    h = 0.2
    newfacMat,newclassVec = file2mat(r'dataSet_kNN.txt')
    normMat,value_range,value_min = autoNorm(newfacMat)
    m = normMat.shape[0]
    numTestData = int(m*h)  #测试向量的总数
    error_cnt=0
    for i in range(numTestData):
        result = classify0(normMat[i,:],normMat[numTestData:m,:],newclassVec[numTestData:m],5)
        print('the result is %d,the real answer is %d\n'%(result,newclassVec[i]))
        if result!=newclassVec[i]:
            error_cnt+=1
    print('the error rate is %f\n'%(error_cnt/float(numTestData)))

前百分之二十的数据作为测试样本,当然这里的测试样本是需要随机选取的,由于文本文件中就是随机的,我们这里取前20%,剩下的作为训练样本。k取5。如果预测的标签跟实际的标签不符合,就令预测错误的总数+1。

结果为

这里的错误率为8.6%。

k-NN算法到这里就介绍的差不多了,说简单也不简单,说复杂好像也没多少步骤,最关键的是自己要去想,要去做,才能更好的掌握!

 

这里贴上代码及文本文件的下载链接 http://pan.baidu.com/s/1o7Pi9JG

posted @ 2017-10-27 21:19  嗜血的草  阅读(623)  评论(0编辑  收藏  举报