一、KNN算法简介
1.1、定义
KNN(K-Nearest Neighbor)算法是一种基于实例的学习方法,通过测量数据点之间的距离进行分类或回归分析。它是一种简单易懂的多分类技术,依赖于距离最近的邻居来推断数据点的类别或数值,为许多实际应用提供了有效的解决方案。
k-近邻算法
邻近算法,或者说K最近邻(K-Nearest Neighbor,KNN)分类算法是数据挖掘分类技术中最简单的方法之 一,是著名的模式识别统计学方法,在机器学习分类算法中占有相当大的地位。它是一个理论上比较成 熟的方法。既是最简单的机器学习算法之一。
所谓K最近邻,就是k个最近的邻居的意思,说的是每个样本都可以用它最接近的k个邻居来代表。Cover 和Hart在1968年提出了最初的邻近算法。
KNN是一种分类(classification)算法,它输入基于实例的学习 (instance-based learning),属于懒惰学习(lazy learning)即KNN没有显式的学习过程,也就是说 没有训练阶段,数据集事先已有了分类和特征值,待收到新样本后直接进行处理。与急切学习(eager learning)相对应。
KNN是一种惰性学习算法,它是基于实例的,并没有经过大量的训练来学习模型或者特征,而是仅仅记 住了需要训练的相关实例,KNN是监督学习的一种。
KNN是给定测试实例,基于某种距离度量找出训练集中与其最靠近的k个实例点,然后基于这k个最近邻 的信息来进行预测,简言之,需要预测的实例与哪一类离得更近,就属于哪一类。
1.2、原理
KNN算法的原理可以用“近朱者赤,近墨者黑”来概括,它是一种基于实例的学习方法,属于懒惰学习,因为没有显式的训练过程。KNN通过测量待预测样本与训练数据集中最近的K个样本之间的距离来进行分类或回归分析。
在分类问题中,通过投票决定待预测样本的类别;
而在回归问题中,则计算这K个样本的平均值作为最终的预测结果。
1.3、特点
KNN算法简单易懂,易于实现;
无需训练阶段,直接进行分类或回归;
适用于多分类问题;
对数据集的大小和维度不敏感。
二、决策边界
2.1、定义
决策边界是分类算法中用于区分不同类别的虚拟边 界,即上一章节所指出的“房间”。
2.2、边界效果
决策边界是否合理,直接影响到分类效果的好坏。
2.3、KNN与决策边界
KNN算法通过计算待分类样本与已知样本之间的距 离,找到最近的k个样本,并根据这些样本的类别 信息进行投票,以确定待分类样本的类别。
三、KNN的决策边界计算
决策边界计算
导入模块
from sklearn.neighbors import KNeighborsClassifier
import numpy as np
from matplotlib import pyplot as plt
定义数据集
#定义数据集
point1 = [[7.7, 6.1], [3.1, 5.9], [8.6, 8.8], [9.5, 7.3], [3.9, 7.4], [5.0, 5.3], [1.0, 7.3]]
point2 = [[0.2, 2.2], [4.5, 4.1], [0.5, 1.1], [2.7, 3.0], [4.7, 0.2], [2.9, 3.3], [7.3, 7.9]]
point3 = [[9.2, 0.7], [9.2, 2.1], [7.3, 4.5], [8.9, 2.9], [9.5, 3.7], [7.7, 3.7], [9.4, 2.4]]
#特征合并
np_train_data=np.concatenate(np.array((point1,point2,point3)),axis=0)
#创建标签
np_train_label=np.array([0]*7+[1]*7+[2]*7)
构建KNN算法:实例化KNN算法,KNN训练
knn_clf=KNeighborsClassifier(3)
knn_clf.fit(np_train_data,np_train_label)
设定未知点,设定坐标点网络
x=np.linspace(0,10,100)
y=np.linspace(0,10,100)
x0,y0=np.meshgrid(x,y)
axis_xy=np.c_[x0.ravel(),y0.ravel()]
KNN的预测与绘制决策边界
y_predict=knn_clf.predict(axis_xy)
y_predit=y_predict.reshape(x0.shape)
plt.contour(x0,y0,y_predit)
plt.scatter(np_train_data[np_train_label==0,0],np_train_data[np_train_label==0,1],marker='^')
plt.scatter(np_train_data[np_train_label==1,0],np_train_data[np_train_label==1,1],marker='*')
plt.scatter(np_train_data[np_train_label==2,0],np_train_data[np_train_label==2,1],marker='s')
plt.show()
完整代码
from sklearn.neighbors import KNeighborsClassifier # 从 scikit-learn 导入 K 最近邻分类器
import numpy as np # 导入 NumPy 库以进行数组操作
from matplotlib import pyplot as plt # 从 matplotlib 导入 pyplot 用于绘图
# 定义数据集
point1 = [[7.7, 6.1], [3.1, 5.9], [8.6, 8.8], [9.5, 7.3], [3.9, 7.4], [5.0, 5.3], [1.0, 7.3]] # 类别 0
point2 = [[0.2, 2.2], [4.5, 4.1], [0.5, 1.1], [2.7, 3.0], [4.7, 0.2], [2.9, 3.3], [7.3, 7.9]] # 类别 1
point3 = [[9.2, 0.7], [9.2, 2.1], [7.3, 4.5], [8.9, 2.9], [9.5, 3.7], [7.7, 3.7], [9.4, 2.4]] # 类别 2
# 特征合并
np_train_data = np.concatenate(np.array((point1, point2, point3)), axis=0) # 将三个类别的数据组合成一个训练数据集
# 创建标签
np_train_label = np.array([0] * 7 + [1] * 7 + [2] * 7) # 创建对应每个数据点的标签
# 构建 KNN 算法,实例化,KNN 训练
knn_clf = KNeighborsClassifier(3) # 创建 KNN 分类器,k=1 表示考虑最近的一个邻居
knn_clf.fit(np_train_data, np_train_label) # 使用训练数据和标签来训练 KNN 模型
# 定义未知点坐标网格
x = np.linspace(0, 10, 100) # 创建 x 坐标的线性空间,从 0 到 10,包含 100 个点
y = np.linspace(0, 10, 100) # 创建 y 坐标的线性空间,从 0 到 10,包含 100 个点
x0, y0 = np.meshgrid(x, y) # 创建二维网格坐标
axis_xy = np.c_[x0.ravel(), y0.ravel()] # 将网格坐标展平,以便于预测
# KNN 的预测绘制决策边界
y_predict = knn_clf.predict(axis_xy) # 对所有未知点进行分类预测
y_predit = y_predict.reshape(x0.shape) # 将预测结果按原网格形状排列
# 绘制决策边界
plt.contour(x0, y0, y_predit) # 绘制决策边界
plt.scatter(np_train_data[np_train_label == 0, 0], np_train_data[np_train_label == 0, 1], marker='^') # 绘制类别 0 的点
plt.scatter(np_train_data[np_train_label == 1, 0], np_train_data[np_train_label == 1, 1], marker='*') # 绘制类别 1 的点
plt.scatter(np_train_data[np_train_label == 2, 0], np_train_data[np_train_label == 2, 1], marker='s') # 绘制类别 2 的点
plt.show() # 显示绘图结果
K值选择
在KNN中,需要人为选择不同的K的不同取值,这 个参数是需要人为选择的。需要人为确定的参数称 为超参数(hyperparameter)。
选择太小:
优点:复杂的数据集,K值较小可能会提供更详细 的决策边界,因为模型更加灵活。
缺点:容易受到局部结构的影响,模型对噪声和异 常值的影响更大。
选择太大:
优点:考虑了更多的全局信息,对于平滑的数据集, 较大的K值可以提供更稳定的决策边界。
缺点:对于复杂的数据集,较大的K值可能会导致 模型过于简单,无法准确捕获数据的局部特征。
四、交叉验证
步骤
1、准备数据集:
准备好包含特征和标签的 数据集。
2、K折交叉验证:
确定K的值,例如选择5或7 作为K值。
3、划分数据集:
将数据集划分为K个大小相似的子集
4、循环训练和评估:
使用K-1个子集作为训练集, 剩余的1个子集作为测试集。 在训练集上训练KNN模型。 使用训练好的模型对测试集进 行预测。 使用性能指标评估。
5、计算平均性能:
将K次验证的性能指标取 平均值,作为KNN模型的 最终性能评估。
6、选择最佳K值:
可以尝试不同的K值,并 通过交叉验证选择性能最 好的K值。然后选择在交 叉验证中表现最佳的K值。
7、选定最终K值:
使用选择的最佳K值,在整 个训练集上重新训练KNN 模型,以获得最终的模型。
五、库函数
5.1、KNeighborsClassifier()
是 scikit-learn 库中的一个分类模型实现,属于 K 最近邻(KNN)算法。该模型通过计算输入数据点与训练数据集中每个点的距离,找出最近的 K 个邻居,来进行分类预测。
class sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, *, weights='uniform', algorithm='auto', leaf_size=30, p=2, metric='minkowski', metric_params=None, n_jobs=None)
方法 | 描述 |
---|---|
n_neighbors | 默认值 = 5 默认情况下用于查询的邻居数。 |
weights | 预测中使用的权重函数。可能的值:
|
algorithm | 用于计算最近邻的算法:
注意:拟合稀疏输入将覆盖 this 参数,使用蛮力。 |
leaf_size | 传递给 BallTree 或 KDTree 的叶大小。这可能会影响 构造和查询的速度以及内存 需要来存储树。最佳值取决于 问题的性质。 |
p | Minkowski 度量的 Power 参数。当 p = 1 时,这是等效的 使用 manhattan_distance (L1) 和 euclidean_distance (L2) 表示 p = 2。 对于任意 p,使用 minkowski_distance (l_p)。此参数是预期的 要积极。 |
metric | 用于距离计算的公制。默认值为 “minkowski”,它 当 p = 2 时,得到标准欧几里得距离。请参阅 scipy.spatial.distance 和 Valid metric 中列出的量度 值。 如果度量是“预先计算的”,则假定 X 是一个距离矩阵,并且 拟合时必须为方形。X 可以是稀疏图,其中 只有 “nonzero” 元素才能被视为邻居。 如果 metric 是一个可调用函数,它需要两个表示 1D 的数组 vectors 作为输入,并且必须返回一个表示距离 在这些向量之间。这适用于 Scipy 的指标,但效果较弱 效率高于将指标名称作为字符串传递。 |
metric_params | metric 函数的其他关键字参数。 |
n_jobs | 要为邻居搜索运行的并行作业数。 表示 1,除非在上下文中。 表示使用所有处理器。有关更多详细信息,请参阅 术语表 。 不影响 method。None -1 |
5.2、concatenate()
是 NumPy 库中的一个函数,用于将两个或多个数组沿特定轴(通常是行或列)合并。
numpy.concatenate((a1, a2, ...), axis=0, out=None, dtype=None, casting="same_kind")
方法 | 描述 |
---|---|
a1, a2, … | 数组必须具有相同的形状,但维度 对应于 axis (默认情况下是第一个)。 |
axis | 数组将沿其连接的轴。如果 axis 为 None,则 数组在使用前被展平。默认值为 0。 |
out | 如果提供,则为放置结果的目标。形状必须为 correct,匹配 concatenate 在 no out 参数。 |
dtype | 如果提供,目标数组将具有此 dtype。不能 与 out 一起提供。 |
casting | 控制可能发生的数据类型转换。默认为 'same_kind'。 有关选项的描述,请参阅铸造。 |
import numpy as np
array1 = np.array([[1, 2], [3, 4]])
array2 = np.array([[5, 6], [7, 8]])
result = np.concatenate((array1, array2), axis=0) # 沿行合并
# result: [[1, 2], [3, 4], [5, 6], [7, 8]]
5.3、meshgrid()
是 NumPy 函数,用于生成二维网格坐标,通常用于函数绘图或参数化曲面绘图。meshgrid
接受两个一维数组(通常为 x 和 y 轴的坐标),并返回两个矩阵,分别表示每个坐标点的 x 和 y 值。
numpy.meshgrid(*xi, copy=True, sparse=False, indexing='xy')
方法 | 描述 |
---|---|
x1, x2,…, xn | 表示网格坐标的 1-D 数组。 |
indexing | 输出的笛卡尔 ('xy', default) 或矩阵 ('ij') 索引。 有关更多详细信息,请参阅 Notes。 |
sparse | 如果为 True,则返回的维度 i 坐标数组的形状将从 减少到 。这些稀疏坐标格网是 旨在与 Broadcasting 一起使用。当所有 坐标,则广播仍然会导致 全尺寸结果数组。 默认值为 False。 |
copy | 如果为 False,则返回原始数组的视图,以便 节省内存。默认值为 True。请注意,可能会返回非连续的 阵 列。此外,广播数组的多个元素 可能是指单个内存位置。如果需要写入 数组中,请先制作副本。sparse=False, copy=False |
import numpy as np
x = np.linspace(0, 5, 5) # x 轴的线性空间
y = np.linspace(0, 5, 5) # y 轴的线性空间
X, Y = np.meshgrid(x, y) # 生成网格坐标
# X, Y: 网格中的每个点的坐标
'''
(array([[0. , 1.25, 2.5 , 3.75, 5. ],
[0. , 1.25, 2.5 , 3.75, 5. ],
[0. , 1.25, 2.5 , 3.75, 5. ],
[0. , 1.25, 2.5 , 3.75, 5. ],
[0. , 1.25, 2.5 , 3.75, 5. ]]),
array([[0. , 0. , 0. , 0. , 0. ],
[1.25, 1.25, 1.25, 1.25, 1.25],
[2.5 , 2.5 , 2.5 , 2.5 , 2.5 ],
[3.75, 3.75, 3.75, 3.75, 3.75],
[5. , 5. , 5. , 5. , 5. ]]))
'''
5.4、ravel()
是 NumPy 的一个方法,用于将多维数组展平为一维数组。它返回一个连续的一维数组,表示原数组中的所有元素。与 flatten
方法不同的是,ravel
返回的是原数组的视图,不会复制数据。
numpy.ravel(a, order='C')
方法 | 描述 |
---|---|
a | Input 数组。a 中的元素按照 order 指定的顺序读取,并打包为 1-D 数组。 |
order | 使用此索引顺序读取 a 的元素。“C” 表示 以行优先、C 样式顺序为元素编制索引, 最后一个轴索引变化最快,返回到第一个 轴索引变化最慢。'F' 表示为元素编制索引 以 column-major、Fortran 样式的顺序,使用 第一个索引更改最快,最后一个索引更改 慢。请注意,'C' 和 'F' 选项不考虑 底层数组的内存布局,并且仅引用 轴索引的顺序。“A” 表示读取 如果 a 是 Fortran 连续的,则为类似 Fortran 的索引顺序 memory,否则类似 C 的顺序。'K' 表示读取 元素(按它们在内存中出现的顺序排列),但 当步幅为负数时反转数据。默认情况下,'C' 使用索引顺序。 |
import numpy as np
array = np.array([[1, 2, 3], [4, 5, 6]])
flattened = array.ravel() # 将二维数组展平为一维
# flattened: [1, 2, 3, 4, 5, 6]
5.5、contour()
是 Matplotlib 绘图库中的一个函数,用于绘制等高线图,显示一个二维数组的值在平面上的分布。常用于可视化函数的值在不同坐标下的变化。
contour(X, Y, Z, levels=None, **kwargs)
方法 | 描述 |
---|---|
X, Y | 坐标网格的矩阵,通常由 meshgrid 生成。 |
Z | X 和 Y 有关的值矩阵,决定绘制哪条等高线 |
levels | 可选参数,指定要绘制的等高线的高度值。如果不提供,函数会自动选择 |
kwargs | 其他可选参数,用于控制线条样式、颜色、标签等,例如 colors 、linestyles 、alpha (透明度)等 |
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-3.0, 3.0, 100)
y = np.linspace(-3.0, 3.0, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2)) # 计算 Z 值
plt.contour(X, Y, Z) # 绘制等高线
plt.colorbar() # 添加色条
plt.show()