K近邻 (K-Nearest Neighbor KNN)
K近邻算法(K-Nearest Neighbors, KNN)是一种简单直观的机器学习算法,适用于分类和回归问题。它的核心思想是:判断一个数据点的类别或预测值时,参考它在特征空间中最近的 KKK 个数据点。
1. KNN 的基本原理
KNN 算法基于距离的度量来进行分类或回归。其工作方式可以用以下步骤来描述:
分类问题中的 KNN
假设我们有一些数据点,每个数据点都有一个类别,比如颜色。现在有一个新的点,我们希望知道这个点属于哪一种颜色类别。
-
选择 K:选择一个正整数 K,表示我们要考虑的新点周围最近的 K 个邻居点。
-
计算距离:计算新点与每个已有点之间的距离。常用的距离度量是欧几里得距离,公式如下:
其中 x 和 y 是两个数据点的特征向量。
同时有的情况也会使用曼哈顿距离公式。
-
选择最近的 K 个邻居:从已有数据中,选择与新点距离最近的 K 个点。
-
投票分类:统计这 K 个邻居中各个类别的数量,选择出现次数最多的类别作为新点的预测类别。
回归问题中的 KNN
在回归问题中,KNN 的原理类似,只是预测的是一个数值,而不是一个类别。
- 选择 K:选择一个正整数 K。
- 计算距离:计算新点与每个已有点之间的距离。
- 选择最近的 K 个邻居:选择与新点距离最近的 K 个点。
- 取平均值:对这 K 个邻居的数值取平均值,作为新点的预测值。
2. K 值的选择
- 如果 K 值较小(例如 1),模型会对训练数据的噪声非常敏感,容易导致过拟合。
- 如果 K 值较大(例如接近数据总数),模型会变得非常平滑,可能忽略细节,导致欠拟合。
- 常见的做法是通过交叉验证选择一个合适的 K 值。
3. 使用 Scikit-Learn 实现 KNN
我们可以用 Python 的 Scikit-Learn 库实现一个简单的 KNN 示例。以下是代码示例,用于分类问题:
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
# 生成模拟数据
np.random.seed(42)
X = np.random.rand(100, 2) * 10 # 100 个样本,2 个特征
y = (X[:, 0] + X[:, 1] > 10).astype(int) # 简单规则:如果 x1 + x2 > 10,标记为 1,否则为 0
# 拆分数据为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 创建 KNN 分类器并进行训练
k = 3 # 使用 3 个最近邻居
model = KNeighborsClassifier(n_neighbors=k)
model.fit(X_train, y_train)
# 预测并计算准确率
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(f'模型在测试集上的准确率: {accuracy}')
# 可视化结果
import matplotlib.pyplot as plt
plt.figure(figsize=(8, 6))
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_pred, cmap='coolwarm', marker='o', label='预测结果')
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap='coolwarm', marker='x', alpha=0.5, label='真实值')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.title(f'KNN 分类结果 (K={k})')
plt.legend(loc='upper left')
plt.show()
4. 代码解释
- 生成模拟数据:生成了 100 个样本,每个样本有两个特征。标签 y 是通过 x1+x2>10 来生成的二分类问题。
- 数据集拆分:将数据集分为训练集和测试集,80% 用于训练,20% 用于测试。
- 创建和训练模型:使用
KNeighborsClassifier
类创建 KNN 模型,并用训练集数据进行训练。 - 预测和评估:用测试集数据进行预测,计算模型在测试集上的准确率。
- 可视化结果:使用散点图展示测试集中数据点的预测结果和真实标签。
5. Pytorch实现KNN
import torch
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_classification
# 生成模拟数据
X, y = make_classification(n_samples=100, n_features=2, n_classes=2, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 将数据转换为 PyTorch 张量
X_train_tensor = torch.tensor(X_train, dtype=torch.float32)
X_test_tensor = torch.tensor(X_test, dtype=torch.float32)
y_train_tensor = torch.tensor(y_train, dtype=torch.int64)
# 定义 KNN 预测函数
def knn_predict(X_train, y_train, X_test, k=3):
# 计算所有测试点与训练点的距离
distances = torch.cdist(X_test, X_train)
# 找到距离最近的 K 个训练点的索引
knn_indices = distances.topk(k, largest=False).indices
# 通过 K 个邻居的标签进行投票
knn_labels = y_train[knn_indices]
y_pred = torch.mode(knn_labels, dim=1).values
return y_pred
# 使用 KNN 进行预测
k = 3
y_pred = knn_predict(X_train_tensor, y_train_tensor, X_test_tensor, k)
# 计算准确率
accuracy = (y_pred == torch.tensor(y_test)).float().mean().item()
print(f'KNN 模型在测试集上的准确率: {accuracy}')
# 可视化结果
plt.figure(figsize=(8, 6))
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_pred.numpy(), cmap='coolwarm', marker='o', label='预测结果')
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap='coolwarm', marker='x', alpha=0.5, label='真实值')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.title(f'KNN 分类结果 (K={k})')
plt.legend(loc='upper left')
plt.show()
代码说明
- 数据生成:使用
make_classification
生成二维分类数据,便于可视化。然后使用train_test_split
将数据拆分为训练集和测试集。 - 数据转换:将数据转换为 PyTorch 的张量,以便后续计算。
- 自定义 KNN 函数
torch.cdist
用于计算测试集和训练集之间的欧几里得距离矩阵。- 使用
topk
找到距离最近的 KKK 个训练样本的索引。 - 使用
torch.mode
进行多数投票,从而确定测试样本的预测标签。
- 计算准确率:比较预测值和真实标签,计算分类的准确率。
- 可视化结果:使用 Matplotlib 绘制预测结果与真实标签的散点图。
6. KNN 的优缺点
优点:
- 简单直观:KNN 没有复杂的训练过程,适合对模型原理的初步理解。
- 无参数学习:KNN 是一种懒惰学习算法,不需要显式地训练模型,而是直接保存训练样本。
- 适合小规模数据集:对于样本量小且特征不多的数据集,KNN 效果较好。
缺点:
- 计算量大:KNN 需要对每个新样本计算与所有训练样本的距离,所以在样本量大时,计算开销大。
- 对特征尺度敏感:KNN 基于距离度量,不同尺度的特征会对距离计算产生影响,因此通常需要对特征进行标准化处理。
- 受噪声影响大:当数据中存在噪声或异常值时,KNN 的分类结果会受到很大影响。
7. 总结
KNN 是一个基于“多数投票”思想的算法:
- 分类:通过计算新样本与训练样本的距离,选择 KKK 个最近的邻居,选择出现次数最多的类别作为新样本的类别。
- 回归:通过选择 KKK 个最近的邻居,取这些邻居标签的平均值作为预测值。