text.html 10.4 KB
Newer Older
ToTensor's avatar
ToTensor 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
 
<p class="content_105">咖哥说:“本课要介绍的第一个算法—<span class="italic">K</span>最近邻算法,简称KNN。它的简称中也有个‘NN’,但它和神经网络没有关系,它的英文是K-Nearest Neighbor,意思是<span class="italic">K</span>个最近的邻居。这个算法的思路特别简单,就是随大流。对于需要贴标签的数据样本,它总是会找几个和自己离得最近的样本,也就是邻居,看看邻居的标签是什么。如果它的邻居中的大多数样本都是某一类样本,它就认为自己也是这一类样本。参数K,是邻居的个数,通常是3、5、7等不超过20的数字。”</p> 
<p class="content_105">“举例来说,右图是某高中选班长的选举地图,选举马上开始,两个主要候选人(一个是小冰,另一个是咖哥,他们是高中同学)的支持者都已经确定了,<span class="italic">A</span>是小冰的支持者, B则是咖哥的支持者。从这些支持者的座位分布上并不难看出,根据KNN算法来确定支持者,可靠率还是蛮高的。因为每个人都有其固定的势力范围。”</p> 
<div class="pic"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0268-0360.jpg"> 
 <p class="imgtitle">根据KNN算法来确定支持者</p> 
</div> 
<p class="content_105">小冰发问:“那么数据样本也不是选民的座位,怎么衡量距离的远和近呢?”</p> 
<p class="content_105">“好问题。”咖哥说,“这需要看特征向量在<span class="bold">特征空间中的距离</span>。我们说过,样本的特征可以用几何空间中的向量来表示。特征的远近,就代表样本的远近。如果样本是一维特征,那就很容易找到邻居。比如一个分数,当然59分和60分是邻居,99分和100分是邻居。那么如果100分是A类,99分也应是A类。如果特征是多维的,也是一样的道理。”</p> 
<p class="content_101"><img alt="" class="h-pic" src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0268-0361.jpg">咖哥发言</p> 
<p class="content">说说向量的距离。在KNN和其他机器学习算法中,常用的距离计算公式包括欧氏距离和曼哈顿距离。两个向量之间,用不同的距离计算公式得出来的结果是不一样的。</p> 
<p class="content">欧氏距离是欧几里得空间中两点间的“普通”(即直线)距离。在欧几里得空间中,点<span class="italic">x</span>=(<span class="italic">x</span><span class="sub">1</span>,…,<span class="italic">x</span><span class="sub">n</span>)和点<span class="italic">y</span>=(<span class="italic">y</span><span class="sub">1</span>,…,<span class="italic">y</span><span class="sub">n</span>)之间的欧氏距离为:</p> 
<div class="bodyPic_104"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0268-0362.jpg"> 
</div> 
<p class="content">曼哈顿距离,也叫方格线距离或城市区块距离,是两个点在标准坐标系上的绝对轴距的总和。在欧几里得空间的固定直角坐标系上,曼哈顿距离的意义为两点所形成的线段对轴产生的投影的距离总和。在平面上,点<span class="italic">x</span>=(<span class="italic">x</span><span class="sub">1</span>,…, <span class="italic">x</span><span class="sub">n</span>)和点<span class="italic">y</span>=(<span class="italic">y</span><span class="sub">1</span>,…,<span class="italic">y</span><span class="sub">n</span>)之间的曼哈顿距离为:</p> 
<div class="bodyPic_104"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0268-0363.jpg"> 
</div> 
<p class="content">这两种距离的区别,是不是像极了MSE和MAE误差计算公式之间的区别呢?其实这两种距离也就是向量的L1范数(曼哈顿)和L2范数(欧氏)的定义。</p> 
<p class="content">右图的两个点之间,1、2与3线表示的各种曼哈顿距离长度都相同,而4线表示的则是欧氏距离。</p> 
<div class="pic"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0268-0364.jpg"> 
 <p class="imgtitle">欧氏距离和曼哈顿距离</p> 
</div> 
<p class="content">下图中的两个特征,就形成了二维空间,图中心的问号代表一个未知类别的样本。如何归类呢,它是圆圈还是叉号?如果<span class="italic">K</span>=3,叉号所占比例大,问号样本将被判定为叉号类;如果<span class="italic">K</span>=7,则圆圈所占比例大,问号样本将被判定为圆圈类。</p> 
<div class="pic"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0269-0365.jpg"> 
 <p class="imgtitle">KNN算法示意</p> 
</div> 
<p class="content">因此,KNN算法的结果和K的取值有关系。要注意的是,KNN要找的邻居都是已经“站好队的人”,也就是已经正确分类的对象。</p> 
<p class="content">原理很简单,下面直接进入实战。咱们重用第4课中的案例,根据调查问卷中的数据推断客户是否有心脏病。</p> 
<p class="content">用下列代码读取数据:</p> 
<div class="content_106"> 
 <p class="content_105">import numpy as np # 导入Num Py库</p> 
 <p class="content_105">import pandas as pd # 导入Pandas库</p> 
 <p class="content_105">df_heart = pd.read_csv("../input/heart-dataset/heart.csv") # 读取文件</p> 
 <p class="content_105">df_heart.head() # 显示前5行数据</p> 
</div> 
<p class="content">那么数据分析、特征工程部分的代码不再重复(同学们可参考第4课中的代码段或源码包中的内容),直接定义KNN分类器。而这个分类器,也不需要自己做,Scikit-Learn库里面有,直接使用即可。</p> 
<p class="content">示例代码如下:</p> 
<div class="content_106"> 
 <p class="content_105">from sklearn.neighbors import KNeighbors Classifier # 导入KNN模型</p> 
 <p class="content_105">K = 5 # 设定初始K值为5</p> 
 <p class="content_105">KNN = KNeighbors Classifier(n_neighbors = K) # KNN模型</p> 
 <p class="content_105">KNN.fit(X_train, y_train) # 拟合KNN模型</p> 
 <p class="content_105">y_pred = KNN.predict(X_test) # 预测心脏病结果</p> 
 <p class="content_105">from sklearn.metrics import (f1_score, confusion_matrix) # 导入评估指标</p> 
 <p class="content_105">print("{}NN预测准确率: {:.2f}%".format(K, KNN.score(X_test, y_test)*100))</p> 
 <p class="content_105">print("{}NN预测F1分数: {:.2f}%".format(K, f1_score(y_test, y_pred)*100))</p> 
 <p class="content_105">print('KNN混淆矩阵:\n', confusion_matrix(y_pred, y_test))</p> 
</div> 
<p class="content">预测结果显示,KNN算法在这个问题上的准确率为85.25%:</p> 
<div class="content_113"> 
 <p class="content_109">5NN预测准确率: 85.25%</p> 
 <p class="content_109">5NN预测F1分数: 86.15%</p> 
 <p class="content_109">KNN混淆矩阵:</p> 
 <p class="content_110">[[24 6]</p> 
 <p class="content_110">[ 3 28]]</p> 
</div> 
<p class="content">怎么知道<span class="italic">K</span>值为5是否合适呢?</p> 
<p class="content">这里,5只是随意指定的。下面让我们来分析一下到底<span class="italic">K</span>取何值才是此例的最优选择。请看下面的代码。</p> 
<div class="content_106"> 
 <p class="content_105"># 寻找最佳K值</p> 
 <p class="content_105">f1_score_list = []</p> 
 <p class="content_105">acc_score_list = []</p> 
 <p class="content_105">for i in range(1, 15):</p> 
 <p class="content_121">KNN = KNeighbors Classifier(n_neighbors = i) # n_neighbors means K</p> 
 <p class="content_121">KNN.fit(X_train, y_train)</p> 
 <p class="content_121">acc_score_list.append(KNN.score(X_test, y_test))</p> 
 <p class="content_121">y_pred = KNN.predict(X_test) # 预测心脏病结果</p> 
 <p class="content_121">f1_score_list.append(f1_score(y_test, y_pred))</p> 
 <p class="content_105">index = np.arange(1, 15, 1)</p> 
 <p class="content_105">plt.plot(index, acc_score_list, c='blue', linestyle='solid')</p> 
 <p class="content_105">plt.plot(index, f1_score_list, c='red', linestyle='dashed')</p> 
 <p class="content_105">plt.legend(["Accuracy", "F1 Score"])</p> 
 <p class="content_105">plt.xlabel("k value")</p> 
 <p class="content_105">plt.ylabel("Score")</p> 
 <p class="content_105">plt.grid('false')</p> 
 <p class="content_105">plt.show()</p> 
 <p class="content_105">KNN_acc = max(f1_score_list)*100</p> 
 <p class="content_105">print("Maximum KNN Score is {:.2f}%".format(KNN_acc))</p> 
</div> 
<p class="content">这个代码用于绘制出1~12,不同<span class="italic">K</span>值的情况下,模型所取得的测试集准确率和F1分数。通过观察这个曲线(如下图所示),就能知道针对当前问题,<span class="italic">K</span>的最佳取值。</p> 
<div class="pic"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0270-0366.jpg"> 
 <p class="imgtitle">不同K值时,模型所取得的测试集准确率和F1分数</p> 
</div> 
<p class="content">就这个案例而言,当<span class="italic">K</span>=3时,F1分数达到89.86%。而当<span class="italic">K</span>=7或<span class="italic">K</span>=8时,准确率虽然也达到峰值88%左右,但是此时的F1分数不如K=3时高。</p> 
<p class="content">很简单吧。如果你们觉得不过瘾,想要看看KNN算法的实现代码,可以进入Sklearn官网,单击source之后,到Git Hub里面看Python源码,如下图所示。而且官网上也有这个库的各种参数的解释,课堂上不赘述了。</p> 
<div class="pic"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0271-0367.jpg"> 
 <p class="imgtitle">单击source,可以看KNN算法的实现代码</p> 
</div> 
<div class="pic"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0271-0368.jpg"> 
 <p class="imgtitle">KNN算法的实现代码</p> 
</div> 
<p class="content">KNN算法在寻找最近邻居时,要将余下所有的样本都遍历一遍,以确定谁和它最近。因此,如果数据量特别大,它的计算成本还是比较高的。</p>