k近邻算法学习笔记(详细)

序言

之前复习了好几次k-NN算法,总想找个时间将这段内容记录下来,这次借着整理机器学习内容的机会,将它完整的梳理一遍,希望能够让自己日后温习更加方便。

这篇笔记是按照李航老师的统计学习方法这本书梳理下来的,其中夹杂的一些例子是在学习过程中为了更好的理解算法所想出的,可能不是很恰当。另外通过参考其他资料,例如机器学习实战周志华老师的机器学习,还加入了一些别的内容,所以可能形式上有点散乱。

k-NN算法算是机器学习最基础的一部分内容。这篇笔记将详细的介绍k-NN算法的原理和python代码实现。

k近邻算法简介

k近邻法(k-nearest neighbor)是一种基本分类回归方法,也是一种很基本的机器学习方法,具有简单直观的特点。
这里我们借用李航博士在书中对k-NN的描述:给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的k个实例,这k个实例的多数属于某个类,就把该输入实例分为这个类。

k近邻法
在这里插入图片描述

k近邻分类算法代码实现(python版)

下面我们来通过一些实际问题,来更好的理解k近邻的使用

先来看一个简单的例子,假设我们买了个西瓜,现在要从以往的购买经历中,来判断我们买的这个瓜是好瓜还是坏瓜。

以往购买瓜的经历绘制成的表格

瓜的长度瓜的宽度瓜的质量
3020坏瓜
3530坏瓜
4030好瓜
4331好瓜

我们新买到的瓜的数据

瓜的长度瓜的宽度瓜的质量
4130

将数据绘制到图中
在这里插入图片描述
瓜的长度为41,宽度为30,通过k近邻来求瓜的质量

我们来分析问题,实现k近邻,首先应该获取训练数据集

import numpy as np

def createData():
    group = np.array([[30,20],[35,30],[40,30],[43,31]])
    labels = ["坏瓜", "坏瓜", "好瓜", "好瓜"]
    return group,labels

获取数据后下一步就应该实现k-NN算法了

import numpy as np
import operator

def createData():
    group = np.array([[30,20],[35,30],[40,30],[43,31]])
    labels = ["坏瓜", "坏瓜", "好瓜", "好瓜"]
    return group,labels
    
//inX表示输入实例,group表示训练数据集中属性,labels表示训练数据集中类别,k为近邻点的个数
def kNN(inX,group,labels,k):
	//先获取训练数据集数据的个数
    dataSize = group.shape[0]
    dataDiff = np.tile(inX, (dataSize, 1)) - group
    //以下几步计算训练数据集中各个点到输入实例的欧式距离
    distance = dataDiff ** 2
    distance = distance.sum(axis=1)
    distance = distance ** 0.5
    //将距离从小到大进行排列
    sortDistance = distance.argsort()
    classCount = {}
    for i in range(k):
    	//选出第i个点所属的类别
        klabel = labels[sortDistance[i]]
        //为该类别加一
        classCount[klabel] = classCount.get(klabel, 0) + 1
    //选择出k个近邻点所属类别最多的那个类
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

group, labels = createData()
print(kNN([41,30],group,labels,3))

运行结果为:

好瓜

以上实例只是一个较为简单的例子,下面我们来探讨这样一个问题

上面这个例题中我们选取的两个西瓜属性是瓜的长度和宽度,这两个属性对于瓜的好坏的影响我们在设计程序时认为是等价的,原因是这两个属性差值的范围相差不大。那么如果我们现在选取的属性为瓜的长度和瓜的重量,经过统计后绘制成表格

瓜的长度瓜的重量瓜的质量
306 .5坏瓜
357.6坏瓜
408好瓜
438.5好瓜

我们买的瓜的数据为

瓜的长度瓜的重量瓜的质量
418

将表格中的数据绘制成图像
在这里插入图片描述
我们可以看出,对于瓜的长度,训练数据集与输入实例之间的数字差值为,分别为11,6,1,2
对于瓜的重量,训练数据集与输入实例之间的数字差值为1.5,0.4,0,0.5
结合图像来看,如果我们采用欧式距离,由于瓜的长度这一属性数字差值要大于瓜的重量这一属性的数字差值,这就导致了瓜的长度对于计算结果的影响将大于瓜的质量。但我们在判断是否为好瓜时,希望这两个属性对于判断瓜的好坏时处于同等地位的,所以我们要进行归一化。

归一化将所有属性的取值范围处理为0到1之间。使用以下公式:
新的属性值=(原属性值-该属性的最小值)/(该属性的最大值-该属性的最小值)

接下来我们用代码实现

import numpy as np
import operator

def createData():
    group = np.array([[30,6.5],[35,7.6],[40,8],[43,8.5]])
    labels = ["坏瓜", "坏瓜", "好瓜", "好瓜"]
    return group,labels
    

def kNN(inX,group,labels,k):
    dataSize = group.shape[0]
    dataDiff = np.tile(inX, (dataSize, 1)) - group
    distance = dataDiff ** 2
    distance = distance.sum(axis=1)
    distance = distance ** 0.5
    sortDistance = distance.argsort()
    classCount = {}
    for i in range(k):
        klabel = labels[sortDistance[i]]
        classCount[klabel] = classCount.get(klabel, 0) + 1
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

//归一化代码
def autoNorm(group):
    minValues = group.min(0)
    maxValues = group.max(0)
    ranges = maxValues - minValues
    normDataSet = np.zeros(np.shape(group))
    dataSize = group.shape[0]
    normDataSet = group - np.tile(minValues,(dataSize,1))
    normDataSet = normDataSet / np.tile(ranges, (dataSize, 1))
    return normDataSet

group, labels = createData()
group = autoNorm(group)
print(kNN([41, 8], group, labels, 3))

运行结果:

好瓜

距离度量与k值的选择

距离度量

特征空间中两个实例点的距离是两个实例点相似程度的反应。

在以上两个问题中,我们采用的都是欧式距离。除了欧式距离外,常用的还有曼哈顿距离,切比雪夫距离等

明白了各个距离计算方法,我们就用python代码来实现一下,我们先看前面问题中计算欧式距离的代码

 	dataDiff = np.tile(inX, (dataSize, 1)) - group
    distance = dataDiff ** 2
    distance = distance.sum(axis=1)
    distance = distance ** 0.5

同理,曼哈顿距离应该为

	dataDiff = np.tile(inX,(dataSize,1)) - group
	distance = math.sqrt(dataDiff ** 2)
	distance = distance.sum(axis=1)

切比雪夫距离为

	dataDiff = np.tile(inX,(dataSize,1)) - group
	distance = distance.max(axis=1)

对于不同的问题我们采取不同的距离度量,具体哪种情况下使用,先不做过多介绍。

k值的选择

k值的选择对于k近邻算法来说至关重要,若果k值过小,预测结果将会对邻近的实例点非常敏感,如果邻近的实例点恰好是噪声,那么预测结果就非常容易出错。
下面我们从图中理解一下这个问题
在这里插入图片描述
在这个问题中,如果k取1,距离小黑点最近点是小蓝点,所以算法将判定输入实例为坏瓜。
但是我们从图中根据黑点周围的其他点可以判断出,输入实例应当是好瓜,但是由于k取值过小,将噪声学习了进来。所以k的取值不应当过小。
那如果我们将k的取值调的大一点呢,我们不妨极端一点,将k的值设为11,也就是说包含了所有的训练数据集中的点,这样的话,整个模型变得十分简单,与输入实例距离非常远的点也会被计算在内,误差就会增大。

在应用中,k值应该案取一个较小的数值,通常采用交叉验证法来选取最优k值

下面来看什么是交叉验证法
交叉验证法现将数据集划分为k个大小相似的互斥子集,每个子集使用分层抽样取得,这样就可以尽可能保持数据分布的一致性,然后每次用k-1个子集的并集作为训练集,剩下的那个子集作为测试集,然后进行k次测试。

下面是代码实现

import operator
import numpy as np
from sklearn.model_selection import StratifiedKFold
def createDataSet():
    group = np.array([[30, 10], [31, 20], [32, 30], [20, 10], [20, 20], [24, 30],
                      [50,14],[52,19],[53,36],[54,33],[24,24]])
    labels = ['good', 'good', 'good', 'good', 'good', 'good', 'bad', 'bad',
               'bad', 'bad','bad']
    return group, labels

def classify0(inX, dataSet, labels, k):
    //先计算训练集中实例的个数
    dataSetSize = dataSet.shape[0]
    //将输入实例按照实例个数进行复制,并相减
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
    //以下几步继续计算输入实例与训练集中各实例的距离
    sqDiffMat = diffMat ** 2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances ** 0.5
    //将数组中距离最短的的数据的索引从小到大排列
    sortedDistIndicies = distances.argsort()
    classCount = {}
    for i in range(k):
        //获取第i个近邻点的类别
        voteIlabel = labels[sortedDistIndicies[i]]
        //为对应的类别加一
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    //将投票结果从大到小排列
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    //选择出其中近邻点个数最多点的类别
    return sortedClassCount[0][0]
/*
本函数用来测试确定训练集,测试集和k值后经测试的错误率
trainData:训练集属性
testData:测试集属性
trainlabels:训练集结果
testlabels:测试集结果
k:选取的近邻点个数
*/
def comapre(trainData, testData, trainLabels, testLabels, k):
    falseNum = 0
    //测试集中错误的个数
    for i in range(len(testData)):
        lLabel = classify0(testData[i], trainData, trainLabels, k)
        if lLabel!=testLabels[i]:
            falseNum += 1
    //返回的是错误率
    return falseNum/len(testLabels)

/*
dataSet:属性集
labels:结果集
*/
def crossVal(dataSet, labels):
    //四折折交叉验证
    kf = StratifiedKFold(n_splits=4)
    kf.get_n_splits(labels)
    smallFalse = 10000
    //设置不同k值时的错误率之和
    best_k = 1
    //设置错误率之和最低时的k值
    //k值得我们假设从1到7里选择
    for k in range(1,7):
        print(k)
        avgFalse = 0
        //错误率之和
        for train,test in kf.split(dataSet,labels):
            falseNum = comapre(np.array(dataSet)[train], np.array(dataSet[test]), np.array(labels)[train], np.array(labels)[test], k)
            avgFalse += falseNum
        print(avgFalse)
        if smallFalse > avgFalse:
            smallFalse = avgFalse
            best_k = k
    return best_k

        
group, labels = createDataSet()
//输出结果
print(crossVal(group,labels))

运行结果为:

3

分类决策规则

k近邻方法中的分类决策规则往往是多数表决,即由输入实例的k个邻近的训练实例中的多数类决定输入实例的类。这个知识点不过多的展开

k近邻回归算法代码实现(python版)

下面我们开始用k近邻算法处理回归问题。

我们先来看看什么是分类?什么是回归?
如果我们预测的是离散值,那么此类学习任务就是分类,例如知道了瓜的长度和宽度,来判断瓜的好坏,只有好瓜和坏瓜两种可能。

如果我们预测的是连续值,那么此类学习任务就是回归,例如知道了瓜的长度和宽度,来判断瓜的价格,瓜的价格可以是20元,21元,22元

理解了两者的区别,我们来看这样一个问题,从以往购买瓜的数据中,来判断一个西瓜的价格。
这是以往购买瓜的信息绘制成的表格。

瓜的长度瓜的宽度瓜的价格
30206
30256.5
30276.8
34237
37227.5
40278
40318.5

绘制到图中:
在这里插入图片描述

import numpy as np
import operator

def createData():
    group = np.array([[30,20],[30,25],[30,27],[34,23],[37,22],[40,27],[40,31]])
    labels = [6,6.5,6.8,7,7.5,8,8.5]
    return group,labels
    

def kNN(inX,group,labels,k):
    dataSize = group.shape[0]
    dataDiff = np.tile(inX, (dataSize, 1)) - group
    distance = dataDiff ** 2
    distance = distance.sum(axis=1)
    distance = distance ** 0.5
    sortDistance = distance.argsort()
    classCount = []
    for i in range(k):
        klabel = labels[sortDistance[i]]
        classCount.append(klabel)
    moneySum = 0
    for i in range(0, len(classCount)):
        moneySum += classCount[i]
    return moneySum/len(classCount)


group, labels = createData()
print(kNN([24,24],group,labels,3))

在代码中我们选取的k值为3,我们将距离输入示例最近的三个点转化为绿色
在这里插入图片描述
将这三个点的价格进行平均,即得到了输入实例的价格
运算结果为:

6.433333333333334

该算法预测输入实例的价格为6.43元

参考

1.李航,统计学习方法,清华大学出版社
2.Peter Harrington 机器学习实战,人民邮电出版社
3.周志华,机器学习,清华大学出版社

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 算法个人学习笔记pdf是一种以电子文档形式呈现的算法学习笔记资源。在这个pdf中,个人学习者可以记录和整理自己在学习算法过程中的思考、心得和解题方法。在这个学习笔记pdf中,个人学习者可以自由地添加和编辑自己的学习内容,包括算法的原理、算法实现的代码以及相应的思路和分析。通过这种方式,个人学习者可以更系统地学习和理解算法,并且能够随时查看自己的学习进展和学习成果。 通过编写和整理算法个人学习笔记pdf,个人学习者可以更好地理解和应用学习到的算法知识。这种记录和整理的过程可以帮助个人学习者更深入地思考问题和解决问题的方法,同时也可以帮助个人学习者更好地复习和回顾已学习算法知识。 对于其他学习者来说,算法个人学习笔记pdf也是一个宝贵的学习资源。其他学习者可以通过查阅个人学习者的学习笔记pdf,借鉴和学习其中的思路和方法。这样可以帮助其他学习者更好地理解和应用算法知识,同时也可以促进知识的分享和交流。 总的来说,算法个人学习笔记pdf是一个为个人学习者提供记录和整理学习过程的工具,同时也是一个为其他学习者提供学习资源和参考的媒介。通过编写和整理算法个人学习笔记pdf,个人学习者可以更好地学习和理解算法知识,同时也可以促进算法知识的分享和交流。 ### 回答2: 算法个人学习笔记pdf是一份记录个人学习算法的文档,具有以下特点和优势。 首先,这份学习笔记是以PDF格式保存的,这意味着可以在任何设备上方便地查看和阅读,无需依赖特定的平台或软件。无论是在电脑、平板还是手机上,都可以轻松地浏览和学习。 其次,这份学习笔记是个人整理的,因此具有个性化的特点。不同的人在学习算法时可能会关注和理解的重点有所不同,通过个人学习笔记,可以反映出个人对算法知识的理解和思考。这样的学习笔记对于个人的学习和复习过程非常有帮助。 此外,这份学习笔记应当具有清晰的结构和逻辑。算法知识通常是有层次结构的,基本的知识点和概念通常是必须掌握的基础,而进阶的知识则需要在掌握基础知识的基础上构建。学习笔记应当按照这个结构和逻辑进行组织,便于学习者理解和掌握。 最后,这份学习笔记应当具有实例和练习题。算法知识的学习不能仅仅停留在理论层面,还需要通过实际的例子和练习题进行实践和巩固。学习笔记应当包含这些实例和练习题,并给出相应的解析和答案,方便学习者进行练习和巩固。 总而言之,算法个人学习笔记pdf是一份方便、个性化、结构清晰、包含实例和练习题的文档,对于学习者来说非常有价值。 ### 回答3: 算法学习笔记PDF是一份用于记录个人学习算法的文档。通过编写学习笔记,我可以对算法的理论和实践有更深入的理解和掌握。 首先,在学习算法的过程中,理论与实践结合是非常重要的。在学习笔记中,我可以记录下算法的原理和相关的数学推导,以及对应的代码实现和应用场景。通过这样的记录方式,我可以更好地理解算法的本质和使用方式。 其次,学习笔记可以帮助我回顾和巩固所学的知识。通过整理和总结学习笔记,我可以梳理出算法的基础知识和重要思想,并将其记忆固定下来。同时,学习笔记也可以作为复习的资料,提供方便快捷的回顾方式。 此外,学习笔记还可以促进自我思考和学习方法的改进。在编写笔记的过程中,我可以思考和提出自己的问题,并通过查阅相关资料和与他人讨论,来找到问题的答案和解决方案。这样的思考过程可以帮助我提高问题解决的能力和学习效果。 最后,学习笔记可以与他人分享和交流。通过分享学习笔记,我可以与其他学习者进行交流和讨论,互相学习和提高。同时,学习笔记也可以作为自己学习和成长的见证,激励自己坚持学习和进步。 总之,算法个人学习笔记PDF是一份记录、回顾、思考和分享的文档,对于个人的算法学习具有重要的意义。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值