E.C.2 K 近邻

KNN 会看离新样本最近的已标注样本,再让它们投票。它几乎没有训练成本,但预测时要比较距离,所以数据大时会变慢。
- Python 3.10+
- 当前稳定版
scikit-learn和numpy
python -m pip install -U scikit-learn numpy- K:参与投票的邻居数量。
- 距离度量:如何计算“近”。
- Lazy learning(惰性学习):训练时做得少,预测时做得多。
- Scaling(缩放):特征范围不一致时必须做。
运行邻居投票
Section titled “运行邻居投票”创建 knn_vote.py:
import numpy as npfrom sklearn.neighbors import KNeighborsClassifierfrom sklearn.pipeline import make_pipelinefrom sklearn.preprocessing import StandardScaler
X = np.array([ [1, 1], [2, 2], [2, 1], [8, 8], [9, 9], [8, 9],])y = np.array([0, 0, 0, 1, 1, 1])
model = make_pipeline( StandardScaler(), KNeighborsClassifier(n_neighbors=3),)
model.fit(X, y)pred = model.predict([[3, 3], [8.5, 8.2]])print("predictions:", pred.tolist())运行:
python knn_vote.py预期输出:
predictions: [0, 1]这个模型没有学复杂公式。它保存样本、缩放特征、计算距离,然后投票。
把 n_neighbors=3 改成 1 和 5。K 小时更敏感,K 大时决策更平滑。
适合尝试 KNN:
- 数据集较小。
- 特征距离有实际意义。
- 想快速做一个可解释 baseline。
- 预测延迟要求不严格。
如果数据量巨大,或者服务是高 QPS 实时预测,就不要默认用它。
Baseline 复盘
Section titled “Baseline 复盘”复盘 KNN 时,不要只看最终标签。至少保留一个预测样本的最近邻、邻居标签和距离值。这样才能看出结果来自有意义的相似性,还是被某个尺度过大的特征带偏。
KNN 很容易解释,也很容易误用。如果缩放前后预测完全不同,或者数据变大后预测延迟明显上升,就把这个限制写进 baseline 结论。
交付检查时,用同一个测试点分别运行有缩放和无缩放版本,并记录最近邻变化。这个小对比能让你判断特征距离是否可信。若距离本身没有业务意义,继续调 K 往往只是调噪声。
再记录一次预测耗时或样本数量。KNN 的成本常常出现在预测阶段,这一点要在 baseline 结论里说清楚。
如果只是小数据离线分析,KNN 很适合做解释性 baseline;如果要实时服务,就要提前说明它的预测成本。
学完这一页,至少保留这张证据卡:
- 模型家族
- SVM、KNN、朴素贝叶斯、LDA 或其他传统基线
- 数据视图
- 特征缩放、类别平衡、决策边界和训练/测试划分
- 指标
- 准确率/F1、混淆矩阵、边距、邻近行为或投影质量
- 失败检查
- 缩放、高维度、假设薄弱、泄漏或基线拟合差
- 期望产出
- 经典机器学习基线结果,以及一条局限性说明
- 忘记缩放特征。
- 以为 KNN 已经“训练好了”,忽略预测时才是主要成本。
- 没检查特征是否表达相似性,就先调 K。
添加第三个特征,数值范围在 10000 左右。移除 StandardScaler(),观察距离投票如何被扭曲。
参考实现与讲解
没有缩放时,10000 左右的大尺度特征会主导欧氏距离。这样 KNN 可能几乎只按这个大尺度特征投票,即使原来的两个特征更能描述类别模式。
好的答案会比较有无 StandardScaler() 时的预测结果,并解释哪个特征控制了距离。核心结论是:KNN 很依赖特征尺度,因为它没有学到权重来自动修正糟糕的距离几何。