K-NN近邻算法

                        **小白的搬运python学习**
                              **K-NN近邻算法**

k邻近算法
 K-NN近邻法是一种基本分类与回归方法
工作原理是:存在一个样本数据集合,也称作为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一个数据与所属分类的对应关系。输入没有标签的新数据后,将新的数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。


KNN近邻算法的原理和判断的核心
 没错,就是距离度量。
  为了便于理解,我先讲最邻近算法,再引入K-NN算法。
如图所示为了判断(101,20)是属于什么电影分类,我们会很自然的想到离哪个标准点近就属于哪种电影。因此我们会使用高中学习到的两点间距离公式来判断距离量度的大小。
在这里插入图片描述通过计算,我们可以得到如下结果:
 (101,20)->动作片(108,5)的距离约为16.55
 (101,20)->动作片(115,8)的距离约为18.44
 (101,20)->爱情片(5,89)的距离约为118.22
 (101,20)->爱情片(1,101)的距离约为128.69
 通过计算可知,红色圆点标记的电影到动作片 (108,5)的距离最近,为16.55。如果算法直接根据这个结果,判断该红色圆点标记的电影为动作片,这个算法就是最近邻算法,而非K-NN近邻算法。那么K-NN近邻算法是什么呢?


KNN近邻算法步骤如下:
1.计算已知类别数据集中的点与当前点之间的距离;
2.按照距离递增次序排序;
3.选取与当前点距离最小的k个点;
4.确定前k个点所在类别的出现频率;
5.返回前k个点所出现频率最高的类别作为当前点的预测分类。
距离量度
常见的两种距离量度

  • 曼哈顿距离
    在这里插入图片描述

  • 欧式距离

在这里插入图片描述


优点

  • 精度高

  • 对异常值不敏感(可以这么理解,就一组数据中有一个异常值,但是我们k取一万,这个异常值对整体的影响很小了)

  • 无数据输入输出假定(不像朴素贝页斯算法假设样本独立并符合高斯分布,对输入假设)

缺点

  • 计算复杂度高

  • 空间复杂度高
    (在算距离量度时,测试样本要和每个训练样本计算空间距离,每一个点都要算,这个计算量就很大了)

适合

  • 数值型数据(身高这种)

  • 标称型数据(男生女生)


实现

  • First

 既然我们已经了解KNN算法的原理,那我们就先使用KNN算法来判断上面例子的电影分类吧

import operator
import numpy as np
import matplotlib.pyplot as plt
"""
函数说明:创建数据集
group 二维训练数据集(对应x,y轴的数据)
labels 对应训练集的标签
"""
def createDataSet():
    #四组数据的特征
    group = np.array([[1,101],[5,89],[108,5],[115,8]])
    #四组数据的标签
    labels = np.array(['爱情片','爱情片','动作片','动作片' ])
    return group,labels

"""
函数说明:kNN分类器
inX        输入的测试样本
dataSet    训练样本集
labels     标签
k          近邻个数


"""
def KnnClassify(inX, dataSet, labels, k):
    #numpy函数shape[0]返回dataSet的行数
    #numpy函数shape[1]返回dataSet的列数
    dataSetSize = dataSet.shape[0]
    #np.tile函数作用是按照某个方向复制元素,我们inX定为[101,20]  dataSetSize=4
    #np.tile([101,20],(4,1))就是将[101,20]在行方向上复制4行,在列方向上复制1行
    # 转化为           -       dataSet      =       diffMat
    #  ||101 20|             |[1,101]             |[100,-81]
    #   |101 20|              [5,89]               [96,-69]
    #   |101 20|              [108,5]              [-7,15]
    #   |101 20||             [115,8]|             [-14,12]|
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
    #平方          sqDiffMat
    #             ||10000 6561|
    #              |9216  4761|
    #              |49    225|
    #              |196   144||
    sqDiffMat = diffMat ** 2
    # sum()所有元素相加,sum(0)列相加,sum(1)行相加
    #sqDiffMat
    #|16561 13977 274 340|
    sqDistances = sqDiffMat.sum(axis=1)
    # 开方,计算出距离
    #得出测试样本和每个训练样本的欧氏距离
    # |x0 x1 x2 x3|
    distances = sqDistances ** 0.5
    #返回distances中元素从小到大排序后的索引值
    #排序算法 返回索引sortedDistIndices[2 3 1 0]
    sortedDistIndices = distances.argsort()
    # 定一个记录类别次数的字典
    classCount = {}
    for i in range(k):
        # 取出前k个元素的类别
        voteIlabel = labels[sortedDistIndices[i]]
        # dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。
        # 计算类别次数
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    # python3中用items()替换python2中的iteritems()
    # key=operator.itemgetter(1)根据字典的值进行排序
    # key=operator.itemgetter(0)根据字典的键进行排序
    # reverse降序排序字典
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    # 返回次数最多的类别,即所要分类的类别
    return sortedClassCount[0][0]
'''
函数说明:主函数
'''
if __name__=='__main__':
    #创建数据集
    group, labels = createDataSet()
    #测试集
    test = [101,20]
    test_class = KnnClassify(test, group, labels, 3)
    #打印分类结果
    print(test_class)
    if(test_class == '动作片'):
        plt.scatter(101,20,color='r', marker='*')
    else:
        plt.scatter(101,20,color='g', marker='+')
    # 对于类别为动作片的数据集我们使用红色六角形表示
    plt.scatter(group[labels == '动作片', 0], group[labels == '动作片', 1], color='r', marker='*')
    # 对于类别为爱情片的数据集我们使用绿色加号表示
    plt.scatter(group[labels == '爱情片', 0], group[labels == '爱情片', 1], color='g', marker='+')
    plt.show()

在这里插入图片描述
在这里插入图片描述

 因此根据代码运行的结果,我们可以得到我们要预测的点是动作片

second

1.创建数据集

import numpy as np
import matplotlib.pyplot as plt
"""
函数说明:创建数据集
group 二维训练数据集(对应x,y轴的数据)
labels 对应训练集的标签
"""
def createDataSet():
    group = np.array([[1.0,2.0],[1.2,0.1],[0.1,1.4],[0.3,3.5],[1.1,1.0],[0.5,1.5]])
    labels = np.array(['A','A','B','B','A','B'])
    return group,labels
if __name__=='__main__':
    group, labels = createDataSet()
    #对于类别为A的数据集我们使用红色六角形表示
    plt.scatter(group[labels=='A',0],group[labels=='A',1],color = 'r',marker= '*')
    #对于类别为B的数据集我们使用绿色六角形表示
    plt.scatter(group[labels == 'B', 0], group[labels == 'B', 1], color='g', marker='+')
    plt.show()

在这里插入图片描述
其实对于group和labels的关系,就是[1.0,2.0]这个数据对应的类别是’A’

对于代码中的小问题的详细解答

(1)引入numpy库Numpy
由于机器学习算法在数据处理过程中大都涉及线性代数的知识,需要用到矩阵操作,Python本身没有处理矩阵的数据类型,因此需要使用附加的函数库。其中NumPy函数库是Python开发环境的一个独立模块,是Python的一种开源的数值计算扩展工具,是处理数值计算最为基础的类库。这种工具可以用来存储和处理大型多维矩阵,比Python自身的列表结构要高效的多。尽管Python的list类型已经提供了类似于矩阵的表示形式,但是NumPy提供了更多的科学计算函数。NumPy被定位为数学基础库,属于比较底层的Python库,其地位趋向于成为一个被其它库调用的核心库。
导入方式如下: import numpy as np
(2)对于if__name__==‘main’:的解释
详细解释请看这位博主的链接

2.(基于欧拉公式)实现属于我们的KNN分类器
到这里,也许有人会疑惑上面例子中的特征是2维的,这样的距离度量可以用两点距离公式计算,但是如果是更高维的呢?对,没错。我们可以用欧氏距离(也称欧几里德度量)
在这里插入图片描述
而我们高中所学的两点间距离公式就是欧氏距离在n=2时的情况

****代码如下****
import numpy as np
import operator
import matplotlib.pyplot as plt
"""
函数说明:创建数据集
group 二维训练数据集(对应x,y轴的数据)
labels 对应训练集的标签
"""
def createDataSet():
    group = np.array([[1.0,2.0],[1.2,0.1],[0.1,1.4],[0.3,3.5],[1.1,1.0],[0.5,1.5]])
    labels = np.array(['A','A','B','B','A','B'])
    return group,labels


"""
函数说明:KNN分类器
"""


def classify0(inX, dataSet, labels, k):
    # numpy函数shape[0]返回dataSet的行数
    dataSetSize = dataSet.shape[0]
    # 在列向量方向上重复inX共1次(横向),行向量方向上重复inX共dataSetSize次(纵向)
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
    # 二维特征相减后平方
    sqDiffMat = diffMat ** 2
    # sum()所有元素相加,sum(0)列相加,sum(1)行相加
    sqDistances = sqDiffMat.sum(axis=1)
    # 开方,计算出距离
    distances = sqDistances ** 0.5
    # 返回distances中元素从小到大排序后的索引值
    sortedDistIndices = distances.argsort()
    # 定一个记录类别次数的字典
    classCount = {}
    for i in range(k):
        # 取出前k个元素的类别
        voteIlabel = labels[sortedDistIndices[i]]
        # dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。
        # 计算类别次数
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    # python3中用items()替换python2中的iteritems()
    # key=operator.itemgetter(1)根据字典的值进行排序
    # key=operator.itemgetter(0)根据字典的键进行排序
    # reverse降序排序字典
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    # 返回次数最多的类别,即所要分类的类别
    return sortedClassCount[0][0]


if __name__ == '__main__':
    # 创建数据集
    group, labels = createDataSet()
    # 测试集
    test = [1.0,2.1]
    # kNN分类
    test_class = classify0(test, group, labels, 3)
    # 打印分类结果
    print(test_class)
    test = [0.4, 2.0]
    # kNN分类
    test_class = classify0(test, group, labels, 3)
    # 打印分类结果
    print(test_class)

在这里插入图片描述
总结
分类器一定是正确的吗?答案是否定的,分类器并不会得到百分百正确的结果,我们可以使用多种方法检测分类器的正确率。此外分类器的性能也会受到多种因素的影响,如分类器设置和数据集等。不同的算法在不同数据集上的表现可能完全不同。为了测试分类
器的效果,我们可以使用已知答案的数据,当然答案不能告诉分类器,检验分类器给出的结果是否符合预期结果。通过大量的测试数据,我们可以得到分类器的错误率-分类器给出错误结果的次数除以测试执行的总数。错误率是常用的评估方法,主要用于评估分类器在某个数据集上的执行效果。完美分类器的错误率为0,最差分类器的错误率是1.0。同时,我们也不难发现,k-近邻算法没有进行数据的训练,直接使用未知的数据与已知的数据进行比较,得到结果。因此,可以说k-近邻算法不具有显式的学习过程。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值