本节你会学到
- 说清楚「KNN 分类:让距离最近的样本帮忙投票」解决的核心问题
- 知道它在「传统机器学习基础(21~34)」中的位置
- 把 分类与回归、机器学习、监督学习 这些关键词联系起来
理解 K 近邻算法的核心思想、距离度量、K 值选择、特征缩放和 sklearn 训练流程。
学习路线:传统机器学习基础(21~34) · 第 30 课
补齐文本分类项目背后的数据处理、算法、评估和模型选择知识。
学完本阶段你能做到:拿到一份结构化表格数据,独立完成清洗、特征工程、建模、评估、模型选择和保存,并用规范流程比较多个算法。
推荐读法:建议系统读。做项目时遇到数据和模型选择问题,也可以按需回查。
查看完整阶段 · 14 篇先看目标,再带着问题读正文。读完后用练习确认自己真的理解了。
合格答案不是复述标题,而是说清它在“数据进入模型、模型学习、结果评估或项目落地”中的作用。
它应该为下一课「决策树是什么?模型怎样一步步做判断」铺路:读完后要知道下一课为什么自然出现。
学完逻辑回归后,我们已经见过一种“先学习一组参数,再用公式预测”的分类模型。KNN 的思路完全不一样:它几乎不在训练阶段学习参数,而是在预测时临时去看“离我最近的样本都是什么类别”。
一句话理解 KNN:新样本属于哪一类,先看它附近的 K 个老样本怎么投票。
假设我们想根据电影的“打斗镜头数”和“接吻镜头数”判断电影类型。历史数据里已经有一些标好标签的电影,新电影来了以后,KNN 会做三件事:
如果 K=3,最近的三部电影分别是“动作片、动作片、爱情片”,那么投票结果就是动作片。这个过程很直观,所以 KNN 经常适合作为初学者理解分类任务的第一批算法。
很多模型的训练阶段比较重:比如逻辑回归要反复优化参数,神经网络要进行多轮前向传播和反向传播。KNN 不一样,它的 fit() 更像是把训练数据保存起来。
真正的工作发生在 predict():
这就是为什么 KNN 看起来简单,但在大数据量、高维数据场景下不一定高效。
KNN 的核心是“近”。问题是,近不近由距离决定。最常见的是欧氏距离:
distance = sqrt((x1 - y1)^2 + (x2 - y2)^2 + ...)
如果只有两个特征,可以把样本想象成平面上的点。两个点之间越近,模型就认为它们越相似。
但这里有一个很容易踩的坑:特征量纲会影响距离。
比如预测房屋类型时:
如果直接计算距离,面积这个特征的数值跨度更大,它会在距离里占主导。房间数量虽然也重要,却可能被面积“盖住”。所以 KNN 通常需要标准化或归一化。
特征缩放的目标不是让模型“更高级”,而是让不同特征在距离计算中更公平。
常见做法有两种:
| 方法 | 结果 | 常见场景 |
|---|---|---|
| 标准化 StandardScaler | 均值约为 0,标准差约为 1 | 通用、常配合 KNN/SVM/线性模型 |
| 归一化 MinMaxScaler | 缩放到 0 到 1 | 明确希望特征落在固定范围时 |
注意,缩放器必须只在训练集上 fit,测试集只能 transform。如果你在测试集上重新 fit,就会让测试集信息提前泄漏进预处理过程,评估结果会虚高。
K 是 KNN 最核心的超参数。
实际项目里不要凭感觉拍脑袋,建议在验证集或交叉验证里比较多个 K 值,例如 3、5、7、9、11,再选择表现稳定的那个。
下面用鸢尾花数据集演示 KNN 分类。重点看 Pipeline:它把标准化和模型放在一起,避免训练、测试流程不一致。
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import classification_report
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y,
)
pipe = Pipeline(steps=[
("scaler", StandardScaler()),
("knn", KNeighborsClassifier(n_neighbors=5, weights="distance")),
])
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)
print("测试集准确率:", pipe.score(X_test, y_test))
print(classification_report(y_test, y_pred))
这里的 weights="distance" 表示距离越近的邻居投票权重越高。默认的 uniform 是每个邻居一票,distance 则更相信近处的样本。
名字都带 K,很容易混。
| 算法 | 任务类型 | 是否需要标签 | 核心动作 |
|---|---|---|---|
| KNN | 监督学习 | 需要 | 找最近邻并投票 |
| K-Means | 无监督学习 | 不需要 | 把样本聚成 K 组 |
KNN 是拿带标签的历史样本预测新样本。K-Means 是在没有标签时,把数据自动分组。一个是分类或回归算法,一个是聚类算法。
KNN 的优点是直观、容易实现、几乎没有训练成本,对非线性边界也有一定表达能力。只要数据局部结构清晰,它就能给出不错的结果。
但它也有明显局限:
所以 KNN 很适合理解“基于样本相似度的预测”,但并不总是生产环境的首选模型。
只要模型依赖距离,就要优先检查特征尺度。KNN、SVM、K-Means、神经网络输入通常都需要关注缩放。
正确流程是:
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)
使用 Pipeline 后,sklearn 会帮你把这件事封装好。
KNN 的效果很依赖 K。至少比较几个候选值,再结合验证集结果选择。
把示例代码里的 n_neighbors 分别改成 1、3、5、9,记录测试集准确率。再把 weights 从 "distance" 改成 "uniform",观察结果有没有变化。
下一课我们学习决策树。它不再用“距离”判断相似,而是像做问卷一样,连续提出一系列问题,把样本一步步分到叶节点。
留言讨论