机器学习实战第2章-K近邻算法(KNN)

一、K近邻算法概念:

找一个输入数据周围K个样本里大多数的类别,则输入数据就是这个类别。

二:优缺点

优点:
1.简单,易于理解,易于实现,无需估计参数,无需训练;
2.适合对稀有事件进行分类;
3.特别适合于多分类问题(multi-modal,对象具有多个类别标签), kNN比SVM的表现要好。

缺点:
1.该算法在分类时有个主要的不足是,当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的K个邻居中大容量类的样本占多数。 该算法只计算“最近的”邻居样本,某一类的样本数量很大,那么或者这类样本并不接近目标样本,或者这类样本很靠近目标样本。无论怎样,数量并不能影响运行结果。
2.惰性算法,计算量较大,因为对每一个待分类的文本都要计算它到全体已知样本的距离,才能求得它的K个最近邻点。时间和空间复杂度都高。
3.可理解性差,无法给出像决策树那样的规则。

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

三、算法步骤:

输入数据与样本数据集合里的每个样本计算距离,设置一个整数K,从距离里找出前K个,里面最多的类别就是输入数据的类别
1)计算测试数据与各个训练数据之间的距离;
2)按照距离的递增关系进行排序;
3)选取距离最小的K个点;
4)确定前K个点所在类别的出现频率;
5)返回前K个点中出现频率最高的类别作为测试数据的预测分类。

注:一般距离公式可采用欧式距离,不过不同情景有其他的距离公式选择:

欧氏距离和余弦距离各自有不同的计算方式和衡量特征,因此它们适用于不同的数据分析模型:
1.欧氏距离能够体现个体数值特征的绝对差异,所以更多的用于需要从维度的数值大小中体现差异的分析,如使用用户行为指标分析用户价值的相似度或差异。
2.余弦距离更多的是从方向上区分差异,而对绝对的数值不敏感,更多的用于使用用户对内容评分来区分兴趣的相似度和差异,同时修正了用户间可能存在的度量标准不统一的问题(因为余弦距离对绝对数值不敏感)。

四、常见问题(此部分转载自网络)

1、k值设定为多大?

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

2、类别如何判定最合适?

投票法没有考虑近邻的距离的远近,距离更近的近邻也许更应该决定最终的分类,所以加权投票法更恰当一些。

3、如何选择合适的距离衡量?

高维度对距离衡量的影响:众所周知当变量数越多,欧式距离的区分能力就越差。
变量值域对距离的影响:值域越大的变量常常会在距离计算中占据主导作用,因此应先对变量进行标准化。

4、训练样本是否要一视同仁?

在训练集中,有些样本可能是更值得依赖的。
可以给不同的样本施加不同的权重,加强依赖样本的权重,降低不可信赖样本的影响。

5、性能问题?

kNN是一种懒惰算法,平时不好好学习,考试(对测试样本分类)时才临阵磨枪(临时去找k个近邻)。
懒惰的后果:构造模型很简单,但在对测试样本分类地的系统开销大,因为要扫描全部训练样本并计算距离。
已经有一些方法提高计算的效率,例如压缩训练样本量等。

6、能否大幅减少训练样本量,同时又保持分类精度?

浓缩技术(condensing)
编辑技术(editing)

机器学习实战里的主要代码部分加了点注释
from numpy import *
from os import listdir
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
#knn,inX测试集,dataSet训练集 ,labels训练集标签,k
def classify0(inX,dataSet,labels,k):
    #返回第一维度长度
    dataSetSize=dataSet.shape[0]
    #inX 列复制1次,行dataSetSize(4)次
    #  array([[0, 0],
    #         [0, 0],
    #         [0, 0],
    #         [0, 0],])
    #然后减去dataset
    diffMat=tile(inX,(dataSetSize,1))-dataSet
    #平方
    sqDiffMat=diffMat**2
    #axis=1就是将一个矩阵每一行相加
    sqDistances=sqDiffMat.sum(axis=1)
    distances=sqDistances**0.5
    #返回排序以后的索引值 从小到大
    sortedDistIndicies=distances.argsort()
    classCount={}
    #for循环在训练集里找3个离它最近的点,看他们分类是什么的多 312
    for i in range(k):
        #找到最近k个点的分类
        voteIlabel=labels[sortedDistIndicies[i]]
        #从字典里找,找不到就放里这个分类,并且数目+1;有,直接加一
        classCount[voteIlabel]=classCount.get(voteIlabel,0)+1
    #按照字典中第一个位置的值降序排序,找数目最多的分类
    # Python3.5中:iteritems变为item
    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列的0矩阵
    returnMat=zeros((numberOfLines,3))
    classLabelVector=[]
    index=0
    for line in arrayOLines:
        line=line.strip()
        listFromLine=line.split('\t')
        #复制进returnMat
        returnMat[index,:]=listFromLine[0:3]
        #存储下最后一列的数据,这里是类型
        classLabelVector.append(int(listFromLine[-1]))
        index+=1
    return returnMat,classLabelVector
#归一化,消除某些大数据的影响
def autoNorm(dataSet):
    #min(0)是从没列开始找最小
    minVals=dataSet.min(0)
    maxVals=dataSet.max(0)
    ranges=maxVals-minVals
    normDataSet=zeros(shape(dataSet))
    #读取e的第一维度长度,使用shape[0],获取行数
    m=dataSet.shape[0]
    #minVals行复制m次,列1次
    normDataSet=dataSet-tile(minVals,(m,1))
    #在这里是具体特征值相除,矩阵对应位置;而不是两个矩阵相除
    normDataSet=normDataSet/tile(ranges,(m,1))
    return normDataSet,ranges,minVals
#分类器针对约会网站的测试代码
# def datingClassText():
#     hoRatio=0.10
#     datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
#     normMat,ranges,minVals=autoNorm(datingDataMat)
#     m=normMat.shape[0]
#     #numTestVecs为测试集数目
#     numTestVecs=int(m*hoRatio)
#     errorCount=0.0
#     for i in range(numTestVecs):
#          #normMat一行一行循环作为测试集;训练集用从测试集的行数开始到结尾;标签;K
#          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]))
#          if (classifierResult!=datingLabels[i]):
#            errorCount+=1.0
#     print("the total error rate is: %f" %(errorCount/float(numTestVecs)))
#     #print ("the errorCount is: %f" %errorCount)

def datingClassTest():
    hoRatio = 0.10      #hold out 10%
    datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')       #load data setfrom file
    normMat, ranges, minVals = autoNorm(datingDataMat)
    m = normMat.shape[0]
    #     #numTestVecs为测试集数目
    numTestVecs = int(m*hoRatio)
    errorCount = 0.0
    for i in range(numTestVecs):
        #          #normMat一行一行循环作为测试集;训练集用从测试集的行数开始到结尾;标签;K
        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]))
        if (classifierResult != datingLabels[i]):
             errorCount += 1.0
    print ("the total error rate is: %f" % (errorCount/float(numTestVecs)))
    print ("the errorCount is: %f" %errorCount)
#约会网站预测函数
def classifyPerson():
    resultList=['not at all','in small doses','in large doses']
    #py3 raw_input 是input
    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 years?"))
    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])
#2.3
#把图像处理成vector
def img2vector(filename):
    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)
    #创建一个m行1024列的矩阵,每行存储一个图像
    trainingMat=zeros((m,1024))
    #从文件名解析类别数据
    for i in range(m):
        fileNameStr=trainingFileList[i]
        #按照.截取,取第0个
        fileStr=fileNameStr.split('.')[0]
        #按照_截取,取第0个
        classNumStr=int(fileStr.split('_')[0])
        hwLabels.append(classNumStr)
        trainingMat[i,:]=img2vector('trainingDigits/%s'%fileNameStr)

    testFileList=listdir('testDigits')
    errorCount=0.0
    mTest=len(testFileList)
    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 error is: %d" % errorCount)
    print("\nthe total error rate is: %f" % (errorCount/float(mTest)))
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值