KNN算法超详细讲解(附Python完整实现+鸢尾花分类实战)
KNN(K-近邻,K-Nearest Neighbors)是机器学习中最经典的入门算法,兼具分类和回归能力,核心逻辑简单到无需复杂的数学推导,属于基于实例的懒惰学习(Lazy Learning) 算法——无需提前训练模型,直接用全部训练数据完成预测。
它的思想源于生活中的“物以类聚,人以群分”,通过寻找新样本的“邻居”来判断其类别/预测其数值,是本科生和研究生入门机器学习的必学算法,也是理解“距离度量”“模型泛化”等核心概念的基础。
本文将从通俗原理、核心理论、完整算法流程、Python实战实现、优缺点与算法对比五个维度展开,内容通俗易懂,公式做简化拆解,附带可直接运行的鸢尾花分类实战代码和可视化效果,适配本科课程学习和研究生入门实践。
一、什么是KNN算法?(通俗理解)
KNN的核心逻辑可以用一句话概括:“新样本的类别/数值,由它周围最近的K个训练样本决定”,全程无需训练模型,仅通过计算距离和少数服从多数/取平均完成预测,用一个水果分类的例子就能轻松理解:
已知3个水果的特征(大小+颜色)和类别:
现在来一个新水果:中等大小+红色,需要判断它是苹果还是橙子。
这个过程就是KNN的全部逻辑,没有复杂的模型训练,没有繁琐的参数求解,核心只有3个关键点:
KNN的核心特点:懒惰学习(Lazy Learning)
KNN被称为懒惰学习算法,与线性回归、SVM、决策树等急切学习(Eager Learning) 算法有本质区别:
- 急切学习:训练阶段会从训练数据中学习出一个模型公式/规则(如线性回归的y=wx+by=wx+by=wx+b),训练完成后丢弃训练数据,仅用模型做预测;
- 懒惰学习:无训练阶段,不学习任何模型公式,直接保存全部训练数据;预测阶段才用训练数据计算距离、完成预测,训练数据不可或缺。
简单来说,KNN的“懒惰”体现在把所有计算工作都放到了预测阶段。
二、KNN的核心理论基础
KNN的理论基础围绕距离度量、K值选择、预测规则三个核心展开,数学公式简单易懂,无复杂的推导过程,是入门机器学习的绝佳选择。
2.1 距离度量:如何判断样本的“远近”?
距离是KNN的核心,距离越近,样本的相似性越高,常用的距离度量有3种,其中欧几里得距离是最常用的一种。
假设存在两个nnn维特征样本:x=(x1,x2,…,xn)x=(x_1,x_2,…,x_n)x=(x1,x2,…,xn),y=(y1,y2,…,yn)y=(y_1,y_2,…,y_n)y=(y1,y2,…,yn),以下是3种距离的计算公式:
(1)欧几里得距离(Euclidean Distance)
即两点之间的直线距离,是最符合人类直觉的距离度量,也是KNN的默认选择,公式为:
d(x,y)=∑i=1n(xi−yi)2d(x,y) = \\sqrt{\\sum_{i=1}^{n}(x_i – y_i)^2}d(x,y)=i=1∑n(xi−yi)2
二维示例:样本A(1,2),样本B(3,4),欧几里得距离d=(1−3)2+(2−4)2=8≈2.828d=\\sqrt{(1-3)^2+(2-4)^2}=\\sqrt{8}≈2.828d=(1−3)2+(2−4)2=8≈2.828。
(2)曼哈顿距离(Manhattan Distance)
也叫城市街区距离,模拟在城市中沿街道行走的距离(只能走直线和直角),公式为:
d(x,y)=∑i=1n∣xi−yi∣d(x,y) = \\sum_{i=1}^{n}|x_i – y_i|d(x,y)=i=1∑n∣xi−yi∣
二维示例:样本A(1,2),样本B(3,4),曼哈顿距离d=∣1−3∣+∣2−4∣=4d=|1-3|+|2-4|=4d=∣1−3∣+∣2−4∣=4。
(3)闵可夫斯基距离(Minkowski Distance)
是欧几里得距离和曼哈顿距离的通用形式,通过参数ppp控制距离类型,公式为:
d(x,y)=(∑i=1n∣xi−yi∣p)1pd(x,y) = \\left( \\sum_{i=1}^{n}|x_i – y_i|^p \\right)^{\\frac{1}{p}}d(x,y)=(i=1∑n∣xi−yi∣p)p1
- 当p=2p=2p=2时,闵可夫斯基距离=欧几里得距离;
- 当p=1p=1p=1时,闵可夫斯基距离=曼哈顿距离。
2.2 K值选择:选几个邻居最合适?
KKK是KNN唯一的核心超参数,代表选择最近的K个训练样本作为邻居,K的取值直接决定模型的性能,无固定值,需通过实验选择。
K的取值规律
- K太小(如K=1):模型过于敏感,易受噪声/异常值影响,导致过拟合(对训练集预测极准,对测试集泛化能力差);
- 例:K=1时,若新样本的最近邻是一个噪声点,直接会被误分类。
- K太大(如K=样本总数):模型过于粗糙,忽略样本的局部特征,导致欠拟合(对训练集和测试集的预测准确率都低);
- 例:K=样本总数时,新样本的类别直接是训练集中的多数类,失去分类意义。
- K通常取奇数:分类任务中取奇数(如1、3、5、7),避免出现平票(如K=2时,1个苹果、1个橙子,无法判断)。
K的选择方法
实战中常用交叉验证(Cross Validation) 选择最优K值:
2.3 预测规则:分类与回归的不同逻辑
KNN既可以做分类任务(预测离散类别,如苹果/橙子、鸢尾花的品种),也可以做回归任务(预测连续数值,如房价、温度),二者的预测规则不同,核心分别是多数投票和取平均值。
(1)分类任务:多数投票法
设新样本xxx的K个最近邻的类别为y1,y2,…,yKy_1,y_2,…,y_Ky1,y2,…,yK,则xxx的预测类别y^\\hat{y}y^为K个邻居中出现次数最多的类别(众数),公式为:
y^=mode(y1,y2,…,yK)\\hat{y} = mode(y_1,y_2,…,y_K)y^=mode(y1,y2,…,yK)
例:K=3,邻居类别为[苹果,苹果,橙子],众数是苹果,预测类别为苹果。
(2)回归任务:平均值法
设新样本xxx的K个最近邻的数值为y1,y2,…,yKy_1,y_2,…,y_Ky1,y2,…,yK,则xxx的预测值y^\\hat{y}y^为K个邻居数值的算术平均值,公式为:
y^=1K∑i=1Kyi\\hat{y} = \\frac{1}{K}\\sum_{i=1}^{K}y_iy^=K1i=1∑Kyi
例:K=3,邻居房价为[100万,120万,110万],预测房价=(100+120+110)/3=110万。
三、KNN算法的完整流程
KNN的算法流程极其简单,无训练阶段,仅包含数据准备和预测两个阶段,分类和回归任务的流程基本一致,仅最后一步的预测规则不同。
3.1 通用算法流程(分类+回归)
阶段1:数据准备(仅执行1次)
阶段2:新样本预测(对每个新样本执行1次)
- 分类任务:对K个最近邻的类别做多数投票,得到新样本的预测类别;
- 回归任务:对K个最近邻的数值做算术平均,得到新样本的预测数值;
3.2 简单案例推导(二维分类任务)
用一个二维平面的简单案例,完整推导KNN的分类过程,帮助理解核心步骤:
已知训练样本:
- 点A(1,2) → 类别0
- 点B(2,3) → 类别0
- 点C(3,3) → 类别1
- 点D(6,5) → 类别1
新样本:x(3,2)x(3,2)x(3,2),选择K=3,判断其类别。
步骤1:计算新样本与所有训练样本的欧几里得距离
- d(A,x)=(1−3)2+(2−2)2=4=2d(A,x)=\\sqrt{(1-3)^2+(2-2)^2}=\\sqrt{4}=2d(A,x)=(1−3)2+(2−2)2=4=2
- d(B,x)=(2−3)2+(3−2)2=2≈1.414d(B,x)=\\sqrt{(2-3)^2+(3-2)^2}=\\sqrt{2}≈1.414d(B,x)=(2−3)2+(3−2)2=2≈1.414
- d(C,x)=(3−3)2+(3−2)2=1=1d(C,x)=\\sqrt{(3-3)^2+(3-2)^2}=\\sqrt{1}=1d(C,x)=(3−3)2+(3−2)2=1=1
- d(D,x)=(6−3)2+(5−2)2=18≈4.242d(D,x)=\\sqrt{(6-3)^2+(5-2)^2}=\\sqrt{18}≈4.242d(D,x)=(6−3)2+(5−2)2=18≈4.242
步骤2:寻找K=3个最近邻
距离升序排序:d(C,x)<d(B,x)<d(A,x)<d(D,x)d(C,x) < d(B,x) < d(A,x) < d(D,x)d(C,x)<d(B,x)<d(A,x)<d(D,x),最近3个邻居为点C、点B、点A。
步骤3:多数投票预测类别
3个邻居的类别:点C(1)、点B(0)、点A(0),类别0出现2次(多数),因此新样本xxx的预测类别为0。
四、KNN算法的Python完整实现(鸢尾花分类实战)
本次实战基于经典的鸢尾花(Iris)数据集,使用Python+sklearn实现KNN分类,包含数据加载与可视化、数据预处理、最优K值选择、模型训练与评估、混淆矩阵可视化全流程,代码可直接复制运行,注释详细,适配Python3.7+、sklearn1.0+。
4.1 实战目标
4.2 完整代码与注释
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 加载数据集
from sklearn import datasets
# 数据划分、交叉验证
from sklearn.model_selection import train_test_split, cross_val_score
# 特征标准化
from sklearn.preprocessing import StandardScaler
# KNN模型
from sklearn.neighbors import KNeighborsClassifier
# 模型评估指标
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# ===================== 1. 加载数据集并可视化 =====================
# 加载鸢尾花数据集
iris = datasets.load_iris()
X = iris.data # 特征矩阵:4个特征,150个样本
y = iris.target # 类别标签:0(Setosa)、1(Versicolour)、2(Virginica)
feature_names = iris.feature_names # 特征名
target_names = iris.target_names # 类别名
# 转换为DataFrame,方便可视化
df = pd.DataFrame(np.c_[X, y], columns=feature_names + ['target'])
df['target'] = df['target'].map({0:target_names[0], 1:target_names[1], 2:target_names[2]})
# 特征两两组合可视化,展示数据分布
sns.pairplot(df, hue='target', markers=["o", "s", "D"])
plt.title('鸢尾花数据集特征两两组合分布', y=1.05)
plt.show()
# ===================== 2. 数据预处理:划分+标准化 =====================
# 划分训练集(70%)和测试集(30%),random_state保证结果可复现
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y # stratify=y:分层抽样,保证训练/测试集类别分布一致
)
# 特征标准化:KNN必做步骤,消除量纲影响
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train) # 训练集拟合+转换
X_test_scaled = scaler.transform(X_test) # 测试集仅转换,避免数据泄露
# ===================== 3. 交叉验证选择最优K值 =====================
# 设定K的取值范围:1~30
k_range = range(1, 31)
k_scores = [] # 存储每个K值的交叉验证平均准确率
# 遍历每个K值,计算10折交叉验证的准确率
for k in k_range:
knn = KNeighborsClassifier(n_neighbors=k, metric='euclidean') # 欧几里得距离
scores = cross_val_score(knn, X_train_scaled, y_train, cv=10, scoring='accuracy')
k_scores.append(scores.mean())
# 可视化K值与准确率的关系
plt.figure(figsize=(10, 6))
plt.plot(k_range, k_scores, color='#FF6B6B', linewidth=2, marker='o', markersize=6)
plt.xlabel('K值(邻居数量)', fontsize=14)
plt.ylabel('10折交叉验证平均准确率', fontsize=14)
plt.title('KNN最优K值选择', fontsize=16)
plt.xticks(k_range)
plt.grid(linestyle='–', alpha=0.7)
plt.show()
# 选择最优K值(准确率最高的K值,若有多个选最小的)
best_k = k_range[np.argmax(k_scores)]
print(f"KNN最优K值:{best_k}")
print(f"最优K值对应的交叉验证准确率:{max(k_scores):.4f}")
# ===================== 4. 训练最优K值的KNN模型并评估 =====================
# 初始化并训练KNN模型
knn_best = KNeighborsClassifier(n_neighbors=best_k, metric='euclidean')
knn_best.fit(X_train_scaled, y_train)
# 对测试集进行预测
y_pred = knn_best.predict(X_test_scaled)
# 模型评估
print("\\n===================== KNN模型测试集评估结果 =====================")
print(f"测试集准确率:{accuracy_score(y_test, y_pred):.4f}")
print("\\n分类报告(精确率/召回率/F1分数):")
print(classification_report(y_test, y_pred, target_names=target_names))
# ===================== 5. 混淆矩阵可视化:直观展示分类效果 =====================
# 计算混淆矩阵
conf_mat = confusion_matrix(y_test, y_pred)
# 可视化混淆矩阵
plt.figure(figsize=(8, 6))
sns.heatmap(
conf_mat, annot=True, fmt='d', cmap='Blues',
xticklabels=target_names,
yticklabels=target_names
)
plt.xlabel('预测类别', fontsize=14)
plt.ylabel('真实类别', fontsize=14)
plt.title('KNN模型分类混淆矩阵', fontsize=16)
plt.show()
# ===================== 6. 新样本预测 =====================
# 构造一个新样本(花萼长5.0,花萼宽3.5,花瓣长1.5,花瓣宽0.2)
new_sample = np.array([[5.0, 3.5, 1.5, 0.2]])
# 新样本标准化(必须用训练集的scaler)
new_sample_scaled = scaler.transform(new_sample)
# 预测类别
new_pred = knn_best.predict(new_sample_scaled)
new_pred_name = target_names[new_pred[0]]
print(f"\\n新样本的预测鸢尾花品种:{new_pred_name}")
4.3 运行结果说明
4.4 KNN核心参数说明(sklearn)
sklearn中KNeighborsClassifier(分类)和KNeighborsRegressor(回归)的核心参数一致,常用参数如下:
- n_neighbors:K值,默认5,需通过交叉验证选择最优值;
- metric:距离度量,默认euclidean(欧几里得距离),可选manhattan(曼哈顿距离)、minkowski(闵可夫斯基距离);
- weights:邻居权重,默认uniform(等权重),可选distance(距离越近,权重越大),能提升模型抗噪声能力;
- p:闵可夫斯基距离的参数,p=1为曼哈顿距离,p=2为欧几里得距离。
五、KNN算法的优缺点
KNN是入门级算法,核心逻辑简单、易实现,但“懒惰学习”的特性也导致其存在明显的局限性,优缺点总结如下,适配本科/研究生课程考点和实战选型参考。
5.1 核心优点
5.2 核心缺点
六、KNN与相似算法的对比
为了让大家在实战中精准选算法,以下从核心思想、优缺点、训练/预测速度、适合数据类型四个维度,对比KNN与决策树、SVM两种经典算法的差异,表格简洁明了,适配本科课程考核和研究生实战选型:
| 核心思想 | 基于距离的近邻投票/平均,懒惰学习 | 基于特征划分的树形规则,急切学习 | 基于最大间隔的超平面分类,急切学习 |
| 核心优点 | 简单直观、无训练、支持分类/回归、非线性友好 | 可解释性强、无需标准化、适配离散/连续特征 | 高维数据效果好、抗噪声、非线性拟合能力强 |
| 核心缺点 | 预测慢、内存大、对量纲/噪声/高维数据敏感 | 易过拟合、对噪声敏感、贪心策略局部最优 | 调参复杂、大规模数据训练慢、可解释性差 |
| 训练速度 | 无训练阶段,速度为∞ | 快,递归划分数据即可 | 慢,需求解二次规划问题 |
| 预测速度 | 慢,随训练集规模增大而降低 | 快,沿树形规则遍历即可 | 中等,仅依赖支持向量 |
| 特征标准化 | 必须做,否则距离计算偏差 | 无需做,对量纲不敏感 | 建议做,提升模型效果 |
| 适合数据类型 | 小样本、低维、特征尺度一致、分布简单的数据集 | 中小样本、结构化数据、离散/连续特征混合 | 中小样本、高维数据、非线性分类任务 |
| 典型应用 | 简单分类/回归、基准模型测试、推荐系统 | 金融风控、决策支持、规则引擎 | 文本分类、图像识别、高维数据分类 |
七、KNN的适用场景与实战建议
KNN的“懒惰学习”特性决定了它适合小样本、低维的简单任务,工业级大规模场景中通常被其他算法替代,但作为基准模型仍有重要价值,以下是实战中的选型建议和优化技巧。
7.1 优先选择KNN的场景
7.2 考虑其他算法的场景
7.3 KNN的实战优化技巧
若实战中确需使用KNN,可通过以下技巧提升模型性能和运行效率:
八、总结
拓展学习:掌握KNN后,可依次学习决策树(ID3/C4.5/CART)、逻辑回归、SVM等经典算法,对比理解“懒惰学习”和“急切学习”的差异;再过渡到集成学习(Bagging/Boosting),掌握随机森林、XGBoost等工业级算法,形成完整的机器学习知识体系。





