机器学习---4.k-近邻(knn)

k-近邻算法也算是分类模型中比较简单的算法之一,把它的思想简单概述一下就是测量不同特征值之间的距离由此进行分类,现在有个大概的思想再来讲原理和实现。

k-近邻算法

knn的优缺点:

优点精度高,对异常值不敏感……
缺点计算的复杂度高

关于距离的计算问题
我们平时是如何计算距离的呢?根据两点之间直线最短的原理,那就连一条直线去测量就好了,这就是我们最常用的度量方法,也叫欧式距离
在这里插入图片描述
比如求A,B之间的距离那我们就用
在这里插入图片描述
这个公式就可以求出任意n维空间点之间的欧氏距离。
我们计算knn就还是用欧氏距离就好,关于其他几种距离的度量可以去看看慕课的这篇文章,讲的非常清楚
https://www.imooc.com/article/67203

思想: 有了不同特征值之间的距离,那如何做到分类,这就需要用到一个很基本的思想了—距离近的总是一类的,也可以说一个圈子的是一类人。
实现的流程:

  1. 计算测试数据与各个训练数据之间的距离
  2. 对计算得到的距离进行排序
  3. 选取距离最小的k个点
  4. 确定这k个点所代表的类别
  5. 返回这k个点中频率最高的类别作为测试数据的类别

关于k的选择问题
因为我们很重要的一步就是选取离测试值最近的k个点,所以k的选择就尤为重要,如果k取的太小,那就有可能受到数据噪音的干扰导致测试数据分类错误;如果k取的特别大(全部训练样本数),那就只会取到样本数目最多的类别。所以我们一般选择k值的时候会利用交叉验证,评估一系列k值从而选择出最好的k值。

knn的代码实现

def knn(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]  # shape读取数据矩阵第一维度的长度
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet  # tile重复数组inX,有dataSet行 1个dataSet列,减法计算差值
    sqDiffMat = diffMat ** 2
    sqDisttances = sqDiffMat.sum(axis=1)  # 普通sum默认参数为axis=0为普通相加,axis=1为一行的行向量相加
    distances = sqDisttances ** 0.5
    sortedDistIndicies = distances.argsort()  # argsort返回数值从小到大的索引值(数组索引0,1,2,3)
    # 选择距离最小的k个点
    classCount = {}
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]  # 根据排序结果的索引值返回靠近的前k个标签
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1  # 各个标签出现频率
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)  # 排序频率
    # sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list。
    # reverse默认升序 key关键字排序itemgetter(1)按照第一维度排序(0,1,2,3)
    return sortedClassCount[0][0]  # 找出频率最高的

来个简单的数据集检验一下

import numpy as np
import operator

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

group,labels=createData()
print("预测点的类别为:",classify0([1.0,1.2],group,labels,3))

在这里插入图片描述

来个被用烂透了的实战例子—约会网站数据判断是否喜欢
先来看看数据
在这里插入图片描述
前三列分别是飞行里数,游戏时间占比,吃冰淇淋的升数,最后一列是喜欢程度,有三类:不喜欢,一般喜欢,非常喜欢;
然后我们需要把最后一列换成1,2,3这样的数字去代替喜欢程度,因为只有数字才是计算机能够识别的。

读取数据

def file2matrix(filename):
    fr = open(filename)
    arrayOLines = fr.readlines()
    numberOfLines = len(arrayOLines)  # 读出数据行数
    returnMat = np.zeros((numberOfLines, 3))  # 创建返回矩阵
    classLabelVector = []
    index = 0
    for line in arrayOLines:
        line = line.strip()  # 删除空白符
        listFromLine = line.split('\t')  # split指定分隔符对数据切片
        returnMat[index, :] = listFromLine[0:3]  # 选取前3个元素(特征)存储在返回矩阵中
        classLabelVector.append(int(listFromLine[-1]))
        # -1索引表示最后一列元素,位label信息存储在classLabelVector
        index += 1
    return returnMat, classLabelVector
datamat,datalabels=file2matrix('datingTestSet2.txt')
print(datamat,'\n',datalabels)

在这里插入图片描述
对这些数据画个散点图可视化一下

fig=plt.figure()
ax=fig.add_subplot(111)
ax.scatter(datamat[:,1],datamat[:,2],15.0*np.array(datalabels),15.0*np.array(datalabels))
plt.xlabel('玩游戏的时间占比')
plt.ylabel('每周消费的冰淇淋数量')
plt.show()

在这里插入图片描述
现在实现给数据就能判断喜欢程度

# 归一化特征值
# 归一化公式  :(当前值-最小值)/range
def autoNorm(dataSet):
    minVals = dataSet.min(0)  # 存放每列最小值,参数0使得可以从列中选取最小值,而不是当前行
    maxVals = dataSet.max(0)  # 存放每列最大值
    ranges = maxVals - minVals
    normDataSet = np.zeros(np.shape(dataSet))  # 初始化归一化矩阵为读取的dataSet
    m = dataSet.shape[0]  # m保存第一行
    # 特征矩阵是3x1000,min max range是1x3 因此采用tile将变量内容复制成输入矩阵同大小
    normDataSet = dataSet - np.tile(minVals, (m, 1))
    normDataSet = normDataSet / np.tile(ranges, (m, 1))
    return normDataSet, ranges, minVals



# 测试约会网站分类结果代码
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 = int(m * hoRatio)
    errorCount = 0.0
    for i in range(numTestVecs):
        classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 3)
        print("分类器返回的值: %s, 正确的值: %s" % (classifierResult, datingLabels[i]))
        if (classifierResult != datingLabels[i]): errorCount += 1.0
    print("总的错误率是: %f" % (errorCount / float(numTestVecs)))
    print("错误的个数:%f" % errorCount)



# 完整的约会网站预测:给定一个人,判断时候适合约会
def classifyPerson():
    resultList = ['不喜欢', '一般喜欢', '特别喜欢']
    percentTats = float(input("玩游戏占的百分比"))
    ffMiles = float(input("每年坐飞机多少公里"))
    iceCream = float(input("每年吃多少公升的冰淇淋"))
    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    inArr = np.array([ffMiles, percentTats, iceCream])
    classifierResult = classify0((inArr - minVals) / ranges, normMat, datingLabels, 3)

    print("你将有可能对这个人是:", resultList[int(classifierResult) - 1])

datingClassTest() #分类器针对约会网站的测试代码

在这里插入图片描述

classifyPerson()  # 约会网站预测

在这里插入图片描述

结束

其实knn实现起来很简单,计算欧氏距离—>对各个距离排序—>取前k个距离的值所代表的类—>根据取出的类中最多的类别作为自己的类别。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shelgi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值