跳到主要内容

K 近邻

本节定位

KNN 是特别适合建立直觉的一类算法。
它几乎没有复杂训练过程,核心想法非常直接:

看一个新样本周围最像它的几个邻居是谁,再根据邻居做判断。

也正因为它足够直接,KNN 很适合帮助你理解:

  • 距离度量
  • 特征缩放
  • 局部决策

这些经典机器学习里非常重要的概念。

学习目标

  • 理解 KNN 的基本工作机制
  • 理解为什么 K 值和距离度量会显著影响结果
  • 理解为什么特征缩放对 KNN 尤其关键
  • 通过可运行示例掌握 KNN 的最小使用方式

一、KNN 到底在做什么?

1.1 它几乎不“训练”

KNN 经常被称为:

  • lazy learner

因为它不像很多模型那样在训练阶段学出一套明确参数。
它更像是:

  • 把样本先记住
  • 等预测时再去找最近邻居

1.2 为什么它很容易理解?

因为逻辑真的非常像人类直觉:

  • 这个新客户和谁最像?
  • 这张新图片和哪几张旧图片最像?

如果最近的几个样本大多属于某类,
新样本也大概率属于那类。

1.3 一个类比

KNN 就像问:

  • “你附近的邻居大多是什么职业?”

如果周围 5 个最像的人里 4 个都是工程师,
你会倾向判断这个人也更像工程师。


二、K 值为什么这么关键?

2.1 K 太小

如果 K=1,模型非常敏感:

  • 容易被噪声点影响

2.2 K 太大

如果 K 很大,
模型会变得过于平均:

  • 局部差异被抹平

2.3 所以 K 本质上在控制什么?

可以先粗略理解成:

  • K 小:更看局部细节
  • K 大:更看整体平均

这就是为什么 KNN 虽然简单,但也很依赖调参。


三、先跑一个真正能体现“邻居投票”的示例

import numpy as np
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier

X = np.array([
[1, 1],
[2, 2],
[2, 1],
[8, 8],
[9, 9],
[8, 9],
])
y = np.array([0, 0, 0, 1, 1, 1])

clf = make_pipeline(
StandardScaler(),
KNeighborsClassifier(n_neighbors=3),
)

clf.fit(X, y)
pred = clf.predict([[3, 3], [8.5, 8.2]])
print(pred.tolist())

3.1 这段代码最想让你抓住什么?

它抓住了 KNN 的两个最基本点:

  1. n_neighbors=3
    就是在说“看 3 个最近邻”
  2. StandardScaler()
    表示距离度量前先做特征缩放

3.2 为什么 KNN 也要配缩放?

因为它的核心就是距离。
如果某一维数值范围远大于另一维,这一维会在距离里占主导。

例如:

  • 年龄是几十
  • 收入是几十万

不缩放时,收入这维会几乎“吞掉”年龄这维。


四、KNN 的优点和代价是什么?

4.1 优点

  • 思路直观
  • 训练阶段很轻
  • 常作为小数据基线很好用

4.2 代价

  • 预测阶段可能更慢
  • 数据量大时查询成本高
  • 对特征缩放和距离定义很敏感

4.3 一个工程判断

KNN 很适合:

  • 样本不大
  • 特征不复杂
  • 想先快速建立可解释基线

不太适合:

  • 超大样本量
  • 高实时性推理要求

五、距离度量为什么值得注意?

5.1 欧氏距离只是最常见的一种

很多时候默认用的是:

  • Euclidean distance

但不同任务可能也会考虑:

  • Manhattan distance
  • Cosine distance

5.2 为什么距离定义会改变模型行为?

因为 KNN 的全部判断都建立在:

  • “谁更近”

如果“近”的定义变了,邻居集合也会变。


六、KNN 最容易踩的坑

6.1 误区一:数据一大还默认上 KNN

预测阶段查邻居会越来越贵。

6.2 误区二:忘了缩放

这和 SVM 一样,也是高频坑。

6.3 误区三:只调 K,不看特征质量

如果特征本身没有区分度,
K 调得再漂亮也很难救。


小结

这节最重要的,是把 KNN 看成一个“局部投票”模型:

它不靠复杂参数学习,而是靠距离度量和邻居投票来做判断,所以特别适合帮助你建立对距离、特征缩放和局部决策的直觉。

只要这层理解清楚了,KNN 就不再只是入门算法,而会变成你做基线时很有用的一把小工具。


练习

  1. n_neighbors 改成 15,看看预测会不会变化。
  2. 去掉 StandardScaler() 再试一次,观察结果差异。
  3. 想一想:为什么说 KNN 的“训练很轻”,但“预测可能不轻”?
  4. 你会在什么样的项目里优先试 KNN 作为基线?