KNN算法

一、算法概述
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 operator
import matplotlib.pyplot as plt
import numpy
from 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_min


def 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, k


if __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.050000
when k is 4 ,error rate is 0.040500
when k is 5 ,error rate is 0.040405
when k is 6 ,error rate is 0.040404
when k is 7 ,error rate is 0.040404
when k is 8 ,error rate is 0.040404
when k is 9 ,error rate is 0.050404
the 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)+1
dic = 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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值