机器学习基础:K近邻算法(KNN)详解
一、什么是K近邻算法?
K近邻算法(K-Nearest Neighbors, KNN)是一种经典且直观的监督学习方法。它的核心思想非常简单:“物以类聚,人以群分”。当我们需要对一个未知样本进行预测时,KNN会观察它在特征空间中距离最近的 K个“邻居”(即训练集中的已知样本),并根据这些邻居的类别标签或数值来做出决策。KNN没有显式的训练过程(它只是存储了所有的训练数据),因此属于惰性学习(Lazy Learning)的代表算法。
二、KNN的基本原理
-
距离度量 距离度量是KNN的核心,它定义了样本之间“相似性”的量化方式。最常用的是欧氏距离(Euclidean Distance),它衡量的是多维空间中两点之间的直线距离。
- 二维空间例子:点 A(1, 2)和点 B(4, 6)的欧氏距离为: d = \sqrt{(1 - 4)^2 + (2 - 6)^2} = \sqrt{9 + 16} = \sqrt{25} = 5
- 三维空间例子:点 A(1, 2, 3)和点 B(4, 6, 5)的欧氏距离为: d = \sqrt{(1 - 4)^2 + (2 - 6)^2 + (3 - 5)^2} = \sqrt{9 + 16 + 4} = \sqrt{29} \approx 5.385
- 其他常用距离包括曼哈顿距离(Manhattan Distance)、切比雪夫距离(Chebyshev Distance)、闵可夫斯基距离(Minkowski Distance) 以及适用于分类数据的汉明距离(Hamming Distance)。选择哪种距离取决于数据的类型和具体应用场景。
-
分类与回归 KNN既可以用于分类任务,也可以用于回归任务。
- 分类(Classification):对于一个新的数据点,找出它的 K 个最近邻,然后采用投票法(Majority Voting),将这 K个邻居中出现次数最多的类别标签赋予新数据点。
- 例子:假设 K=3,一个新样本的三个最近邻分别属于类别
A、A、B。根据多数表决,该新样本将被分类为A。
- 例子:假设 K=3,一个新样本的三个最近邻分别属于类别
- 回归(Regression):对于一个新的数据点,找出它的 K 个最近邻,然后计算这些邻居目标值的平均值作为新数据点的预测值。
- 例子:预测房价。一个新房屋的三个最近邻的房价分别是 300k, 320k, 310k。那么该新房屋的预测房价为 (300 + 320 + 310) / 3 = 310k。
- 分类(Classification):对于一个新的数据点,找出它的 K 个最近邻,然后采用投票法(Majority Voting),将这 K个邻居中出现次数最多的类别标签赋予新数据点。
三、KNN算法步骤
KNN算法的执行步骤清晰明了:
-
数据准备与预处理:
- 收集并清洗数据(处理缺失值、异常值)。
- 关键步骤:特征标准化/归一化。由于KNN依赖于距离计算,不同特征具有不同的量纲和范围会严重影响距离结果。例如,一个范围在 $[0, 1]$ 的特征和一个范围在 [0, 1000]的特征,后者在距离计算中会占据主导地位。常见的标准化方法包括Z-score标准化: x_{new} = \frac{x - \mu}{\sigma} 和最小-最大归一化: x_{new} = \frac{x - min}{max - min}
- 例子:假设有两个特征:
年龄(20-60岁)和收入(0-100000元)。如果不标准化,收入差异(比如相差10000元)在距离计算中的影响会远大于年龄差异(相差10岁)。
-
计算距离:
- 对于需要预测的每一个新样本点,计算它与训练集中每一个样本点之间的距离(如欧氏距离)。这会产生一个距离列表或距离矩阵。
-
选择最近邻:
- 将计算出的所有距离按照从小到大排序。
- 选取距离最小的前 K 个训练样本作为新样本点的“最近邻”。
-
决策输出:
- 分类任务:统计这 K个邻居中各个类别出现的次数,将出现次数最多的类别作为预测结果(多数表决)。若出现平票,可考虑增加 K值或随机选择。
- 回归任务:计算这 K 个邻居目标值的算术平均值作为预测结果。也可考虑使用加权平均,根据距离远近赋予邻居不同的权重(距离越近权重越大)。
四、KNN的优缺点
-
优点:
- 原理简单,易于理解和实现:算法逻辑直观,入门门槛低。
- 无需显式训练过程:没有复杂的模型训练阶段,直接存储数据即可。
- 非参数化,适应性强:对数据分布没有特定假设(如线性、正态分布),可以适应复杂的数据模式。
-
缺点:
- 计算开销大:这是KNN最显著的缺点。预测时需要计算新样本与训练集中所有样本的距离。当训练集很大(N 很大)或特征维度很高(D很大)时,计算量会变得非常大,预测速度慢。其时间复杂度通常是 O(N \times D)。
- 对异常值敏感:异常点(噪声)可能因为其位置特殊而被选为最近邻,从而影响预测结果的准确性。
- 例子:在分类任务中,如果某个异常点距离新样本很近(K 较小时),它可能错误地将新样本归入其类别。
- 样本不平衡问题:当某些类别的样本数量远多于其他类别时,多数表决机制会倾向于数量多的类别,导致对稀有类别的预测效果不佳。
- 对特征尺度敏感:如前所述,不同特征的范围差异会影响距离计算,必须进行特征缩放。
- 高维空间挑战(维度灾难):随着特征维度 D 的增加,样本点在特征空间中会变得极其稀疏。欧氏距离在高维空间中可能失去意义,因为所有点之间的距离可能变得非常相似。
五、K值的选择与影响
K值是KNN算法中最重要的超参数,其选择对模型性能有直接影响:
- K 值过小(例如 K=1):
- 模型会只关注最近的极少数点,学习到的局部特征非常具体。
- 优点:决策边界更“崎岖”,能捕捉复杂模式。
- 缺点:容易受到噪声和异常值干扰,预测结果波动性大,容易过拟合(模型过于复杂,泛化能力差)。
- K 值过大:
- 模型会考虑越来越多的邻居,决策边界变得更平滑。
- 优点:减少噪声影响,预测结果更稳定。
- 缺点:可能忽略数据的局部特征,导致模型过于简单,欠拟合,预测精度下降。极端情况下(K等于整个训练集大小),对于分类问题,预测结果总是训练集中的多数类;对于回归问题,预测结果是整个训练集目标的平均值。
- 如何选择 K值?
- 没有绝对最优的 K 值,需要通过实验确定。
- 常用方法:交叉验证(Cross-Validation)。例如,使用k折交叉验证,尝试不同的 K 值(如 K= 1, 3, 5, 7, ..., 21),在验证集上评估模型性能(如分类准确率、回归的均方误差),选择性能最好的 K 值。
- 通常从较小的 K 值开始尝试(如 K=3 或 K=5)。
六、惰性学习 vs. 急切学习
- 惰性学习(Lazy Learning) - KNN:
- 特点:训练阶段仅存储训练数据,不做或做很少的计算。将主要计算工作(如距离计算)推迟到预测阶段进行。
- 优点:训练快;可以适应新的训练数据而无需重新训练整个模型。
- 缺点:预测慢;需要存储所有训练数据。
- 急切学习(Eager Learning) - 如决策树、逻辑回归、SVM:
- 特点:在训练阶段就基于训练数据构建一个显式的、概括性的模型(如决策规则、数学公式)。
- 优点:预测阶段速度快(只需应用训练好的模型);通常不需要存储原始训练数据。
- 缺点:训练阶段计算开销可能较大;添加新训练数据可能需要重新训练模型。
七、适用场景与注意事项
- 适用场景:
- 样本数量不是特别巨大(否则预测速度太慢)。
- 特征维度适中或较低(避免维度灾难)。
- 对预测速度要求不是特别高的场景。
- 需要快速原型验证或理解数据基本模式时。
- 关键注意事项:
- 数据预处理至关重要:务必进行特征标准化/归一化。
- 处理高维数据:如果特征维度很高,考虑使用特征选择或降维技术(如PCA、LDA)来减少维度,缓解维度灾难并提升效率和效果。
- 处理样本不平衡:可以采用加权投票(根据类别样本量调整权重)、过采样(如SMOTE)、欠采样或使用专门处理不平衡数据的算法。
- 选择最优 K 值:务必使用交叉验证来选择合适的 K 值。
- 考虑距离加权:在投票或平均时,根据距离的倒数或其他函数赋予近邻更高的权重,有时能提升性能。
- 使用高效数据结构:对于大型数据集,可以使用如KD-Tree、Ball Tree等空间数据结构来加速最近邻搜索,减少预测时的计算时间。
八、Python代码示例
# KNN分类示例 (使用scikit-learn)
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score
# 加载数据
iris = load_iris()
X, y = iris.data, iris.target
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 特征标准化 (非常重要!)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test) # 使用训练集的scaler转换测试集
# 创建KNN分类器 (选择K=3)
knn_classifier = KNeighborsClassifier(n_neighbors=3)
# "训练"模型 (KNN的"训练"就是存储数据)
knn_classifier.fit(X_train, y_train)
# 预测
y_pred = knn_classifier.predict(X_test)
# 评估
accuracy = accuracy_score(y_test, y_pred)
print(f"测试集准确率: {accuracy:.2f}")
# KNN回归示例 (使用scikit-learn)
from sklearn.neighbors import KNeighborsRegressor
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
# 加载数据
diabetes = load_diabetes()
X, y = diabetes.data, diabetes.target
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 特征标准化
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
# 创建KNN回归器 (选择K=5)
knn_regressor = KNeighborsRegressor(n_neighbors=5)
# "训练"模型
knn_regressor.fit(X_train, y_train)
# 预测
y_pred = knn_regressor.predict(X_test)
# 评估 (均方误差)
mse = mean_squared_error(y_test, y_pred)
print(f"测试集均方误差(MSE): {mse:.2f}")
九、总结
K近邻算法(KNN)凭借其直观性和无需训练的特点,成为机器学习入门者的首选算法之一。它通过距离度量和邻居投票/平均来进行预测。然而,其性能高度依赖于数据质量(标准化、降维)、参数选择(尤其是 K 值)以及数据本身的特性(规模、维度、平衡性)。KNN的主要瓶颈在于预测时的计算开销。在实际应用中,需要仔细权衡其优缺点,结合具体场景(如数据规模、预测速度要求)进行选择和优化。理解KNN的工作原理和调优方法,是掌握更复杂机器学习模型的重要基础。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)