KNN算法初试

一、KNN算法介绍

KNN的全称是K Nearest Neighbors,意思是K个最近的邻居,从名字我们可以看出K的取值是至关重要的。那么最近的邻居又是怎么回事呢?其实啊,KNN的原理就是当预测一个新的值x的时候,根据它距离最近的K个点是什么类型来判断x属于哪个类别。
例如:
在这里插入图片描述
图中绿色的点就是我们要预测的那个点(是三角形还是圆形),假设K=3.那么KNN算法就会找到与它距离最近的三个点(这里用圆圈把它圈起来了),看看哪种类别多一些,比如这个例子中是蓝色三角形多一些,新来的绿色点就归类到蓝三角了。

二、KNN的工作原理

1.计算测试点x与训练集中每一个数据的“距离”
2.将所求的距离进行升序排序,选择前K个
3.在上一步中所得到的K个数据中,根据决策规则(如多数表决)决定x的类别预测结果

K值的选择

你能看出整个 KNN 的分类过程,K 值的选择还是很重要的。那么问题来了,K 值选择多少是适合的呢?
如果 K 值比较小,容易受个例影响,波动较大。
如果 K 值比较大,分类容易模糊。
所以 K 值应该是个实践出来的结果,并不是我们事先而定的。在工程上,我们一般采用交
叉验证的方式选取 K 值。
交叉验证的思路就是,把样本集中的大部分样本作为训练集,剩余的小部分样本用于预测,
来验证分类模型的准确性。所以在 KNN 算法中,我们一般会把 K 值选取在较小的范围内,同时在验证集上准确率最高的那一个最终确定作为 K 值。

距离如何计算

两个样本点之间的距离代表了这两个样本之间的相似度。距离越大,差异性越大;距离越小,相似度越大。
关于距离的计算方式有下面五种方式:

  1. 欧氏距离;
  2. 曼哈顿距离;
  3. 闵可夫斯基距离;
  4. 切比雪夫距离;
  5. 余弦距离。
    其中前三种距离是 KNN 中最常用的距离。
    欧氏距离是我们最常用的距离公式,也叫做欧几里得距离。在二维空间中,两点的欧式距离
    就是:
    在这里插入图片描述
    同理,我们也可以求得两点在 n 维空间中的距离:
    在这里插入图片描述
    曼哈顿距离在几何空间中用的比较多。以下图为例,绿色的直线代表两点之间的欧式距离,
    而红色和黄色的线为两点的曼哈顿距离。所以曼哈顿距离等于两个点在坐标系上绝对轴距总
    和。用公式表示就是:在这里插入图片描述
    在这里插入图片描述

闵可夫斯基距离不是一个距离,而是一组距离的定义。对于 n 维空间中的两个点 x(x1,x2,
…,xn) 和 y(y1,y2,…,yn) , x 和 y 两点之间的闵可夫斯基距离为:在这里插入图片描述
其中 p 代表空间的维数,当 p=1 时,就是曼哈顿距离;当 p=2 时,就是欧氏距离;当
p→∞时,就是切比雪夫距离
那么切比雪夫距离怎么计算呢?二个点之间的切比雪夫距离就是这两个点坐标数值差的绝对
值的最大值,用数学表示就是:max(|x1-y1|,|x2-y2|)。
余弦距离实际上计算的是两个向量的夹角,是在方向上计算两者之间的差异,对绝对数值不
敏感。在兴趣相关性比较上,角度关系比距离的绝对值更重要,因此余弦距离可以用于衡量
用户对内容兴趣的区分度。比如我们用搜索引擎搜索某个关键词,它还会给你推荐其他的相
关搜索,这些推荐的关键词就是采用余弦距离计算得出的。

KNN 做回归

对于一个新点,我们需要找出这个点的 K 个最近邻居,然后将这些邻居的属性的平均值赋给该点,就可以得到该点的属性。当然不同邻居的影响力权重可以设置成不同的。举个例子,比如一部电影 A,已知它是动作片,当 K=3 时,最近的 3 部电影是《战狼》,《红海行动》和《碟中谍 6》,那么它的打斗次数和接吻次数的预估值分别为(100+95+105)/3=100 次、(5+3+31)/3=13 次。

三、 如何在 sklearn 中使用 KNN

在 Python 的 sklearn 工具包中有 KNN 算法。KNN 既可以做分类器,也可以做回归。如
果是做分类,你需要引用:

from sklearn.neighbors import KNeighborsClassifier

如果是做回归,你需要引用:

from sklearn.neighbors import KNeighborsRegressor

这里,我们使用构造函数 KNeighborsClassifier(n_neighbors=5, weights=‘uniform’,
algorithm=‘auto’, leaf_size=30)
1.n_neighbors:即 KNN 中的 K 值,代表的是邻居的数量。K 值如果比较小,会造成过拟
合。如果 K 值比较大,无法将未知物体分类出来。一般我们使用默认值 5。
2.weights:是用来确定邻居的权重,有三种方式:
weights=uniform,代表所有邻居的权重相同;
weights=distance,代表权重是距离的倒数,即与距离成反比;
自定义函数,你可以自定义不同距离所对应的权重。大部分情况下不需要自己定义函数。
3.algorithm:用来规定计算邻居的方法,它有四种方式:
algorithm=auto,根据数据的情况自动选择适合的算法,默认情况选择 auto;
algorithm=kd_tree,也叫作 KD 树,是多维空间的数据结构,方便对关键数据进行检
索,不过 KD 树适用于维度少的情况,一般维数不超过 20,如果维数大于 20 之后,效
率反而会下降;
algorithm=ball_tree,也叫作球树,它和 KD 树一样都是多维空间的数据结果,不同于
KD 树,球树更适用于维度大的情况;
algorithm=brute,也叫作暴力搜索,它和 KD 树不同的地方是在于采用的是线性扫描,
而不是通过构造树结构进行快速检索。当训练集大的时候,效率很低。
4.leaf_size:代表构造 KD 树或球树时的叶子数,默认是 30,调整 leaf_size 会影响到树的
构造和搜索速度。
创建完 KNN 分类器之后,我们就可以输入训练集对它进行训练,这里我们使用 fit() 函
数,传入训练集中的样本特征矩阵和分类标识,会自动得到训练好的 KNN 分类器。然后可
以使用 predict() 函数来对结果进行预测,这里传入测试集的特征矩阵,可以得到测试集的
预测分类结果。

四、如何用 KNN 对手写数字进行识别分类

手写数字数据集是个非常有名的用于图像识别的数据集。数字识别的过程就是将这些图片与
分类结果 0-9 一一对应起来。完整的手写数字数据集 MNIST 里面包括了 60000 个训练样
本,以及 10000 个测试样本。如果你学习深度学习的话,MNIST 基本上是我们接触的第一
个数据集。
今天我们用 sklearn 自带的手写数字数据集做 KNN 分类,你可以把这个数据集理解成一个
简版的 MNIST 数据集,它只包括了 1797 幅数字图像,每幅图像大小是 8*8 像素。
好了,我们先来规划下整个 KNN 分类的流程:
在这里插入图片描述
整个训练过程基本上都会包括三个阶段:

  1. 数据加载:我们可以直接从 sklearn 中加载自带的手写数字数据集;
  2. 准备阶段:在这个阶段中,我们需要对数据集有个初步的了解,比如样本的个数、图像
    长什么样、识别结果是怎样的。你可以通过可视化的方式来查看图像的呈现。通过数据
    规范化可以让数据都在同一个数量级的维度。另外,因为训练集是图像,每幅图像是个
    8*8 的矩阵,我们不需要对它进行特征选择,将全部的图像数据作为特征值矩阵即可;
  3. 分类阶段:通过训练可以得到分类器,然后用测试集进行准确率的计算。
    下面我们来进行这个项目:

加载数据和对数据的探索:

# 加载数据
digits = load_digits()
data = digits.data
# 数据探索
print(data.shape)
# 查看第一幅图像
print(digits.images[0])
# 第一幅图像代表的数字含义
print(digits.target[0])
# 将第一幅图像显示出来
plt.gray()
plt.imshow(digits.images[0])
plt.show()

运行结果:
在这里插入图片描述

在这里插入图片描述

我们对原始数据集中的第一幅进行数据可视化,可以看到图像是个 88 的像素矩阵,上面
这幅图像是一个“0”,从训练集的分类标注中我们也可以看到分类标注为“0”。
sklearn 自带的手写数字数据集一共包括了 1797 个样本,每幅图像都是 8
8 像素的矩阵。
因为并没有专门的测试集,所以我们需要对数据集做划分,划分成训练集和测试集。因为
KNN 算法和距离定义相关,我们需要对数据进行规范化处理,采用 Z-Score 规范化,代码
如下:

# 分割数据,将 25% 的数据作为测试集,其余作为训练集(你也可以指定其他比例的数据作为训练集)
train_x, test_x, train_y, test_y = train_test_split(data, digits.target, test_size=0.25
# 采用 Z-Score 规范化
ss = preprocessing.StandardScaler()
train_ss_x = ss.fit_transform(train_x)
test_ss_x = ss.transform(test_x)

然后我们构造一个 KNN 分类器 knn,把训练集的数据传入构造好的 knn,并通过测试集进
行结果预测,与测试集的结果进行对比,得到 KNN 分类器准确率,代码如下:

# 创建 KNN 分类器
knn = KNeighborsClassifier()
knn.fit(train_ss_x, train_y)
predict_y = knn.predict(test_ss_x)
print("KNN 准确率: %.4lf" % accuracy_score(predict_y, test_y))
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值