美文网首页工作生活
机器学习实战笔记-KNN(k近邻法)篇

机器学习实战笔记-KNN(k近邻法)篇

作者: 取个合情合理的名字 | 来源:发表于2019-06-30 10:27 被阅读0次

    本文是作者学习机器学习KNN的学习记录与笔记,文章会从简介、kd树算法、代码实现等几个方面展开。本文的目的是为了记录作者的学习历程以及进行总结,为之后的复习做准备。当然,如果同时能够帮组到同样在入门机器学习的小伙伴当然就更好了。

    一、简介

    k近邻法是一种基本分类与回归方法,这篇文章只讨论分类问题中的k近邻法。k近邻法的输⼊为实例的特征向量,对应于特征空间的点;输出为实例的类别,可以取多类。k近邻法假设给定⼀个训练数据集,其中的实例类别已定。分类时,对新的实例,根据其k个最近邻的训练实 例的类别,通过多数表决等⽅式进⾏预测。因此,k近邻法不具有显式的学习过程。k近邻法实际上利⽤ 训练数据集对特征向量空间进⾏划分,并作为其分类的“模型”。k值的选择、距离度量及分类决策规则是k近邻法的三个基本要素。k近邻法1968年由Cover和Hart提出。

    1、例子

    有一种兔子叫作悲伤(Grief),它们的平均身高是5050厘米,平均体重55公斤。我们拿来一百个悲伤,分别测量它们的身高和体重,画在坐标图上,用绿色方块表示。还有一种兔子呢,叫作痛苦(Agony)。它们体型比较小,平均身高是3030厘米,平均体重是44公斤。我们将一百个痛苦的身高和体重画在同一个坐标图上,用蓝色三角表示。最后一种兔子叫绝望(Despair)。它们的平均身高45厘米,但体重较轻,平均只有2.5公斤。一百只绝望的数据用黄色圆圈表示。

    image

    在这些数据中,(身高,体重) 的二元组叫做特征(features),兔子的品种则是分类标签(class label)。我们想解决的问题是,给定一个未知分类的新样本的所有特征,通过已知数据来判断它的类别。

    所以现在我们想解决的问题是,如果有一只新的兔子,我们知道它的身高和体重,然后我们想要知道它属于这三类的那一类。这个时候k邻近法就派上用场了。

    我们对兔子进行建模,使其形成关于身高体重的特征向量,然后找到离新兔子在向量空间上举例最近的k个兔子(k由自己设定),然后根据这k个兔子的类型去预测判断新兔子的类型。

    2、算法

    image

    2.1、距离度量

    image

    一般情况下,我们都是用p=2,也就是欧式距离。

    2.2、k值选择

    k值的选择会对k近邻法的结果产⽣重⼤影响。 如果选择较⼩的k值, 就相当于⽤较⼩的邻域中的训练实例进⾏预测, “学习”的近似误差 (approximation error)会减⼩,只有与输⼊实例较近的(相似的)训练实例才会对预测结果起作⽤。但缺点是“学习”的估计误差(estimation error)会增⼤,预测结果会对近邻的实例点⾮常敏感。如果邻近的实例点恰巧是噪声,预测就会出错。换句话说,k值的减⼩就意味着整体模型变得复杂,容易发⽣过 拟合。

    如果选择较⼤的k值,就相当于⽤较⼤邻域中的训练实例进⾏预测。其优点是可以减少学习的估计 误差。但缺点是学习的近似误差会增⼤。这时与输⼊实例较远的(不相似的)训练实例也会对预测起作⽤,使预测发⽣错误。k值的增⼤就意味着整体的模型变得简单。

    如果k=N,那么⽆论输⼊实例是什么,都将简单地预测它属于在训练实例中最多的类。这时,模 型过于简单,完全忽略训练实例中的⼤量有⽤信息,是不可取的。

    在应⽤中,k值⼀般取⼀个⽐较⼩的数值。通常采⽤交叉验证法来选取最优的k值。

    3、概率KNN

    简单的来说,就是结果是属于每一类的概率大小,而不是绝对的属于某一个类别。

    二、kd树算法实现

    本来打算贴上算法流程,但是看了一遍又一遍,感觉算法实在是晦涩难懂。干脆直接贴链接上例子。

    https://www.joinquant.com/view/community/detail/c2c41c79657cebf8cd871b44ce4f5d97

    三、代码实现

    现导入需要使用到的包

    
    from numpy import random
    
    from sklearn import neighbors
    
    import numpy as np
    
    import matplotlib.pyplot as plt
    
    from matplotlib.colors import ListedColormap
    
    

    生成期望、方差不同的三组随机数据

    
    x1 = random.normal(50, 6, 200)
    
    y1 = random.normal(5, 0.5, 200)
    
    x2 = random.normal(30,6,200)
    
    y2 = random.normal(4,0.5,200)
    
    x3 = random.normal(45,6,200)
    
    y3 = random.normal(2.5, 0.5, 200)
    

    对数据进行可视化

    plt.scatter(x1,y1,c='b',marker='s',s=50,alpha=0.8)
    
    plt.scatter(x2,y2,c='r', marker='^', s=50, alpha=0.8)
    
    plt.scatter(x3,y3, c='g', s=50, alpha=0.8)
    
    image

    从图中我们可以看出来,三类数据分布还是比较明显。

    之后,我们进行数据的预处理,并且给数据分类到具体标签里

    x_val = np.concatenate((x1,x2,x3))
    
    y_val = np.concatenate((y1,y2,y3))
    
    x_diff = max(x_val)-min(x_val)
    
    y_diff = max(y_val)-min(y_val)
    
    x_normalized = [x/(x_diff) for x in x_val]
    
    y_normalized = [y/(y_diff) for y in y_val]
    
    xy_normalized = zip(x_normalized,y_normalized)
    
    xy_train = list(xy_normalized)
    
    labels = [1]*200+[2]*200+[3]*200 # 三类标签,1(蓝色),2(红色),3(绿色)
    

    开始训练模型

    clf = neighbors.KNeighborsClassifier(30) # 设置k值为30, 其他默认
    
    clf.fit(xy_train, labels)
    

    进行预测

    prediction = clf.predict([(50/x_diff, 5/y_diff),(30/x_diff, 3/y_diff)])
    
    print(prediction)
    
    image

    可视化结果

    plt.scatter(x1,y1,c='b',marker='s',s=50,alpha=0.8)
    
    plt.scatter(x2,y2,c='r', marker='^', s=50, alpha=0.8)
    
    plt.scatter(x3,y3, c='g', s=50, alpha=0.8)
    
    plt.scatter(50,5, c='y',s = 50, alpha = 0.8)
    
    plt.scatter(30,3, c='y',s = 50, alpha = 0.8)
    
    image

    我们可以看到,这两个黄色的新实例,第一个确实在1(蓝色)中,第二个感觉比较模糊,在2(红色)和3(绿色)之间,并没有很明显的差别。让我们看下概率KNN.

    prediction_proba = clf.predict_proba([(50/x_diff, 5/y_diff),(30/x_diff, 3/y_diff)])
    
    print(prediction_proba)
    
    image

    我们可以看到第一个是100%的概率在分类1中,而第二个实例分别是77%和23%的概率属于分类2或者分类3.

    接下来,我们再生成一些新的实例来作为测试集,对我们的模型进行测试。

    x1_test = random.normal(50, 6, 100)
    
    y1_test = random.normal(5, 0.5, 100)
    
    x2_test = random.normal(30,6,100)
    
    y2_test = random.normal(4,0.5,100)
    
    x3_test = random.normal(45,6,100)
    
    y3_test = random.normal(2.5, 0.5, 100)
    
    xy_test_normalized = zip(np.concatenate((x1_test,x2_test,x3_test))/x_diff,\
    
                            np.concatenate((y1_test,y2_test,y3_test))/y_diff)
    
    xy_test = list(xy_test_normalized)
    
    labels_test = [1]*100+[2]*100+[3]*100
    

    来看下我们的模型分数

    score = clf.score(xy_test, labels_test)
    
    print(score)
    
    image

    可以看到我们的分数还不错,主要因为数据都是我们生成的,规律比较明显。以后还是要多用实际数据去试一试。

    之前我们提到过,不同的k值会导致结果的不同。接下来我们就想看一下不同的k值,结果有什么不同,哪一个k值得结果最好。

    record = []
    
    for i in range(1,31):
    
        clf = neighbors.KNeighborsClassifier(i)
    
        clf.fit(xy_train, labels)
    
        score = clf.score(xy_test, labels_test)
    
        record.append(score)
    
        print("when k = %i, score = %f"%(i,score))
    
    image
    k = []
    
    for i, value in enumerate(record):
    
        if  value == np.max(record):
    
              k.append(i+1)
    
    print("when k = ", k)
    
    print("the score is the highest = ", np.max(record))
    
    image

    从结果我们可以看到,最高的分数就是98.67%,很多种不同的k的选择都会有这一个结果。这个时候我们是不是应该用较为小的k值呢,去降低算法复杂度,加快预算时间。

    参考:

    李航博士《统计学习方法》

    https://www.joinquant.com/view/community/detail/a98b7021e7391c62f6369207242700b2

    https://www.joinquant.com/view/community/detail/c2c41c79657cebf8cd871b44ce4f5d97

    https://www.joinquant.com/view/community/detail/bb850ee76d1cae16cc587f29c4439ebd

    相关文章

      网友评论

        本文标题:机器学习实战笔记-KNN(k近邻法)篇

        本文链接:https://www.haomeiwen.com/subject/iyivcctx.html