K近邻算法(K-Nearest Neighbors, KNN)详解
K近邻(KNN)是一种简单且直观的监督学习算法,适用于分类和回归任务。其核心思想是“物以类聚”:通过计算样本间的相似性,找到与目标样本最接近的K个邻居,根据这些邻居的类别或值进行预测。
1. 核心思想
- 分类任务:根据K个最近邻的多数类别决定目标样本的类别。
- 回归任务:根据K个最近邻的均值或中位数预测目标样本的连续值。
- 非参数模型:KNN不假设数据分布,直接依赖数据本身的局部结构。
2. 算法步骤
(1) 计算距离
- 欧氏距离(Euclidean Distance)(最常用):d(x,y)=i=1∑n(xi−yi)2
- 曼哈顿距离(Manhattan Distance):d(x,y)=i=1∑n∣xi−yi∣
- 余弦相似度(Cosine Similarity):cos(θ)=∥x∥∥y∥x⋅y
- 闵可夫斯基距离(Minkowski Distance)(通用形式):d(x,y)=(i=1∑n∣xi−yi∣p)1/p其中 p=1 为曼哈顿距离,p=2 为欧氏距离。
(2) 选择K值
- K的含义:需要预先指定的邻居数量。
- K过小:对噪声敏感,易过拟合(模型复杂度高)。
- K过大:忽略局部特征,可能欠拟合(模型复杂度低)。
- 经验法则:通常通过交叉验证选择,初始值可设为样本数的平方根。
(3) 投票或加权
- 分类任务:统计K个邻居中多数类别(如5个邻居中3个为“猫”,则预测为“猫”)。
- 回归任务:取K个邻居的均值或加权均值(如距离越近权重越高)。
(4) 预测结果
- 输出目标样本的类别(分类)或数值(回归)。
3. 算法特点
优点
- 简单直观:无需训练,仅依赖数据存储和距离计算。
- 适用于多分类:天然支持多类别问题。
- 动态适应数据:新增数据无需重新训练模型。
- 无需假设数据分布:适合复杂和非线性数据。
缺点
- 计算复杂度高:预测时需遍历所有样本,时间复杂度 O(n)。
- 对高维数据敏感:维度灾难(高维下距离区分度下降)。
- 对噪声和不平衡数据敏感:少数类易被多数类淹没。
- 需要特征标准化:不同量纲的特征需归一化处理。
4. 关键参数与优化
(1) K值选择
- 交叉验证:将数据分为训练集和验证集,尝试不同K值选择最优。
- 误差曲线:绘制K值与误差(如分类错误率或回归MSE)的关系,选择拐点处的K值。
(2) 距离加权
- 加权投票:给距离近的邻居更高权重(如 w=1/d),缓解噪声影响。
- 示例:三个邻居距离为1、2、3,类别为A、A、B,加权投票权重为1+0.5 vs 0.33,预测为A。
(3) 降维与特征选择
- PCA/LDA:降低维度,减少计算量并提升距离有效性。
- 过滤式方法:选择与目标相关性高的特征。
5. 实际应用场景
(1) 分类任务
- 图像识别:根据像素相似度分类手写数字(如MNIST数据集)。
- 推荐系统:基于用户行为相似度推荐商品(如“喜欢A的用户也喜欢B”)。
(2) 回归任务
- 房价预测:根据相似户型的房价均值预测目标房价。
- 股票趋势预测:基于历史相似K线形态预测未来走势。
(3) 异常检测
- 欺诈检测:与正常交易距离远的样本标记为异常。
6. 代码示例(Python)
(1) 分类任务
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 加载数据
data = load_iris()
X, y = data.data, data.target
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
# 训练模型
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train, y_train)
# 预测与评估
y_pred = knn.predict(X_test)
print(f"准确率: {accuracy_score(y_test, y_pred):.2f}")
(2) 回归任务
from sklearn.neighbors import KNeighborsRegressor
from sklearn.datasets import fetch_california_housing
from sklearn.preprocessing import StandardScaler
# 加载数据
data = fetch_california_housing()
X, y = data.data, data.target
# 特征标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2)
# 训练模型
knn_reg = KNeighborsRegressor(n_neighbors=3)
knn_reg.fit(X_train, y_train)
# 预测与评估
y_pred = knn_reg.predict(X_test)
print(f"MSE: {mean_squared_error(y_test, y_pred):.2f}")
7. 处理不平衡数据
- 加权KNN:为少数类样本赋予更高权重。
- SMOTE过采样:生成合成少数类样本平衡数据。
- 调整K值:增大K值以避免少数类被忽略。
8. 与其它算法的对比
算法 | KNN | 决策树 | SVM |
---|---|---|---|
原理 | 基于局部邻居投票 | 基于特征分割规则 | 基于间隔最大化的超平面 |
优点 | 简单、无需训练、动态适应数据 | 可解释性强、适合类别特征 | 高维有效、适合小样本 |
缺点 | 计算量大、对噪声敏感 | 容易过拟合、对数据分布敏感 | 参数调优复杂、核函数选择困难 |
9. 易混淆点:
a. 对比KNN与K-means
维度 | KNN(监督学习) | K-means(无监督学习) |
---|---|---|
任务类型 | 分类、回归 | 聚类 |
是否需要标签 | 是 | 否 |
输入数据 | 特征 + 标签 | 仅特征 |
目标 | 利用已知标签预测新样本 | 发现数据内在结构 |
算法流程 | 1. 计算距离 2. 找邻居 3. 投票/取均值 | 1. 随机初始化中心点 2. 迭代优化簇分配 |
b. KNN中的“邻居”是按单个样本计算的, 不是按照一”簇“样本来算
在K近邻(KNN)算法中,“邻居”是指与目标样本距离最近的单个样本,而非预先定义的“簇”。每个目标样本独立计算其最近的K个邻居,具体原理如下:
邻居的定义:
- 每个样本独立计算:对于待预测的样本(测试样本),算法会在训练集中逐个计算其与所有训练样本的距离(如欧氏距离),选出最近的K个独立样本作为邻居。
- 动态选择:不同的测试样本会有不同的邻居组合,邻居的选择完全基于当前测试样本的位置。