机器学习——K近邻算法(KNN)

1 前言  

  Kjin邻法(k-nearest neighbors,KNN)是一种基本的机器学习方法,采用类似“物以类聚,人以群分”的思想。比如,判断一个人的人品,只需观察他来往最密切的几个人的人品好坏就可以得出。这里就运用了KNN的思想。KNN方法可以做分类,也可以做回归,这点和决策树算法相同。

  KNN做回归和分类的主要区别在于做预测时候的决策方式不同。

  KNN做分类预测时,一般是选择多数表决法,即训练集里和预测的样本特征最jin的K个样本,预测为里面有最多类别数的类别。
  KNN做回归时,一般是选择平均法,即最jin的K个样本的样本输出的平均值作为回归预测值。

2 KNN算法原理  

  给定一个训练集,对新输入的实例,在训练集中找到与该实例最邻jin的k个实例,这k个实例的多数属于某个类,我们就把该输入实例分为这个类。

2.1 算法描述

  输入:训练数据集$T={(x_1,y_1),(x_2,y_2),...,(x_N,y_N)}$,其中$x_i\in{\chi}$为实例的特征向量,$y_i\in{\{c_1,c_2,...,c_m\}}$为实例的类别,实例特征向量x。
  输出:实例 x 所属的类别 y。
  1.根据给定的距离度量方式,在训练集T中找到与x最邻jin的k个点,涵盖着k个点的x的领域记住$N_k(x)$。
  2.在$N_k(x)$中根据分类决策规则决定x的类别y
    $y=argmax_{c_j}\sum_{x_i\in{N_k(x)}}I(y_i=c_j)$
  其中$I(y_i=c_j)$为指示函数,当$y_i=c_j$的时候$I=1$,后者$I=0$ 。

3. KNN的基本要素

  对于一个确定的训练集,确定距离度量、k值和分类决策规则,就能对任何一个新的实例,确定它的分类。

3.1 距离度量

  距离度量是描述特征空间中两个实例的距离,也是这两个实例的相似程度。在n维实数向量空间中,我们主要使用的距离度量方式是欧式距离,但也可以使用更加一般化$L_p$距离(闵可夫斯基距离)。
  在特征空间中,取出两个特征$x_i$,$x_j$,它们分别是n维的特征向量。
  Lp距离(闵可夫斯基距离)
    $L_p(x_i,x_j)=(\sum_{l=1}^n|x_i^l-x_j^l|^p)^{\frac{1}{p}}$
  欧氏距离
    $L_2(x_i,x_j)=(\sum_{l=1}^n|x_i^l-x_j^l|^2)^{\frac{1}{2}}$
  曼哈顿距离
    $L_1(x_i,x_j)=\sum_{l=1}^n|x_i^l-x_j^l|$
  $L_{\infty}$距离
    $L_{\infty}(x_i-x_j)=\max_{l}|x_i^l-x_j^l|$

3.2 k值的选择

  对于k值的选择,没有一个固定的经验,一般根据样本的分布,选择一个较小的值,然后通过交叉验证选择一个合适的k值。
  • 如果选择较小的K值
    • “学习”的jin似误差(aproximation eror)会减小,但 “学习”的估计误差(estimation eror) 会增大。
    • 噪声敏感。
    • K值的减小就意味着整体模型变得复杂,容易发生过 拟合。
  • 如果选择较大的K值,
    •减少学习的估计误差,但缺点是学习的jin似误差增大。
    •K值的增大 就意味着整体的模型变得简单。
  一个极端是k等于样本数m,则完全没有分类,此时无论输入实例是什么,都只是简单的预测它属于在训练实例中最多的类,模型过于简单。

3.3 分类决策规则

  对于分类决策规则,一般都是使用前面提到的多数表决法。

4. kd树  

  KNN算法最简单的实现方式:计算输入实例和所有训练实例的距离,进行排序,取前k个,进行分类。当训练集特别大的时候,非常耗时。
  下面介绍kd树的方式,kd树通过减少输入实例和训练实例的计算次数来达到优化的目的。
  kd树是一种对K维空间中的实例点进行存储以便对其进行快速检索的树形数据结构。
  kd树是二叉树,表示对K维空间的一个划分。构造Kd树相当于不断地用垂直于坐标轴的超平面将k维空间切分,构成一 系列的k维超矩形区域。Kd树的每个结点对应于一个k维超矩形区域.
  kd树算法包括三步,第一步是建树,第二步是搜索最jin邻,最后一步是预测。

  

4.2 kd树搜索最jin邻居

  当我们生成kd树以后,就可以去预测测试集里面的样本目标点了。预测的过程如下:
  1、对于一个目标点,我们首先在kd树里面找到包含目标点的叶子节点。以目标点为圆心,以目标点到叶子节点样本实例的距离为半径,得到一个超球体,最jin邻的点一定在这个超球体内部。
  2、然后返回叶子节点的父节点,检查另一个子节点包含的超矩形体是否和超球体相交,如果相交就到这个子节点寻找是否有更加jin的jin邻,有的话就更新最jin邻,并且更新超球体。如果不相交那就简单了,我们直接返回父节点的父节点,在另一个子树继续搜索最jin邻。
  3、当回溯到根节点时,算法结束,此时保存的最jin邻节点就是最终的最jin邻。
  从上面的描述可以看出,kd树划分后可以大大减少无效的最jin邻搜索,很多样本点由于所在的超矩形体和超球体不相交,根本不需要计算距离。大大节省了计算时间。
  搜索过程,大致如下:

4.3 kd树预测

  有了kd树搜索最jin邻的办法,kd树的预测就很简单了,在kd树搜索最jin邻的基础上,我们选择到了第一个最jin邻样本,就把它置为已选。在第二轮中,我们忽略置为已选的样本,重新选择最jin邻,这样跑k次,就得到了目标的k个最jin邻。然后根据多数表决法,如果是KNN分类,预测为k个最jin邻里面有最多类别数的类别。如果是KNN回归,用k个最jin邻样本输出的平均值作为回归预测值。

4.4 示例:使用k-jin邻算法进行分类

from sklearn.datasets import make_blobs
# 生成数据
"""
代码中,生成60个训练样本,这60个样本分布在以centers参数指定中心点周围。cluster_std是标准差,用来指明生成的点分布的松散程度。
生成的训练数据集放在变量X里面,数据集的类别标记放在y里面。
make_blobs函数是为聚类产生数据集
产生一个数据集和相应的标签
n_samples:表示数据样本点个数,默认值100
n_features:表示数据的维度,默认值是2
centers:产生数据的中心点,默认值3
cluster_std:数据集的标准差,浮点数或者浮点数序列,默认值1.0
center_box:中心确定之后的数据边界,默认值(-10.0, 10.0)
shuffle :洗乱,默认值是True
random_state:官网解释是随机生成器的种子
"""
centers = [[-2,2], [2,2], [0,4]]
X, y = make_blobs(n_samples=100,centers=centers,random_state=6, cluster_std=0.60)
print(X)  #坐标点
print(y)  #类别
# 画出数据
"""
这些点的分布情况在坐标轴上一目了然,其中三角形的点即各个类别的中心节点。
"""
import matplotlib.pyplot as plt
import numpy as np
plt.figure(figsize=(16,10), dpi=144)
c = np.array(centers)
# 画出样本
plt.scatter(X[:,0], X[:,1], c=y, s=100, cmap='cool')
# 画出中心点
plt.scatter(c[:,0], c[:,1], s=100, marker='^',c='orange')
plt.savefig('knn_centers.png')
plt.show()

# 使用KNeighborsClassifier对算法进行训练,我们选择参数k=5
from sklearn.neighbors import KNeighborsClassifier
# 模型训练
k = 5
clf = KNeighborsClassifier(n_neighbors = k)
clf.fit(X, y)
# 对一个新样本进行预测:
# 进行预测
"""
我们要预测的样本是[0, 2],使用kneighbors()方法,把这个样本周围距离最*的5个点取出来。
取出来的点是训练样本X里的索引,从0开始计算。
"""
X_sample = np.array([[0, 2]])
y_sample = clf.predict(X_sample)
neighbors = clf.kneighbors(X_sample, return_distance=False)

# 把待预测的样本以及和其最*的5个样本在图上标记出来。
# 画出示意图
plt.figure(figsize=(16,10), dpi=144)
c = np.array(centers)
plt.scatter(X[:,0], X[:,1], c=y, s=100, cmap='cool') # 出样本
plt.scatter(c[:,0], c[:,1], s=100, marker='^',c='k') # 中心点
plt.scatter(X_sample[0][0], X_sample[0][1], marker="x",
           s=100, cmap='cool')      # 待预测的点
for i in neighbors[0]:
    plt.plot([X[i][0], X_sample[0][0]], [X[i][1], X_sample[0][1]],
            'k--', linewidth=0.6)  # 预测点与距离最*的5个样本的连线
plt.savefig('knn_predict.png')
plt.show()
View Code

posted @ 2021-06-02 11:42  图神经网络  阅读(769)  评论(0编辑  收藏  举报
Live2D