问题与目标

KNN 的决策来自查询点周围的样本。标准化让两个特征在距离中使用可比尺度。
KNN 不显式学习公式,而是根据附近样本投票。它直观但对量纲、噪声和数据规模敏感。
完成标准:比较缩放前后的 KNN,理解 k 太小或太大分别带来的风险。
核心概念
预测步骤是计算距离、选最近的 k 个样本、按邻居标签投票。较小 k 容易受噪声影响,较大 k 会抹平局部结构。欧氏距离要求各特征尺度可比较。
KNN 训练成本低,但预测要保存并搜索训练样本,数据量增大时延迟和内存会增长。
手工计算一次邻居
from math import dist
query = (3.0, 4.0)
samples = [((1.0, 4.0), "A"), ((5.0, 5.0), "B"), ((2.0, 1.0), "A")]
ranked = sorted((dist(query, point), label) for point, label in samples)
print(ranked)
欧氏距离是各维差值平方和开方;曼哈顿距离是绝对差之和。距离公式表达了“相似”的假设,选错特征或量纲时,最近邻没有业务意义。
k 控制局部程度
k=1 的边界非常曲折,容易记住噪声;k 很大时,多数类会覆盖局部少数结构。二分类通常选择奇数减少平票,但多分类仍可能平票。可通过交叉验证比较候选值:
from sklearn.model_selection import cross_val_score
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
for neighbors in (1, 3, 7, 15, 31):
candidate = make_pipeline(
StandardScaler(), KNeighborsClassifier(n_neighbors=neighbors)
)
score = cross_val_score(candidate, X_train, y_train, cv=5).mean()
print(neighbors, round(score, 3))
候选范围应小于每折训练样本数。选中 k 后再用未参与选择的测试集评估。
可运行实现
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
rng = np.random.default_rng(12)
size = rng.normal(40, 12, 500)
queue = rng.normal(5, 2, 500)
noise_feature = rng.normal(20_000, 4_000, 500)
y = ((size + queue * 4 + rng.normal(0, 8, 500)) > 62).astype(int)
X = np.column_stack([size, queue, noise_feature])
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, stratify=y, random_state=42,
)
plain = KNeighborsClassifier(n_neighbors=7).fit(X_train, y_train)
scaled = make_pipeline(
StandardScaler(), KNeighborsClassifier(n_neighbors=7)
).fit(X_train, y_train)
print("plain:", round(accuracy_score(y_test, plain.predict(X_test)), 3))
print("scaled:", round(accuracy_score(y_test, scaled.predict(X_test)), 3))
第三个无关特征量纲巨大,会支配未缩放距离。输入输出展示缩放不是通用装饰,而是距离模型的必要步骤。
距离加权与高维问题
weights="distance" 让近邻票权更高,可能改善边界附近预测,也可能放大异常点。维度升高时样本之间的距离差异趋于缩小,即“维度灾难”;增加无关特征会让邻居关系失真。特征选择、PCA 或换用其他模型通常比无限调 k 更有效。
不能只看交叉验证均值
候选 k 的分数很接近时,应同时查看各折波动。均值高 0.002 但方差明显更大,未必值得选择:
from sklearn.model_selection import cross_validate
for neighbors in (3, 7, 15, 31):
candidate = make_pipeline(
StandardScaler(),
KNeighborsClassifier(n_neighbors=neighbors, weights="distance"),
)
result = cross_validate(candidate, X_train, y_train, cv=5, scoring="f1")
print(
neighbors,
"mean=", round(result["test_score"].mean(), 3),
"std=", round(result["test_score"].std(), 3),
)
应使用与任务相符的指标。类别不平衡时,默认准确率可能掩盖少数类没有被识别。
评估预测成本
KNN 的 fit 主要是保存数据,真正工作发生在预测时。在不依赖特定硬件的前提下,可用下面的方式记录同一批查询的相对耗时:
from time import perf_counter
for query_count in (1, 50, 200):
started = perf_counter()
scaled.predict(X_test[:query_count])
elapsed_ms = (perf_counter() - started) * 1000
print(query_count, round(elapsed_ms, 3), "ms")
这不是跨机器的性能基准,但能提醒一个重要边界:数据量不断增长、请求延迟又受限时,KNN 可能不是合适的服务化模型。
进一步验证
- 分别用未缩放、标准化和删除噪声特征三种方案训练 KNN。
- 将准确率换成 F1,记录最佳
k是否变化。 - 比较单条与批量预测耗时,说明该模型的部署边界。
常见问题与排查
- 在全数据上标准化:统计量泄漏,应放入 Pipeline。
- 只试一个
k:在交叉验证中比较候选值。 - 类别数量不均衡:普通投票偏向多数类,可评估距离加权与重采样。
- 高维效果下降:距离趋于相似,应做特征选择或降维。
小结
KNN 的核心假设是相近样本具有相近标签。尺度、邻居数、维度和预测成本共同决定它是否适用。
License: CC BY-NC 4.0
Updated 2 hours ago
Was this article helpful? Give it a like.
0 comments


