KNN算法

来源:互联网 发布:摩天手无线鼠标 知乎 编辑:程序博客网 时间:2024/05/16 12:33

一、算法概述
KNN是Machine Learning领域一个简单又实用的算法,与之前讨论过的算法主要存在两点不同:

1、它是一种非参方法。即不必像线性回归、逻辑回归等算法一样有固定格式的模型,也不需要去拟合参数。
它既可用于分类,又可应用于回归。
KNN的基本思想有点类似“物以类聚,人以群分”,打个通俗的比方就是“如果你要了解一个人,可以从他最亲近的几个朋友去推测他是什么样的人”。

在分类领域,对于一个未知点,选取K个距离(可以是欧氏距离,也可以是其他相似度度量指标)最近的点,然后统计这K个点,在这K个点中频数最多的那一类就作为分类结果。比如下图,若令K=4,则?处应分成红色三角形;若令K=6,则?处应分类蓝色正方形。
这里写图片描述

二、K近邻模型
k近邻法使用的模型实际上对应于对特征空间的划分。模型由三个基本要素组成—-距离度量、k值的选择、分类决策规则决定。

2.1 距离度量
曼哈顿距离:对应特征值相减的绝对值的累和。类似一范数
这里写图片描述
欧式距离:对应特征值相减后的平方和再取根号。类似二范数
这里写图片描述

说到这里,看到一篇很好的解释范数的文章,贴在这儿,以后还会用到的。
机器学习中的范数规则化之(一)L0、L1与L2范数

余弦距离:两个向量越接近,其余弦值越小。疑问,(1,2,4)(2,4,8)这两个怎么看,余弦值为0.。。。???
这里写图片描述

2.2 k值的选择
如果选择较小的k值,相当于用较小的邻域中的训练值进行预测,“学习”的误差会比较大,预测结果会对近邻的实例点非常敏感。如果近邻点恰好是噪声呢对吧
如果选择较大的k值,优点是减小了学习中的估计误差,但学习中的近似误差会增大,什么意思呢?就是原本与输入实例较远的训练实例,也会对预测结果起作用。
实际应用中,k一般取较小值,采用交叉验证法来选择k值。
N折的交叉验证
这里写图片描述
将训练集分为5部分,然后分别将其中的4部分作为train data,另外一个作为cross validation,计算平均准确率,即对应的k=5的准确率。

代码实现

import operatorimport matplotlib.pyplot as pltimport numpyfrom numpy.lib.shape_base import tile###处理文本文件def loadDataSet(filename):    with open(filename, 'r')as fr:        for line in fr.readlines():            lineArr = line.strip().split('\t')  #            dataMat.append([float(lineArr[0]), float(lineArr[1]), float(lineArr[1])])            labelMat.append(lineArr[3])        return numpy.array(dataMat), numpy.array(labelMat)  ##把list转化为ndarrary进行计算##归一化数据,这个必须要有。使每个特征的权重一致def normalData(dataMat):    dataSetSize = dataMat.shape[0]    # train_max = max(dataMat[:, 0:2]) 这么写是不对的    train_max = dataMat.max(0)  ##max(0)列向量取最大值 ,max(1)行向量取最大值 ,输出都是一维数组    # train_min = min(dataMat[:, 0:2])    train_min = dataMat.min(0)    normaldata = (dataMat - tile(train_min, (dataSetSize, 1))) / (train_max - train_min)    return normaldata, train_max, train_mindef classify0(inX, dataMat, labelMat, k):    dataSetSize = dataMat.shape[0]    # shape是求行列数的,shape[0]表示第一维的个数,在这里表示样本个数?    diffMat = (tile(inX, (dataSetSize, 1)) - dataMat) ** 2    ##tile表示inX,随着后面shape变化,行数重复datasetsize次,列重复一次    distances = diffMat.sum(axis=1) ** 0.5    # axis =1行向量元素相加。在这里表示在一个样本里对应特征值差的平方的类和,可以看出来是用的欧氏距离    sortedDistIndeices = distances.argsort()    ##argsort()将x中的元素从小到大排列,提取其对应的index(索引),然后输出到y    classCount = {}    for i in range(k):        voteIlabel = labelMat[sortedDistIndeices[i]]   ###将距离较小的对应的类标签赋予voteIlabel        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1  ##字典key = voteIlabel ,    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)    ###选取出现的类别次数最多的类别    return sortedClassCount[0][0]##采用N折交叉验证发,测试不同的k值的错误率def errorCount(dataMat, labelMat):    hoRatio = 0.10    normaldata, train_max, train_min = normalData(dataMat)    dataSize = dataMat.shape[0]    numTest = int(hoRatio * dataSize)  ##把1000个数据样本分成10份,以其中100格样本作为cross validation    errorcount = 0.0    temp = {}    for k in range(3, 10):        for i in range(numTest):            classResult = classify0(normaldata[i, :], normaldata[numTest:dataSize, :], labelMat[numTest:dataSize], k)            if (classResult != labelMat[i]):                errorcount += 1.0        errorcount /= float(numTest)        print("when k is %i" % k, ",error rate is %f" % errorcount)        temp[errorcount] = k    #建立一个字典key = k ,value = errorcount    a = sorted(temp.items())    ##排序后是list[tuple,tuple..]    errorcount = a[0][0]    k = a[0][1]    return errorcount, kif __name__ == '__main__':    dataMat = []    labelMat = []    dataMat, labelMat = loadDataSet('knn_testdata.txt')    # x_min, x_max = dataMat[:, 0].min() - .5, dataMat[:, 0].max() + .5    # y_min, y_max = dataMat[:, 1].min() - .5, dataMat[:, 1].max() + .5    # z_min, z_max = dataMat[:, 2].min() - .5, dataMat[:, 2].max() + .5    # h = 0.01    #    # plt.scatter(dataMat[:, 0], dataMat[:, 1], dataMat[:, 2], c='r')    # plt.show()    errorcount, k = errorCount(dataMat, labelMat)    normaldata, train_max, train_min = normalData(dataMat)    input = [20000, 1.0000, 0.50000]    normal_input = (numpy.array(input) - train_min) / (train_max - train_min)    output = classify0(normal_input, normaldata, labelMat, k)    print("the k is: %i" % k, ",the error rate is :%f" % errorcount, ",the real answer is %s" % output)

运行结果

when k is 3 ,error rate is 0.050000when k is 4 ,error rate is 0.040500when k is 5 ,error rate is 0.040405when k is 6 ,error rate is 0.040404when k is 7 ,error rate is 0.040404when k is 8 ,error rate is 0.040404when k is 9 ,error rate is 0.050404the k is: 8 ,the error rate is :0.040404 ,the real answer is smallDoses

编程中遇到的问题
第14行,python中默认的list转化为numpy中ndarrary来进行计算。
第21、23行,max(0)多维数组的列向量求和,max(1)行向量求和
第29行,shape求多维数组的行列数(a,b). shape[0]求行数,shape[1]求列输
第30行,def tile(A, reps): “”“Construct an array by repeating A the number of times given by reps.重复inX,使其shape与(dataSetSize,1)一致。
第35行,argsort()将x中的元素从小到大排列,提取其对应的index(索引),然后输出到y.输出的结果是list,故list[0]就是最小的那一个。
第41行,字典的排序

a = [5,6,7,8,9,4,5,6,7,9,5]dic = {}for i in a:    dic[i] = dic.get(i,0)+1dic = sorted(dic.items(),key=lambda d:d[1],reverse=True)print(dic)dic = [(5, 3), (6, 2), (7, 2), (9, 2), (4, 1), (8, 1)]d= []for i in range(len(dic)):    d.append(dic[i][0])print(d)
原创粉丝点击