K近邻算法简介
K近邻算法(KNN)是一个经典算法,分类 和 回归任务都支持,给定一个样本,根据与训练集样本的距离最近的 $K$ 个样本,来判断该样本的类别。
核心概念
- 解决问题:分类问题、回归问题
- 算法思想:若一个样本在特征空间中的 $K$ 个最相似的样本大多数属于某一个类别,则该样本也属于这个类别。
- 如何找?:距离度量(api底层自动计算),默认欧氏距离
怎么预测
- 先算未知样本和所有训练样本的距离,并按从近到远排序。
- 取最近的 $K$ 个邻居。
- 如果是分类,就看这 $K$ 个邻居里哪个类别人最多,投票决定结果。
- 如果是回归,就把这 $K$ 个邻居的目标值取平均,作为预测结果。
数据集划分
将数据切分成了四块,是进行任何监督学习(如KNN、线性回归等)的标准步骤.

- x_train(训练集特征):训练时输入给模型的特征数据,KNN 会基于它和新样本计算距离,通常占数据集的大部分。
- y_train(训练集标签):
x_train对应的标准答案,KNN 会保存特征与标签的对应关系。 - x_test(测试集特征):训练完成后用于预测的输入数据,相当于考题,训练阶段不能提前使用。
- y_test(测试集标签):
x_test对应的真实答案,用来和预测结果比对并计算准确率等评估指标。
KNN算法API
sklearn框架
scikit-learn 是一个基于 Python 的机器学习框架,它提供了丰富的算法实现,以及方便的工具函数。
官网地址:https://scikit-learn.org/stable/
安装:
pip install scikit-learn
导包:
from sklearn import datasets
分类任务
# 1.导包 此处导入knn分类模型
from sklearn.neighbors import KNeighborsClassifier
# 2.准备数据(此处我们模拟数据)
# 先模拟特征数据x
x_train = [[0], [1], [2], [3]]
x_test = [[4]]
# 再模拟标签数据y (假设2分类 1:垃圾邮件,0正常邮件)
y_train = [0, 0, 0, 1] # 此时0和1是标签索引
# TODO 3.需求: 预测x_test中的4属于垃圾邮件还是正常邮件
# todo 3.1 创建分类模型
# 注意: 如果两个邻居,一个是垃圾,一个是正常邮件,底层根据标签索引选择小的
# 初始化了一个 K近邻(KNN)分类器模型,参数 n_neighbors(即邻居数量 K 值)设置为了 3
# n_neighbors=3:这是 KNN 算法中最关键的参数,代表在预测新数据点时,
# 算法会去寻找距离最近的 3个 训练样本(邻居)来进行投票决策
knn_model = KNeighborsClassifier(n_neighbors=3)
# todo 3.2 模型训练
knn_model.fit(x_train, y_train)
# todo 3.3 模型预测
y_pred = knn_model.predict(x_test)
# todo 3.4 打印预测结果
# 4距离 321最近,这三个y轴中两个是0,所以预测结果是0
print(f"分类预测结果为:{y_pred}") # [0]
回归任务
# 1.导包 此处导入knn回归模型
from sklearn.neighbors import KNeighborsRegressor
# 2.准备数据(此处我们模拟数据)
# 先模拟特征数据x
x_train = [[0], [1], [2], [3]]
x_test = [[4]]
# 再模拟标签数据y (房价0->70w,1->80w,2->100w,3->110w)
y_train = [70, 80, 100, 110] # 此时就是房价
# TODO 3.需求: 预测x_test中的4对应房价
# todo 3.1 创建分类模型
knn_model = KNeighborsRegressor(n_neighbors=3)
# todo 3.2 模型训练
knn_model.fit(x_train, y_train)
# todo 3.3 模型预测
y_pred = knn_model.predict(x_test)
# todo 3.4 打印预测结果
print(f"分类预测结果为:{y_pred}") # [96.66666667]
距离度量
距离度量(distance measure) - 常见距离公式
| 距离类型 | 核心思想 | 关键参数 | 对异常值敏感性 | 适用场景 |
|---|---|---|---|---|
| 欧氏距离 | 直线最短距离 | $p=2$ | 高 | 连续数值、低维空间(如KNN) |
| 曼哈顿距离 | 各维度差异绝对值之和 | $p=1$ | 中 | 高维稀疏数据、离散特征 |
| 切比雪夫距离 | 仅关注最大维度差异 | $p \to \infty$ | 极高 | 极值主导场景(如路径规划) |
| 闵可夫斯基距离 | 通过 $p$ 灵活调整距离性质 | 任意 $p$ | 依赖 $p$ | 需平衡不同维度差异的通用场景 |
什么时候更适合欧氏距离和曼哈顿距离?
可以先把它们想成两种“量距离”的方式:
- 欧氏距离:像拿尺子直接量两点之间的直线距离。
- 曼哈顿距离:像在城市街区走路,只能横着走、竖着走,最后把每一段路加起来。
所以一般可以这样理解:
- 连续数值、低维空间:更常用欧氏距离,因为它更符合“实际远近”的直觉,KNN 里最常见的默认选择就是它。
- 高维稀疏数据、离散特征:更常用曼哈顿距离,因为它按维度分别累计差异,不容易被某一维的极端偏差带得太厉害。
一句话理解:想看“直线有多近”,偏向欧氏距离;想看“每一维总共差多少”,偏向曼哈顿距离。

常见距离的计算方法(公式和例子)
欧氏距离的计算方法
欧氏距离其实就是两点之间的直线距离。在二维平面里,它本质上就是勾股定理。
假设有两个点:$A(x_1, y_1)$ 和 $B(x_2, y_2)$。
先看两个点在两个方向上差多少:
- 横向差值:$x_1 - x_2$
- 纵向差值:$y_1 - y_2$
再把这两个差值当成直角三角形的两条直角边,套用勾股定理,就能得到两点之间的直线距离:
$$ d(A, B) = \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2} $$例如点 $A(1, 2)$ 和点 $B(4, 6)$:
- 横向差值是
3 - 纵向差值是
4
所以距离就是:
$$ \sqrt{3^2 + 4^2} = 5 $$因此,欧氏距离你可以直接理解成:先求“横着差多少、竖着差多少”,再用勾股定理算直线距离。
如果推广到 $n$ 维空间,公式写成:
$$ d(x, y) = \sqrt{\sum_{i=1}^{n}(x_i - y_i)^2} $$在 KNN 里,欧氏距离很常用,但它会受到量纲影响,所以不同特征的取值范围差别很大时,通常要先做归一化或标准化。
曼哈顿距离的计算方法
曼哈顿距离其实很简单,就是看两个点在每个坐标轴上分别差多少,再把这些差值的绝对值加起来。
假设有两个点:$A(x_1, y_1)$ 和 $B(x_2, y_2)$
那么二维情况下:
$$ d(A, B) = |x_1 - x_2| + |y_1 - y_2| $$例如点 $A(1,2)$ 和点 $B(4,6)$:
- x 轴差值:$|1 - 4| = 3$
- y 轴差值:$|2 - 6| = 4$
所以曼哈顿距离就是:
$$ 3 + 4 = 7 $$因此,曼哈顿距离你可以直接理解成:不走直线,只按横着和竖着分别走,最后把路程加起来。
了解切比雪夫距离的计算方法
切比雪夫距离(Chebyshev Distance)可以理解为在所有坐标轴的差值中,只取那个最大的差值作为距离。比如点 $A(1,2)$ 和点 $B(4,6)$,横向差 $3$,纵向差 $4$,因为 $4 > 3$,所以它们的切比雪夫距离就是 $4$。它通常用在只关心最大偏差的场景(比如国王在国际象棋棋盘上走步)。
了解闵可夫斯基距离的计算方法
闵可夫斯基距离(Minkowski Distance)并不是一种新的距离,而是一个通用的距离公式模板。它包含一个参数 $p$:当 $p=1$ 时,它就变成了曼哈顿距离;当 $p=2$ 时,它就变成了欧氏距离;当 $p$ 趋于无穷大时,它就变成了切比雪夫距离。所以它其实是前几种距离的“老大哥”。
特征预处理
数据归一化
数据归一化通过对原始数据进行变换,把数据映射到 [mi, mx] 区间内(默认区间为 $[0, 1]$)。
特点与应用场景:
- 异常值影响:如果数据中出现异常点,会直接影响最大值和最小值,从而导致最终的缩放结果发生明显改变。
- 应用场景:最大值与最小值非常容易受异常点影响,鲁棒性较差,只适合传统精确小数据场景。

归一化公式和手算示例
计算公式
-
第一步:线性缩放到 [0, 1] 区间
$$ X' = \frac{x - min}{max - min} $$(分母为「最大值 - 最小值」,例:max=20、min=3 时,分母为 $20-3$)
-
第二步:映射到目标区间 [mi, mx]
$$ X'' = X' \times (mx - mi) + mi $$
计算示例
原始特征数据
| 特征1 | 特征2 | 特征3 | 特征4 |
|---|---|---|---|
| 90 | 2 | 10 | 40 |
| 60 | 4 | 15 | 45 |
| 75 | 3 | 13 | 46 |
第一步计算过程
| 特征1 | 特征2 | 特征3 | 特征4 |
|---|---|---|---|
| $\frac{90-60}{90-60}$ | $\frac{2-2}{4-2}$ | $\frac{10-10}{15-10}$ | $\frac{40-40}{46-40}$ |
| $\frac{60-60}{90-60}$ | $\frac{4-2}{4-2}$ | $\frac{15-10}{15-10}$ | $\frac{45-40}{46-40}$ |
| $\frac{75-60}{90-60}$ | $\frac{3-2}{4-2}$ | $\frac{13-10}{15-10}$ | $\frac{46-40}{46-40}$ |
归一化结果([0, 1] 区间)
| 特征1 | 特征2 | 特征3 | 特征4 |
|---|---|---|---|
| 1.0 | 0.0 | 0.0 | 0.0 |
| 0.0 | 1.0 | 1.0 | 0.83 |
| 0.5 | 0.5 | 0.6 | 1.0 |
注:上表为第一步计算结果;当目标区间为默认的 [0,1] 时,第二步运算为 $X' \times (1-0) + 0$,结果与第一步一致。
数据归一化 API
sklearn.preprocessing.MinMaxScaler(feature_range=(0,1))fit_transform(X):将特征进行归一化缩放
代码示例:
# 1.导包 此处导入归一化模型
from sklearn.preprocessing import MinMaxScaler
# 2.准备数据(模拟数据)
x_train = [
[90, 2, 10, 40],
[60, 4, 15, 45],
[75, 3, 13, 46]
]
# TODO 3.需求: 在模型训练特征进行规范化数据
# todo 3.1 创建归一化模型
model = MinMaxScaler()
# todo 3.2 规范化数据
new_x_train = model.fit_transform(x_train)
# todo 3.3 打印结果
print(new_x_train)
打印:
[[1. 0. 0. 0. ]
[0. 1. 1. 0.83333333]
[0.5 0.5 0.6 1. ]]
数据标准化
使用居多
数据标准化通过对原始数据进行标准化,转换为均值为0、标准差为1的标准正态分布的数据。
通俗解释:数据标准化到底在做什么 数据标准化,就是给所有特征“统一度量衡”,把它们都拉到同一个标准尺度上:
转换后,每一列特征的平均值 = 0,数据的分散程度(标准差) = 1。 用大白话拆解两步计算 第一步:平移归零
每个数据都减去这一列的平均值。
减完之后:比平均水平高的数变成正数,比平均低的变成负数,整列数据的中心点刚好落在 0 上。 第二步:缩放统一
把上面的结果,再除以这一列的“标准差”(可以理解成数据的“波动幅度/分散程度”)。
除完之后:不管原来的数据是几十、几百还是几万,最终的波动幅度都被统一成 1。 举个生活化的例子 一句话总结
标准化的核心作用:消除不同特征之间的数值量级差异,让模型公平对待每个特征,训练更稳定、收敛更快。
通俗解释标准化
做AI模型时,我们经常会遇到不同特征的数值尺度差很大的情况。
比如预测房价:“房间数量”是 2-5 的个位数,“房屋面积”是 50-200 的两位数,“房屋总价”是几十万到几百万的大数。
数值差这么大,模型会误以为“数值越大的特征越重要”,训练时容易跑偏、收敛慢。
班里两门考试:语文满分 100,分数集中在 60-90;数学满分 150,分数集中在 90-130。
直接拿原始分数对比不公平,标准化就相当于把两门课都换成“相对排名分”:
特点与应用场景:
- 异常值影响:如果出现异常点,由于具有一定数据量,少量的异常点对于平均值的影响并不大,因此鲁棒性较强。
- 应用场景:适合现代嘈杂大数据场景。(实际开发中以后基本就是用它了)
计算公式
mean:特征的平均值,可以理解成这一列数据的“中心位置”。标准化时先减去它,就是先把整列数据平移到以 0 为中心,方便比较每个样本离平均水平有多远。
sklearn.preprocessing.StandardScaler()fit_transform(X):将特征进行标准化缩放
代码示例:
# 1.导包 此处导入归一化模型
from sklearn.preprocessing import StandardScaler
# 2.准备数据(模拟数据)
x_train = [
[90, 2, 10, 40],
[60, 4, 15, 45],
[75, 3, 13, 46]
]
# TODO 3.需求: 在模型训练特征进行规范化数据
# todo 3.1 创建归一化模型
model = StandardScaler()
# todo 3.2 规范化数据
new_x_train = model.fit_transform(x_train)
# todo 3.3 打印结果
print(new_x_train)
打印:
[[ 1.22474487 -1.22474487 -1.29777137 -1.3970014 ]
[-1.22474487 1.22474487 1.13554995 0.50800051]
[ 0. 0. 0.16222142 0.88900089]]
数学扩展
$X'$
在数学和机器学习的公式中,$X'$(读作 “X prime”)右侧的单引号 ' 通常表示 “变换后的变量” 或 “新变量”。
具体含义如下:
-
区分原始数据与处理后数据
- $x$(小写):代表原始数据(还没处理过的数值)。
- $X'$(带单引号):代表经过第一步计算后得到的中间结果(也就是归一化到 [0, 1] 之间的数值)。
- $X''$(带双引号):代表经过第二步计算后得到的最终结果(映射到任意区间 [mi, mx] 的数值)。
-
为什么不用新字母(比如 Y)? 使用 $X'$ 而不是 $Y$,是为了强调这个新变量仍然属于同一个特征 X,只是它的数值状态发生了变化(被缩放或平移了)。这是一种表示“衍生关系”的常用记号。
公式解读 这两个公式是 数据归一化(Min-Max Normalization) 的核心计算步骤,用于将原始特征值线性映射到指定区间(如 [0,1])。x 右侧的点(·)在数学中表示乘法运算,即“乘以”的意思。
第一个公式:
$$ X' = \frac{x - \min}{\max - \min} $$- 作用:将原始数据 x 缩放到 [0, 1] 区间。
- 含义:
- $x$:原始数据点;
- $min$:该特征列的最小值;
- $max$:该特征列的最大值;
- $X'$:归一化后的中间值,范围在 [0, 1]。
第二个公式:
$$X'' = X' * (mx - mi) + mi$$- 作用:将已归一化到 [0,1] 的数据,进一步映射到任意目标区间 [mi, mx]。
- 含义:
- $X'$:第一步得到的 [0,1] 区间值;
- $mx$:目标区间的最大值;
- $mi$:目标区间的最小值;
- $X''$:最终映射到 [mi, mx] 的结果。