☰
Python-100-Days 机器学习实战:k 最近邻(kNN)算法从原理到 scikit-learn 实现
2026/10/1 12:18:00 网站建设 项目流程
  • 文档
  • 教程

【免费下载链接】Python-100-Days

Python - 100天从新手到大师

项目地址:https://gitcode.com/GitHub_Trending/py/Python-100-Days
点击查看免费下载

本文是《Python - 100天从新手到大师》Day 82 的核心内容(对应仓库文档 82.k最近邻算法.md)。本篇指南以经典的鸢尾花数据集为主线,系统讲解 k 最近邻(kNN)算法的距离度量、数据划分、手撕实现与 scikit-learn 实现、模型评估(混淆矩阵、精确率、召回率、F1、ROC/AUC)以及网格搜索与交叉验证调参,最后扩展到 kNN 回归。读完本文,你将能够在真实数据集上独立完成 kNN 分类与回归的全流程建模与评估。

算法概述:无显式训练过程的“近朱者赤”

k 最近邻算法(k-Nearest Neighbors,简称 kNN)是一种用于分类和回归的非参数统计方法,由美国统计学家伊芙琳·费克斯(Evelyn Fix)和小约瑟夫·霍奇斯(Joseph Hodges Jr.)于 1951 年提出。它的原理非常简单:从历史数据中找到 $\small{k}$ 个跟新输入实例最邻近的实例,根据它们中多数所属的类别对新实例进行分类,或根据它们的目标值输出新实例的预测值。

与主流的机器学习算法不同,kNN 没有显式的学习训练过程,它不建立模型、不拟合参数,而是直接“用历史数据说话”,其思想可以概括为“近朱者赤,近墨者黑”。kNN 有两个关键问题:

  1. $\small{k}$ 值如何选择——用多少个最近邻来判定新实例的类别或确定目标值;
  2. 如何判定两个实例是近还是远——这就涉及到特征空间中距离的度量方式。

距离的度量:如何定义“近”与“远”

我们可以用距离(distance)来衡量特征空间中两个实例之间的相似度,常用的距离度量包括闵氏距离、马氏距离、余弦距离、编辑距离等。

闵氏距离(Minkowski Distance)是其中最具代表性的一种,对于两个 $\small{n}$ 维向量 $\small{\mathbf{x}=(x_{1}, x_{2}, \cdots, x_{n})}$ 和 $\small{\mathbf{y}=(y_{1}, y_{2}, \cdots, y_{n})}$,它们之间的距离定义为:

$$ d(\mathbf{x}, \mathbf{y}) = (\sum_{i=1}^{n}{\vert x_{i} - y_{i} \rvert}^{p})^{\frac{1}{p}} $$

其中 $\small{p \ge 1}$。虽然 $\small{p \lt 1}$ 时公式仍可计算,但不再严格满足距离的定义(三角不等式等性质失效),因此通常不被视为真正的距离。改变 $\small{p}$ 的取值,闵氏距离会退化为我们熟知的各种具体距离:

  • 当 $\small{p = 1}$ 时,闵氏距离即曼哈顿距离(Manhattan Distance):

$$ d(\mathbf{x}, \mathbf{y}) = \sum_{i=1}^{n} \lvert x_{i} - y_{i} \rvert $$

  • 当 $\small{p = 2}$ 时,闵氏距离即欧几里得距离(Euclidean Distance),也是使用最广泛的度量方式:

$$ d(\mathbf{x}, \mathbf{y}) = \sqrt{\sum_{i=1}^{n}(x_{i} - y_{i})^{2}} $$

  • 当 $\small{p \to \infty}$ 时,闵氏距离即切比雪夫距离(Chebyshev Distance),它只关心所有维度上差距的最大值:

$$ d(\mathbf{x}, \mathbf{y}) = \underset{i}{max}(\lvert x_{i} - y_{i} \rvert) $$

在使用 kNN 做分类时,我们的数据集通常都是数值型数据,此时直接使用欧几里得距离是一个不错的选择。下图直观展示了欧几里得距离与曼哈顿距离的几何含义差异:

补充说明:在 kNN 的分类场景中,如果特征之间存在量纲差异(例如一个特征单位是“厘米”、另一个是“千克”),直接计算距离会导致量纲大的特征主导结果,实际应用中通常需要先对特征做标准化或归一化处理。本文使用的鸢尾花数据集四个特征同以厘米为单位,量纲一致,因此可以直接计算欧氏距离。

数据集介绍:机器学习入门的经典——鸢尾花数据集

接下来隆重介绍本文后续会持续使用的重要数据集——鸢尾花数据集(iris dataset)。它是机器学习领域中最著名、最经典的数据集之一,由植物学家 Edgar S. Anderson 在加拿大魁北克加斯帕半岛采集,由英国统计学家 Ronald A. Fisher 于 1936 年在论文《The Use of Multiple Measurements in Taxonomic Problems》中首次引入,被广泛用于机器学习算法的入门和实验。

鸢尾花数据集共有150 条样本,包含 3 种类型的鸢尾花,分别是:

  • 山鸢尾(Iris setosa)
  • 多彩鸢尾(Iris versicolor)
  • 维吉尼亚鸢尾(Iris virginica)

每种各 50 条样本。每条样本包含4 个特征(features)和 1 个类别标签(class label):

特征名含义单位
Sepal length花萼长度厘米
Sepal width花萼宽度厘米
Petal length花瓣长度厘米
Petal width花瓣宽度厘米

数据集中的类别标签取值为 0、1、2,分别对应上述三种鸢尾花类型。

数据集的加载

我们可以通过著名的 Python 机器学习库 scikit-learn 来加载这个数据集。scikit-learn 包含了各种分类、回归、聚类算法,同时还提供了多层感知机、支持向量机、随机森林等模型,覆盖了从数据预处理、特征工程到模型训练、模型评估和参数调优等各项功能。由于后续课程基本都会用到这个库,推荐先安装它:

pip install scikit-learn

如果你已经打开了 IPython 或 Jupyter 但尚未安装 scikit-learn,可以通过下面的魔法指令来安装:

%pip install scikit-learn

安装完成后,用下面的代码加载鸢尾花数据集并查看数据集的介绍:

from sklearn.datasets import load_iris # 加载鸢尾花数据集 iris = load_iris() # 查看数据集的介绍 print(iris.DESCR)

接下来,获取数据集中的特征和标签:

# 特征(150行4列的二维数组,分别是花萼长、花萼宽、花瓣长、花瓣宽) X = iris.data # 标签(150个元素的一维数组,包含0、1、2三个值分别代表三种鸢尾花) y = iris.target

如果希望更直观地查看鸢尾花数据集,可以用上面获得的特征和标签创建一个 pandas 的 DataFrame 对象,有兴趣的读者可以自己动手试一试。

数据集的划分

通常,我们需要将原始数据划分成训练集和测试集:训练集用于训练模型,测试集用于检验模型训练效果。对于鸢尾花数据集,我们可以选择 80% 的数据(120 条)作为训练集,保留 20% 的数据(30 条)作为测试集。下面的代码用 NumPy 实现了对数据集的划分:

import numpy as np # 将特征和标签堆叠到同一个数组中 data = np.hstack((X, y.reshape(-1, 1))) # 通过随机乱序函数将原始数据打乱 np.random.shuffle(data) # 选择80%的数据作为训练集 train_size = int(y.size * 0.8) train, test = data[:train_size], data[train_size:] X_train, y_train = train[:, :-1], train[:, -1] X_test, y_test = test[:, :-1], test[:, -1]

更简便的划分方式是用 scikit-learn 封装好的train_test_split函数,只需传入特征和标签,并指定train_size或test_size:

from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, random_state=3)

train_test_split返回一个四元组,四个元素分别代表:用于训练的特征、用于训练的标签、用于测试的特征、用于测试的标签。

说明:train_test_split函数的random_state参数可以理解成随机数的种子。如果使用相同的随机数种子,那么划分出的训练集和测试集也是完全相同的,这保证了实验的可复现性。

kNN 分类的实现

下面我们先不用 scikit-learn,而是用基础的数据科学库 NumPy 和 SciPy 手撕 kNN 算法。这样做的目的是帮助大家更好地理解算法原理,在此基础上再感受 scikit-learn 的强大,并明白其中类、函数、参数为什么要如此设定。

基于 NumPy 的手撕实现

kNN 算法需要计算距离,先设计一个计算两个数据点欧氏距离的函数:

import numpy as np def euclidean_distance(u, v): """计算两个n维向量的欧式距离""" return np.sqrt(np.sum(np.abs(u - v) ** 2))

接下来,设计根据邻居的标签为新数据生成标签的函数:

from scipy import stats def make_label(X_train, y_train, X_one, k): """ 根据历史数据中k个最近邻为新数据生成标签 :param X_train: 训练集中的特征 :param y_train: 训练集中的标签 :param X_one: 待预测的样本(新数据)特征 :param k: 邻居的数量 :return: 为待预测样本生成的标签(邻居标签的众数) """ # 计算x跟每个训练样本的距离 distes = [euclidean_distance(X_one, X_i) for X_i in X_train] # 通过一次划分找到k个最小距离对应的索引并获取到相应的标签 labels = y_train[np.argpartition(distes, k - 1)[:k]] # 获取标签的众数 return stats.mode(labels).mode

说明:np.partition函数可以对数组进行一次划分,将 k 个比较小的元素放在数组的左边,n - k 个比较大的元素放在数组的右边,跟快速排序算法中做一次划分操作的效果是一样的。np.argpartition则是把 k 个较小元素的索引放在数组的左边,这样经过[:k]切片操作和对y_train的花式索引运算,就可以获得 k 个跟新数据距离较小的样本对应的标签。最后,通过 scipy 的 stats 模块的mode函数获得标签的众数,并用它作为给新数据预测的类别标签。

在完成上述准备工作后,用 k 最近邻做预测的函数就呼之欲出了:

def predict_by_knn(X_train, y_train, X_new, k=5): """ KNN算法 :param X_train: 训练集中的特征 :param y_train: 训练集中的标签 :param X_new: 待预测的样本构成的数组 :param k: 邻居的数量(默认值为5) :return: 保存预测结果(标签)的数组 """ return np.array([make_label(X_train, y_train, X, k) for X in X_new])

用上面准备好的鸢尾花训练集和测试集做实验,看看predict_by_knn能否很好地运转起来。下面的y_pred是函数预测出的 30 条鸢尾花类型,跟y_test做比较即可看到预测效果:

y_pred = predict_by_knn(X_train, y_train, X_test) y_pred == y_test

输出:

array([ True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, False, True, True, True, True, True, True, True, True, True, True, True, True, True])

结果中有一个False,表示这条样本的预测标签与真实标签不同,即准确率为 $\small{\frac{29}{30}}$,也就是96.67%。当然,如果你划分训练集和测试集时使用的random_state与上文不同,得到的结果可能会有所不同。

基于 scikit-learn 的实现

使用 scikit-learn 实现 kNN 分类要简单得多,基本上只需要三个动作:

from sklearn.neighbors import KNeighborsClassifier # 创建模型 model = KNeighborsClassifier() # 训练模型 model.fit(X_train, y_train) # 预测结果 y_pred = model.predict(X_test)

看看输出结果,是不是跟我们自己手撕的代码完全一致:

y_pred == y_test

输出:

array([ True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, False, True, True, True, True, True, True, True, True, True, True, True, True, True])

当然,scikit-learn 考虑得比手撕代码丰富得多,例如想知道模型的预测准确率(accuracy),可以直接调用score方法:

model.score(X_test, y_test)

输出:

0.9666666666666667

模型评估:准确率之外还要看什么

评价一个分类器的预测效果是否良好,不能只看准确率——在类别不平衡的情况下,准确率很可能误导你的判断。例如,若测试数据中维吉尼亚鸢尾花的样本数量非常小,即便模型根本无法判断维吉尼亚鸢尾花,模型也会表现出很高的准确率。因此,我们还需要考察查准率(精确率)、查全率(召回率)、F1 分数等指标,而混淆矩阵则是用于详细展示分类模型性能的工具,适用于二分类和多分类任务。

二分类问题的混淆矩阵与指标

二分类问题的混淆矩阵如下所示:

预测为正类(Positive)预测为负类(Negative)
实际为正类(Positive)True Positive(TP)False Negative(FN)
实际为负类(Negative)False Positive(FP)True Negative(TN)

用一个例子说明如何利用混淆矩阵计算评估指标:假设某种医学检测系统用于预测是否患有某种疾病,类别“正类”表示“患病”,类别“负类”表示“未患病”,1000 个样本的测试结果如下:

预测为患病预测为未患病
实际为患病80(TP)20(FN)
实际为未患病30(FP)870(TN)

由此可以计算出以下六类指标:

  1. 准确率(Accuracy)——所有样本中预测正确的比例:

$$ \text{Accuracy} = \frac{\text{TP} + \text{TN}}{\text{TP} + \text{FP} + \text{FN} + \text{TN}} $$

上面的例子:$\frac{80 + 870}{80 + 30 + 20 + 870} = \frac{950}{1000} = 0.95$。

  1. 精确率(Precision)——在所有被预测为正类的样本中,实际属于正类的比例,也称查准率:

$$ \text{Precision} = \frac{\text{TP}}{\text{TP} + \text{FP}} $$

上面的例子:$\frac{80}{80 + 30} = \frac{80}{110} = 0.73$。

  1. 召回率(Recall)——在所有实际为正类的样本中,被模型正确预测为正类的比例,也称查全率或真正例率(True Positive Rate):

$$ \text{Recall} = \frac{\text{TP}}{\text{TP} + \text{FN}} $$

上面的例子:$\frac{80}{80 + 20} = \frac{80}{100} = 0.8$。

  1. F1 分数(F1 Score)——精确率和召回率的调和平均数,在两者之间寻求平衡,尤其适用于两者有权衡取舍的情况:

$$ \text{F1 Score} = \frac{2}{\frac{1}{\text{Precision}} + \frac{1}{\text{Recall}}} = 2 \times \frac{\text{Precision} \times \text{Recall}}{\text{Precision} + \text{Recall}} $$

上面的例子:$2 \times \frac{0.7273 \times 0.8}{0.7273 + 0.8} = 0.76$。

  1. 特异度(Specificity)和假正例率(False Positive Rate,FPR)——特异度衡量在所有实际为负类的样本中,被模型正确预测为负类的比例,类似于针对负类样本的“召回率”:

$$ \text{Specificity} = \frac{\text{TN}}{\text{TN} + \text{FP}} \\ \text{FPR} = 1 - \text{Specificity} $$

上面的例子,模型预测的特异度为:$\frac{870}{870 + 30} = \frac{870}{900} = 0.97$。

  1. ROC 曲线与 AUC 值:
    • ROC(Receiver Operating Characteristic Curve)绘制了召回率(TPR)与假正例率(FPR)的关系,是衡量分类器阈值特性的标准工具;
    • AUC(Area Under the Curve)是 ROC 曲线下的面积,衡量模型区分正类和负类的能力。AUC 的取值范围是 $\small[0, 1]$,值越接近 1 表示模型区分正负类的能力越强。其中:0.5 < AUC < 1说明模型优于随机猜测,只要妥善设置阈值就具备预测价值;AUC = 0.5说明模型与随机猜测无异,没有预测价值;AUC < 0.5说明模型比随机猜测还差,但只要总能反向预测,其实际效果反而优于随机猜测。

多分类问题的混淆矩阵与评估报告

对于多分类问题,混淆矩阵的行数和列数都等于类别数,是一个 $\small{n \times n}$ 的方阵。根据上面鸢尾花数据集的预测结果,先输出真实值和预测值:

print(y_test) print(y_pred)

输出:

[0 0 0 0 0 2 1 0 2 1 1 0 1 1 2 0 1 2 2 0 2 2 2 1 0 2 2 1 1 1] [0 0 0 0 0 2 1 0 2 1 1 0 1 1 2 0 2 2 2 0 2 2 2 1 0 2 2 1 1 1]

可以制作出如下的三分类混淆矩阵:

预测为类别0(山鸢尾)预测为类别1(多彩鸢尾)预测为类别2(维吉尼亚鸢尾)
实际为类别0(山鸢尾)1000
实际为类别1(多彩鸢尾)091
实际为类别2(维吉尼亚鸢尾)0010

用 scikit-learn 的confusion_matrix和classification_report函数输出混淆矩阵和评估报告:

from sklearn.metrics import classification_report, confusion_matrix # 输出分类模型混淆矩阵 print('混淆矩阵: ') print(confusion_matrix(y_test, y_pred)) # 输出分类模型评估报告 print('评估报告: ') print(classification_report(y_test, y_pred))

输出:

混淆矩阵: [[10 0 0] [ 0 9 1] [ 0 0 10]] 评估报告: precision recall f1-score support 0 1.00 1.00 1.00 10 1 1.00 0.90 0.95 10 2 0.91 1.00 0.95 10 accuracy 0.97 30 macro avg 0.97 0.97 0.97 30 weighted avg 0.97 0.97 0.97 30

如果希望用可视化的方式输出混淆矩阵:

import matplotlib.pyplot as plt from sklearn.metrics import ConfusionMatrixDisplay # 创建混淆矩阵显示对象 cm_display_obj = ConfusionMatrixDisplay(confusion_matrix(y_test, y_pred), display_labels=iris.target_names) # 绘制并显示混淆矩阵 cm_display_obj.plot(cmap=plt.cm.Reds) plt.show()

说明:iris.target_names就是类别标签 0、1、2 对应的三种鸢尾花的英文名。

对于二分类问题,如果要绘制 ROC 曲线并显示 AUC 值,可以使用如下代码:

from sklearn.metrics import roc_curve, auc from sklearn.metrics import RocCurveDisplay # 手动构造一组真实值和对应的预测值 y_test_ex = np.array([0, 0, 0, 1, 1, 0, 1, 1, 1, 0]) y_pred_ex = np.array([1, 0, 0, 1, 1, 0, 1, 1, 0, 1]) # 通过roc_curve函数计算出FPR(假正例率)和TPR(真正例率) fpr, tpr, _ = roc_curve(y_test_ex, y_pred_ex) # 通过auc函数计算出AUC值并通过RocCurveDisplay类绘制图形 RocCurveDisplay(fpr=fpr, tpr=tpr, roc_auc=auc(fpr, tpr)).plot() plt.show()

参数调优:k 值选择与网格搜索交叉验证

之前说过,kNN 算法有两个关键问题:距离的度量与 k 值的选择。使用 scikit-learn 的KNeighborsClassifier创建分类器模型时,可以对模型的超参数进行设置,这里有几个比较重要的参数:

参数默认值含义与说明
n_neighbors5近邻的数量,即 kNN 算法中 k 的值
weightsuniform可选uniform或distance:前者所有样本权重相同,后者距离越近权重越高;也可传入自定义函数确定每个样本的权重
algorithmauto可选auto、ball_tree、kd_tree、brute,见下方详解
leaf_size30使用ball_tree或kd_tree时,限制树结构叶子节点的最大样本数量,影响树的构建和节点查找性能
p2闵可夫斯基距离公式中的 p,默认值为 2,即计算欧氏距离

algorithm四个选项的差异如下:

  • ball_tree:一种基于球体划分的树形结构,将数据点分配到层次化的树结构中,在高维数据和稀疏数据场景下有较好的性能;
  • kd_tree:通过选择一个维度将空间划分为若干个子区域再进行搜索,从而避免跟所有邻居逐一比较,对于低维度和空间分布均匀的数据效果较好,但在高维空间会遇到维度灾难问题;
  • auto:根据输入数据的维度自动选择ball_tree或kd_tree;
  • brute:使用暴力搜索(穷举法),处理小数据集时是简单而有效的选择。

网格搜索与 K-Fold 交叉验证

可以使用网格搜索(Grid Search)和交叉验证(Cross Validation)的方式对超参数进行调整,评估模型的泛化能力,提升预测效果:

  • 网格搜索:通过穷举法遍历给定的超参数空间,找到最优的超参数组合;
  • 交叉验证:将训练集分成多个子集,通过在不同的训练集和验证集上进行多次训练和评估,对模型的预测效果进行综合评判。

K-Fold 交叉验证是最常用的交叉验证方法:将数据集划分为 K 个子集,每次选取其中一个子集作为验证集,剩下的 K-1 个子集作为训练集,对每个子集重复这个过程,完成 K 次训练和评估,并将平均值作为模型的最终性能评估:

下面的代码用 scikit-learn 的GridSearchCV做网格搜索和交叉验证,寻找针对鸢尾花数据集实施 kNN 的最优参数:

from sklearn.model_selection import GridSearchCV # 网格搜索交叉验证 gs = GridSearchCV( estimator=KNeighborsClassifier(), param_grid={ 'n_neighbors': [1, 3, 5, 7, 9, 11, 13, 15], 'weights': ['uniform', 'distance'], 'p': [1, 2] }, cv=5 ) gs.fit(X_train, y_train)

通过下面的代码获得最优参数及其评分:

print('最优参数:', gs.best_params_) print('评分:', gs.best_score_)

输出:

最优参数: {'n_neighbors': 5, 'p': 2, 'weights': 'uniform'} 评分: 0.9666666666666666

如果需要使用训练好的最优模型进行预测:

gs.predict(X_test)

kNN 回归:从分类走向数值预测

kNN 算法通常用于解决分类问题,当然也可以用于解决回归问题,其基本思想与分类一致:找出跟新实例最近的 k 个邻居,然后根据这 k 个邻居的目标值预测新实例的目标值。与分类不同,回归预测的是一个数值而非类别标签,因此通常会使用平均或加权平均的方式处理 k 个邻居的目标值。

回到上一课 81.浅谈机器学习.md 中用月收入预测网购支出的例子,用 scikit-learn 的KNeighborsRegressor构建回归模型。先做一些准备工作:

# 每月收入 incomes = np.array([ 9558, 8835, 9313, 14990, 5564, 11227, 11806, 10242, 11999, 11630, 6906, 13850, 7483, 8090, 9465, 9938, 11414, 3200, 10731, 19880, 15500, 10343, 11100, 10020, 7587, 6120, 5386, 12038, 13360, 10885, 17010, 9247, 13050, 6691, 7890, 9070, 16899, 8975, 8650, 9100, 10990, 9184, 4811, 14890, 11313, 12547, 8300, 12400, 9853, 12890 ]) # 每月网购支出 outcomes = np.array([ 3171, 2183, 3091, 5928, 182, 4373, 5297, 3788, 5282, 4166, 1674, 5045, 1617, 1707, 3096, 3407, 4674, 361, 3599, 6584, 6356, 3859, 4519, 3352, 1634, 1032, 1106, 4951, 5309, 3800, 5672, 2901, 5439, 1478, 1424, 2777, 5682, 2554, 2117, 2845, 3867, 2962, 882, 5435, 4174, 4948, 2376, 4987, 3329, 5002 ]) X = np.sort(incomes).reshape(-1, 1) # 将收入排序后处理成二维数组 y = outcomes[np.argsort(incomes)] # 将网购支出按照收入进行排序

说明:即便只有一个特征,我们也需要将自变量处理成二维数组的形式,因为训练模型时使用的fit方法并不能接受一维数组作为它的第一个参数。这里提前对 X 和 y 排序,是为了待会绘制散点图和折线图时根据 X 的值从小到大依次绘制。

接下来创建 kNN 回归模型:

from sklearn.neighbors import KNeighborsRegressor # 创建模型 model = KNeighborsRegressor() # 训练模型 model.fit(X, y) # 预测结果 y_pred = model.predict(X)

上面直接用所有历史数据进行模型训练,通过绘制图形来看看预测效果:

# 原始数据散点图 plt.scatter(X, y, color='navy') # 预测结果折线图 plt.plot(X, y_pred, color='coral') plt.show()

从图中可以看出,kNN 回归的本质是对样本点做局部的平滑拟合。kNN 回归模型计算复杂度较高,而且对噪声数据非常敏感,在很多场景下它可能并不是一个很好的选择。至于如何评估一个回归模型的效果(如 MSE、R² 等指标),后续章节(参见 85.回归模型.md)会继续讲解。

总结:kNN 的优势与局限

kNN 算法是一种简单但不失强大的机器学习算法,适用于小数据集上的分类和回归任务。它的优点非常突出:

  • 简单易懂:思想朴素(“近朱者赤,近墨者黑”),无需复杂的模型解释;
  • 没有显式的训练过程:不需要拟合参数,新数据到来时即时计算;
  • 不依赖于对数据分布的假设:可以适应复杂的数据模式。

但 kNN 的缺点也同样明显,选择算法前务必权衡:

  1. 计算效率问题:预测时需要计算新样本与所有训练样本的距离,数据集较大时效率低下,是 kNN 最大的短板;
  2. 对噪声敏感:个别噪声样本可能直接影响近邻的选择结果;
  3. 预测结果依赖 k 的取值:k 值过小容易过拟合(只看到局部少数邻居),k 值过大又容易欠拟合(被较远的邻居干扰);
  4. 受样本均衡性影响:不均衡的样本可能导致类别偏倚(Class Imbalance Bias)——如果大部分训练样本属于类别 A 而类别 B 样本较少,分类时 kNN 更容易将测试样本预测为类别 A。

掌握 kNN 之后,建议继续深入学习仓库中的后续章节:83.决策树和随机森林.md、84.朴素贝叶斯算法.md、85.回归模型.md,并在 90.机器学习实战.md 中将所学算法应用到真实业务数据集上,完成从理论到实战的完整闭环。

  • 文档
  • 教程

【免费下载链接】Python-100-Days

Python - 100天从新手到大师

项目地址:https://gitcode.com/GitHub_Trending/py/Python-100-Days
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询