新小区里一家店好不好、一套房大概值多少——你往往会先问附近几个人。
机器学习里的 KNN,干的就是同一件事。
你刚搬来,想知道楼下水果店好不好。最自然的做法不是做复杂调研,而是问离你最近的几位邻居:3 个人说「好」,2 个人说「一般」,你多半会相信「还不错」。
这里你做的是投票——多数邻居的答案,变成你的答案。后面会把它叫做「分类」。
中介报价听着虚,你会去查附近几套面积、楼层、朝向都差不多的成交价,再取个平均:比如 580、600、610 万,你心里大概就有 597 万这个数。
这里你做的是取平均——邻居们的数字,合成你的数字。后面会把它叫做「回归」。
两件事共用同一条直觉:相似的东西,答案往往也接近。机器学习把「相似」变成可计算的距离,把「问几个人」变成一个可调的数字 K——这套方法就叫 K 近邻(K-Nearest Neighbors,KNN)。
KNN:要预测一个新样本时,先在已有数据里找出与它最相似(距离最近)的 K 个样本,再根据这 K 个邻居的答案来做决定。
很多算法会先花很长时间学出一套权重。KNN 更「懒」:训练阶段往往只是把数据存起来;真正算活,发生在你送来一个新样本的那一刻。所以它也常被叫做惰性学习(lazy learning)。
找谁当邻居,两种任务一样:都按距离挑最近的 K 个。差别只在最后一步——还是用小区里那两件事:
邻居说「好、好、一般」→ 投票选好。
答案是类别:好 / 一般 / 差。
邻居成交价 580、600、610 → 平均约 597。
答案是数字:温度、销量、房价。
假设 K=3,三位最近邻居分别是:
| 邻居 | 距离 | 问水果店(分类) | 估房价(回归) |
|---|---|---|---|
| 张阿姨 | 最近 | 好 | 580 万 |
| 王叔叔 | 次近 | 好 | 600 万 |
| 李奶奶 | 第三近 | 一般 | 610 万 |
分类:好 2 票、一般 1 票 → 判为好。
回归:(580 + 600 + 610) / 3 = 597 万。
KNN 能「身兼两职」,是因为它本质上只承诺一件事:近邻的答案值得参考。标签是类别就统计票数,标签是数字就统计均值——算法骨架不变,只换了汇总方式。
生活中「近」可以指走路近、直线近;在数据里,「近」取决于你选哪把尺子。每个样本是一组数字(特征),距离就是两串数字之间的「差多少」。
直线距离,像乌鸦飞。邻域形状是圆。最直觉,也是 KNN 默认最常见的选择。
只能沿网格走,像城市街区拐弯。邻域形状是菱形。对个别维度的大偏差更「温和」一些。
一族距离的「总开关」:p=1 就是曼哈顿,p=2 就是欧氏。调 p,邻域形状会在菱形与圆形之间过渡。
把蓝点、橙点放在马路十字路口上。欧氏可以斜穿街区;曼哈顿只能贴着灰色马路先横走、再竖走(不能抄斜路)。
蓝虚线 ≈ 欧氏(抄斜路) 橙折线 ≈ 曼哈顿(贴马路)
设相邻路口之间算 1 格。从起点到终点:横走 2 格、竖走 2 格——
| 尺子 | 怎么走 | 怎么算 | 距离 | 邻域形状 |
|---|---|---|---|---|
| 欧氏 | 斜穿街区 | √(2² + 2²) | ≈ 2.83 | 圆 |
| 曼哈顿 | 先横 2 格,再竖 2 格 | |2| + |2| | 4 | 菱形 |
| 明可夫斯基 | 由 p 决定合并不同轴的差 | (|2|ᵖ + |2|ᵖ)1/p | p=1→4;p=2→2.83 | 随 p 变化 |
同一对点,曼哈顿「路程」更长——因为它不许抄斜路,必须沿网格累加。
假设用两个特征找「相似邻居」:身高(厘米)和年收入(元)。
| 身高差 | 收入差 | 欧氏距离(示意) | |
|---|---|---|---|
| 不缩放 | 5 cm | 50,000 元 | ≈ 50000(收入几乎独占) |
| 缩放到相近量级后 | 0.3 | 0.4 | ≈ 0.5(两轴都能说话) |
不缩放时,收入把距离「带跑」,身高几乎没用。所以用 KNN 前,通常要做标准化 / 归一化,让每根轴分量差不多。
可以。KNN 的骨架只要求:你能算出「两个样本有多不像」。换掉默认的欧氏距离,换成你自己的尺子,算法其余步骤(取最近 K 个 → 投票或平均)不变。
| 常见自定义想法 | 适合什么数据 | 直觉 |
|---|---|---|
| 余弦距离(1 − 余弦相似度) | 文本、高维向量 | 看方向像不像,不太看长短 |
| 加权欧氏 | 有些特征更重要 | 重要维度差一点点也算「远」 |
| 汉明距离 | 0/1、类别编码 | 数有多少位不一样 |
工具里常见两种接法:直接传入自定义函数;或先算好「两两距离矩阵」,再告诉模型「距离我已经算好了」(如 scikit-learn 的 metric='precomputed')。
你可以试试:先点「放到交界处」,再切换距离——半透明区域会在圆 / 菱形之间变化(明可夫斯基 p=3 介于两者之间);拖动 K,看邻居圈变大变小。
K 太小:只信最近一两个人,灵敏但容易被噪声带偏。
K 太大:几乎全民公投,边界变糊。
上面 Demo 里把 K 从 1 拖到 15,就能亲眼看到邻居圈变大、票数被「冲淡」。
二分类时,K 常取奇数,减少投票平票。但这只是经验,不是铁律。
和模型自己学出来的权重不同,K 必须在训练开始前由人指定——这类事先设定的旋钮叫超参数。常见做法是列出候选,比如:
把候选铺成一张「网格」、逐个试完再挑最优——正是网格搜索的思路。距离种类、是否加权,也可以一起放进网格。
想系统搞清「参数 vs 超参数」以及网格搜索怎么和交叉验证配合,请读:
没有放之四海皆准的「最佳 K」。合适的 K 取决于数据噪声、样本密度和任务目标——所以实践里我们用验证集 / 交叉验证来选,而不是凭感觉拍一个。
· 概念简单,结果好解释
· 数据量中等、特征维数不太高
· 决策边界不规则、不想先假设线性
· 需要快速搭一个「能跑的基线」
· 样本很多时,每次预测都要算距离,可能变慢
· 特征很多时,「远近」会变得模糊(维度灾难)
· 特征尺度不一,必须先缩放
· 类别极不平衡时,多数类容易「霸占」投票