01 K-近邻算法(1)

来源:互联网 发布:闪电软件下载 编辑:程序博客网 时间:2024/06/05 18:35

——《机器学习实战》

 

优点:精度高、对异常值不敏感、无数据输入假定

缺点:计算复杂度高、空间复杂度高

适用数据范围:数值型和标称型

 

原理:存在一个训练样本,样本中的每一条数据都有一个对应的标签,我们根据标签就可以知道每一条数据的类别。然后我们还有一部分没有标签的数据集,我们要做的就是将这些没有标签的数据 X跟我们的训练样本中的每一条数据进行多个特征值的对比得出一个相似度(一般是欧氏距离),我们将训练集里匹配后相似度最高的同类数据项的标签赋予我们的Xi,这样一条一条循环下去,最终所有的Xi就都完成了分类。

 

注意:一般我们将 Xi跟我们的 m项的训练集进行匹配后,只选择相似度最高的前 k项来识别数据项的标签,通常我们的 k值不会大于20

 


# -*- coding: UTF-8 -*-from numpy import  *import  numpyimport 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#kNN分类器#inX 等待被分类的特征向量#dataSet 训练集#labels 训练集对应的标签集#k 用于选择最近邻居的数目#此分类器使用的是欧氏距离def classify0(inX, dataSet, labels, k):    #计算输入向量与dataSet中的每一个单元的欧氏距离    dataSetSize = dataSet.shape[0] #计算训练集中的数据条数    diffMat = tile(inX, (dataSetSize, 1))-dataSet #将待分类向量扩展成跟训练集一样大小的矩阵,并相减    sqDiffMat = diffMat**2 #差值矩阵内每个差值再平方    sqDistances = sqDiffMat.sum(axis=1) #平方后的差值方矩阵每一行相加,合并成一个跟dateSet同维的列向量    distances = sqDistances**0.5 #再将得到的列向量进行开方,就算出了对应每一行训练集数据元的欧氏距离    #计算完毕后进行一次排序,一般以升序排序    sortedDistIndicies = numpy.argsort(distances)    classCount = {} #创建一个空dict,用于存储前K个最近邻居的label频率    #将前k个相关度最大的单元的label取出,并计算每个label出现的次数    for i in range(k):        voteIlabel = labels[sortedDistIndicies[i]]        classCount[voteIlabel] = classCount.get(voteIlabel, 0)+1    #再次进行排序,得到出现次数最多的label并将其作为结果返回    sortedClassCount = sorted(classCount.items(),                              key=operator.itemgetter(1),                              reverse=True)    return sortedClassCount[0][0]#将文件中的数据导入,并构建成矩阵,方便分类器进行计算和排序def file2matrix(filename):    #通过文件名打开文件    fr = open(filename)    #数出文件到底多少行    arrayOLines = fr.readlines()    numberOfLines = len(arrayOLines)    #根据行数,创造一个对应的零矩阵,    #另一维由于数据文件本身的特性,我们在此约定为3    returnMat = zeros((numberOfLines, 3))    #遍历文件里的每一行,进行格式化处理,将其中的信息提取出来,构建测试集    classLabelVector = []    index = 0    for line in arrayOLines :        #首先用strip函数去掉每一行中的\r        line.strip()        #然后用制表符将一行中的元素分离        listFromLine = line.split('\t')        #取出每一行中的前三位的元素,作为data单元        returnMat[index, :] = listFromLine[0:3]        #取出每一行中的最后一个元素,作为label单元        classLabelVector.append(int(listFromLine[-1]))        index += 1    #返回数据集和对应的label    return returnMat, classLabelVector#将不同数量级的特征值进行归一化,这样我们计算出的欧氏距离才更为的客观,#均衡的考虑到等权重的不同特征值对欧氏距离的影响#归一化公式为:newValue = (oldValue-min)/(max-min)def autoNorm(dataSet):    #选取每一列的最小值    minVals = dataSet.min(0)    #选取每一列的最大值    maxVals = dataSet.max(0)    #归一化底数    ranges = maxVals-minVals    #创建归一化结果矩阵    normDataSet = zeros(shape(dataSet))    # 归一化计算流程    m = dataSet.shape[0]    normDataSet = dataSet-tile(minVals, (m, 1))    normDataSet = normDataSet/tile(ranges, (m, 1))    return normDataSet, ranges, minVals#kNN分类器用于约会网站数据集def datingClassTest():    #从文本文档中导入数据    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')    #将数据进行归一化    normMat, ranges, minVals = autoNorm(datingDataMat)    #调整测试样式跟训练样本之间的占比    hoRatio = 0.05    m = normMat.shape[0]    numTestVecs = int(m*hoRatio)    #计算测试样式里的预测错误数量    errorCount = 0.0    #循环调用分类器,对前numTestVecs个样本进行分类    for i in range(numTestVecs):        classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m,:],\                                     datingLabels[numTestVecs:m], 3)        #输出分类器的预测结果和样本的真值        print("the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i]))        #如果分类器的结果错误,计数器+1        if(classifierResult != datingLabels[i]):            errorCount += 1.0    #输出分类器最终的测试失误率    print("the total error rate is : %lf" % (errorCount/float(numTestVecs)))#基于整个数据集,在控制台输入数据后,对输入的数据进行预测分类def classifyPerson():    #创建判断结果列表    resultList = ['not at all', 'in small doses', 'in large doses']    #控制台输入以下三项预测时使用的参数    percentTats = float(input("percentage of time spent playing video games?"))    ffMiles = float(input("frequent flier miles earned per year?"))    iceCream = float(input("liters of ice cream consumed per year?"))    #从文本文档导入数据集    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')    #将数据集进行归一化处理    normMat, ranges, minVals = autoNorm(datingDataMat)    #将输入的参数组成数组,然后传递给分类器进行分类预测    inArr = array([ffMiles, percentTats, iceCream])    classifierResult = classify0((inArr-minVals)/ranges, normMat, datingLabels, 3)    #输出测试结果    print("you will probably like this person : ", resultList[classifierResult-1])

下图为代码测试用的约会网站数据集


from numpy import *from os import listdirimport operator#KNN分类器def classify0(inX, dataSet, labels, k):    #计算输入向量与dataset中的每一个单元的欧氏距离    dataSetSize = dataSet.shape[0]    diffMat = tile(inX, (dataSetSize, 1))-dataSet    sqDiffMat = diffMat**2    sqDistances = sqDiffMat.sum(axis=1)    distances = sqDistances**0.5    #计算完毕后进行一次排序,一般以升序排序    sortedDistIndicies = distances.argsort()    classCount = {}    #将前k个相关度最大的单元的label取出,并计算每个label出现的次数    for i in range(k):        voteIlabel = labels[sortedDistIndicies[i]]        classCount[voteIlabel] = classCount.get(voteIlabel, 0)+1    #再次进行排序,得到出现次数最多的label并将其作为结果返回    sortedClassCount = sorted(classCount.items(),                              key=operator.itemgetter(1),                              reverse=True)    return sortedClassCount[0][0]#将图像转换成便于程序进行计算的向量def img2vector(filename):    #创建输出向量,由于图像格式为32*32,所以向量的规格为1*1024    returnVect = zeros((1,1024))    #打开图像,创建图像句柄    fr = open(filename)    #按行扫描图像,并将值存入到向量的对应位置中    for i in range(32):        lineStr = fr.readline()        for j in range(32):            returnVect[0, 32*i+j] = int(lineStr[j])    #返回结果    return  returnVect#手写数字识别测试def handwritingClassTest():    #创建标签数组    hwLabels = []    #通过系统函数将训练集的目录内容导出    trainingFileList = listdir('trainingDigits')    #得到训练集目录的条目个数    m = len(trainingFileList)    #创建训练矩阵    trainingMat = zeros((m, 1024))    #依次循环,读出每条目录带有的数字,并将整个文档转换成向量    for i in range(m):        fileNameStr = trainingFileList[i]        fileStr = fileNameStr.split('.')[0]        classNumStr = int(fileStr.split('_')[0])        hwLabels.append(classNumStr)        trainingMat[i, :] = img2vector('trainingDigits/%s' % fileNameStr)    #通过系统函数将测试集的目录内容导出    testFileList = listdir('testDigits')    #创建错误计数器    errorCount = 0.0    #得到测试集目录的条目数    mTest = len(testFileList)    # 依次循环,读出每条目录带有的数字,并将整个文档转换成向量,然后用kNN分类器,依据训练集,对文档中的数字进行预测    for i in range(mTest):        fileNameStr = testFileList[i]        fileStr = fileNameStr.split('.')[0]        classNumStr = int(fileStr.split('_')[0])        vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)        classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)        #输出预测结果和真值,并统计错误个数        print("the classifier came back with : %d, the real answer is : %d" %(classifierResult, classNumStr))        if(classifierResult != classNumStr):            errorCount += 1.0    #输出测试集测试的统计结果    print("\nthe total number of errors is : %d" % errorCount)    print("\nthe total error rate is : %lf" % (errorCount/float(mTest)))

下图为数字识别使用的数据集