KNN
K-近邻(K-Nearest Neighbors, KNN)算法是一种基于实例的学习方法,它属于监督学习的范畴。KNN的核心思想是:如果一个样本在特征空间中的k个最相邻的样本中的大多数属于某一个类别,则该样本也属于这个类别,并具有这个类别上样本的特性。KNN算法既可以用于分类问题也可以用于回归问题。
基本原理
- **选择距离度量**:计算新样本与训练集中所有已知类别的样本之间的距离。
- **确定邻居**:根据距离从小到大排序,选取最近的k个邻居。
- **投票决策**:
- 对于分类任务,通常采用多数表决法,即选择这k个邻居中出现次数最多的类别作为预测结果。
- 对于回归任务,通常取这k个邻居的目标值的平均数或加权平均数作为预测结果。
公式推导
假设我们有两个n维向量\(X = (x_1, x_2, ..., x_n)\) 和 \(Y = (y_1, y_2, ..., y_n)\),它们之间的距离可以通过不同的度量方法来计算:
- **欧几里得距离** (Euclidean Distance):
\[ d(X, Y) = \sqrt{\sum_{i=1}^{n}(x_i - y_i)^2} \]
- **曼哈顿距离** (Manhattan Distance):
\[ d(X, Y) = \sum_{i=1}^{n}|x_i - y_i| \]
- **闵可夫斯基距离** (Minkowski Distance):
\[ d(X, Y) = \left( \sum_{i=1}^{n} |x_i - y_i|^p \right)^{1/p} \]
当\(p=1\)时,为曼哈顿距离;当\(p=2\)时,为欧几里得距离;当\(p \to \infty\)时,为切比雪夫距离。
- **切比雪夫距离** (Chebyshev Distance):
\[ d(X, Y) = \max(|x_i - y_i|) \]
算法分析
- **优点**:
- 简单易懂,易于实现。
- 对异常值不敏感。
- 不需要显式的训练阶段,因为训练阶段只是存储训练数据。
- 可以处理多分类问题。
- **缺点**:
- 计算成本高,特别是对于大规模的数据集。
- 对参数k的选择非常敏感,不同k值可能导致不同的结果。
- 必须进行特征缩放,否则某些特征可能会对距离产生不成比例的影响。
- 没有明确的概率输出,无法直接提供概率估计。
KNN算法优化之KDTree
KD树通过递归地分割空间来组织数据点,这使得它可以有效地支持上述提到的各种操作。它能够快速确定哪些点与查询相关,哪些不相关,从而加速搜索过程。
K-Dimensional Tree,简称KD树,是一种用于组织多维空间中点的数据结构。它特别适合用来解决高维空间中的最近邻搜索和范围查询问题。KD树本质上是一种二叉树,其构建过程涉及到选择合适的维度进行分割,并将数据递归地分成两个子集。
关于机器学习更系统更形象的讲解请看本人的课机器学习理论与实战
KD树的构建
构建KD树的过程主要包括以下几个步骤:
- 确定分裂维度:通常从根节点开始,依次选择不同的维度来分割数据。一种常见的做法是按照维度循环选择(例如,在二维空间中,第一次按x轴分,第二次按y轴分,然后重复),或者根据当前数据在各个维度上的方差来选择,即选择方差最大的那个维度以获得更好的分割效果。
- 找到分裂点:选定分裂维度后,需要在这个维度上找到一个值作为分裂点,以便将数据分为左右两部分。常用的方法是选取该维度上的中位数,这样可以保证左右子树大小相对均衡。
- 递归分割:使用分裂点将数据分为两部分,左边的数据进入左子树,右边的数据进入右子树。然后对每个子集重复上述步骤,直到满足某个终止条件(如子集中数据点的数量小于预设阈值)。
KD树的操作
最近邻搜索
- 从根节点开始,沿着树向下查找,选择离目标点更近的那一侧分支继续。
- 当到达叶子节点时,记录当前距离最短的点。
- 回溯到父节点,检查是否需要探索另一侧的子树(如果另一侧可能包含更近的邻居,则需要访问)。
- 通过这种方式,逐步向上回溯并更新最近邻点,直至回到根节点。
范围查询
- 类似于最近邻搜索,但是这次我们关注的是目标区域内所有点。
- 同样从根节点开始,选择与查询区域相交的一侧或两侧分支进行深入。
- 在叶子节点处收集符合条件的点。
- 如果另一侧也可能包含满足条件的点,则同样需要进行探索。
KD树的优势与局限性
优势:
- 对于静态数据集,KD树能够提供非常高效的最近邻和范围查询性能。
- 结构简单,易于理解和实现。
局限性:
- 当数据分布不均匀时,可能导致树的高度不平衡,从而影响查询效率。
- 更新操作复杂度较高,对于动态变化的数据集,维护KD树的成本较大。
- 随着维度增加,所谓的“维度灾难”会导致KD树性能下降,因为高维空间中点之间的距离变得难以区分。
Python示例 (使用scikit-learn库)
下面是一个使用scikit-learn库中的KDTree类来创建和使用KD树的例子:
from sklearn.neighbors import KDTree
import numpy as np
# 创建一些随机二维点
data = np.random.rand(10, 2) # 生成10个随机点,每个点有2个坐标
# 构建KD树
kdtree = KDTree(data, leaf_size=2)
# 查询最邻近的点
query_point = [0.5, 0.5]
distances, indices = kdtree.query([query_point], k=1)
print(f"Nearest point to {query_point} is at index {indices[0][0]}, distance: {distances[0][0]}")
# 范围查询
radius = 0.2
indices_within_radius = kdtree.query_radius([query_point], r=radius)
print(f"Indices of points within radius {radius} of {query_point}: {indices_within_radius[0]}")
这段代码展示了如何用Python创建一个基于随机数据点的KD树,并执行最近邻搜索和范围查询。leaf_size参数控制了叶节点中存储的数据点数量,它会影响树的深度以及查询的速度。